Showing
1 changed file
with
82 additions
and
101 deletions
| @@ -2,6 +2,7 @@ import 'dart:math'; | @@ -2,6 +2,7 @@ import 'dart:math'; | ||
| 2 | 2 | ||
| 3 | import 'package:flutter/material.dart'; | 3 | import 'package:flutter/material.dart'; |
| 4 | import 'package:mobile_scanner/mobile_scanner.dart'; | 4 | import 'package:mobile_scanner/mobile_scanner.dart'; |
| 5 | +import 'package:mobile_scanner_example/scanner_button_widgets.dart'; | ||
| 5 | import 'package:mobile_scanner_example/scanner_error_widget.dart'; | 6 | import 'package:mobile_scanner_example/scanner_error_widget.dart'; |
| 6 | 7 | ||
| 7 | class BarcodeScannerReturningImage extends StatefulWidget { | 8 | class BarcodeScannerReturningImage extends StatefulWidget { |
| @@ -13,11 +14,7 @@ class BarcodeScannerReturningImage extends StatefulWidget { | @@ -13,11 +14,7 @@ class BarcodeScannerReturningImage extends StatefulWidget { | ||
| 13 | } | 14 | } |
| 14 | 15 | ||
| 15 | class _BarcodeScannerReturningImageState | 16 | class _BarcodeScannerReturningImageState |
| 16 | - extends State<BarcodeScannerReturningImage> | ||
| 17 | - with SingleTickerProviderStateMixin { | ||
| 18 | - BarcodeCapture? barcode; | ||
| 19 | - // MobileScannerArguments? arguments; | ||
| 20 | - | 17 | + extends State<BarcodeScannerReturningImage> { |
| 21 | final MobileScannerController controller = MobileScannerController( | 18 | final MobileScannerController controller = MobileScannerController( |
| 22 | torchEnabled: true, | 19 | torchEnabled: true, |
| 23 | // formats: [BarcodeFormat.qrCode] | 20 | // formats: [BarcodeFormat.qrCode] |
| @@ -27,26 +24,10 @@ class _BarcodeScannerReturningImageState | @@ -27,26 +24,10 @@ class _BarcodeScannerReturningImageState | ||
| 27 | returnImage: true, | 24 | returnImage: true, |
| 28 | ); | 25 | ); |
| 29 | 26 | ||
| 30 | - bool isStarted = true; | ||
| 31 | - | ||
| 32 | - void _startOrStop() { | ||
| 33 | - try { | ||
| 34 | - if (isStarted) { | ||
| 35 | - controller.stop(); | ||
| 36 | - } else { | ||
| 37 | - controller.start(); | ||
| 38 | - } | ||
| 39 | - setState(() { | ||
| 40 | - isStarted = !isStarted; | ||
| 41 | - }); | ||
| 42 | - } on Exception catch (e) { | ||
| 43 | - ScaffoldMessenger.of(context).showSnackBar( | ||
| 44 | - SnackBar( | ||
| 45 | - content: Text('Something went wrong! $e'), | ||
| 46 | - backgroundColor: Colors.red, | ||
| 47 | - ), | ||
| 48 | - ); | ||
| 49 | - } | 27 | + @override |
| 28 | + void initState() { | ||
| 29 | + super.initState(); | ||
| 30 | + controller.start(); | ||
| 50 | } | 31 | } |
| 51 | 32 | ||
| 52 | @override | 33 | @override |
| @@ -57,20 +38,55 @@ class _BarcodeScannerReturningImageState | @@ -57,20 +38,55 @@ class _BarcodeScannerReturningImageState | ||
| 57 | child: Column( | 38 | child: Column( |
| 58 | children: [ | 39 | children: [ |
| 59 | Expanded( | 40 | Expanded( |
| 60 | - child: barcode?.image != null | ||
| 61 | - ? Transform.rotate( | ||
| 62 | - angle: 90 * pi / 180, | ||
| 63 | - child: Image( | ||
| 64 | - gaplessPlayback: true, | ||
| 65 | - image: MemoryImage(barcode!.image!), | ||
| 66 | - fit: BoxFit.contain, | ||
| 67 | - ), | ||
| 68 | - ) | ||
| 69 | - : const Center( | 41 | + child: StreamBuilder<BarcodeCapture>( |
| 42 | + stream: controller.barcodes, | ||
| 43 | + builder: (context, snapshot) { | ||
| 44 | + final barcode = snapshot.data; | ||
| 45 | + | ||
| 46 | + if (barcode == null) { | ||
| 47 | + return const Center( | ||
| 70 | child: Text( | 48 | child: Text( |
| 71 | 'Your scanned barcode will appear here!', | 49 | 'Your scanned barcode will appear here!', |
| 72 | ), | 50 | ), |
| 73 | - ), | 51 | + ); |
| 52 | + } | ||
| 53 | + | ||
| 54 | + final barcodeImage = barcode.image; | ||
| 55 | + | ||
| 56 | + if (barcodeImage == null) { | ||
| 57 | + return const Center( | ||
| 58 | + child: Text('No image for this barcode.'), | ||
| 59 | + ); | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + return Image.memory( | ||
| 63 | + barcodeImage, | ||
| 64 | + fit: BoxFit.contain, | ||
| 65 | + errorBuilder: (context, error, stackTrace) { | ||
| 66 | + return Center( | ||
| 67 | + child: Text('Could not decode image bytes. $error'), | ||
| 68 | + ); | ||
| 69 | + }, | ||
| 70 | + frameBuilder: ( | ||
| 71 | + BuildContext context, | ||
| 72 | + Widget child, | ||
| 73 | + int? frame, | ||
| 74 | + bool? wasSynchronouslyLoaded, | ||
| 75 | + ) { | ||
| 76 | + if (wasSynchronouslyLoaded == true || frame != null) { | ||
| 77 | + return Transform.rotate( | ||
| 78 | + angle: 90 * pi / 180, | ||
| 79 | + child: child, | ||
| 80 | + ); | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + return const Center( | ||
| 84 | + child: CircularProgressIndicator(), | ||
| 85 | + ); | ||
| 86 | + }, | ||
| 87 | + ); | ||
| 88 | + }, | ||
| 89 | + ), | ||
| 74 | ), | 90 | ), |
| 75 | Expanded( | 91 | Expanded( |
| 76 | flex: 2, | 92 | flex: 2, |
| @@ -84,11 +100,6 @@ class _BarcodeScannerReturningImageState | @@ -84,11 +100,6 @@ class _BarcodeScannerReturningImageState | ||
| 84 | return ScannerErrorWidget(error: error); | 100 | return ScannerErrorWidget(error: error); |
| 85 | }, | 101 | }, |
| 86 | fit: BoxFit.contain, | 102 | fit: BoxFit.contain, |
| 87 | - onDetect: (barcode) { | ||
| 88 | - setState(() { | ||
| 89 | - this.barcode = barcode; | ||
| 90 | - }); | ||
| 91 | - }, | ||
| 92 | ), | 103 | ), |
| 93 | Align( | 104 | Align( |
| 94 | alignment: Alignment.bottomCenter, | 105 | alignment: Alignment.bottomCenter, |
| @@ -99,69 +110,39 @@ class _BarcodeScannerReturningImageState | @@ -99,69 +110,39 @@ class _BarcodeScannerReturningImageState | ||
| 99 | child: Row( | 110 | child: Row( |
| 100 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, | 111 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, |
| 101 | children: [ | 112 | children: [ |
| 102 | - IconButton( | ||
| 103 | - color: Colors.white, | ||
| 104 | - icon: ValueListenableBuilder<TorchState>( | ||
| 105 | - valueListenable: controller.torchState, | ||
| 106 | - builder: (context, state, child) { | ||
| 107 | - switch (state) { | ||
| 108 | - case TorchState.off: | ||
| 109 | - return const Icon( | ||
| 110 | - Icons.flash_off, | ||
| 111 | - color: Colors.grey, | ||
| 112 | - ); | ||
| 113 | - case TorchState.on: | ||
| 114 | - return const Icon( | ||
| 115 | - Icons.flash_on, | ||
| 116 | - color: Colors.yellow, | ||
| 117 | - ); | ||
| 118 | - } | ||
| 119 | - }, | ||
| 120 | - ), | ||
| 121 | - iconSize: 32.0, | ||
| 122 | - onPressed: () => controller.toggleTorch(), | ||
| 123 | - ), | ||
| 124 | - IconButton( | ||
| 125 | - color: Colors.white, | ||
| 126 | - icon: isStarted | ||
| 127 | - ? const Icon(Icons.stop) | ||
| 128 | - : const Icon(Icons.play_arrow), | ||
| 129 | - iconSize: 32.0, | ||
| 130 | - onPressed: _startOrStop, | 113 | + ToggleFlashlightButton(controller: controller), |
| 114 | + StartStopMobileScannerButton( | ||
| 115 | + controller: controller, | ||
| 131 | ), | 116 | ), |
| 132 | - Center( | ||
| 133 | - child: SizedBox( | ||
| 134 | - width: MediaQuery.of(context).size.width - 200, | ||
| 135 | - height: 50, | ||
| 136 | - child: FittedBox( | ||
| 137 | - child: Text( | ||
| 138 | - barcode?.barcodes.first.rawValue ?? | 117 | + Expanded( |
| 118 | + child: Center( | ||
| 119 | + child: StreamBuilder<BarcodeCapture>( | ||
| 120 | + stream: controller.barcodes, | ||
| 121 | + builder: (context, snapshot) { | ||
| 122 | + final barcodes = snapshot.data?.barcodes; | ||
| 123 | + | ||
| 124 | + if (barcodes == null || barcodes.isEmpty) { | ||
| 125 | + return const Text( | ||
| 139 | 'Scan something!', | 126 | 'Scan something!', |
| 140 | - overflow: TextOverflow.fade, | ||
| 141 | - style: Theme.of(context) | ||
| 142 | - .textTheme | ||
| 143 | - .headlineMedium! | ||
| 144 | - .copyWith(color: Colors.white), | ||
| 145 | - ), | 127 | + style: TextStyle( |
| 128 | + color: Colors.white, | ||
| 129 | + fontSize: 20, | ||
| 130 | + ), | ||
| 131 | + ); | ||
| 132 | + } | ||
| 133 | + | ||
| 134 | + return Text( | ||
| 135 | + barcodes.first.rawValue ?? 'No raw value', | ||
| 136 | + overflow: TextOverflow.fade, | ||
| 137 | + style: const TextStyle( | ||
| 138 | + color: Colors.white, | ||
| 139 | + ), | ||
| 140 | + ); | ||
| 141 | + }, | ||
| 146 | ), | 142 | ), |
| 147 | ), | 143 | ), |
| 148 | ), | 144 | ), |
| 149 | - IconButton( | ||
| 150 | - color: Colors.white, | ||
| 151 | - icon: ValueListenableBuilder<CameraFacing>( | ||
| 152 | - valueListenable: controller.cameraFacingState, | ||
| 153 | - builder: (context, state, child) { | ||
| 154 | - switch (state) { | ||
| 155 | - case CameraFacing.front: | ||
| 156 | - return const Icon(Icons.camera_front); | ||
| 157 | - case CameraFacing.back: | ||
| 158 | - return const Icon(Icons.camera_rear); | ||
| 159 | - } | ||
| 160 | - }, | ||
| 161 | - ), | ||
| 162 | - iconSize: 32.0, | ||
| 163 | - onPressed: () => controller.switchCamera(), | ||
| 164 | - ), | 145 | + SwitchCameraButton(controller: controller), |
| 165 | ], | 146 | ], |
| 166 | ), | 147 | ), |
| 167 | ), | 148 | ), |
| @@ -177,8 +158,8 @@ class _BarcodeScannerReturningImageState | @@ -177,8 +158,8 @@ class _BarcodeScannerReturningImageState | ||
| 177 | } | 158 | } |
| 178 | 159 | ||
| 179 | @override | 160 | @override |
| 180 | - void dispose() { | ||
| 181 | - controller.dispose(); | 161 | + Future<void> dispose() async { |
| 162 | + await controller.dispose(); | ||
| 182 | super.dispose(); | 163 | super.dispose(); |
| 183 | } | 164 | } |
| 184 | } | 165 | } |
-
Please register or login to post a comment