Selectable OCR

A powerful, plug-and-play Flutter widget that lets users select and crop any portion of your UI and automatically extracts text on-device with zero backend setup.

Built on the coordinate capture foundations of selectable_area, selectable_ocr integrates hardware-accelerated Optical Character Recognition directly into the selection lifecycle.


✨ Features

  • Zero Backend Required: Runs 100% on-device using Google ML Kit on Android & iOS. No servers, no API keys, and no extra configuration.
  • 🔒 Privacy-First: Sensitive documents (passports, IDs, credit cards, medical forms) never leave the user's device.
  • 🚀 Blazing Fast: Hardware-accelerated neural network inference finishes in ~50ms–120ms.
  • 🎯 Wrap Any Widget: Works seamlessly over an Image, ListView, custom document viewer, or any Flutter widget tree.
  • 🔄 In-Situ Visual Feedback: Displays an intuitive, floating loading badge over the active selection while text is being processed.
  • 🔌 Pluggable Architecture: Need a custom cloud backend or server-side Python/Vercel microservice? Easily plug in HttpOcrEngine.
  • 🌐 Multi-Language Scripts: Supports Latin, Devanagari, Japanese, Korean, and Chinese recognition scripts.

📦 Getting Started

Add selectable_ocr to your pubspec.yaml:

dependencies:
  selectable_ocr: ^1.0.0

Then install dependencies:

flutter pub get

Platform Requirements

  • Android: minSdkVersion 21 (default for modern Flutter apps).
  • iOS: iOS 15.5+ (or iOS 13.0+).

No additional device permissions (like Camera or External Storage) are required, as the widget captures and processes in-memory widget pixels directly from the screen!


🚀 Quick Start (Zero Setup)

Wrap any widget with SelectableOcr and listen to onTextExtracted:

import 'package:flutter/material.dart';
import 'package:selectable_ocr/selectable_ocr.dart';

class SimpleOcrView extends StatelessWidget {
  const SimpleOcrView({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Snip & OCR')),
      body: Center(
        child: SelectableOcr(
          // Any widget you want to capture from
          child: Image.network('https://example.com/document.png'),
          
          // Called when OCR extraction completes
          onTextExtracted: (String text) {
            ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(content: Text('Extracted: $text')),
            );
          },
        ),
      ),
    );
  }
}

🛠️ Advanced Usage

1. Access Detailed Results (OcrResult)

If you need line-by-line breakdowns, processing duration, or raw recognition objects:

SelectableOcr(
  child: myDocumentWidget,
  onOcrResult: (OcrResult result) {
    print('Consolidated Text: ${result.cleanText}');
    print('Processing Time: ${result.processingTime.inMilliseconds} ms');
    print('Lines detected: ${result.lines.length}');
    for (final line in result.lines) {
      print('• $line');
    }
  },
  onImageCaptured: (Uint8List pngBytes) {
    // Access the raw cropped image bytes if needed
  },
)

2. Custom Styling (SelectionStyle)

Customize the rectangle stroke, fill color, corner radius, and in-situ loading badge:

SelectableOcr(
  style: const SelectionStyle(
    strokeColor: Colors.purpleAccent,
    fillColor: Color(0x33AB47BC),
    strokeWidth: 2.5,
    cornerRadius: 8.0,
    showLoadingBadge: true,
    loadingText: 'Scanning receipt...',
    loadingBadgeColor: Colors.purpleAccent,
  ),
  child: myReceiptWidget,
  onTextExtracted: (text) => print(text),
)

3. Multi-Language Script Support

To recognize non-Latin scripts such as Japanese, Devanagari, Korean, or Chinese:

SelectableOcr(
  engine: OnDeviceOcrEngine(
    script: TextRecognitionScript.devanagari, // or .japanese, .korean, .chinese
  ),
  child: myImageWidget,
  onTextExtracted: (text) => print(text),
)

4. Custom Cloud / HTTP Backend (HttpOcrEngine)

If your organization prefers running OCR on a remote Python server (e.g. Flask, FastAPI, or Vercel):

SelectableOcr(
  engine: HttpOcrEngine(
    endpoint: Uri.parse('https://your-api.vercel.app/ocr'),
    headers: {
      'Authorization': 'Bearer YOUR_TOKEN',
    },
    timeout: const Duration(seconds: 10),
  ),
  child: myWidget,
  onTextExtracted: (text) => print('Server extracted: $text'),
)

📱 Interactive Demo

Check out the example/ directory for a complete, dual-pane interactive passport and document auto-fill demo app.


🤝 Acknowledgments

Inspired by selectable_area and the article "Stop Screenshotting: A Better Way to Capture UI in Flutter" by Adhil Latheef.

📄 License

MIT License. Feel free to use and contribute!

Libraries

selectable_ocr