mobile_scanner

repository·develop·Indexed 22 days ago

https://github.com/juliansteenbakker/mobile_scanner

A high-performance Flutter plugin for barcode and QR code scanning across Android, iOS, macOS, and Web. It provides real-time detection and fine-grained control over camera lenses and scanning parameters via the MobileScanner widget and MobileScannerController. Features include flashlight toggle, zoom control, support for multiple barcode formats, and configurable web detection backends (Native BarcodeDetector, zxing-wasm, and ZXing-js).

Tokens
8.9K
Snippets
24
Records
40
Agent score
78%

What's inside mobile_scanner

  1. Overview of mobile_scanner

    develop
    mobile_scanner is a fast and lightweight Flutter plugin designed for scanning barcodes and QR codes using a device's camera. It provides real-time detection, support for multiple barcode formats, and customizable camera/scanner behavior. It is suitable for high-performance scanning applications across mobile and desktop platforms.
  2. Platform support and feature availability

    develop

    mobile_scanner supports Android, iOS, macOS, and Web. Note that feature availability varies by platform:

    FeatureAndroidiOSmacOSWeb
    analyzeImage
    returnImage
    scanWindow
    autoZoom
    lensType
    getSupportedLenses(facing:)
    getBestCloseRangeScanningLens✔ (normal)✔ (iOS 15+, else normal)✔ (normal)✘ (normal)
  3. Configure Android MLKit Barcode-scanning

    develop

    By default, mobile_scanner uses the bundled version of MLKit for Android, which is immediately available but increases app size by 3-10 MB.

    You can switch to the unbundled version to reduce app size by ~600KB. This version is downloaded via Google Play Services upon first use.

    To use the unbundled version, add the following line to your /android/gradle.properties file:

    dev.steenbakker.mobile_scanner.useUnbundled=true
  4. Run the mobile_scanner example project

    develop

    To run the official demonstration app to see how the plugin works in practice, follow these steps in your terminal:

    1. Clone the repository.
    2. Navigate to the example directory.
    3. Fetch dependencies.
    4. Run the application.
    git clone https://github.com/juliansteenbakker/mobile_scanner.git
    cd mobile_scanner/example/lib
    flutter pub get
    flutter run
  5. Manage MobileScanner lifecycle with WidgetsBindingObserver

    develop

    To prevent the scanner from running while the app is inactive, manually manage the lifecycle using WidgetsBindingObserver.

    Steps:

    1. Initialize MobileScannerController with autoStart: false.
    2. Mix in WidgetsBindingObserver to your State class.
    3. In didChangeAppLifecycleState:
      • When resumed: Listen to controller.barcodes and call controller.start().
      • When inactive: Cancel the subscription and call controller.stop().
    4. In initState: Add the observer and start the scanner.
    5. In dispose: Remove the observer, cancel subscriptions, and call controller.dispose().
    class MyState extends State<MyStatefulWidget> with WidgetsBindingObserver {
      final MobileScannerController controller = MobileScannerController(autoStart: false);
      StreamSubscription<Object?>? _subscription;
    
      @override
      void initState() {
        super.initState();
        WidgetsBinding.instance.addObserver(this);
        _subscription = controller.barcodes.listen(_handleBarcode);
        unawaited(controller.start());
      }
    
      @override
      void didChangeAppLifecycleState(AppLifecycleState state) {
        if (!controller.value.hasCameraPermission) return;
    
        switch (state) {
          case AppLifecycleState.detached:
          case AppLifecycleState.hidden:
          case AppLifecycleState.paused:
            return;
          case AppLifecycleState.resumed:
            _subscription = controller.barcodes.listen(_handleBarcode);
            unawaited(controller.start());
            break;
          case AppLifecycleState.inactive:
            unawaited(_subscription?.cancel());
            _subscription = null;
            unawaited(controller.stop());
            break;
        }
      }
    
      @override
      Future<void> dispose() async {
        WidgetsBinding.instance.removeObserver(this);
        unawaited(_subscription?.cancel());
        _subscription = null;
        super.dispose();
        await controller.dispose();
      }
    }
  6. Configure iOS permissions

    develop

    To use the camera and local gallery features on iOS, you must add the following keys to your Info.plist file (located at <project root>/ios/Runner/Info.plist):

    • NSCameraUsageDescription: A description of why your app needs camera access.
    • NSPhotoLibraryUsageDescription: A description of why your app needs access to the photo library (required if using image_picker for local gallery features).
    <key>NSCameraUsageDescription</key>
    <string>This app needs camera access to scan QR codes</string>
    
    <key>NSPhotoLibraryUsageDescription</key>
    <string>This app needs photos access to get QR code from photo library</string>
  7. Customize iOS launch screen assets

    develop

    To change the appearance of the launch screen in your iOS application, you can replace the existing image files in the example/ios/Runner/Assets.xcassets/LaunchImage.imageset/ directory with your own assets.

    Alternatively, you can manage these assets using Xcode:

    1. Open your Flutter project's iOS workspace using open ios/Runner.xcworkspace.
    2. In the Xcode Project Navigator, navigate to Runner/Assets.xcassets.
    3. Drag and drop your desired images into the asset catalog to replace the current launch images.
    open ios/Runner.xcworkspace
  8. Configure the scanWindow for barcode scanning

    develop

    The scanWindow property allows you to define a specific Rect area within the MobileScanner widget's layout. The scanner will only detect barcodes that intersect this rectangle.

    Important Notes:

    • Web Support: scanWindow is not supported on the web because the scanner does not expose barcode size information there.
    • Coordinate System: The rectangle is relative to the layout size of the MobileScanner widget in the widget tree, not the raw camera output size. The fit property (e.g., BoxFit.cover) affects how this window is mapped to the camera texture.
    • Performance: If updating the scan window causes performance issues, use scanWindowUpdateThreshold to prevent frequent updates when layout constraints change slightly.

    Example: Centered Scan Window

    To create a scan window that is centered and occupies a specific portion of the widget's size:

    LayoutBuilder(
      builder: (BuildContext context, BoxConstraints constraints) {
        final Size layoutSize = constraints.biggest;
    
        final double scanWindowWidth = layoutSize.width / 3;
        final double scanWindowHeight = layoutSize.height / 2;
    
        final Rect scanWindow = Rect.fromCenter(
          center: layoutSize.center(Offset.zero),
          width: scanWindowWidth,
          height: scanWindowHeight,
        );
    
        return MobileScanner(
          scanWindow: scanWindow,
          // ... other properties
        );
      },
    );
    LayoutBuilder(
      builder: (BuildContext context, BoxConstraints constraints) {
        final Size layoutSize = constraints.biggest;
    
        final double scanWindowWidth = layoutSize.width / 3;
        final double scanWindowHeight = layoutSize.height / 2;
    
        final Rect scanWindow = Rect.fromCenter(
          center: layoutSize.center(Offset.zero),
          width: scanWindowWidth,
          height: scanWindowHeight,
        );
      }
    );
  9. Understand the MobileScannerState object

    develop

    The MobileScannerState class represents the current status of a MobileScannerController. It provides real-time information about the camera hardware, the scanner's lifecycle, and potential errors. You can use this state to build reactive UIs that respond to camera changes, zoom levels, or permission issues.

    Key properties include:

    • isInitialized: Indicates if the scanner has successfully initialized (note: this is distinct from camera permission).
    • isRunning: true if the camera is currently active.
    • isStarting: true if the scanner is in the process of starting; use this to prevent duplicate calls to MobileScannerController.start().
    • hasCameraPermission: A helper getter that returns true if the scanner is initialized and no permissionDenied error is present.
    • cameraDirection: The current facing direction (CameraFacing).
    • torchState: The current state of the flashlight (TorchState).
    • zoomScale: The current zoom level.
    • error: Contains a MobileScannerException if something went wrong.
  10. Find the best lens for close-range scanning

    develop

    On devices with multiple cameras, some lenses are better suited for close-up scanning. You can query for the best lens and then switch to it.

    1. Use getBestCloseRangeScanningLens(facing: ...) to find the ideal CameraLensType.
    2. Use getSupportedLenses(facing: ...) to verify the lens is actually available on the device.
    3. Use switchCamera(SelectCamera(...)) to apply the change.
    final bestLens = await controller.getBestCloseRangeScanningLens(facing: CameraFacing.back);
    final supported = await controller.getSupportedLenses(facing: CameraFacing.back);
    
    if (bestLens != null && supported.contains(bestLens)) {
      await controller.switchCamera(
        SelectCamera(facingDirection: CameraFacing.back, lensType: bestLens),
      );
    }