Navaron Bracke

clean up the zoom slider example

  1 +import 'dart:async';
  2 +
1 import 'package:flutter/material.dart'; 3 import 'package:flutter/material.dart';
2 -import 'package:image_picker/image_picker.dart';  
3 import 'package:mobile_scanner/mobile_scanner.dart'; 4 import 'package:mobile_scanner/mobile_scanner.dart';
4 5
  6 +import 'package:mobile_scanner_example/scanner_button_widgets.dart';
5 import 'package:mobile_scanner_example/scanner_error_widget.dart'; 7 import 'package:mobile_scanner_example/scanner_error_widget.dart';
6 8
7 class BarcodeScannerWithZoom extends StatefulWidget { 9 class BarcodeScannerWithZoom extends StatefulWidget {
@@ -15,60 +17,51 @@ class _BarcodeScannerWithZoomState extends State<BarcodeScannerWithZoom> @@ -15,60 +17,51 @@ class _BarcodeScannerWithZoomState extends State<BarcodeScannerWithZoom>
15 with SingleTickerProviderStateMixin { 17 with SingleTickerProviderStateMixin {
16 BarcodeCapture? barcode; 18 BarcodeCapture? barcode;
17 19
18 - MobileScannerController controller = MobileScannerController( 20 + final MobileScannerController controller = MobileScannerController(
19 torchEnabled: true, 21 torchEnabled: true,
20 ); 22 );
21 23
22 - bool isStarted = true;  
23 double _zoomFactor = 0.0; 24 double _zoomFactor = 0.0;
24 25
  26 + StreamSubscription<Object?>? _barcodesSubscription;
  27 +
25 @override 28 @override
26 - Widget build(BuildContext context) {  
27 - return Scaffold(  
28 - appBar: AppBar(title: const Text('With zoom slider')),  
29 - backgroundColor: Colors.black,  
30 - body: Builder(  
31 - builder: (context) {  
32 - return Stack(  
33 - children: [  
34 - MobileScanner(  
35 - controller: controller,  
36 - fit: BoxFit.contain,  
37 - errorBuilder: (context, error, child) {  
38 - return ScannerErrorWidget(error: error);  
39 - },  
40 - onDetect: (barcode) { 29 + void initState() {
  30 + super.initState();
  31 + _barcodesSubscription = controller.barcodes.listen((event) {
41 setState(() { 32 setState(() {
42 - this.barcode = barcode; 33 + barcode = event;
43 }); 34 });
44 - },  
45 - ),  
46 - Align(  
47 - alignment: Alignment.bottomCenter,  
48 - child: Container(  
49 - alignment: Alignment.bottomCenter,  
50 - height: 100,  
51 - color: Colors.black.withOpacity(0.4),  
52 - child: Column(  
53 - children: [  
54 - Padding( 35 + });
  36 +
  37 + controller.start();
  38 + }
  39 +
  40 + Widget _buildZoomScaleSlider() {
  41 + return ValueListenableBuilder(
  42 + valueListenable: controller,
  43 + builder: (context, state, child) {
  44 + if (!state.isInitialized || !state.isRunning) {
  45 + return const SizedBox.shrink();
  46 + }
  47 +
  48 + final TextStyle labelStyle = Theme.of(context)
  49 + .textTheme
  50 + .headlineMedium!
  51 + .copyWith(color: Colors.white);
  52 +
  53 + return Padding(
55 padding: const EdgeInsets.symmetric(horizontal: 8.0), 54 padding: const EdgeInsets.symmetric(horizontal: 8.0),
56 child: Row( 55 child: Row(
57 children: [ 56 children: [
58 Text( 57 Text(
59 - "0%", 58 + '0%',
60 overflow: TextOverflow.fade, 59 overflow: TextOverflow.fade,
61 - style: Theme.of(context)  
62 - .textTheme  
63 - .headlineMedium!  
64 - .copyWith(color: Colors.white), 60 + style: labelStyle,
65 ), 61 ),
66 Expanded( 62 Expanded(
67 child: Slider( 63 child: Slider(
68 - max: 100,  
69 - divisions: 100,  
70 value: _zoomFactor, 64 value: _zoomFactor,
71 - label: "${_zoomFactor.round()} %",  
72 onChanged: (value) { 65 onChanged: (value) {
73 setState(() { 66 setState(() {
74 _zoomFactor = value; 67 _zoomFactor = value;
@@ -78,54 +71,47 @@ class _BarcodeScannerWithZoomState extends State<BarcodeScannerWithZoom> @@ -78,54 +71,47 @@ class _BarcodeScannerWithZoomState extends State<BarcodeScannerWithZoom>
78 ), 71 ),
79 ), 72 ),
80 Text( 73 Text(
81 - "100%", 74 + '100%',
82 overflow: TextOverflow.fade, 75 overflow: TextOverflow.fade,
83 - style: Theme.of(context)  
84 - .textTheme  
85 - .headlineMedium!  
86 - .copyWith(color: Colors.white), 76 + style: labelStyle,
87 ), 77 ),
88 ], 78 ],
89 ), 79 ),
90 - ),  
91 - Row(  
92 - mainAxisAlignment: MainAxisAlignment.spaceEvenly,  
93 - children: [  
94 - IconButton(  
95 - color: Colors.white,  
96 - icon: ValueListenableBuilder<TorchState>(  
97 - valueListenable: controller.torchState,  
98 - builder: (context, state, child) {  
99 - switch (state) {  
100 - case TorchState.off:  
101 - return const Icon(  
102 - Icons.flash_off,  
103 - color: Colors.grey,  
104 ); 80 );
105 - case TorchState.on:  
106 - return const Icon(  
107 - Icons.flash_on,  
108 - color: Colors.yellow, 81 + },
109 ); 82 );
110 } 83 }
  84 +
  85 + @override
  86 + Widget build(BuildContext context) {
  87 + return Scaffold(
  88 + appBar: AppBar(title: const Text('With zoom slider')),
  89 + backgroundColor: Colors.black,
  90 + body: Builder(
  91 + builder: (context) {
  92 + return Stack(
  93 + children: [
  94 + MobileScanner(
  95 + controller: controller,
  96 + fit: BoxFit.contain,
  97 + errorBuilder: (context, error, child) {
  98 + return ScannerErrorWidget(error: error);
111 }, 99 },
112 ), 100 ),
113 - iconSize: 32.0,  
114 - onPressed: () => controller.toggleTorch(),  
115 - ),  
116 - IconButton(  
117 - color: Colors.white,  
118 - icon: isStarted  
119 - ? const Icon(Icons.stop)  
120 - : const Icon(Icons.play_arrow),  
121 - iconSize: 32.0,  
122 - onPressed: () => setState(() {  
123 - isStarted  
124 - ? controller.stop()  
125 - : controller.start();  
126 - isStarted = !isStarted;  
127 - }),  
128 - ), 101 + Align(
  102 + alignment: Alignment.bottomCenter,
  103 + child: Container(
  104 + alignment: Alignment.bottomCenter,
  105 + height: 100,
  106 + color: Colors.black.withOpacity(0.4),
  107 + child: Column(
  108 + children: [
  109 + _buildZoomScaleSlider(),
  110 + Row(
  111 + mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  112 + children: [
  113 + ToggleFlashlightButton(controller: controller),
  114 + StartStopMobileScannerButton(controller: controller),
129 Center( 115 Center(
130 child: SizedBox( 116 child: SizedBox(
131 width: MediaQuery.of(context).size.width - 200, 117 width: MediaQuery.of(context).size.width - 200,
@@ -143,53 +129,8 @@ class _BarcodeScannerWithZoomState extends State<BarcodeScannerWithZoom> @@ -143,53 +129,8 @@ class _BarcodeScannerWithZoomState extends State<BarcodeScannerWithZoom>
143 ), 129 ),
144 ), 130 ),
145 ), 131 ),
146 - IconButton(  
147 - color: Colors.white,  
148 - icon: ValueListenableBuilder<CameraFacing>(  
149 - valueListenable: controller.cameraFacingState,  
150 - builder: (context, state, child) {  
151 - switch (state) {  
152 - case CameraFacing.front:  
153 - return const Icon(Icons.camera_front);  
154 - case CameraFacing.back:  
155 - return const Icon(Icons.camera_rear);  
156 - }  
157 - },  
158 - ),  
159 - iconSize: 32.0,  
160 - onPressed: () => controller.switchCamera(),  
161 - ),  
162 - IconButton(  
163 - color: Colors.white,  
164 - icon: const Icon(Icons.image),  
165 - iconSize: 32.0,  
166 - onPressed: () async {  
167 - final ImagePicker picker = ImagePicker();  
168 - // Pick an image  
169 - final XFile? image = await picker.pickImage(  
170 - source: ImageSource.gallery,  
171 - );  
172 - if (image != null) {  
173 - if (await controller.analyzeImage(image.path)) {  
174 - if (!context.mounted) return;  
175 - ScaffoldMessenger.of(context).showSnackBar(  
176 - const SnackBar(  
177 - content: Text('Barcode found!'),  
178 - backgroundColor: Colors.green,  
179 - ),  
180 - );  
181 - } else {  
182 - if (!context.mounted) return;  
183 - ScaffoldMessenger.of(context).showSnackBar(  
184 - const SnackBar(  
185 - content: Text('No barcode found!'),  
186 - backgroundColor: Colors.red,  
187 - ),  
188 - );  
189 - }  
190 - }  
191 - },  
192 - ), 132 + SwitchCameraButton(controller: controller),
  133 + AnalyzeImageFromGalleryButton(controller: controller),
193 ], 134 ],
194 ), 135 ),
195 ], 136 ],
@@ -202,4 +143,11 @@ class _BarcodeScannerWithZoomState extends State<BarcodeScannerWithZoom> @@ -202,4 +143,11 @@ class _BarcodeScannerWithZoomState extends State<BarcodeScannerWithZoom>
202 ), 143 ),
203 ); 144 );
204 } 145 }
  146 +
  147 + @override
  148 + Future<void> dispose() async {
  149 + _barcodesSubscription?.cancel();
  150 + await controller.dispose();
  151 + super.dispose();
  152 + }
205 } 153 }