dynamsoft_mrz_scanner_bundle_flutter 3.4.1300 copy "dynamsoft_mrz_scanner_bundle_flutter: ^3.4.1300" to clipboard
dynamsoft_mrz_scanner_bundle_flutter: ^3.4.1300 copied to clipboard

The Dynamsoft MRZ Scanner Flutter SDK provides a wrapper for building MRZ scanning applications with Flutter SDK.

example/lib/main.dart

import 'dart:io';
import 'dart:typed_data';

import 'package:dynamsoft_mrz_scanner_bundle_flutter/dynamsoft_mrz_scanner_bundle_flutter.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:gal/gal.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Scan MRZ',
      theme: ThemeData.dark().copyWith(
        colorScheme: ColorScheme.dark(
          primary: Colors.orange,
          secondary: Colors.orange,
        ),
        scaffoldBackgroundColor: const Color(0xFF1A1A1A),
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  MRZData? _mrzData;
  String _displayString = "";
  bool _showResult = false;
  int _selectedTab = 0;

  Uint8List? _portraitBytes;
  Uint8List? _mrzProcessedBytes;
  Uint8List? _oppProcessedBytes;
  Uint8List? _mrzOriginalBytes;
  Uint8List? _oppOriginalBytes;

  final ScrollController _scrollController = ScrollController();

  void _launchMrzScanner() async {
    var config = MRZScannerConfig(
      license: "DLS2eyJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSJ9",
    );
    MRZScanResult mrzScanResult = await MRZScanner.launch(config);

    if (mrzScanResult.status == EnumResultStatus.finished &&
        mrzScanResult.mrzData != null) {
      await _populateResult(mrzScanResult);
    } else if (mrzScanResult.status == EnumResultStatus.canceled) {
      setState(() {
        _displayString = "Scan canceled";
        _showResult = false;
      });
    } else {
      setState(() {
        _displayString =
            "ErrorCode: ${mrzScanResult.errorCode}\n\nErrorString: ${mrzScanResult.errorMessage}";
        _showResult = false;
      });
    }
  }

  Future<void> _populateResult(MRZScanResult result) async {
    final data = result.mrzData!;

    final portraitBytes = result.portraitImage;
    final mrzProcBytes = result.mrzSideDocumentImage;
    final oppProcBytes = result.oppositeSideDocumentImage;
    final mrzOrigBytes = result.mrzSideOriginalImage;
    final oppOrigBytes = result.oppositeSideOriginalImage;

    if (!mounted) return;

    setState(() {
      _mrzData = data;
      _portraitBytes = portraitBytes;
      _mrzProcessedBytes = mrzProcBytes;
      _oppProcessedBytes = oppProcBytes;
      _mrzOriginalBytes = mrzOrigBytes;
      _oppOriginalBytes = oppOrigBytes;
      _selectedTab = 0;
      _showResult = true;
    });

    // Scroll to top
    if (_scrollController.hasClients) {
      _scrollController.jumpTo(0);
    }
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Column(
          children: [
            Expanded(
              child: _showResult && _mrzData != null
                  ? _buildResultContent()
                  : Center(
                      child: Text(
                        _displayString,
                        style: Theme.of(context).textTheme.bodyLarge,
                        textAlign: TextAlign.center,
                      ),
                    ),
            ),
            Padding(
              padding: const EdgeInsets.all(16),
              child: SizedBox(
                width: double.infinity,
                height: 48,
                child: ElevatedButton(
                  onPressed: _launchMrzScanner,
                  style: ElevatedButton.styleFrom(
                    backgroundColor: Colors.orange,
                    foregroundColor: Colors.white,
                    shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(8),
                    ),
                  ),
                  child: const Text("Scan MRZ", style: TextStyle(fontSize: 16)),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildResultContent() {
    final data = _mrzData!;
    final sex = data.sex.isNotEmpty
        ? data.sex.substring(0, 1).toUpperCase() + data.sex.substring(1)
        : '';
    final docTypeText = switch (data.documentType) {
      'MRTD_TD1_ID' => 'ID (TD1)',
      'MRTD_TD2_ID' => 'ID (TD2)',
      'MRTD_TD3_PASSPORT' => 'Passport (TD3)',
      _ => data.documentType,
    };

    return SingleChildScrollView(
      controller: _scrollController,
      padding: const EdgeInsets.all(16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          // Header: name + portrait
          Row(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      '${data.firstName} ${data.lastName}',
                      style: const TextStyle(
                        fontSize: 22,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    const SizedBox(height: 4),
                    Text(
                      '$sex, ${data.age} years old',
                      style: const TextStyle(fontSize: 14, color: Colors.grey),
                    ),
                    const SizedBox(height: 4),
                    Text(
                      'Expiry: ${data.dateOfExpire}',
                      style: const TextStyle(fontSize: 14, color: Colors.grey),
                    ),
                  ],
                ),
              ),
              GestureDetector(
                onLongPress: _portraitBytes == null
                    ? null
                    : () => _showSaveDialog(_portraitBytes!),
                child: Container(
                  width: 80,
                  height: 80,
                  decoration: BoxDecoration(
                    color: Colors.grey[800],
                    borderRadius: BorderRadius.circular(8),
                  ),
                  clipBehavior: Clip.antiAlias,
                  child: _portraitBytes != null
                      ? FittedBox(
                          fit: BoxFit.cover,
                          child: Image.memory(_portraitBytes!),
                        )
                      : Image.asset(
                          'assets/images/portrait_placeholder.jpg',
                          fit: BoxFit.cover,
                        ),
                ),
              ),
            ],
          ),
          const SizedBox(height: 24),

          // Tabs: Processed / Original
          _buildTabs(),
          const SizedBox(height: 12),
          _buildTabImages(),
          const SizedBox(height: 24),

          // Personal Info
          const Text(
            'Personal Info',
            style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
          ),
          const SizedBox(height: 10),
          _infoRow('Given Name', data.firstName),
          _infoRow('Surname', data.lastName),
          _infoRow('Date of Birth', data.dateOfBirth),
          _infoRow('Gender', sex),
          _infoRow('Nationality', data.nationalityRaw),
          const SizedBox(height: 20),

          // Document Info
          const Text(
            'Document Info',
            style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
          ),
          const SizedBox(height: 10),
          _infoRow('Doc. Type', docTypeText),
          _infoRow('Doc. Number', data.documentNumber),
          _infoRow('Expiry Date', data.dateOfExpire),
          const SizedBox(height: 20),

          // Raw MRZ Text
          const Text(
            'Raw MRZ Text',
            style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
          ),
          const SizedBox(height: 10),
          Text(
            data.mrzText,
            style: const TextStyle(
              fontFamily: 'monospace',
              fontSize: 13,
              color: Color(0xFFCCCCCC),
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildTabs() {
    return Row(
      children: [
        Expanded(
          child: GestureDetector(
            behavior: HitTestBehavior.opaque,
            onTap: () => setState(() => _selectedTab = 0),
            child: Column(
              children: [
                Text(
                  'Processed',
                  style: TextStyle(
                    color: _selectedTab == 0 ? Colors.white : Colors.grey,
                    fontWeight: _selectedTab == 0
                        ? FontWeight.bold
                        : FontWeight.normal,
                  ),
                ),
                const SizedBox(height: 10),
                Container(
                  height: 2,
                  color: _selectedTab == 0 ? Colors.white : Colors.transparent,
                ),
              ],
            ),
          ),
        ),
        Expanded(
          child: GestureDetector(
            behavior: HitTestBehavior.opaque,
            onTap: () => setState(() => _selectedTab = 1),
            child: Padding(
              padding: const EdgeInsets.symmetric(vertical: 12),
              child: Column(
                children: [
                  Text(
                    'Original',
                    style: TextStyle(
                      color: _selectedTab == 1 ? Colors.white : Colors.grey,
                      fontWeight: _selectedTab == 1
                          ? FontWeight.bold
                          : FontWeight.normal,
                    ),
                  ),
                  const SizedBox(height: 10),
                  Container(
                    height: 2,
                    color: _selectedTab == 1
                        ? Colors.white
                        : Colors.transparent,
                  ),
                ],
              ),
            ),
          ),
        ),
      ],
    );
  }

  Widget _buildTabImages() {
    final img1 = _selectedTab == 0 ? _mrzProcessedBytes : _mrzOriginalBytes;
    final img2 = _selectedTab == 0 ? _oppProcessedBytes : _oppOriginalBytes;

    final hasFirst = img1 != null;
    final hasSecond = img2 != null;

    if (hasFirst && hasSecond) {
      return SizedBox(
        height: 140,
        child: Row(
          children: [
            Expanded(child: _buildImageCard(img1)),
            const SizedBox(width: 12),
            Expanded(child: _buildImageCard(img2)),
          ],
        ),
      );
    } else if (hasFirst || hasSecond) {
      return SizedBox(
        height: 200,
        child: Center(child: _buildImageCard(hasFirst ? img1 : img2)),
      );
    } else {
      return const SizedBox.shrink();
    }
  }

  Future<void> _saveImageToGallery(Uint8List bytes) async {
    try {
      final granted = await Gal.requestAccess();
      if (!granted) {
        if (mounted) {
          ScaffoldMessenger.of(context).showSnackBar(
            const SnackBar(content: Text('Photo library access denied')),
          );
        }
        return;
      }
      final dir = await Directory.systemTemp.createTemp('mrz_img');
      final file = File(
        '${dir.path}/MRZ_${DateTime.now().millisecondsSinceEpoch}.jpg',
      );
      await file.writeAsBytes(bytes);
      await Gal.putImage(file.path);
      await file.delete();
      await dir.delete();
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(content: Text('Image saved to photo library')),
        );
      }
    } catch (e) {
      if (mounted) {
        ScaffoldMessenger.of(
          context,
        ).showSnackBar(SnackBar(content: Text('Failed to save image: $e')));
      }
    }
  }

  Widget _buildImageCard(Uint8List? bytes) {
    final card = Container(
      decoration: BoxDecoration(borderRadius: BorderRadius.circular(8)),
      clipBehavior: Clip.antiAlias,
      child: bytes != null
          ? Image.memory(bytes, fit: BoxFit.contain)
          : const Center(
              child: Icon(Icons.image, size: 36, color: Colors.grey),
            ),
    );
    if (bytes == null) return card;
    return GestureDetector(
      onLongPress: () => _showSaveDialog(bytes),
      child: card,
    );
  }

  void _showSaveDialog(Uint8List bytes) {
    showCupertinoDialog(
      context: context,
      builder: (ctx) => CupertinoAlertDialog(
        title: const Text('Save Image'),
        content: const Text('Save this image to photo library?'),
        actions: [
          CupertinoDialogAction(
            isDefaultAction: true,
            onPressed: () => Navigator.pop(ctx),
            child: const Text('Cancel'),
          ),
          CupertinoDialogAction(
            onPressed: () {
              Navigator.pop(ctx);
              _saveImageToGallery(bytes);
            },
            child: const Text('Save'),
          ),
        ],
      ),
    );
  }

  Widget _infoRow(String label, String value) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 10),
      child: Row(
        children: [
          Expanded(
            child: Text(
              label,
              style: const TextStyle(color: Color(0xFFAAAAAA), fontSize: 14),
            ),
          ),
          Expanded(
            child: Text(
              value,
              style: const TextStyle(color: Colors.white, fontSize: 14),
            ),
          ),
        ],
      ),
    );
  }
}
20
likes
120
points
44
downloads

Documentation

API reference

Publisher

verified publisherdynamsoft.com

Weekly Downloads

The Dynamsoft MRZ Scanner Flutter SDK provides a wrapper for building MRZ scanning applications with Flutter SDK.

Homepage

License

unknown (license)

Dependencies

dynamsoft_capture_vision_flutter, flutter, plugin_platform_interface

More

Packages that depend on dynamsoft_mrz_scanner_bundle_flutter

Packages that implement dynamsoft_mrz_scanner_bundle_flutter