Navaron Bracke

add error builder to mobile scanner widget; handle startup error

@@ -3,6 +3,7 @@ import 'dart:async'; @@ -3,6 +3,7 @@ import 'dart:async';
3 import 'package:flutter/foundation.dart'; 3 import 'package:flutter/foundation.dart';
4 import 'package:flutter/material.dart'; 4 import 'package:flutter/material.dart';
5 import 'package:mobile_scanner/src/mobile_scanner_controller.dart'; 5 import 'package:mobile_scanner/src/mobile_scanner_controller.dart';
  6 +import 'package:mobile_scanner/src/mobile_scanner_exception.dart';
6 import 'package:mobile_scanner/src/objects/barcode_capture.dart'; 7 import 'package:mobile_scanner/src/objects/barcode_capture.dart';
7 import 'package:mobile_scanner/src/objects/mobile_scanner_arguments.dart'; 8 import 'package:mobile_scanner/src/objects/mobile_scanner_arguments.dart';
8 9
@@ -13,6 +14,13 @@ class MobileScanner extends StatefulWidget { @@ -13,6 +14,13 @@ class MobileScanner extends StatefulWidget {
13 /// If this is null, the scanner will manage its own controller. 14 /// If this is null, the scanner will manage its own controller.
14 final MobileScannerController? controller; 15 final MobileScannerController? controller;
15 16
  17 + /// The function that builds an error widget when the scanner
  18 + /// could not be started.
  19 + ///
  20 + /// If this is null, defaults to a black [ColoredBox]
  21 + /// with a centered white [Icons.error] icon.
  22 + final Widget Function(BuildContext, Object, Widget?)? errorBuilder;
  23 +
16 /// The [BoxFit] for the camera preview. 24 /// The [BoxFit] for the camera preview.
17 /// 25 ///
18 /// Defaults to [BoxFit.cover]. 26 /// Defaults to [BoxFit.cover].
@@ -38,6 +46,7 @@ class MobileScanner extends StatefulWidget { @@ -38,6 +46,7 @@ class MobileScanner extends StatefulWidget {
38 /// and [onBarcodeDetected] callback. 46 /// and [onBarcodeDetected] callback.
39 const MobileScanner({ 47 const MobileScanner({
40 this.controller, 48 this.controller,
  49 + this.errorBuilder,
41 this.fit = BoxFit.cover, 50 this.fit = BoxFit.cover,
42 required this.onDetect, 51 required this.onDetect,
43 @Deprecated('Use onScannerStarted() instead.') this.onStart, 52 @Deprecated('Use onScannerStarted() instead.') this.onStart,
@@ -62,6 +71,23 @@ class _MobileScannerState extends State<MobileScanner> @@ -62,6 +71,23 @@ class _MobileScannerState extends State<MobileScanner>
62 /// when the application comes back to the foreground. 71 /// when the application comes back to the foreground.
63 bool _resumeFromBackground = false; 72 bool _resumeFromBackground = false;
64 73
  74 + MobileScannerException? _startException;
  75 +
  76 + Widget __buildPlaceholderOrError(BuildContext context, Widget? child) {
  77 + final error = _startException;
  78 +
  79 + if (error != null) {
  80 + return widget.errorBuilder?.call(context, error, child) ??
  81 + const ColoredBox(
  82 + color: Colors.black,
  83 + child: Center(child: Icon(Icons.error, color: Colors.white)),
  84 + );
  85 + }
  86 +
  87 + return widget.placeholderBuilder?.call(context, child) ??
  88 + const ColoredBox(color: Colors.black);
  89 + }
  90 +
65 /// Start the given [scanner]. 91 /// Start the given [scanner].
66 void _startScanner(MobileScannerController scanner) { 92 void _startScanner(MobileScannerController scanner) {
67 if (!_controller.autoStart) { 93 if (!_controller.autoStart) {
@@ -74,6 +100,12 @@ class _MobileScannerState extends State<MobileScanner> @@ -74,6 +100,12 @@ class _MobileScannerState extends State<MobileScanner>
74 // ignore: deprecated_member_use_from_same_package 100 // ignore: deprecated_member_use_from_same_package
75 widget.onStart?.call(arguments); 101 widget.onStart?.call(arguments);
76 widget.onScannerStarted?.call(arguments); 102 widget.onScannerStarted?.call(arguments);
  103 + }).catchError((error) {
  104 + if (mounted) {
  105 + setState(() {
  106 + _startException = error as MobileScannerException;
  107 + });
  108 + }
77 }); 109 });
78 } 110 }
79 111
@@ -123,8 +155,7 @@ class _MobileScannerState extends State<MobileScanner> @@ -123,8 +155,7 @@ class _MobileScannerState extends State<MobileScanner>
123 valueListenable: _controller.startArguments, 155 valueListenable: _controller.startArguments,
124 builder: (context, value, child) { 156 builder: (context, value, child) {
125 if (value == null) { 157 if (value == null) {
126 - return widget.placeholderBuilder?.call(context, child) ??  
127 - const ColoredBox(color: Colors.black); 158 + return __buildPlaceholderOrError(context, child);
128 } 159 }
129 160
130 return ClipRect( 161 return ClipRect(