Home
Dart
OOP
Flutter
Contact

AI Integration in Flutter

Hands-on guide to building real AI-powered features in Flutter applications.

📋 Prerequisites Read Introduction to AI in Flutter first. You should have a working Flutter development environment.

Text Recognition (OCR)

Optical Character Recognition lets you extract text from images — perfect for scanning documents, receipts, or handwritten notes.

Setup

pubspec.yaml
dependencies:
  google_mlkit_text_recognition: ^0.13.0
  image_picker: ^1.0.7

Implementation

ocr_service.dart
import 'dart:io';
import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart';

class OcrService {
  final _textRecognizer = TextRecognizer(
    script: TextRecognitionScript.latin,
  );

  Future<String> recognizeText(File imageFile) async {
    final inputImage = InputImage.fromFile(imageFile);
    final recognized = await _textRecognizer.processImage(inputImage);

    if (recognized.text.isEmpty) {
      return 'No text found in image.';
    }

    // Process individual blocks
    for (final block in recognized.blocks) {
      print('Block: ${block.text}');
      for (final line in block.lines) {
        print('  Line: ${line.text}');
      }
    }

    return recognized.text;
  }

  void dispose() => _textRecognizer.close();
}

Widget Usage

scanner_screen.dart
class ScannerScreen extends StatefulWidget {
  const ScannerScreen({super.key});

  @override
  State<ScannerScreen> createState() => _ScannerScreenState();
}

class _ScannerScreenState extends State<ScannerScreen> {
  final _ocrService = OcrService();
  String _result = '';
  bool _isProcessing = false;

  Future<void> _pickAndScan() async {
    final picker = ImagePicker();
    final image = await picker.pickImage(source: ImageSource.camera);
    if (image == null) return;

    setState(() => _isProcessing = true);
    final text = await _ocrService.recognizeText(File(image.path));
    setState(() {
      _result = text;
      _isProcessing = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Text Scanner')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          children: [
            ElevatedButton.icon(
              onPressed: _isProcessing ? null : _pickAndScan,
              icon: const Icon(Icons.camera_alt),
              label: Text(_isProcessing ? 'Scanning...' : 'Scan Text'),
            ),
            const SizedBox(height: 16),
            Expanded(
              child: SelectableText(_result, style: const TextStyle(fontSize: 16)),
            ),
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    _ocrService.dispose();
    super.dispose();
  }
}

Barcode Scanning

Scan QR codes, EAN-13, Code 128, and 20+ other barcode formats.

barcode_service.dart
import 'dart:io';
import 'package:google_mlkit_barcode_scanning/google_mlkit_barcode_scanning.dart';

class BarcodeService {
  final _barcodeScanner = BarcodeScanner(
    formats: [BarcodeFormat.qrCode, BarcodeFormat.ean13],
  );

  Future<Barcode?$> scanBarcode(File imageFile) async {
    final inputImage = InputImage.fromFile(imageFile);
    final barcodes = await _barcodeScanner.processImage(inputImage);

    if (barcodes.isEmpty) return null;

    final barcode = barcodes.first;
    print('Type: ${barcode.type}');
    print('Value: ${barcode.rawValue}');
    return barcode;
  }

  void dispose() => _barcodeScanner.close();
}

Image Labeling

Identify objects, scenes, and concepts in images automatically.

label_service.dart
import 'dart:io';
import 'package:google_mlkit_image_labeling/google_mlkit_image_labeling.dart';

class ImageLabelService {
  final _labeler = ImageLabeler(
    options: ImageLabelerOptions(confidenceThreshold: 0.5),
  );

  Future<List<ImageLabel>> labelImage(File imageFile) async {
    final inputImage = InputImage.fromFile(imageFile);
    final labels = await _labeler.processImage(inputImage);

    for (final label in labels) {
      print('${label.label} (${(label.confidence * 100).toStringAsFixed(1)}%)');
    }
    return labels;
  }

  void dispose() => _labeler.close();
}

Custom TFLite Model

Run your own trained TensorFlow Lite models for specialized tasks.

Project Setup

  1. Place your .tflite model in assets/model/
  2. Add the model to pubspec.yaml
  3. Load and run the model
pubspec.yaml
flutter:
  assets:
    - assets/model/my_model.tflite
    - assets/model/labels.txt

dependencies:
  tflite_flutter: ^0.10.4
custom_classifier.dart
import 'dart:io';
import 'dart:typed_data';
import 'package:tflite_flutter/tflite_flutter.dart';
import 'package:image/image.dart' as img;

class CustomClassifier {
  late Interpreter _interpreter;
  List<String> _labels = [];

  Future<void> loadModel() async {
    _interpreter = await Interpreter.fromAsset(
      'assets/model/my_model.tflite',
    );

    // Load labels
    final labelData = await File('assets/model/labels.txt').readAsLines();
    _labels = labelData;
  }

  Future<Map<String, double>> classifyImage(File imageFile) async {
    // Resize image to model input size (e.g., 224x224)
    final image = img.decodeImage(await imageFile.readAsBytes())!;
    final resized = img.copyResize(image, width: 224, height: 224);

    // Convert to input tensor
    var input = List.generate(
      224,
      (y) => List.generate(
        224,
        (x) => [resized.getPixel(x, y).r / 255.0, resized.getPixel(x, y).g / 255.0, resized.getPixel(x, y).b / 255.0],
      ),
    );

    // Run inference
    var output = List<List<double>>.filled(1, List.filled(_labels.length, 0.0));
    _interpreter.run([input], output);

    // Map results
    final results = <String, double>{};
    for (var i = 0; i < _labels.length; i++) {
      results[_labels[i]] = output[0][i];
    }

    return results;
  }

  void dispose() => _interpreter.close();
}

Gemini API Integration

Google's Gemini API provides powerful generative AI capabilities for text, code, and multimodal content.

Setup

pubspec.yaml
dependencies:
  google_generative_ai: ^0.4.3
gemini_service.dart
import 'package:google_generative_ai/google_generative_ai.dart';

class GeminiService {
  late GenerativeModel _model;

  GeminiService() {
    _model = GenerativeModel(
      model: 'gemini-1.5-flash',
      apiKey: 'YOUR_API_KEY',
    );
  }

  Future<String> generateText(String prompt) async {
    final response = await _model.generateContent([Content.text(prompt)]);
    return response.text ?? 'No response generated.';
  }

  Future<String> summarizeText(String text) async {
    final response = await _model.generateContent([
      Content.text('Summarize the following text concisely:\\n\\n$text'),
    ]);
    return response.text ?? 'No summary generated.';
  }

  Future<String> chatWithContext(String message, List<Content> history) async {
    final chat = _model.startChat(history: history);
    final response = await chat.sendMessage(Content.text(message));
    return response.text ?? 'No response.';
  }
}
⚠️ API Key Security Never hardcode API keys in production apps. Use environment variables, --dart-define, or a backend proxy to keep keys secure.

Permissions Setup

ML features often require camera and storage permissions. Here's the setup for both platforms:

Android

AndroidManifest.xml
<uses-permission android:name="android.permission.CAMERA"/>
<uses-feature android:name="android.hardware.camera" android:required="false"/>

iOS

Info.plist
<key>NSCameraUsageDescription</key>
<string>This app needs camera access to scan text and barcodes.</string>
💡 Tip Use the permission_handler package to request runtime permissions in a cross-platform way.

Full Example: AI-Powered Scanner App

Here's a complete example combining OCR, barcode scanning, and image labeling into a single scanner screen:

ai_scanner_screen.dart
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'ocr_service.dart';
import 'barcode_service.dart';
import 'label_service.dart';

class AiScannerScreen extends StatefulWidget {
  const AiScannerScreen({super.key});

  @override
  State<AiScannerScreen> createState() => _AiScannerScreenState();
}

class _AiScannerScreenState extends State<AiScannerScreen> {
  final _ocr = OcrService();
  final _barcode = BarcodeService();
  final _labeling = ImageLabelService();
  final _picker = ImagePicker();

  String _result = '';
  bool _loading = false;

  Future<void> _analyze(ImageSource source) async {
    final file = await _picker.pickImage(source: source);
    if (file == null) return;

    setState(() => _loading = true);
    final imageFile = File(file.path);

    // Run all three analyses in parallel
    final results = await Future.wait([
      _ocr.recognizeText(imageFile),
      _barcode.scanBarcode(imageFile),
      _labeling.labelImage(imageFile),
    ]);

    final text = results[0] as String;
    final barcode = results[1] as Barcode?;
    final labels = results[2] as List;

    setState(() {
      _result = '📝 Text:\\n$text\\n\\n'
        '📊 Barcode: ${barcode?.rawValue ?? "None"}\\n\\n'
        '🏷️ Labels: ${labels.map((l) => l.label).join(", ")}';
      _loading = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('AI Scanner')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          children: [
            Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                ElevatedButton.icon(
                  onPressed: () => _analyze(ImageSource.camera),
                  icon: const Icon(Icons.camera_alt),
                  label: const Text('Camera'),
                ),
                ElevatedButton.icon(
                  onPressed: () => _analyze(ImageSource.gallery),
                  icon: const Icon(Icons.photo_library),
                  label: const Text('Gallery'),
                ),
              ],
            ),
            const SizedBox(height: 16),
            if (_loading) const CircularProgressIndicator(),
            if (!_loading && _result.isNotEmpty)
              Expanded(child: SelectableText(_result)),
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    _ocr.dispose();
    _barcode.dispose();
    _labeling.dispose();
    super.dispose();
  }
}

Summary

  • Text Recognition: Use google_mlkit_text_recognition for OCR
  • Barcode Scanning: Use google_mlkit_barcode_scanning for QR/barcodes
  • Image Labeling: Use google_mlkit_image_labeling for object detection
  • Custom Models: Use tflite_flutter for specialized ML tasks
  • Generative AI: Use google_generative_ai for Gemini API integration
  • Always dispose ML resources to prevent memory leaks
🚀 Next Step Explore Flutter Best Practices to learn about performance optimization and clean architecture patterns.
K
Kesavaraja Murugesan
Flutter Developer & Educator

Flutter developer with 3 years of experience building production-grade mobile applications. Passionate about teaching clean architecture patterns and helping developers write maintainable, scalable code.

← AI Introduction Next: Best Practices →