advanced_business_card_reader 1.0.0 copy "advanced_business_card_reader: ^1.0.0" to clipboard
advanced_business_card_reader: ^1.0.0 copied to clipboard

Business/visiting card reader using Google ML Kit Text Recognition (Flutter). Extracts text and parses name, company, phone, email, website.

example/lib/main.dart

import 'dart:io';

import 'package:advanced_business_card_reader/advanced_business_card_reader.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';

void main() {
  runApp(const CardReaderExampleApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Business Card Reader',
      theme: ThemeData(
        colorSchemeSeed: Colors.blue,
        useMaterial3: true,
      ),
      home: const HomePage(),
    );
  }
}

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

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  final _picker = ImagePicker();

  bool _loading = false;
  String? _imagePath;

  // Script selection
  bool _auto = true;
  OcrScript _script = OcrScript.latin;

  BusinessCardScanResult? _result;
  String? _error;

  Future<void> _scanPath(String path) async {
    setState(() {
      _loading = true;
      _error = null;
      _result = null;
      _imagePath = path;
    });

    try {
      final r = _auto
          ? await BusinessCardReader.scanAutoFromFile(
              path,
              preferredScripts: const [
                OcrScript.latin,
                OcrScript.devanagari,
                OcrScript.chinese,
                OcrScript.japanese,
                OcrScript.korean,
              ],
              defaultIsoCountry: 'IN',
            )
          : await BusinessCardReader.scanFromFile(
              path,
              script: _script,
              defaultIsoCountry: 'IN',
            );

      setState(() => _result = r);
    } catch (e) {
      setState(() => _error = e.toString());
    } finally {
      setState(() => _loading = false);
    }
  }

  Future<void> _scanCamera() async {
    final x = await _picker.pickImage(source: ImageSource.camera, imageQuality: 92);
    if (x == null) return;
    await _scanPath(x.path);
  }

  Future<void> _scanGallery() async {
    final x = await _picker.pickImage(source: ImageSource.gallery, imageQuality: 92);
    if (x == null) return;
    await _scanPath(x.path);
  }

  Future<void> _scanFile() async {
    final res = await FilePicker.platform.pickFiles(
      type: FileType.custom,
      allowedExtensions: const ['jpg', 'jpeg', 'png', 'webp'],
    );
    final path = res?.files.single.path;
    if (path == null) return;
    await _scanPath(path);
  }

  @override
  Widget build(BuildContext context) {
    final cs = Theme.of(context).colorScheme;

    return Scaffold(
      appBar: AppBar(
        title: const Text('Business Card Reader'),
      ),
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.all(14),
          child: Column(
            children: [
              _controls(cs),
              const SizedBox(height: 12),
              if (_loading) const LinearProgressIndicator(),
              if (!_loading) const SizedBox(height: 4),
              Expanded(
                child: _result == null && _error == null
                    ? _emptyState(cs)
                    : _result != null
                        ? _resultView(_result!)
                        : _errorView(_error!),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Widget _controls(ColorScheme cs) {
    return Card(
      elevation: 0,
      color: cs.surfaceContainerHighest,
      child: Padding(
        padding: const EdgeInsets.all(12),
        child: Column(
          children: [
            Row(
              children: [
                Expanded(
                  child: FilledButton.icon(
                    onPressed: _loading ? null : _scanCamera,
                    icon: const Icon(Icons.camera_alt_rounded),
                    label: const Text('Camera'),
                  ),
                ),
                const SizedBox(width: 10),
                Expanded(
                  child: OutlinedButton.icon(
                    onPressed: _loading ? null : _scanGallery,
                    icon: const Icon(Icons.photo_library_rounded),
                    label: const Text('Gallery'),
                  ),
                ),
                const SizedBox(width: 10),
                Expanded(
                  child: OutlinedButton.icon(
                    onPressed: _loading ? null : _scanFile,
                    icon: const Icon(Icons.folder_open_rounded),
                    label: const Text('File'),
                  ),
                ),
              ],
            ),
            const SizedBox(height: 10),
            Row(
              children: [
                Expanded(
                  child: SwitchListTile.adaptive(
                    contentPadding: EdgeInsets.zero,
                    value: _auto,
                    onChanged: _loading
                        ? null
                        : (v) => setState(() {
                              _auto = v;
                            }),
                    title: const Text('Auto script'),
                    subtitle: const Text('Try multiple scripts and pick best'),
                  ),
                ),
                const SizedBox(width: 8),
                SizedBox(
                  width: 170,
                  child: Opacity(
                    opacity: _auto ? 0.5 : 1,
                    child: IgnorePointer(
                      ignoring: _auto,
                      child: DropdownButtonFormField<OcrScript>(
                        value: _script,
                        decoration: const InputDecoration(
                          labelText: 'Script',
                          border: OutlineInputBorder(),
                          isDense: true,
                        ),
                        items: OcrScript.values
                            .map(
                              (s) => DropdownMenuItem(
                                value: s,
                                child: Text(s.label),
                              ),
                            )
                            .toList(),
                        onChanged: (v) => setState(() => _script = v ?? OcrScript.latin),
                      ),
                    ),
                  ),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }

  Widget _emptyState(ColorScheme cs) {
    return Center(
      child: Card(
        elevation: 0,
        color: cs.surfaceContainerHighest,
        child: const Padding(
          padding: EdgeInsets.all(18),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              Icon(Icons.document_scanner_rounded, size: 48),
              SizedBox(height: 10),
              Text(
                'Scan a business card to extract details',
                style: TextStyle(fontWeight: FontWeight.w800, fontSize: 14.5),
              ),
              SizedBox(height: 4),
              Text(
                'Use Camera, Gallery, or File pick For best results, keep the card flat & clear.',
                textAlign: TextAlign.center,
              ),
            ],
          ),
        ),
      ),
    );
  }

  Widget _errorView(String err) {
    return ListView(
      children: [
        Card(
          child: Padding(
            padding: const EdgeInsets.all(14),
            child: Text(
              err,
              style: const TextStyle(fontFamily: 'monospace'),
            ),
          ),
        ),
      ],
    );
  }

  Widget _resultView(BusinessCardScanResult r) {
    final d = r.data;

    Widget kv(String k, String v) => Padding(
          padding: const EdgeInsets.only(bottom: 10),
          child: Row(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              SizedBox(
                width: 92,
                child: Text(k, style: const TextStyle(fontWeight: FontWeight.w800)),
              ),
              Expanded(child: Text(v)),
            ],
          ),
        );

    return ListView(
      children: [
        if (_imagePath != null)
          ClipRRect(
            borderRadius: BorderRadius.circular(14),
            child: Image.file(
              File(_imagePath!),
              height: 200,
              fit: BoxFit.cover,
            ),
          ),
        const SizedBox(height: 12),
        Card(
          child: Padding(
            padding: const EdgeInsets.all(14),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  'Parsed Details',
                  style: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w900),
                ),
                const SizedBox(height: 10),
                kv('Script', r.scriptUsed.label),
                kv('Name', d.name ?? '-'),
                kv('Company', d.company ?? '-'),
                kv('Phones', d.phones.isEmpty ? '-' : d.phones.join('\n')),
                kv('Emails', d.emails.isEmpty ? '-' : d.emails.join('\n')),
                kv('Websites', d.websites.isEmpty ? '-' : d.websites.join('\n')),
              ],
            ),
          ),
        ),
        Card(
          child: Padding(
            padding: const EdgeInsets.all(14),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  'Raw Text',
                  style: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w900),
                ),
                const SizedBox(height: 10),
                SelectableText(d.rawText.isEmpty ? '(empty)' : d.rawText),
              ],
            ),
          ),
        ),
      ],
    );
  }
}
0
likes
150
points
--
downloads

Publisher

verified publishernh97.co.in

Business/visiting card reader using Google ML Kit Text Recognition (Flutter). Extracts text and parses name, company, phone, email, website.

Repository (GitHub)
View/report issues

Topics

#ocr #business-card #text-recognition #mlkit #scanner

Documentation

API reference

License

MIT (license)

Dependencies

flutter, google_mlkit_text_recognition, phone_numbers_parser

More

Packages that depend on advanced_business_card_reader