barcode_scanner_picklist.dart
5.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
import 'package:mobile_scanner_example/picklist/classes/barcode_at_center.dart';
import 'package:mobile_scanner_example/picklist/widgets/crosshair.dart';
import 'package:mobile_scanner_example/scanner_error_widget.dart';
// This sample implements picklist functionality.
// The scanning can temporarily be suspended by the user by touching the screen.
// When the scanning is active, the crosshair turns red.
// When the scanning is suspended, the crosshair turns green.
// A barcode has to touch the center of viewfinder to be scanned.
// Therefore the Crosshair widget needs to be placed at the center of the
// MobileScanner widget to visually line up.
class BarcodeScannerPicklist extends StatefulWidget {
const BarcodeScannerPicklist({super.key});
@override
State<BarcodeScannerPicklist> createState() => _BarcodeScannerPicklistState();
}
class _BarcodeScannerPicklistState extends State<BarcodeScannerPicklist> {
final _mobileScannerController = MobileScannerController(
// The controller is started from the initState method.
autoStart: false,
// The know the placing of the barcodes, we need to know the size of the
// canvas they are placed on. Unfortunately the only known reliable way
// to get the dimensions, is to receive the complete image from the native
// side.
// https://github.com/juliansteenbakker/mobile_scanner/issues/1183
returnImage: true,
);
// On this subscription the barcodes are received.
StreamSubscription<Object?>? _subscription;
// This boolean indicates if the detection of barcodes is enabled or
// temporarily suspended.
final _scannerEnabled = ValueNotifier(true);
// This boolean is used to prevent multiple pops.
var _validBarcodeFound = false;
@override
void initState() {
// Enable and disable scanning on the native side, so we don't get a stream
// of images when not needed. This also improves the behavior (false
// positives) when the user switches quickly to another barcode after
// enabling the scanner by releasing the finger.
_scannerEnabled.addListener(() {
_scannerEnabled.value
? _mobileScannerController.updateScanWindow(null)
: _mobileScannerController.updateScanWindow(Rect.zero);
});
// Lock to portrait (may not work on iPad with multitasking).
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
// Get a stream subscription and listen to received barcodes.
_subscription = _mobileScannerController.barcodes.listen(_handleBarcodes);
super.initState();
// Start the controller to start scanning.
unawaited(_mobileScannerController.start());
}
@override
void dispose() {
// Cancel the stream subscription.
unawaited(_subscription?.cancel());
_subscription = null;
super.dispose();
// Dispose the controller.
_mobileScannerController.dispose();
}
// Check the list of barcodes only if scannerEnables is true.
// Only take the barcode that is at the center of the image.
// Return the barcode found to the calling page with the help of the
// navigator.
void _handleBarcodes(BarcodeCapture barcodeCapture) {
// Discard all events when the scanner is disabled or when already a valid
// barcode is found.
if (!_scannerEnabled.value || _validBarcodeFound) {
return;
}
final barcode = findBarcodeAtCenter(barcodeCapture);
if (barcode != null) {
_validBarcodeFound = true;
Navigator.of(context).pop(barcode);
}
}
@override
Widget build(BuildContext context) {
return PopScope(
onPopInvokedWithResult: (didPop, result) {
// Reset the page orientation to the system default values, when this page is popped
if (!didPop) {
return;
}
SystemChrome.setPreferredOrientations(<DeviceOrientation>[]);
},
child: Scaffold(
appBar: AppBar(title: const Text('Picklist scanner')),
backgroundColor: Colors.black,
body: Listener(
// Detect if the user touches the screen and disable/enable the scanner accordingly
behavior: HitTestBehavior.opaque,
onPointerDown: (_) => _scannerEnabled.value = false,
onPointerUp: (_) => _scannerEnabled.value = true,
onPointerCancel: (_) => _scannerEnabled.value = true,
// A stack containing the image feed and the crosshair
// The location of the crosshair must be at the center of the MobileScanner, otherwise the detection area and the visual representation do not line up.
child: Stack(
fit: StackFit.expand,
children: [
MobileScanner(
controller: _mobileScannerController,
errorBuilder: (context, error, child) =>
ScannerErrorWidget(error: error),
fit: BoxFit.contain,
),
Crosshair(_scannerEnabled),
],
),
),
),
);
}
}