Showing
1 changed file
with
104 additions
and
49 deletions
| @@ -16,16 +16,75 @@ class BarcodeScannerWithScanWindow extends StatefulWidget { | @@ -16,16 +16,75 @@ class BarcodeScannerWithScanWindow extends StatefulWidget { | ||
| 16 | 16 | ||
| 17 | class _BarcodeScannerWithScanWindowState | 17 | class _BarcodeScannerWithScanWindowState |
| 18 | extends State<BarcodeScannerWithScanWindow> { | 18 | extends State<BarcodeScannerWithScanWindow> { |
| 19 | - late MobileScannerController controller = MobileScannerController(); | ||
| 20 | - Barcode? barcode; | ||
| 21 | - BarcodeCapture? capture; | 19 | + final MobileScannerController controller = MobileScannerController(); |
| 22 | 20 | ||
| 23 | - Future<void> onDetect(BarcodeCapture barcode) async { | ||
| 24 | - capture = barcode; | ||
| 25 | - setState(() => this.barcode = barcode.barcodes.first); | 21 | + @override |
| 22 | + void initState() { | ||
| 23 | + super.initState(); | ||
| 24 | + | ||
| 25 | + controller.start(); | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + Widget _buildBarcodeOverlay() { | ||
| 29 | + return ValueListenableBuilder( | ||
| 30 | + valueListenable: controller, | ||
| 31 | + builder: (context, value, child) { | ||
| 32 | + // Not ready. | ||
| 33 | + if (!value.isInitialized || !value.isRunning || value.error != null) { | ||
| 34 | + return const SizedBox(); | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + return StreamBuilder<BarcodeCapture>( | ||
| 38 | + stream: controller.barcodes, | ||
| 39 | + builder: (context, snapshot) { | ||
| 40 | + final BarcodeCapture? barcodeCapture = snapshot.data; | ||
| 41 | + | ||
| 42 | + // No barcode. | ||
| 43 | + if (barcodeCapture == null || barcodeCapture.barcodes.isEmpty) { | ||
| 44 | + return const SizedBox(); | ||
| 26 | } | 45 | } |
| 27 | 46 | ||
| 28 | - MobileScannerArguments? arguments; | 47 | + final scannedBarcode = barcodeCapture.barcodes.first; |
| 48 | + | ||
| 49 | + // No barcode corners, or size, or no camera preview size. | ||
| 50 | + if (scannedBarcode.corners.isEmpty || | ||
| 51 | + value.size.isEmpty || | ||
| 52 | + barcodeCapture.size.isEmpty) { | ||
| 53 | + return const SizedBox(); | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + return CustomPaint( | ||
| 57 | + painter: BarcodeOverlay( | ||
| 58 | + barcodeCorners: scannedBarcode.corners, | ||
| 59 | + barcodeSize: barcodeCapture.size, | ||
| 60 | + boxFit: BoxFit.contain, | ||
| 61 | + cameraPreviewSize: value.size, | ||
| 62 | + ), | ||
| 63 | + ); | ||
| 64 | + }, | ||
| 65 | + ); | ||
| 66 | + }, | ||
| 67 | + ); | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + Widget _buildScanWindow(Rect scanWindowRect) { | ||
| 71 | + return ValueListenableBuilder( | ||
| 72 | + valueListenable: controller, | ||
| 73 | + builder: (context, value, child) { | ||
| 74 | + // Not ready. | ||
| 75 | + if (!value.isInitialized || | ||
| 76 | + !value.isRunning || | ||
| 77 | + value.error != null || | ||
| 78 | + value.size.isEmpty) { | ||
| 79 | + return const SizedBox(); | ||
| 80 | + } | ||
| 81 | + | ||
| 82 | + return CustomPaint( | ||
| 83 | + painter: ScannerOverlay(scanWindowRect), | ||
| 84 | + ); | ||
| 85 | + }, | ||
| 86 | + ); | ||
| 87 | + } | ||
| 29 | 88 | ||
| 30 | @override | 89 | @override |
| 31 | Widget build(BuildContext context) { | 90 | Widget build(BuildContext context) { |
| @@ -34,77 +93,69 @@ class _BarcodeScannerWithScanWindowState | @@ -34,77 +93,69 @@ class _BarcodeScannerWithScanWindowState | ||
| 34 | width: 200, | 93 | width: 200, |
| 35 | height: 200, | 94 | height: 200, |
| 36 | ); | 95 | ); |
| 96 | + | ||
| 37 | return Scaffold( | 97 | return Scaffold( |
| 38 | appBar: AppBar(title: const Text('With Scan window')), | 98 | appBar: AppBar(title: const Text('With Scan window')), |
| 39 | backgroundColor: Colors.black, | 99 | backgroundColor: Colors.black, |
| 40 | - body: Builder( | ||
| 41 | - builder: (context) { | ||
| 42 | - return Stack( | 100 | + body: Stack( |
| 43 | fit: StackFit.expand, | 101 | fit: StackFit.expand, |
| 44 | children: [ | 102 | children: [ |
| 45 | MobileScanner( | 103 | MobileScanner( |
| 46 | fit: BoxFit.contain, | 104 | fit: BoxFit.contain, |
| 47 | scanWindow: scanWindow, | 105 | scanWindow: scanWindow, |
| 48 | controller: controller, | 106 | controller: controller, |
| 49 | - onScannerStarted: (arguments) { | ||
| 50 | - setState(() { | ||
| 51 | - this.arguments = arguments; | ||
| 52 | - }); | ||
| 53 | - }, | ||
| 54 | errorBuilder: (context, error, child) { | 107 | errorBuilder: (context, error, child) { |
| 55 | return ScannerErrorWidget(error: error); | 108 | return ScannerErrorWidget(error: error); |
| 56 | }, | 109 | }, |
| 57 | - onDetect: onDetect, | ||
| 58 | - ), | ||
| 59 | - if (barcode != null && | ||
| 60 | - barcode?.corners != null && | ||
| 61 | - arguments != null) | ||
| 62 | - CustomPaint( | ||
| 63 | - painter: BarcodeOverlay( | ||
| 64 | - barcode: barcode!, | ||
| 65 | - arguments: arguments!, | ||
| 66 | - boxFit: BoxFit.contain, | ||
| 67 | - capture: capture!, | ||
| 68 | - ), | ||
| 69 | - ), | ||
| 70 | - CustomPaint( | ||
| 71 | - painter: ScannerOverlay(scanWindow), | ||
| 72 | ), | 110 | ), |
| 111 | + _buildBarcodeOverlay(), | ||
| 112 | + _buildScanWindow(scanWindow), | ||
| 73 | Align( | 113 | Align( |
| 74 | alignment: Alignment.bottomCenter, | 114 | alignment: Alignment.bottomCenter, |
| 75 | child: Container( | 115 | child: Container( |
| 76 | - alignment: Alignment.bottomCenter, | 116 | + alignment: Alignment.center, |
| 117 | + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), | ||
| 77 | height: 100, | 118 | height: 100, |
| 78 | color: Colors.black.withOpacity(0.4), | 119 | color: Colors.black.withOpacity(0.4), |
| 79 | - child: Row( | ||
| 80 | - mainAxisAlignment: MainAxisAlignment.spaceEvenly, | ||
| 81 | - children: [ | ||
| 82 | - Center( | ||
| 83 | - child: SizedBox( | ||
| 84 | - width: MediaQuery.of(context).size.width - 120, | ||
| 85 | - height: 50, | ||
| 86 | - child: FittedBox( | ||
| 87 | - child: Text( | ||
| 88 | - barcode?.displayValue ?? 'Scan something!', | 120 | + child: StreamBuilder<BarcodeCapture>( |
| 121 | + stream: controller.barcodes, | ||
| 122 | + builder: (context, snapshot) { | ||
| 123 | + final barcodeCapture = snapshot.data; | ||
| 124 | + | ||
| 125 | + String displayValue = 'Scan something!'; | ||
| 126 | + | ||
| 127 | + if (barcodeCapture != null && | ||
| 128 | + barcodeCapture.barcodes.isNotEmpty) { | ||
| 129 | + final String? value = | ||
| 130 | + barcodeCapture.barcodes.first.displayValue; | ||
| 131 | + | ||
| 132 | + if (value != null) { | ||
| 133 | + displayValue = value; | ||
| 134 | + } | ||
| 135 | + } | ||
| 136 | + | ||
| 137 | + return Text( | ||
| 138 | + displayValue, | ||
| 89 | overflow: TextOverflow.fade, | 139 | overflow: TextOverflow.fade, |
| 90 | style: Theme.of(context) | 140 | style: Theme.of(context) |
| 91 | .textTheme | 141 | .textTheme |
| 92 | .headlineMedium! | 142 | .headlineMedium! |
| 93 | .copyWith(color: Colors.white), | 143 | .copyWith(color: Colors.white), |
| 94 | - ), | ||
| 95 | - ), | ||
| 96 | - ), | ||
| 97 | - ), | ||
| 98 | - ], | 144 | + ); |
| 145 | + }, | ||
| 99 | ), | 146 | ), |
| 100 | ), | 147 | ), |
| 101 | ), | 148 | ), |
| 102 | ], | 149 | ], |
| 103 | - ); | ||
| 104 | - }, | ||
| 105 | ), | 150 | ), |
| 106 | ); | 151 | ); |
| 107 | } | 152 | } |
| 153 | + | ||
| 154 | + @override | ||
| 155 | + Future<void> dispose() async { | ||
| 156 | + await controller.dispose(); | ||
| 157 | + super.dispose(); | ||
| 158 | + } | ||
| 108 | } | 159 | } |
| 109 | 160 | ||
| 110 | class ScannerOverlay extends CustomPainter { | 161 | class ScannerOverlay extends CustomPainter { |
| @@ -114,6 +165,8 @@ class ScannerOverlay extends CustomPainter { | @@ -114,6 +165,8 @@ class ScannerOverlay extends CustomPainter { | ||
| 114 | 165 | ||
| 115 | @override | 166 | @override |
| 116 | void paint(Canvas canvas, Size size) { | 167 | void paint(Canvas canvas, Size size) { |
| 168 | + // TODO: use `Offset.zero & size` instead of Rect.largest | ||
| 169 | + // we need to pass the size to the custom paint widget | ||
| 117 | final backgroundPath = Path()..addRect(Rect.largest); | 170 | final backgroundPath = Path()..addRect(Rect.largest); |
| 118 | final cutoutPath = Path()..addRect(scanWindow); | 171 | final cutoutPath = Path()..addRect(scanWindow); |
| 119 | 172 | ||
| @@ -151,7 +204,9 @@ class BarcodeOverlay extends CustomPainter { | @@ -151,7 +204,9 @@ class BarcodeOverlay extends CustomPainter { | ||
| 151 | 204 | ||
| 152 | @override | 205 | @override |
| 153 | void paint(Canvas canvas, Size size) { | 206 | void paint(Canvas canvas, Size size) { |
| 154 | - if (barcodeCorners.isEmpty) { | 207 | + if (barcodeCorners.isEmpty || |
| 208 | + barcodeSize.isEmpty || | ||
| 209 | + cameraPreviewSize.isEmpty) { | ||
| 155 | return; | 210 | return; |
| 156 | } | 211 | } |
| 157 | 212 |
-
Please register or login to post a comment