passport_scanner 1.1.0 copy "passport_scanner: ^1.1.0" to clipboard
passport_scanner: ^1.1.0 copied to clipboard

Scans passports with the device camera or from a gallery photo and extracts the MRZ (name, document number, nationality, dates) on Android and iOS.

Passport Scanner #

Easily scan passports to extract their information from the MRZ code. This package reads the MRZ (Machine Readable Zone) and parses it to extract the information, either live from the device camera or from a still image picked from the gallery.

Setup #

Since this package is using ML Kit for text recognition, you must satisfy its requirements:

iOS #

  • Minimum iOS Deployment Target: 15.5
  • Xcode 15.3.0 or newer
  • Swift 5
  • ML Kit does not support 32-bit architectures (i386 and armv7). ML Kit does support 64-bit architectures (x86_64 and arm64). Check this list to see if your device has the required device capabilities. More info here.

Your Podfile should look like this:

platform :ios, '15.5'  # or newer version

...

# add this line:
$iOSVersion = '15.5'  # or newer version

post_install do |installer|
  # add these lines:
  installer.pods_project.build_configurations.each do |config|
    config.build_settings["EXCLUDED_ARCHS[sdk=*]"] = "armv7"
    config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = $iOSVersion
  end

  installer.pods_project.targets.each do |target|
    flutter_additional_ios_build_settings(target)

    # add these lines:
    target.build_configurations.each do |config|
      if Gem::Version.new($iOSVersion) > Gem::Version.new(config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'])
        config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = $iOSVersion
      end
    end

  end
end

Notice that the minimum IPHONEOS_DEPLOYMENT_TARGET is 15.5, you can set it to something newer but not older.

Android #

  • minSdkVersion: 21
  • targetSdkVersion: 35
  • compileSdkVersion: 35

Camera and photo permissions #

There is no need to request permissions yourself; the camera and the photo picker each ask for what they need.

On iOS, add the usage descriptions to your Info.plist:

<key>NSCameraUsageDescription</key>
<string>The camera is used to scan the machine-readable zone of a passport.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Photos are read so a passport you already photographed can be scanned.</string>

The photo entry is only needed if you use the gallery scan.

Usage #

Scanning with the camera #

Add the PassportScannerWidget to your scaffold and pass an onScanned listener to get the result data. onScanned receives the parsed MRZResult and the path of a JPEG of the scanned passport page, or null if saving the image failed:

Scaffold(
  appBar: AppBar(title: Text('Passport Scanner')),
  body: PassportScannerWidget(
    precision: 2,        // identical validated reads required before success
    showFlashButton: true,
    onScanned: (result, imagePath) {
      print('Scanned: ${result.documentNumber}, ${result.givenNames} ${result.surnames}');
      print('Passport image saved at: $imagePath');
    },
    onParsingFailed: (lines) => print('Could not parse: $lines'),
    onNoMrzFound: () => print('Aim the MRZ inside the frame'),
  ),
)
Parameter Type Default Description
onScanned void Function(MRZResult, String? imagePath) required Called once when the same MRZ has been read precision times. imagePath is a JPEG of the passport page alone — its edges detected around the MRZ and warped upright to the ID-3 aspect ratio — in the temporary directory. Falls back to the scan area as framed when the edges cannot be found.
onParsingFailed void Function(List<String> lines)? null Called when MRZ-shaped lines were found but failed check-digit validation, with the lines as fed to the parser. Throttled to once every 2 seconds.
onNoMrzFound void Function()? null Called when an analyzed frame contains no MRZ-shaped lines. Throttled to once every 2 seconds.
precision int 2 Number of identical, validated reads required before onScanned fires. Must be at least 1; 1 accepts the first validated read.
showFlashButton bool false Whether to show a torch toggle in the top-left corner of the preview.

onScanned fires only once. To scan another passport, rebuild the widget (for example, push a new scanner screen).

The result is an MRZResult object, and it includes this information:

documentType
countryCode
surnames
givenNames
documentNumber
nationalityCountryCode
birthDate
sex
expiryDate
personalNumber
personalNumber2

scanPassportFromGallery() opens the system photo picker and reads the MRZ from whatever the user chooses — no widget, no camera:

final scan = await scanPassportFromGallery();

if (scan.isSuccess) {
  final MRZResult result = scan.result!;
  print('Scanned: ${result.documentNumber}, ${result.givenNames} ${result.surnames}');
  print('Passport page: ${scan.imagePath}'); // the page alone, cut out of the photo
  print('Original photo: ${scan.sourcePath}');
} else {
  switch (scan.failure!) {
    case PassportScanFailure.cancelled:      // the picker was dismissed
    case PassportScanFailure.unreadableImage: // missing or undecodable file
    case PassportScanFailure.noMrzFound:      // no MRZ-shaped lines in the image
    case PassportScanFailure.invalidMrz:      // lines found, check digits failed
  }
}

Every returned result is check-digit validated, exactly like a camera scan. On success imagePath is a JPEG of the passport page alone, cut out of the photo around the MRZ and warped upright, just like a camera capture; sourcePath is the photo as picked. If the page's edges cannot be found, imagePath is the source photo. The image is read as stored first, then retried at 90°, 270° and 180°, so a photo that was taken sideways or upside down still scans; pass tryRotations: false to skip those retries. On failure, scan.mrzLines holds the lines as they were fed to the parser, which is useful for diagnostics.

To scan an image you already have on disk, use scanPassportImage(path). When scanning several images in a row, create one PassportImageScanner, call scanFile / scanFromGallery on it and dispose() it when done — that reuses a single ML Kit recognizer instead of creating one per image:

final scanner = PassportImageScanner();
for (final path in paths) {
  final scan = await scanner.scanFile(path);
  ...
}
await scanner.dispose();

Support Us #

This package was created inside OpenCode. You can support us by liking it on Pub, starring it on GitHub, sharing ideas on how we could enhance a certain functionality, or reporting issues and creating pull requests.

2
likes
160
points
164
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Scans passports with the device camera or from a gallery photo and extracts the MRZ (name, document number, nationality, dates) on Android and iOS.

Repository (GitHub)
View/report issues

License

BSD-3-Clause (license)

Dependencies

camerawesome, flutter, google_mlkit_commons, google_mlkit_text_recognition, image, image_picker, mrz_parser, path_provider

More

Packages that depend on passport_scanner