qr_scanner_kit 0.3.0
qr_scanner_kit: ^0.3.0 copied to clipboard
A ready-to-use QR scanner screen and viewfinder overlay built on mobile_scanner, with torch, camera switching, and a simple scan callback.
qr_scanner_kit #
A QR scanner screen and viewfinder overlay for Flutter, built on
mobile_scanner. You get torch
control, camera switching and one callback for the scanned value. The package
grew out of the Rocketbot app, so the scanner looks and behaves the same
everywhere it's used.
Features #
QrScanner: a full-screen scanner with a live camera preview- Torch toggle and front/back camera switching in the app bar
QrImageScanner: gallery scanning from an image file- Barcode format selection through
formats - Haptic feedback on accepted scans
- Validation hooks:
isAcceptedandonRejected - Continuous scanning mode with a duplicate window for repeated codes
BarcodeOverlay: aShapeBorderthat dims the preview and draws corner brackets around a centred cut-outScanGate: a helper that lets a callback run at most onceScanDeduper: a helper that suppresses repeated codes in a time window- One-shot
onScancallback withpopOnScancontrol - Optional hint text over the camera preview
- Optional injection of a
MobileScannerController - No state-management dependency, only plain Flutter widgets
Install #
flutter pub add qr_scanner_kit
Permissions #
mobile_scanner needs camera access at runtime.
Android #
mobile_scanner declares the CAMERA permission in its own manifest and asks
for it at runtime, so you don't need to change anything. If your app handles
the permission prompt itself, ask before you open QrScanner.
iOS #
Add a usage description to ios/Runner/Info.plist. App Review rejects the app
without this entry:
<key>NSCameraUsageDescription</key>
<string>This app needs camera access to scan QR codes.</string>
Usage #
import 'package:flutter/material.dart';
import 'package:qr_scanner_kit/qr_scanner_kit.dart';
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (BuildContext context) {
return QrScanner(
title: 'Scan QR code',
onScan: (String value) {
print('Scanned: $value');
},
);
},
),
);
Gallery scanning #
QrImageScanner.scanImageFile decodes one barcode from an image file:
final String? value = await QrImageScanner.scanImageFile('/path/to/code.png');
The method creates its own controller, analyses the file and disposes the
controller again. A running camera scan keeps its state. Image analysis works
on Android, physical iOS devices and macOS; other platforms throw an
UnsupportedError. The iOS Simulator can't analyse images.
Barcode formats #
QrScanner detects every barcode format by default. Pass formats to narrow
it down. The list goes to the controller the widget creates:
import 'package:mobile_scanner/mobile_scanner.dart';
QrScanner(
formats: const [BarcodeFormat.qrCode, BarcodeFormat.ean13],
onScan: (String value) {
print('Scanned: $value');
},
);
An injected controller keeps its own formats, so formats does nothing in
that case.
Haptics #
QrScanner plays a light haptic impact when it accepts a code. This is on by
default. Set hapticsEnabled: false to turn it off:
QrScanner(
hapticsEnabled: false,
onScan: (String value) {
print('Scanned: $value');
},
);
Validation hooks #
Use isAccepted to filter scanned values. The scanner checks it before the
one-shot gate. A rejected value uses up no scan attempt, plays no haptics and
never reaches onScan. The scanner stays ready for the next code, and
onRejected receives every value that failed the check:
QrScanner(
isAccepted: (String value) => value.startsWith('SHOP-'),
onRejected: (String value) => print('Rejected: $value'),
onScan: (String value) => print('Accepted: $value'),
);
Continuous scanning #
Set continuous: true to keep the camera running and report every accepted
code. The same code is ignored for duplicateWindow (two seconds by default),
so a barcode held in front of the camera doesn't flood the callback:
QrScanner(
continuous: true,
duplicateWindow: const Duration(seconds: 3),
onScan: (String value) => results.add(value),
);
You can also draw a short instruction over the preview:
QrScanner(
hint: 'Point the camera at a code',
onScan: (String value) => print(value),
);
Camera errors #
When the camera cannot start, QrScanner shows a message and a retry button.
A denied permission gets its own explanation. Pass errorBuilder to replace
the view, for example to link to the system settings:
QrScanner(
errorBuilder: (context, error) => Text(
error.errorCode == MobileScannerErrorCode.permissionDenied
? 'Open settings and allow the camera'
: 'Camera unavailable',
),
onScan: (String value) => print(value),
);
API #
QrScanner #
| Parameter | Type | Default | Purpose |
|---|---|---|---|
onScan |
ValueChanged<String> |
required | Receives the first decoded non-empty barcode value. |
title |
String? |
'Scan QR code' |
App bar title. |
overlay |
BarcodeOverlay? |
red corner brackets | Overlay on the camera preview. |
backgroundColor |
Color? |
Theme.canvasColor |
Background colour of the scanner body. |
popOnScan |
bool |
true |
Pops the route after the first scan. |
controller |
MobileScannerController? |
created internally | Camera controller. The widget respects its autoStart flag. |
formats |
List<BarcodeFormat>? |
null (all formats) |
Formats for the internally created controller. |
hapticsEnabled |
bool |
true |
Plays a light haptic impact on an accepted scan. |
isAccepted |
bool Function(String)? |
null (accept all) |
Filters values before the one-shot gate. |
onRejected |
void Function(String)? |
null |
Receives values that isAccepted rejects. |
continuous |
bool |
false |
Keeps scanning and reports every accepted code. |
duplicateWindow |
Duration |
2 seconds | Ignores repeats of one code in continuous mode. |
hint |
String? |
null |
Instruction drawn over the preview. |
hintStyle |
TextStyle? |
white70 body text | Style of hint. |
scanWindow |
Rect? |
null (whole preview) |
Restricts detection to a rectangle. Not supported on the web. |
errorBuilder |
Widget Function(BuildContext, MobileScannerException)? |
default error view | Replaces the built-in camera error view. |
In the default one-shot mode, onScan runs at most once per QrScanner. The
widget reports the first decoded barcode and cancels the subscription. With
popOnScan: false, the screen stays open but reports nothing more. Push a new
QrScanner to scan again.
With continuous: true, the scanner keeps running, reports each accepted code
once per duplicateWindow and never pops the route. popOnScan applies only
to the one-shot mode.
When you don't pass a controller, QrScanner creates one with
autoStart: false. The widget starts and stops it with the app lifecycle and
disposes it when the widget goes away. Don't dispose an injected controller
yourself, and don't reuse it after the scanner closes. To inject one, import
package:mobile_scanner/mobile_scanner.dart; the package doesn't re-export
mobile_scanner.
QrImageScanner #
QrImageScanner.scanImageFile(path) decodes one barcode from an image file
and returns its value, or null when the image has no barcode. The method
creates its own controller and disposes it after the scan. A running camera
scan keeps its state. Image analysis works on Android, physical iOS devices
and macOS; other platforms throw an UnsupportedError.
BarcodeOverlay #
| Parameter | Default | Purpose |
|---|---|---|
borderColor |
Colors.red |
Colour of the corner brackets. |
borderWidth |
3.0 |
Stroke width of the corner brackets. |
overlayColor |
translucent black | Dim colour outside the cut-out. |
borderRadius |
0 |
Corner radius of the cut-out and brackets. |
borderLength |
40 |
Length of each corner bracket. |
cutOutSize, cutOutWidth, cutOutHeight |
250 | Cut-out dimensions. |
cutOutBottomOffset |
0 |
Vertical offset of the cut-out. |
Use cutOutSize, or use cutOutWidth and cutOutHeight. Not both.
ScanGate #
ScanGate keeps the one-shot guard in a small, testable helper. tryHandle()
returns true once and false on later calls until you call reset().
ScanDeduper #
ScanDeduper suppresses repeated codes in continuous mode. isDuplicate()
returns true when the same value arrived inside the window; reset() clears
the history. Pass clock to keep tests instant.
Example #
A runnable app in example/ pushes the scanner and shows the last
scanned value. The example ships without platform directories, so generate
them before running it on a device with a camera:
cd example
flutter create . --platforms=android,ios
flutter run
Attribution #
BarcodeOverlay is a modified copy of OverlayShape from the
ai_barcode_scanner package
(version 5.2.2, Apache-2.0). It was renamed, documented and adjusted. The full
Apache-2.0 licence text is in THIRD_PARTY_NOTICES.
The package itself is MIT licensed. See LICENSE. Only the derived
BarcodeOverlay remains under Apache-2.0.