Showing
2 changed files
with
60 additions
and
18 deletions
| @@ -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 | } |
-
Please register or login to post a comment