AI prompts
base on A universal scanner for Flutter based on MLKit. Uses CameraX on Android and AVFoundation on iOS. # mobile_scanner
[](https://pub.dev/packages/mobile_scanner)
[](https://pub.dev/packages/mobile_scanner)
[](https://github.com/juliansteenbakker/mobile_scanner/actions/workflows/code-coverage.yml)
[](https://pub.dev/packages/lint)
[](https://codecov.io/gh/juliansteenbakker/mobile_scanner)
[](https://github.com/sponsors/juliansteenbakker)
A fast and lightweight Flutter plugin for scanning barcodes and QR codes using the device’s camera. It supports multiple barcode formats, real-time detection, and customization options for an optimized scanning experience on multiple platforms.
## Features
- Fast barcode and QR code scanning
- Supports multiple barcode formats
- Real-time detection
- Customizable camera and scanner behavior
See the [examples](example/README.md) for runnable examples of various usages, such as the basic usage, applying a scan window, or retrieving images from the barcodes.
## Platform Support
| Android | iOS | macOS | Web | Linux | Windows |
|---------|-----|-------|-----|-------|---------|
| ✔ | ✔ | ✔ | ✔ | :x: | :x: |
### Features Supported
See the example app for detailed implementation information.
| Features | Android | iOS | macOS | Web |
|--------------|--------------------|--------------------|--------------------|-----|
| analyzeImage | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: |
| returnImage | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: |
| scanWindow | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: |
| autoZoom | :heavy_check_mark: | :x: | :x: | :x: |
## Installation
Add the dependency in your `pubspec.yaml` file:
```
dependencies:
mobile_scanner: ^<latest_version>
```
Then run:
`flutter pub get`
## Configuration
### Android
This package uses by default the **bundled version** of MLKit Barcode-scanning for Android. This version is immediately available to the device. But it will increase the size of the app by approximately 3 to 10 MB.
The alternative is to use the **unbundled version** of MLKit Barcode-scanning for Android. This version is downloaded on first use via Google Play Services. It increases the app size by around 600KB.
[You can read more about the difference between the two versions here.](https://developers.google.com/ml-kit/vision/barcode-scanning/android)
To use the **unbundled version** of the MLKit Barcode-scanning, add the following line to your `/android/gradle.properties` file:
```
dev.steenbakker.mobile_scanner.useUnbundled=true
```
### iOS
Since the scanner needs to use the camera, add the following keys to your Info.plist file. (located in <project root>/ios/Runner/Info.plist)
NSCameraUsageDescription - describe why your app needs access to the camera. This is called Privacy - Camera Usage Description in the visual editor.
If you want to use the local gallery feature from [image_picker](https://pub.dev/packages/image_picker), you also need to add the following key.
NSPhotoLibraryUsageDescription - describe why your app needs permission for the photo library. This is called Privacy - Photo Library Usage Description in the visual editor.
Example,
```
<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>
```
### macOS
Ensure that you granted camera permission in XCode -> Signing & Capabilities:
<img width="696" alt="Screenshot of XCode where Camera is checked" src="https://user-images.githubusercontent.com/24459435/193464115-d76f81d0-6355-4cb2-8bee-538e413a3ad0.png">
### Web
As of version 5.0.0 adding the barcode scanning library script to the `index.html` is no longer required,
as the script is automatically loaded on first use.
#### Providing a mirror for the barcode scanning library
If a different mirror is needed to load the barcode scanning library,
the source URL can be set beforehand.
```dart
import 'package:flutter/foundation.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
final String scriptUrl = // ...
if (kIsWeb) {
MobileScannerPlatform.instance.setBarcodeLibraryScriptUrl(scriptUrl);
}
```
## Usage
### Simple
Import the package with `package:mobile_scanner/mobile_scanner.dart`. The only required parameter is `onDetect`, which returns the scanned barcode or qr code.
```dart
MobileScanner(
onDetect: (result) {
print(result.barcodes.first.rawValue);
},
),
```
### Advanced
If you want more control over the scanner, you need to create a new `MobileScannerController` controller. The controller contains multiple parameters to adjust the scanner.
```dart
final MobileScannerController controller = MobileScannerController(
cameraResolution: size,
detectionSpeed: detectionSpeed,
detectionTimeoutMs: detectionTimeout,
formats: selectedFormats,
returnImage: returnImage,
torchEnabled: true,
invertImage: invertImage,
autoZoom: autoZoom,
);
```
```dart
MobileScanner(
controller: controller,
onDetect: (result) {
print(result.barcodes.first.rawValue);
},
);
```
#### Lifecycle changes
If you want to pause the scanner when the app is inactive, you need to use `WidgetsBindingObserver`.
First, provide a `StreamSubscription` for the barcode events. Also, make sure to create a `MobileScannerController` with `autoStart` set to false, since we will be handling the lifecycle ourself.
```dart
final MobileScannerController controller = MobileScannerController(
autoStart: false,
);
StreamSubscription<Object?>? _subscription;
```
Then, ensure that your `State` class mixes in `WidgetsBindingObserver`, to handle lifecyle changes, and add the required logic to the `didChangeAppLifecycleState` function:
```dart
class MyState extends State<MyStatefulWidget> with WidgetsBindingObserver {
// ...
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
// If the controller is not ready, do not try to start or stop it.
// Permission dialogs can trigger lifecycle changes before the controller is ready.
if (!controller.value.hasCameraPermission) {
return;
}
switch (state) {
case AppLifecycleState.detached:
case AppLifecycleState.hidden:
case AppLifecycleState.paused:
return;
case AppLifecycleState.resumed:
// Restart the scanner when the app is resumed.
// Don't forget to resume listening to the barcode events.
_subscription = controller.barcodes.listen(_handleBarcode);
unawaited(controller.start());
case AppLifecycleState.inactive:
// Stop the scanner when the app is paused.
// Also stop the barcode events subscription.
unawaited(_subscription?.cancel());
_subscription = null;
unawaited(controller.stop());
}
}
// ...
}
```
Then, start the scanner in `void initState()`:
```dart
@override
void initState() {
super.initState();
// Start listening to lifecycle changes.
WidgetsBinding.instance.addObserver(this);
// Start listening to the barcode events.
_subscription = controller.barcodes.listen(_handleBarcode);
// Finally, start the scanner itself.
unawaited(controller.start());
}
```
Finally, dispose of the the `MobileScannerController` when you are done with it.
```dart
@override
Future<void> dispose() async {
// Stop listening to lifecycle changes.
WidgetsBinding.instance.removeObserver(this);
// Stop listening to the barcode events.
unawaited(_subscription?.cancel());
_subscription = null;
// Dispose the widget itself.
super.dispose();
// Finally, dispose of the controller.
await controller.dispose();
}
```
", Assign "at most 3 tags" to the expected json: {"id":"9466","tags":[]} "only from the tags list I provide: [{"id":77,"name":"3d"},{"id":89,"name":"agent"},{"id":17,"name":"ai"},{"id":54,"name":"algorithm"},{"id":24,"name":"api"},{"id":44,"name":"authentication"},{"id":3,"name":"aws"},{"id":27,"name":"backend"},{"id":60,"name":"benchmark"},{"id":72,"name":"best-practices"},{"id":39,"name":"bitcoin"},{"id":37,"name":"blockchain"},{"id":1,"name":"blog"},{"id":45,"name":"bundler"},{"id":58,"name":"cache"},{"id":21,"name":"chat"},{"id":49,"name":"cicd"},{"id":4,"name":"cli"},{"id":64,"name":"cloud-native"},{"id":48,"name":"cms"},{"id":61,"name":"compiler"},{"id":68,"name":"containerization"},{"id":92,"name":"crm"},{"id":34,"name":"data"},{"id":47,"name":"database"},{"id":8,"name":"declarative-gui "},{"id":9,"name":"deploy-tool"},{"id":53,"name":"desktop-app"},{"id":6,"name":"dev-exp-lib"},{"id":59,"name":"dev-tool"},{"id":13,"name":"ecommerce"},{"id":26,"name":"editor"},{"id":66,"name":"emulator"},{"id":62,"name":"filesystem"},{"id":80,"name":"finance"},{"id":15,"name":"firmware"},{"id":73,"name":"for-fun"},{"id":2,"name":"framework"},{"id":11,"name":"frontend"},{"id":22,"name":"game"},{"id":81,"name":"game-engine "},{"id":23,"name":"graphql"},{"id":84,"name":"gui"},{"id":91,"name":"http"},{"id":5,"name":"http-client"},{"id":51,"name":"iac"},{"id":30,"name":"ide"},{"id":78,"name":"iot"},{"id":40,"name":"json"},{"id":83,"name":"julian"},{"id":38,"name":"k8s"},{"id":31,"name":"language"},{"id":10,"name":"learning-resource"},{"id":33,"name":"lib"},{"id":41,"name":"linter"},{"id":28,"name":"lms"},{"id":16,"name":"logging"},{"id":76,"name":"low-code"},{"id":90,"name":"message-queue"},{"id":42,"name":"mobile-app"},{"id":18,"name":"monitoring"},{"id":36,"name":"networking"},{"id":7,"name":"node-version"},{"id":55,"name":"nosql"},{"id":57,"name":"observability"},{"id":46,"name":"orm"},{"id":52,"name":"os"},{"id":14,"name":"parser"},{"id":74,"name":"react"},{"id":82,"name":"real-time"},{"id":56,"name":"robot"},{"id":65,"name":"runtime"},{"id":32,"name":"sdk"},{"id":71,"name":"search"},{"id":63,"name":"secrets"},{"id":25,"name":"security"},{"id":85,"name":"server"},{"id":86,"name":"serverless"},{"id":70,"name":"storage"},{"id":75,"name":"system-design"},{"id":79,"name":"terminal"},{"id":29,"name":"testing"},{"id":12,"name":"ui"},{"id":50,"name":"ux"},{"id":88,"name":"video"},{"id":20,"name":"web-app"},{"id":35,"name":"web-server"},{"id":43,"name":"webassembly"},{"id":69,"name":"workflow"},{"id":87,"name":"yaml"}]" returns me the "expected json"