Navaron Bracke

revert controller being required; rename onScannerRestarted

@@ -8,11 +8,10 @@ Breaking changes: @@ -8,11 +8,10 @@ Breaking changes:
8 * The `onPermissionSet` argument of the `MobileScannerController` is now deprecated. 8 * The `onPermissionSet` argument of the `MobileScannerController` is now deprecated.
9 To handle permission errors, consider catching the result of `MobileScannerController.start()`. 9 To handle permission errors, consider catching the result of `MobileScannerController.start()`.
10 * Toggling the device torch now does nothing if the device has no torch, rather than throwing an error. 10 * Toggling the device torch now does nothing if the device has no torch, rather than throwing an error.
11 -* The `controller` of the `MobileScanner` widget is now required.  
12 * The `onDetect` method of `MobileScanner` has been renamed to `onBarcodeDetected`. 11 * The `onDetect` method of `MobileScanner` has been renamed to `onBarcodeDetected`.
13 * The `MobileScanner` widget no longer starts its `controller`. 12 * The `MobileScanner` widget no longer starts its `controller`.
14 Clients of the widget should call `controller.start()` when appropriate. 13 Clients of the widget should call `controller.start()` when appropriate.
15 -* The `onStart` method has been removed. 14 +* The `onStart` method has been renamed to `onScannerStarted`.
16 To retrieve the start arguments of the controller, await the `start()` method: 15 To retrieve the start arguments of the controller, await the `start()` method:
17 ```dart 16 ```dart
18 final arguments = await controller.start(); 17 final arguments = await controller.start();
@@ -9,7 +9,9 @@ import 'package:mobile_scanner/src/objects/mobile_scanner_arguments.dart'; @@ -9,7 +9,9 @@ import 'package:mobile_scanner/src/objects/mobile_scanner_arguments.dart';
9 /// The [MobileScanner] widget displays a live camera preview. 9 /// The [MobileScanner] widget displays a live camera preview.
10 class MobileScanner extends StatefulWidget { 10 class MobileScanner extends StatefulWidget {
11 /// The controller that manages the barcode scanner. 11 /// The controller that manages the barcode scanner.
12 - final MobileScannerController controller; 12 + ///
  13 + /// If this is null, the scanner will manage its own controller.
  14 + final MobileScannerController? controller;
13 15
14 /// The [BoxFit] for the camera preview. 16 /// The [BoxFit] for the camera preview.
15 /// 17 ///
@@ -19,9 +21,8 @@ class MobileScanner extends StatefulWidget { @@ -19,9 +21,8 @@ class MobileScanner extends StatefulWidget {
19 /// The function that signals when new barcodes were detected by the [controller]. 21 /// The function that signals when new barcodes were detected by the [controller].
20 final void Function(BarcodeCapture barcodes) onBarcodeDetected; 22 final void Function(BarcodeCapture barcodes) onBarcodeDetected;
21 23
22 - /// The function that signals when the barcode scanner is restarted,  
23 - /// due to coming back to the foreground.  
24 - final void Function(MobileScannerArguments? arguments)? onScannerRestarted; 24 + /// The function that signals when the barcode scanner is started.
  25 + final void Function(MobileScannerArguments? arguments)? onScannerStarted;
25 26
26 /// The function that builds a placeholder widget when the scanner 27 /// The function that builds a placeholder widget when the scanner
27 /// is not yet displaying its camera preview. 28 /// is not yet displaying its camera preview.
@@ -32,10 +33,10 @@ class MobileScanner extends StatefulWidget { @@ -32,10 +33,10 @@ class MobileScanner extends StatefulWidget {
32 /// Create a new [MobileScanner] using the provided [controller] 33 /// Create a new [MobileScanner] using the provided [controller]
33 /// and [onBarcodeDetected] callback. 34 /// and [onBarcodeDetected] callback.
34 const MobileScanner({ 35 const MobileScanner({
35 - required this.controller, 36 + this.controller,
36 this.fit = BoxFit.cover, 37 this.fit = BoxFit.cover,
37 required this.onBarcodeDetected, 38 required this.onBarcodeDetected,
38 - this.onScannerRestarted, 39 + this.onScannerStarted,
39 this.placeholderBuilder, 40 this.placeholderBuilder,
40 super.key, 41 super.key,
41 }); 42 });
@@ -49,16 +50,28 @@ class _MobileScannerState extends State<MobileScanner> @@ -49,16 +50,28 @@ class _MobileScannerState extends State<MobileScanner>
49 /// The subscription that listens to barcode detection. 50 /// The subscription that listens to barcode detection.
50 StreamSubscription<BarcodeCapture>? _barcodesSubscription; 51 StreamSubscription<BarcodeCapture>? _barcodesSubscription;
51 52
  53 + /// The internally managed controller.
  54 + MobileScannerController? _controller;
  55 +
  56 + /// Get the effective controller.
  57 + MobileScannerController get _effectiveController {
  58 + if (widget.controller != null) {
  59 + return widget.controller!;
  60 + }
  61 +
  62 + return _controller!;
  63 + }
  64 +
52 /// Whether the controller should resume 65 /// Whether the controller should resume
53 /// when the application comes back to the foreground. 66 /// when the application comes back to the foreground.
54 bool _resumeFromBackground = false; 67 bool _resumeFromBackground = false;
55 68
56 - /// Restart a previously paused scanner.  
57 - void _restartScanner() {  
58 - widget.controller.start().then((arguments) {  
59 - widget.onScannerRestarted?.call(arguments); 69 + /// Start the given [scanner].
  70 + void _startScanner(MobileScannerController scanner) {
  71 + scanner.start().then((arguments) {
  72 + widget.onScannerStarted?.call(arguments);
60 }).catchError((error) { 73 }).catchError((error) {
61 - // The scanner somehow failed to restart. 74 + // The scanner somehow failed to start.
62 // There is no way to recover from this, so do nothing. 75 // There is no way to recover from this, so do nothing.
63 }); 76 });
64 } 77 }
@@ -67,29 +80,40 @@ class _MobileScannerState extends State<MobileScanner> @@ -67,29 +80,40 @@ class _MobileScannerState extends State<MobileScanner>
67 void initState() { 80 void initState() {
68 super.initState(); 81 super.initState();
69 WidgetsBinding.instance.addObserver(this); 82 WidgetsBinding.instance.addObserver(this);
70 - _barcodesSubscription = widget.controller.barcodes.listen( 83 +
  84 + if (widget.controller == null) {
  85 + _controller = MobileScannerController();
  86 + }
  87 +
  88 + _barcodesSubscription = _effectiveController.barcodes.listen(
71 widget.onBarcodeDetected, 89 widget.onBarcodeDetected,
72 ); 90 );
  91 +
  92 + final internalController = _controller;
  93 +
  94 + if (internalController != null && !internalController.isStarting) {
  95 + _startScanner(internalController);
  96 + }
73 } 97 }
74 98
75 @override 99 @override
76 void didChangeAppLifecycleState(AppLifecycleState state) { 100 void didChangeAppLifecycleState(AppLifecycleState state) {
77 // App state changed before the controller was initialized. 101 // App state changed before the controller was initialized.
78 - if (widget.controller.isStarting) { 102 + if (_effectiveController.isStarting) {
79 return; 103 return;
80 } 104 }
81 105
82 switch (state) { 106 switch (state) {
83 case AppLifecycleState.resumed: 107 case AppLifecycleState.resumed:
84 _resumeFromBackground = false; 108 _resumeFromBackground = false;
85 - _restartScanner(); 109 + _startScanner(_effectiveController);
86 break; 110 break;
87 case AppLifecycleState.paused: 111 case AppLifecycleState.paused:
88 _resumeFromBackground = true; 112 _resumeFromBackground = true;
89 break; 113 break;
90 case AppLifecycleState.inactive: 114 case AppLifecycleState.inactive:
91 if (!_resumeFromBackground) { 115 if (!_resumeFromBackground) {
92 - widget.controller.stop(); 116 + _effectiveController.stop();
93 } 117 }
94 break; 118 break;
95 case AppLifecycleState.detached: 119 case AppLifecycleState.detached:
@@ -98,9 +122,27 @@ class _MobileScannerState extends State<MobileScanner> @@ -98,9 +122,27 @@ class _MobileScannerState extends State<MobileScanner>
98 } 122 }
99 123
100 @override 124 @override
  125 + void didUpdateWidget(covariant MobileScanner oldWidget) {
  126 + super.didUpdateWidget(oldWidget);
  127 +
  128 + if (oldWidget.controller == null) {
  129 + if (widget.controller != null) {
  130 + _controller?.dispose();
  131 + _controller = widget.controller;
  132 + }
  133 + } else {
  134 + if (widget.controller == null) {
  135 + _controller = MobileScannerController();
  136 + } else if (oldWidget.controller != widget.controller) {
  137 + _controller = widget.controller;
  138 + }
  139 + }
  140 + }
  141 +
  142 + @override
101 Widget build(BuildContext context) { 143 Widget build(BuildContext context) {
102 return ValueListenableBuilder<MobileScannerArguments?>( 144 return ValueListenableBuilder<MobileScannerArguments?>(
103 - valueListenable: widget.controller.startArguments, 145 + valueListenable: _effectiveController.startArguments,
104 builder: (context, value, child) { 146 builder: (context, value, child) {
105 if (value == null) { 147 if (value == null) {
106 return widget.placeholderBuilder?.call(context, child) ?? 148 return widget.placeholderBuilder?.call(context, child) ??
@@ -134,6 +176,7 @@ class _MobileScannerState extends State<MobileScanner> @@ -134,6 +176,7 @@ class _MobileScannerState extends State<MobileScanner>
134 void dispose() { 176 void dispose() {
135 WidgetsBinding.instance.removeObserver(this); 177 WidgetsBinding.instance.removeObserver(this);
136 _barcodesSubscription?.cancel(); 178 _barcodesSubscription?.cancel();
  179 + _controller?.dispose();
137 super.dispose(); 180 super.dispose();
138 } 181 }
139 } 182 }