No history yet

OCR Integration Details

Advanced OCR with ML Kit

Once you have an image of a bill, the next step is to extract the text. We'll use the google_mlkit_text_recognition package, which provides a powerful and accurate text recognizer. The core of this package is the TextRecognizer class. It's a heavy object that consumes significant resources, so you can't just create new instances whenever you need one. Proper lifecycle management is key to building a performant app and avoiding memory leaks.

The best practice is to create a single TextRecognizer instance when your scanning feature starts and release it when it's no longer needed. A dedicated service class, let's call it ImageProcessor, is a clean way to manage this. This class will be responsible for creating, providing, and closing the recognizer instance, ensuring it's handled correctly throughout your app's lifecycle.

import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart';

class ImageProcessor {
  // Private constructor to prevent direct instantiation.
  ImageProcessor._();

  // Singleton instance of the TextRecognizer.
  static final TextRecognizer _textRecognizer = TextRecognizer();

  // A flag to track if the recognizer is busy.
  static bool _isBusy = false;

  static Future<RecognizedText?> processImage(InputImage inputImage) async {
    if (_isBusy) return null;
    _isBusy = true;

    try {
      final recognizedText = await _textRecognizer.processImage(inputImage);
      _isBusy = false;
      return recognizedText;
    } catch (e) {
      // Handle any errors during processing.
      print('Error processing image: $e');
      _isBusy = false;
      return null;
    }
  }

  // Method to close the recognizer when it's no longer needed.
  static void close() {
    if (!_isBusy) {
      _textRecognizer.close();
    }
  }
}

From Image File to Input

The TextRecognizer doesn't work with raw image files directly. It requires the image data to be in a specific format, represented by the InputImage class. Whether you get an image from the device's camera or the photo gallery, you'll have a file path. The ML Kit package makes the conversion simple.

Using the InputImage.fromFilePath() constructor, you can easily create the required object from the path of the selected image. This method handles the low-level work of reading the file and preparing it for the recognition model.

import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart';
import 'package:image_picker/image_picker.dart'; // Example image picker

// Function to get an image and prepare it for ML Kit.
Future<InputImage?> prepareInputImage() async {
  final picker = ImagePicker();
  // Pick an image from the gallery.
  final XFile? imageFile = await picker.pickImage(source: ImageSource.gallery);

  if (imageFile != null) {
    // Create an InputImage from the file path.
    final inputImage = InputImage.fromFilePath(imageFile.path);
    return inputImage;
  } else {
    // User canceled the picker.
    return null;
  }
}

Processing and Extracting Text

With the TextRecognizer initialized and the InputImage ready, the final step is to process the image. Calling _textRecognizer.processImage(inputImage) starts the magic. The model analyzes the image for text, handling common issues like slightly skewed angles and varied lighting conditions automatically.

The result is a RecognizedText object. This isn't just a single string of text. It's a structured tree of data that reflects the layout of the original document. The hierarchy is organized into three levels: s, TextLines, and TextElements (usually individual words or numbers). This structure is essential for accurately parsing a bill, where the location and grouping of text determine its meaning.

This nested structure lets you traverse the recognized text in a logical way. You can loop through each block, then each line within that block, and finally each word. This granular control is what allows you to build sophisticated parsing logic to find and interpret the exact information you need from the bill.

void processMyBill(RecognizedText recognizedText) {
  // A full string representation for debugging or simple display.
  String fullText = recognizedText.text;
  print('Full recognized text: $fullText');

  // Iterate through the structured data.
  for (TextBlock block in recognizedText.blocks) {
    print('--- Found a TextBlock ---');
    print('Block text: ${block.text}');
    print('Block bounding box: ${block.boundingBox}');

    for (TextLine line in block.lines) {
      print('  Line text: ${line.text}');

      for (TextElement element in line.elements) {
        print('    Element text: ${element.text}');
      }
    }
  }
}

The RecognizedText object is the bridge between the raw image and your app's logic. By managing the TextRecognizer's lifecycle efficiently and understanding this structured output, you can build a reliable foundation for any bill scanning feature.