How to Build a Flutter MRZ and VIN Scanner for Android, iOS, Web, Windows, and Linux

Benefiting from the cross-platform nature of Dynamsoft Capture Vision, we created flutter_ocr_sdk to enable MRZ recognition across Android, iOS, Web, Windows, and Linux. With the latest version of Dynamsoft Capture Vision, the plugin now supports VIN scanning as well, offering unified APIs across all platforms. In this article, we’ll cover how to add VIN capabilities to the existing Flutter plugin and how to build a cross-platform MRZ and VIN scanner app for Android, iOS, Web, Windows, and Linux. Flutter MRZ/VIN Scanner Demo Android iOS Web Windows Linux Integrating Dynamsoft Capture Vision SDKs In the following sections, we’ll walk through how to integrate Dynamsoft Capture Vision SDKs for each supported platform. Desktop Integration Download and unzip the Dynamsoft Capture Vision C++ SDK. Due to the 100MB size limitation on pub.dev, we compress the model and template files into a zip archive and download it at runtime on on Windows and Linux. import 'dart:io'; import 'package:path/path.dart' as p; import 'package:http/http.dart' as http; import 'package:archive/archive.dart'; const _zipUrl = 'https://github.com/yushulx/flutter_ocr_sdk/releases/download/v2.0.0/resources.zip'; const _zipName = 'resources.zip'; Future download() async { final execDir = Directory(p.dirname(Platform.resolvedExecutable)); final targetDir = Platform.isWindows ? execDir : Directory(p.join(execDir.path, 'lib')); targetDir.createSync(recursive: true); final cacheDir = Directory(p.join(Directory.systemTemp.path, 'flutter_ocr_sdk')); cacheDir.createSync(recursive: true); final zipCacheFile = File(p.join(cacheDir.path, _zipName)); if (!zipCacheFile.existsSync()) { stdout.writeln('Downloading resources.zip to ${zipCacheFile.path} …'); final resp = await http.get(Uri.parse(_zipUrl)); if (resp.statusCode != 200) { stderr.writeln('Failed to download ($_zipUrl): HTTP ${resp.statusCode}'); exit(1); } zipCacheFile.writeAsBytesSync(resp.bodyBytes); stdout.writeln('✅ Cached zip in temp.'); } else { stdout.writeln('✅ Using cached zip: ${zipCacheFile.path}'); } final bytes = zipCacheFile.readAsBytesSync(); final archive = ZipDecoder().decodeBytes(bytes); for (final file in archive) { final outPath = p.join(targetDir.path, file.name); if (file.isFile) { final outFile = File(outPath); outFile.createSync(recursive: true); outFile.writeAsBytesSync(file.content as List); } else { Directory(outPath).createSync(recursive: true); } } stdout.writeln('✅ Resources unpacked to: ${targetDir.path}'); } Windows Copy *.h, *.lib and *.dll files to your Flutter project Windows directory. flutter_ocr_sdk/windows/include flutter_ocr_sdk/windows/lib. flutter_ocr_sdk/windows/bin Update CMakeLists.txt in the windows directory: cmake_minimum_required(VERSION 3.14) set(PROJECT_NAME "flutter_ocr_sdk") project(${PROJECT_NAME} LANGUAGES CXX) set(PLUGIN_NAME "flutter_ocr_sdk_plugin") link_directories("${PROJECT_SOURCE_DIR}/lib/") list(APPEND PLUGIN_SOURCES "flutter_ocr_sdk_plugin.cpp" "flutter_ocr_sdk_plugin.h" ) add_library(${PLUGIN_NAME} SHARED "include/flutter_ocr_sdk/flutter_ocr_sdk_plugin_c_api.h" "flutter_ocr_sdk_plugin_c_api.cpp" ${PLUGIN_SOURCES} ) apply_standard_settings(${PLUGIN_NAME}) set_target_properties(${PLUGIN_NAME} PROPERTIES CXX_VISIBILITY_PRESET hidden) target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) target_include_directories(${PLUGIN_NAME} INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include") target_compile_options(${PLUGIN_NAME} PRIVATE /wd4121 /W3 /WX-) target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin "DynamsoftCorex64" "DynamsoftLicensex64" "DynamsoftCaptureVisionRouterx64" "DynamsoftUtilityx64") set(flutter_ocr_sdk_bundled_libraries "${PROJECT_SOURCE_DIR}/bin/" PARENT_SCOPE ) Linux Copy *.h, *.so files to your Flutter project Linux directory. flutter_ocr_sdk/linux/include flutter_ocr_sdk/linux/lib Update CMakeLists.txt in the linux directory: cmake_minimum_required(VERSION 3.10) set(PROJECT_NAME "flutter_ocr_sdk") project(${PROJECT_NAME} LANGUAGES CXX) set(PLUGIN_NAME "flutter_ocr_sdk_plugin") link_directories("${CMAKE_CURRENT_SOURCE_DIR}/lib") add_library(${PLUGIN_NAME} SHARED "flutter_ocr_sdk_plugin.cc" ) apply_standard_settings(${PLUGIN_NAME}) set_target_properties(${PLUGIN_NAME} PROPERTIES CXX_VISIBILITY_PRESET hidden) target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) target_include_directories(${PLUGIN_NAME} INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include") target_link_libraries(${PLUGIN_NAME} PRIVATE flutter "DynamsoftCore" "DynamsoftLicense" "DynamsoftCaptureVisionRouter" "DynamsoftUtility") target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::GTK) set_target_properties(${PLUGIN_NAME} P

Apr 24, 2025 - 01:31
 0
How to Build a Flutter MRZ and VIN Scanner for Android, iOS, Web, Windows, and Linux

Benefiting from the cross-platform nature of Dynamsoft Capture Vision, we created flutter_ocr_sdk to enable MRZ recognition across Android, iOS, Web, Windows, and Linux. With the latest version of Dynamsoft Capture Vision, the plugin now supports VIN scanning as well, offering unified APIs across all platforms. In this article, we’ll cover how to add VIN capabilities to the existing Flutter plugin and how to build a cross-platform MRZ and VIN scanner app for Android, iOS, Web, Windows, and Linux.

Flutter MRZ/VIN Scanner Demo

  • Android

  • iOS

  • Web

  • Windows

  • Linux

Integrating Dynamsoft Capture Vision SDKs

In the following sections, we’ll walk through how to integrate Dynamsoft Capture Vision SDKs for each supported platform.

Desktop Integration

Download and unzip the Dynamsoft Capture Vision C++ SDK.

Due to the 100MB size limitation on pub.dev, we compress the model and template files into a zip archive and download it at runtime on on Windows and Linux.

import 'dart:io';
import 'package:path/path.dart' as p;
import 'package:http/http.dart' as http;
import 'package:archive/archive.dart';

const _zipUrl =
    'https://github.com/yushulx/flutter_ocr_sdk/releases/download/v2.0.0/resources.zip';

const _zipName = 'resources.zip';

Future<void> download() async {
  final execDir = Directory(p.dirname(Platform.resolvedExecutable));

  final targetDir =
      Platform.isWindows ? execDir : Directory(p.join(execDir.path, 'lib'));
  targetDir.createSync(recursive: true);

  final cacheDir =
      Directory(p.join(Directory.systemTemp.path, 'flutter_ocr_sdk'));
  cacheDir.createSync(recursive: true);
  final zipCacheFile = File(p.join(cacheDir.path, _zipName));

  if (!zipCacheFile.existsSync()) {
    stdout.writeln('Downloading resources.zip to ${zipCacheFile.path} …');
    final resp = await http.get(Uri.parse(_zipUrl));
    if (resp.statusCode != 200) {
      stderr.writeln('Failed to download ($_zipUrl): HTTP ${resp.statusCode}');
      exit(1);
    }
    zipCacheFile.writeAsBytesSync(resp.bodyBytes);
    stdout.writeln('✅ Cached zip in temp.');
  } else {
    stdout.writeln('✅ Using cached zip: ${zipCacheFile.path}');
  }

  final bytes = zipCacheFile.readAsBytesSync();
  final archive = ZipDecoder().decodeBytes(bytes);
  for (final file in archive) {
    final outPath = p.join(targetDir.path, file.name);
    if (file.isFile) {
      final outFile = File(outPath);
      outFile.createSync(recursive: true);
      outFile.writeAsBytesSync(file.content as List<int>);
    } else {
      Directory(outPath).createSync(recursive: true);
    }
  }

  stdout.writeln('✅ Resources unpacked to: ${targetDir.path}');
}

Windows

  1. Copy *.h, *.lib and *.dll files to your Flutter project Windows directory.

    • flutter_ocr_sdk/windows/include
    • flutter_ocr_sdk/windows/lib.
    • flutter_ocr_sdk/windows/bin
  2. Update CMakeLists.txt in the windows directory:

    cmake_minimum_required(VERSION 3.14)
    
    set(PROJECT_NAME "flutter_ocr_sdk")
    project(${PROJECT_NAME} LANGUAGES CXX)
    
    set(PLUGIN_NAME "flutter_ocr_sdk_plugin")
    
    link_directories("${PROJECT_SOURCE_DIR}/lib/") 
    
    list(APPEND PLUGIN_SOURCES
      "flutter_ocr_sdk_plugin.cpp"
      "flutter_ocr_sdk_plugin.h"
    )
    
    add_library(${PLUGIN_NAME} SHARED
      "include/flutter_ocr_sdk/flutter_ocr_sdk_plugin_c_api.h"
      "flutter_ocr_sdk_plugin_c_api.cpp"
      ${PLUGIN_SOURCES}
    )
    
    apply_standard_settings(${PLUGIN_NAME})
    
    set_target_properties(${PLUGIN_NAME} PROPERTIES
      CXX_VISIBILITY_PRESET hidden)
    target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL)
    
    target_include_directories(${PLUGIN_NAME} INTERFACE
      "${CMAKE_CURRENT_SOURCE_DIR}/include")
    
    target_compile_options(${PLUGIN_NAME} PRIVATE /wd4121 /W3 /WX-)
    
    target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin "DynamsoftCorex64" "DynamsoftLicensex64" "DynamsoftCaptureVisionRouterx64" "DynamsoftUtilityx64")
    
    set(flutter_ocr_sdk_bundled_libraries
      "${PROJECT_SOURCE_DIR}/bin/"
      PARENT_SCOPE
    )
    

Linux

  1. Copy *.h, *.so files to your Flutter project Linux directory.

    • flutter_ocr_sdk/linux/include
    • flutter_ocr_sdk/linux/lib
  2. Update CMakeLists.txt in the linux directory:

    cmake_minimum_required(VERSION 3.10)
    
    set(PROJECT_NAME "flutter_ocr_sdk")
    project(${PROJECT_NAME} LANGUAGES CXX)
    
    set(PLUGIN_NAME "flutter_ocr_sdk_plugin")
    
    link_directories("${CMAKE_CURRENT_SOURCE_DIR}/lib")
    
    add_library(${PLUGIN_NAME} SHARED
      "flutter_ocr_sdk_plugin.cc"
    )
    
    apply_standard_settings(${PLUGIN_NAME})
    
    set_target_properties(${PLUGIN_NAME} PROPERTIES
      CXX_VISIBILITY_PRESET hidden)
    target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL)
    
    target_include_directories(${PLUGIN_NAME} INTERFACE
      "${CMAKE_CURRENT_SOURCE_DIR}/include")
    target_link_libraries(${PLUGIN_NAME} PRIVATE flutter "DynamsoftCore" "DynamsoftLicense" "DynamsoftCaptureVisionRouter" "DynamsoftUtility")
    target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::GTK)
    
    set_target_properties(${PLUGIN_NAME} PROPERTIES
      INSTALL_RPATH "$ORIGIN"
      BUILD_RPATH "$ORIGIN"
    )
    
    set(flutter_ocr_sdk_bundled_libraries
      ""
      PARENT_SCOPE
    )
    
    install(DIRECTORY 
      "${PROJECT_SOURCE_DIR}/../linux/lib/"
      DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/"
    )
    

Mobile Integration

Android

Add the following dependencies in android/build.gradle:

dependencies {
    implementation "com.dynamsoft:dynamsoftcapturevisionbundle:2.6.1003"
    implementation "com.dynamsoft:dynamsoftmrz:3.4.20"
    implementation "com.dynamsoft:dynamsoftvin:3.4.20"
}

The dynamsoftcapturevisionbundle contains the core engine, while dynamsoftmrz and dynamsoftvin provide models for MRZ and VIN recognition respectively.

iOS

Add the following dependencies in ios/flutter_ocr_sdk.podspec:

s.dependency 'DynamsoftVIN', '3.4.20'
s.dependency 'DynamsoftMRZ', '3.4.20'
s.dependency 'DynamsoftCaptureVisionBundle', '2.6.1004'

Web Integration

Include the JavaScript library in index.html:


API Overview and Implementation

The Flutter plugin provides the following four methods:

Method Description Parameters Return Type
Future init(String key) Initializes the OCR SDK with a license key. key: License string Future
Future>?> recognizeBuffer(Uint8List bytes, int width, int height, int stride, int format, int rotation) Performs OCR on a raw image buffer. bytes: RGBA image buffer  
width, height: Image dimensions
stride: Row bytes
format: Pixel format index
rotation: 0/90/180/270
Future>?>
Future>?> recognizeFile(String filename) Performs OCR on an image file. filename: Path to the image file Future>?>
Future loadModel({ModelType modelType = ModelType.mrz}) Loads the OCR model by type (mrz or vin). modelType: Optional, defaults to ModelType.mrz Future

To switch between MRZ and VIN scanning, call loadModel with the appropriate ModelType before using the recognition methods. Both recognizeBuffer and recognizeFile return a list of recognized lines, each containing either MRZ or VIN data.

The OcrLine class is defined as follows:


import 'mrz_result.dart';
import 'vin_result.dart';

class OcrLine {
  int confidence = 0;
  String text = '';

  int x1 = 0, y1 = 0, x2 = 0, y2 = 0, x3 = 0, y3 = 0, x4 = 0, y4 = 0;

  MrzResult? mrzResult;
  VinResult? vinResult;

  @override
  String toString() {
    String result = '';
    result += 'Confidence: $confidence\n';
    result += 'Text: $text\n';
    result += 'x1: $x1\n';
    result += 'y1: $y1\n';
    result += 'x2: $x2\n';
    result += 'y2: $y2\n';
    result += 'x3: $x3\n';
    result += 'y3: $y3\n';
    result += 'x4: $x4\n';

    if (mrzResult != null) {
      result += mrzResult.toString();
    }

    if (vinResult != null) {
      result += vinResult.toString();
    }

    return result;
  }
}


class MrzResult {

  String? type;
  String? nationality;
  String? surname;
  String? givenName;
  String? docNumber;
  String? issuingCountry;
  String? birthDate;
  String? gender;
  String? expiration;
  String? lines;

  MrzResult(
      {String? type,
      String? nationality,
      String? surname,
      String? givenName,
      String? docNumber,
      String? issuingCountry,
      String? birthDate,
      String? gender,
      String? expiration,
      String? lines})
      : this.type = type ?? 'N/A',
        this.nationality = nationality ?? 'N/A',
        this.surname = surname ?? 'N/A',
        this.givenName = givenName ?? 'N/A',
        this.docNumber = docNumber ?? 'N/A',
        this.issuingCountry = issuingCountry ?? 'N/A',
        this.birthDate = birthDate ?? 'N/A',
        this.gender = gender ?? 'N/A',
        this.expiration = expiration ?? 'N/A',
        this.lines = lines ?? 'N/A';

  @override
  String toString() {
    if (type == null || type!.isEmpty) return "No results";

    String result = '';

    result += 'Type: $type\n\n';
    result += 'Nationality: $nationality\n\n';
    result += 'Surname: $surname\n\n';
    result += 'Given name: $givenName\n\n';
    result += 'Passport Number: $docNumber\n\n';
    result += 'Issue Country: $issuingCountry\n\n';
    result += 'Date of birth: $birthDate\n\n';
    result += 'Gender: $gender\n\n';
    result += 'Expiration: $expiration\n\n';

    return result;
  }

  Map<String, dynamic> toJson() => {
        'type': type ?? '',
        'nationality': nationality ?? '',
        'surname': surname ?? '',
        'givenName': givenName ?? '',
        'docNumber': docNumber ?? '',
        'issuingCountry': issuingCountry ?? '',
        'birthDate': birthDate ?? '',
        'gender': gender ?? '',
        'expiration': expiration ?? '',
        'lines': lines,
      };

  factory MrzResult.fromJson(Map<String, dynamic> json) {
    return MrzResult(
      type: json['type'],
      nationality: json['nationality'],
      surname: json['surname'],
      givenName: json['givenName'],
      docNumber: json['docNumber'],
      issuingCountry: json['issuingCountry'],
      birthDate: json['birthDate'],
      gender: json['gender'],
      expiration: json['expiration'],
      lines: json['lines'],
    );
  }
}

class VinResult {
  String? vinString;
  String? wmi;
  String? region;
  String? vds;
  String? checkDigit;
  String? modelYear;
  String? plantCode;
  String? serialNumber;

  VinResult({
    String? vinString,
    String? wmi,
    String? region,
    String? vds,
    String? checkDigit,
    String? modelYear,
    String? plantCode,
    String? serialNumber,
  })  : vinString = vinString ?? 'N/A',
        wmi = wmi ?? 'N/A',
        region = region ?? 'N/A',
        vds = vds ?? 'N/A',
        checkDigit = checkDigit ?? 'N/A',
        modelYear = modelYear ?? 'N/A',
        plantCode = plantCode ?? 'N/A',
        serialNumber = serialNumber ?? 'N/A';

  @override
  String toString() {
    return "VIN String: " +
        vinString! +
        "\n" +
        "WMI: " +
        wmi! +
        "\n" +
        "Region: " +
        region! +
        "\n" +
        "VDS: " +
        vds! +
        "\n" +
        "Check Digit: " +
        checkDigit! +
        "\n" +
        "Model Year: " +
        modelYear! +
        "\n" +
        "Manufacturer plant: " +
        plantCode! +
        "\n" +
        "Serial Number: " +
        serialNumber!;
  }

  Map<String, dynamic> toJson() => {
        'vinString': vinString,
        'wmi': wmi,
        'region': region,
        'vds': vds,
        'checkDigit': checkDigit,
        'modelYear': modelYear,
        'plantCode': plantCode,
        'serialNumber': serialNumber,
      };

  factory VinResult.fromJson(Map<String, dynamic> json) {
    return VinResult(
      vinString: json['vinString'],
      wmi: json['wmi'],
      region: json['region'],
      vds: json['vds'],
      checkDigit: json['checkDigit'],
      modelYear: json['modelYear'],
      plantCode: json['plantCode'],
      serialNumber: json['serialNumber'],
    );
  }
}

Dart Method Implementation

  • init() initializes the SDK with a license key. It should be called before any recognition methods. For Windows and Linux, it also downloads the model and template files if they are not already present.

    @override
      Future<int?> init(String key) async {
        if (Platform.isLinux || Platform.isWindows) {
          await download();
        }
        return await methodChannel.invokeMethod<int>('init', {'key': key});
      }
    
  • loadModel() sets the OCR mode by loading the appropriate template:

    enum ModelType { mrz, vin }
    
    @override
    Future<int?> loadModel({ModelType modelType = ModelType.mrz}) async {
      String templateName = "ReadPassportAndId";
    
      if (modelType == ModelType.vin) {
        templateName = "ReadVINText";
      }
    
      int ret = await methodChannel
          .invokeMethod('loadModel', {'template': templateName});
      return ret;
    }
    
  • recognizeFile() performs OCR on an image file. It takes the file path as a parameter and returns a list of recognized lines.

    @override
    Future<List<List<OcrLine>>?> recognizeFile(String filename) async {
      List<dynamic>? results = await methodChannel.invokeMethod('recognizeFile', {
        'filename': filename,
      });
    
      if (results == null || results.isEmpty) return [];
    
      return _resultWrapper(results);
    }
    
  • recognizeBuffer() performs OCR on a raw image buffer. It takes the image data, dimensions, stride, pixel format, and rotation as parameters. The method returns a list of recognized lines.

    @override
    Future<List<List<OcrLine>>?> recognizeBuffer(Uint8List bytes, int width,
        int height, int stride, int format, int rotation) async {
      List<dynamic>? results =
          await methodChannel.invokeMethod('recognizeBuffer', {
        'bytes': bytes,
        'width': width,
        'height': height,
        'stride': stride,
        'format': format,
        'rotation': rotation
      });
    
      if (results == null || results.isEmpty) return [];
    
      return _resultWrapper(results);
    }
    

Platform-Specific Code

Dynamsoft Capture Vision provides both synchronous (capture()) and asynchronous (startCapturing()) recognition APIs. On the web, both are asynchronous.

Desktop (Windows/Linux)

CCaptureVisionRouter *cvr = new CCaptureVisionRouter;
CFileFetcher *fileFetcher = new CFileFetcher();

// File
fileFetcher->SetFile(filename);

// Buffer
CImageData *imageData = new CImageData(stride * height, buffer, width, height, stride, getPixelFormat(format), rotation);
fileFetcher->SetFile(imageData);

// Start capturing
char errorMsg[512] = {0};
int errorCode = cvr->StartCapturing(modelName.c_str(), false, errorMsg, 512);
if (errorCode != 0)
{
    printf("StartCapturing: %s\n", errorMsg);
}

Android & iOS

  • Android

    // File
    final Result r = result;
        mExecutor.execute(new Runnable() {
          @Override
          public void run() {
            ArrayList<ArrayList<HashMap<String, Object>>> ret = null;
            CapturedResult results = null;
            try {
                results = mRouter.capture(fileName, templateName);
                ret = wrapResults(results);
            } catch (Exception e) {
                Log.e(TAG, e.toString());
            }
    
            final ArrayList<ArrayList<HashMap<String, Object>>> finalResults = ret;
            mHandler.post(new Runnable() {
              @Override
              public void run() {
                r.success(finalResults);
              }
            });
    
          }
        });
    
    // Buffer
    final Result r = result;
        mExecutor.execute(new Runnable() {
          @Override
          public void run() {
            ArrayList<ArrayList<HashMap<String, Object>>> ret = null;
            CapturedResult results = null;
    
            try {
                ImageData imageData = new ImageData();
                imageData.bytes = bytes;
                imageData.width = width;
                imageData.height = height;
                imageData.stride = stride;
                imageData.format = format;
                imageData.orientation = rotation;
                results = mRouter.capture(imageData, templateName);
                ret = wrapResults(results);
            } catch (Exception e) {
                Log.e(TAG, e.toString());
            }
            final ArrayList<ArrayList<HashMap<String, Object>>> finalResults = ret;
            mHandler.post(new Runnable() {
              @Override
              public void run() {
                r.success(finalResults);
              }
            });
    
          }
        });
    
    
  • iOS

    // File
    DispatchQueue.global().async {
                let filename: String = arguments.value(forKey: "filename") as! String
                let res = self.cvr.captureFromFile(filename, templateName: self.templateName)
                result(self.wrapResults(result: res))
            }
    
    // Buffer
    DispatchQueue.global().async {
                let buffer: FlutterStandardTypedData =
                    arguments.value(forKey: "bytes") as! FlutterStandardTypedData
                let width: Int = arguments.value(forKey: "width") as! Int
                let height: Int = arguments.value(forKey: "height") as! Int
                let stride: Int = arguments.value(forKey: "stride") as! Int
                let format: Int = arguments.value(forKey: "format") as! Int
                let rotation: Int = arguments.value(forKey: "rotation") as! Int
                let enumImagePixelFormat = ImagePixelFormat(rawValue: format)
                let imageData = ImageData.init()
                imageData.bytes = buffer.data
                imageData.width = UInt(width)
                imageData.height = UInt(height)
                imageData.stride = UInt(stride)
                imageData.format = enumImagePixelFormat!
                imageData.orientation = rotation
                let res = self.cvr.captureFromBuffer(imageData, templateName: self.templateName)
                result(self.wrapResults(result: res))
            }
    

Web

@JS()
@anonymous
class CapturedItem {
  external int get type;
  external String get text;
  external String get formatString;
  external Location get location;
  external int get angle;
  external Uint8List get bytes;
  external int get confidence;
}

@JS()
@anonymous
class CapturedResult {
  external List<CapturedItem> get items;
}

@JS('CVR.CaptureVisionRouter')
class CaptureVisionRouter {
  external static PromiseJsImpl<CaptureVisionRouter> createInstance();
  external PromiseJsImpl<CapturedResult> capture(dynamic data, String template);
}

// File
CapturedResult capturedResult = await handleThenable(_cvr!.capture(file, modelName));

// Buffer
final dsImage = jsify({
      'bytes': bytes,
      'width': width,
      'height': height,
      'stride': stride,
      'format': format,
      'orientation': rotation
    });

CapturedResult capturedResult = await handleThenable(_cvr!.capture(dsImage, modelName));

Updating the Example for MRZ and VIN Recognition

  1. Add ToggleButtons to switch between MRZ and VIN modes in home_page.dart:

    return Scaffold(
      body: Column(
        children: [
          title,
          description,
          Center(
            child: ToggleButtons(
              borderRadius: BorderRadius.circular(10),
              isSelected: [isMrzSelected, !isMrzSelected],
              selectedColor: Colors.white,
              fillColor: Colors.orange,
              color: Colors.grey,
              children: const [
                Padding(
                  padding: EdgeInsets.symmetric(horizontal: 20),
                  child: Text('MRZ'),
                ),
                Padding(
                  padding: EdgeInsets.symmetric(horizontal: 20),
                  child: Text('VIN'),
                ),
              ],
              onPressed: (index) {
                setState(() {
                  isMrzSelected = (index == 0);
                  if (isMrzSelected) {
                    switchModel(ModelType.mrz);
                  } else {
                    switchModel(ModelType.vin);
                  }
                });
              },
            ),
          ),
          ...
        ],
      ),
    );
    
  2. Request a trial license key for Dynamsoft Capture Vision and set it in global.dart:

    Future<int> initSDK() async {
      int? ret = await detector.init(
          "LICENSE-KEY");
    
      if (ret == 0) isLicenseValid = true;
      return await detector.loadModel(modelType: model) ?? -1;
    }
    
    
  3. Run the app on the target platform:

    cd example
    flutter run -d chrome    # Run on Web
    flutter run -d linux     # Run on Linux
    flutter run -d windows   # Run on Windows
    flutter run              # Run on default connected device (e.g., Android)
    

    Flutter Windows MRZ and VIN scanner

Source Code

https://github.com/yushulx/flutter_ocr_sdk