Julian Steenbakker

refactor: revert autoStart and onDetect. See changelog for more

  1 +## 5.1.0
  2 +
  3 +This updates reverts a few breaking changes made in v5.0.0 in order to keep things simple.
  4 +
  5 +* The `onDetect` method has been reinstated in the `MobileScanner` widget, but is nullable. You can
  6 +still listen to `MobileScannerController.barcodes` directly by passing null to this parameter.
  7 +* The `autoStart` attribute has been reinstated in the `MobileScannerController` and defaults to true. However, if you want
  8 +to control which camera is used on start, or you want to manage the lifecycle yourself, you should set
  9 +autoStart to false and manually call `MobileScannerController.start({CameraFacing? cameraDirection})`.
  10 +* The `controller` is no longer required in the `MobileScanner` widget. However if provided, the user should take care
  11 +of disposing it.
  12 +* [Android] Revert Gradle 8 back to Gradle 7, to be inline with most Flutter plugins and prevent build issues.
  13 +* [Android] Revert Kotlin back from 1.9 to 1.7 to be inline with most Flutter plugins. Special 1.9 functionality
  14 +has been refactored to be compatible with 1.7.
  15 +
  16 +
1 ## 5.0.2 17 ## 5.0.2
2 18
3 Bugs fixed: 19 Bugs fixed:
@@ -7,6 +7,10 @@ @@ -7,6 +7,10 @@
7 7
8 A universal scanner for Flutter based on MLKit. Uses CameraX on Android and AVFoundation on iOS. 8 A universal scanner for Flutter based on MLKit. Uses CameraX on Android and AVFoundation on iOS.
9 9
  10 +## Breaking Changes V5.0.0
  11 +
  12 +Version 5.0.0 brings some breaking changes. Please see the changelog for an overview.
  13 +
10 ## Features Supported 14 ## Features Supported
11 15
12 See the example app for detailed implementation information. 16 See the example app for detailed implementation information.
@@ -13,7 +13,8 @@ class BarcodeScannerPageView extends StatefulWidget { @@ -13,7 +13,8 @@ class BarcodeScannerPageView extends StatefulWidget {
13 } 13 }
14 14
15 class _BarcodeScannerPageViewState extends State<BarcodeScannerPageView> { 15 class _BarcodeScannerPageViewState extends State<BarcodeScannerPageView> {
16 - final MobileScannerController controller = MobileScannerController(); 16 + final MobileScannerController controller =
  17 + MobileScannerController(autoStart: false);
17 18
18 final PageController pageController = PageController(); 19 final PageController pageController = PageController();
19 20
  1 +import 'dart:async';
  2 +
  3 +import 'package:flutter/material.dart';
  4 +import 'package:mobile_scanner/mobile_scanner.dart';
  5 +import 'package:mobile_scanner_example/scanner_button_widgets.dart';
  6 +import 'package:mobile_scanner_example/scanner_error_widget.dart';
  7 +
  8 +class BarcodeScannerSimple extends StatefulWidget {
  9 + const BarcodeScannerSimple({super.key});
  10 +
  11 + @override
  12 + State<BarcodeScannerSimple> createState() => _BarcodeScannerSimpleState();
  13 +}
  14 +
  15 +class _BarcodeScannerSimpleState extends State<BarcodeScannerSimple> {
  16 + Barcode? _barcode;
  17 +
  18 + Widget _buildBarcode(Barcode? value) {
  19 + if (value == null) {
  20 + return const Text(
  21 + 'Scan something!',
  22 + overflow: TextOverflow.fade,
  23 + style: TextStyle(color: Colors.white),
  24 + );
  25 + }
  26 +
  27 + return Text(
  28 + value.displayValue ?? 'No display value.',
  29 + overflow: TextOverflow.fade,
  30 + style: const TextStyle(color: Colors.white),
  31 + );
  32 + }
  33 +
  34 + void _handleBarcode(BarcodeCapture barcodes) {
  35 + if (mounted) {
  36 + setState(() {
  37 + _barcode = barcodes.barcodes.firstOrNull;
  38 + });
  39 + }
  40 + }
  41 +
  42 + @override
  43 + Widget build(BuildContext context) {
  44 + return Scaffold(
  45 + appBar: AppBar(title: const Text('Simple scanner')),
  46 + backgroundColor: Colors.black,
  47 + body: Stack(
  48 + children: [
  49 + MobileScanner(
  50 + onDetect: _handleBarcode,
  51 + ),
  52 + Align(
  53 + alignment: Alignment.bottomCenter,
  54 + child: Container(
  55 + alignment: Alignment.bottomCenter,
  56 + height: 100,
  57 + color: Colors.black.withOpacity(0.4),
  58 + child: Row(
  59 + mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  60 + children: [
  61 + Expanded(child: Center(child: _buildBarcode(_barcode))),
  62 + ],
  63 + ),
  64 + ),
  65 + ),
  66 + ],
  67 + ),
  68 + );
  69 + }
  70 +}
@@ -3,6 +3,7 @@ import 'package:mobile_scanner_example/barcode_scanner_controller.dart'; @@ -3,6 +3,7 @@ import 'package:mobile_scanner_example/barcode_scanner_controller.dart';
3 import 'package:mobile_scanner_example/barcode_scanner_listview.dart'; 3 import 'package:mobile_scanner_example/barcode_scanner_listview.dart';
4 import 'package:mobile_scanner_example/barcode_scanner_pageview.dart'; 4 import 'package:mobile_scanner_example/barcode_scanner_pageview.dart';
5 import 'package:mobile_scanner_example/barcode_scanner_returning_image.dart'; 5 import 'package:mobile_scanner_example/barcode_scanner_returning_image.dart';
  6 +import 'package:mobile_scanner_example/barcode_scanner_simple.dart';
6 import 'package:mobile_scanner_example/barcode_scanner_window.dart'; 7 import 'package:mobile_scanner_example/barcode_scanner_window.dart';
7 import 'package:mobile_scanner_example/barcode_scanner_zoom.dart'; 8 import 'package:mobile_scanner_example/barcode_scanner_zoom.dart';
8 import 'package:mobile_scanner_example/mobile_scanner_overlay.dart'; 9 import 'package:mobile_scanner_example/mobile_scanner_overlay.dart';
@@ -31,6 +32,16 @@ class MyHome extends StatelessWidget { @@ -31,6 +32,16 @@ class MyHome extends StatelessWidget {
31 onPressed: () { 32 onPressed: () {
32 Navigator.of(context).push( 33 Navigator.of(context).push(
33 MaterialPageRoute( 34 MaterialPageRoute(
  35 + builder: (context) => const BarcodeScannerSimple(),
  36 + ),
  37 + );
  38 + },
  39 + child: const Text('MobileScanner Simple'),
  40 + ),
  41 + ElevatedButton(
  42 + onPressed: () {
  43 + Navigator.of(context).push(
  44 + MaterialPageRoute(
34 builder: (context) => const BarcodeScannerListView(), 45 builder: (context) => const BarcodeScannerListView(),
35 ), 46 ),
36 ); 47 );
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; @@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
4 import 'package:mobile_scanner/src/mobile_scanner_controller.dart'; 4 import 'package:mobile_scanner/src/mobile_scanner_controller.dart';
5 import 'package:mobile_scanner/src/mobile_scanner_exception.dart'; 5 import 'package:mobile_scanner/src/mobile_scanner_exception.dart';
6 import 'package:mobile_scanner/src/mobile_scanner_platform_interface.dart'; 6 import 'package:mobile_scanner/src/mobile_scanner_platform_interface.dart';
  7 +import 'package:mobile_scanner/src/objects/barcode_capture.dart';
7 import 'package:mobile_scanner/src/objects/mobile_scanner_state.dart'; 8 import 'package:mobile_scanner/src/objects/mobile_scanner_state.dart';
8 import 'package:mobile_scanner/src/scan_window_calculation.dart'; 9 import 'package:mobile_scanner/src/scan_window_calculation.dart';
9 10
@@ -18,7 +19,8 @@ typedef MobileScannerErrorBuilder = Widget Function( @@ -18,7 +19,8 @@ typedef MobileScannerErrorBuilder = Widget Function(
18 class MobileScanner extends StatefulWidget { 19 class MobileScanner extends StatefulWidget {
19 /// Create a new [MobileScanner] using the provided [controller]. 20 /// Create a new [MobileScanner] using the provided [controller].
20 const MobileScanner({ 21 const MobileScanner({
21 - required this.controller, 22 + this.controller,
  23 + this.onDetect,
22 this.fit = BoxFit.cover, 24 this.fit = BoxFit.cover,
23 this.errorBuilder, 25 this.errorBuilder,
24 this.overlayBuilder, 26 this.overlayBuilder,
@@ -29,7 +31,11 @@ class MobileScanner extends StatefulWidget { @@ -29,7 +31,11 @@ class MobileScanner extends StatefulWidget {
29 }); 31 });
30 32
31 /// The controller for the camera preview. 33 /// The controller for the camera preview.
32 - final MobileScannerController controller; 34 + final MobileScannerController? controller;
  35 +
  36 + /// The function that signals when new codes were detected by the [controller].
  37 + /// If null, use the contrller.barcodes stream directly to capture barcodes.
  38 + final void Function(BarcodeCapture barcodes)? onDetect;
33 39
34 /// The error builder for the camera preview. 40 /// The error builder for the camera preview.
35 /// 41 ///
@@ -113,6 +119,7 @@ class MobileScanner extends StatefulWidget { @@ -113,6 +119,7 @@ class MobileScanner extends StatefulWidget {
113 } 119 }
114 120
115 class _MobileScannerState extends State<MobileScanner> { 121 class _MobileScannerState extends State<MobileScanner> {
  122 + late final controller = widget.controller ?? MobileScannerController();
116 /// The current scan window. 123 /// The current scan window.
117 Rect? scanWindow; 124 Rect? scanWindow;
118 125
@@ -139,7 +146,7 @@ class _MobileScannerState extends State<MobileScanner> { @@ -139,7 +146,7 @@ class _MobileScannerState extends State<MobileScanner> {
139 if (scanWindow == null) { 146 if (scanWindow == null) {
140 scanWindow = newScanWindow; 147 scanWindow = newScanWindow;
141 148
142 - unawaited(widget.controller.updateScanWindow(scanWindow)); 149 + unawaited(controller.updateScanWindow(scanWindow));
143 150
144 return; 151 return;
145 } 152 }
@@ -154,7 +161,7 @@ class _MobileScannerState extends State<MobileScanner> { @@ -154,7 +161,7 @@ class _MobileScannerState extends State<MobileScanner> {
154 if (widget.scanWindowUpdateThreshold == 0.0) { 161 if (widget.scanWindowUpdateThreshold == 0.0) {
155 scanWindow = newScanWindow; 162 scanWindow = newScanWindow;
156 163
157 - unawaited(widget.controller.updateScanWindow(scanWindow)); 164 + unawaited(controller.updateScanWindow(scanWindow));
158 165
159 return; 166 return;
160 } 167 }
@@ -167,14 +174,14 @@ class _MobileScannerState extends State<MobileScanner> { @@ -167,14 +174,14 @@ class _MobileScannerState extends State<MobileScanner> {
167 dy >= widget.scanWindowUpdateThreshold) { 174 dy >= widget.scanWindowUpdateThreshold) {
168 scanWindow = newScanWindow; 175 scanWindow = newScanWindow;
169 176
170 - unawaited(widget.controller.updateScanWindow(scanWindow)); 177 + unawaited(controller.updateScanWindow(scanWindow));
171 } 178 }
172 } 179 }
173 180
174 @override 181 @override
175 Widget build(BuildContext context) { 182 Widget build(BuildContext context) {
176 return ValueListenableBuilder<MobileScannerState>( 183 return ValueListenableBuilder<MobileScannerState>(
177 - valueListenable: widget.controller, 184 + valueListenable: controller,
178 builder: (BuildContext context, MobileScannerState value, Widget? child) { 185 builder: (BuildContext context, MobileScannerState value, Widget? child) {
179 if (!value.isInitialized) { 186 if (!value.isInitialized) {
180 const Widget defaultPlaceholder = ColoredBox(color: Colors.black); 187 const Widget defaultPlaceholder = ColoredBox(color: Colors.black);
@@ -234,10 +241,36 @@ class _MobileScannerState extends State<MobileScanner> { @@ -234,10 +241,36 @@ class _MobileScannerState extends State<MobileScanner> {
234 ); 241 );
235 } 242 }
236 243
  244 + StreamSubscription? barcodeSubscription;
  245 +
  246 + @override
  247 + void initState() {
  248 + if (widget.onDetect != null) {
  249 + barcodeSubscription = controller.barcodes.listen(widget.onDetect);
  250 + }
  251 + if (controller.autoStart) {
  252 + controller.start();
  253 + }
  254 + super.initState();
  255 + }
  256 +
237 @override 257 @override
238 void dispose() { 258 void dispose() {
239 super.dispose(); 259 super.dispose();
  260 + if (barcodeSubscription != null) {
  261 + barcodeSubscription!.cancel();
  262 + barcodeSubscription = null;
  263 + }
  264 +
  265 + if (controller.autoStart) {
  266 + controller.stop();
  267 + }
240 // When this widget is unmounted, reset the scan window. 268 // When this widget is unmounted, reset the scan window.
241 - unawaited(widget.controller.updateScanWindow(null)); 269 + unawaited(controller.updateScanWindow(null));
  270 +
  271 + // Dispose default controller if not provided by user
  272 + if (widget.controller == null) {
  273 + controller.dispose();
  274 + }
242 } 275 }
243 } 276 }
@@ -17,6 +17,7 @@ import 'package:mobile_scanner/src/objects/start_options.dart'; @@ -17,6 +17,7 @@ import 'package:mobile_scanner/src/objects/start_options.dart';
17 class MobileScannerController extends ValueNotifier<MobileScannerState> { 17 class MobileScannerController extends ValueNotifier<MobileScannerState> {
18 /// Construct a new [MobileScannerController] instance. 18 /// Construct a new [MobileScannerController] instance.
19 MobileScannerController({ 19 MobileScannerController({
  20 + this.autoStart = true,
20 this.cameraResolution, 21 this.cameraResolution,
21 this.detectionSpeed = DetectionSpeed.normal, 22 this.detectionSpeed = DetectionSpeed.normal,
22 int detectionTimeoutMs = 250, 23 int detectionTimeoutMs = 250,
@@ -47,6 +48,9 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> { @@ -47,6 +48,9 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> {
47 /// Currently only supported on Android. 48 /// Currently only supported on Android.
48 final Size? cameraResolution; 49 final Size? cameraResolution;
49 50
  51 + /// Automatically start the scanner on initialization.
  52 + final bool autoStart;
  53 +
50 /// The detection speed for the scanner. 54 /// The detection speed for the scanner.
51 /// 55 ///
52 /// Defaults to [DetectionSpeed.normal]. 56 /// Defaults to [DetectionSpeed.normal].
1 name: mobile_scanner 1 name: mobile_scanner
2 description: A universal barcode and QR code scanner for Flutter based on MLKit. Uses CameraX on Android, AVFoundation on iOS and Apple Vision & AVFoundation on macOS. 2 description: A universal barcode and QR code scanner for Flutter based on MLKit. Uses CameraX on Android, AVFoundation on iOS and Apple Vision & AVFoundation on macOS.
3 -version: 5.0.2 3 +version: 5.1.0
4 repository: https://github.com/juliansteenbakker/mobile_scanner 4 repository: https://github.com/juliansteenbakker/mobile_scanner
5 5
6 screenshots: 6 screenshots: