Showing
8 changed files
with
522 additions
and
2 deletions
| @@ -2,7 +2,6 @@ import 'package:flutter/foundation.dart'; | @@ -2,7 +2,6 @@ import 'package:flutter/foundation.dart'; | ||
| 2 | import 'package:flutter/material.dart'; | 2 | import 'package:flutter/material.dart'; |
| 3 | import 'package:mobile_scanner/mobile_scanner.dart'; | 3 | import 'package:mobile_scanner/mobile_scanner.dart'; |
| 4 | import 'package:mobile_scanner_example/scanned_barcode_label.dart'; | 4 | import 'package:mobile_scanner_example/scanned_barcode_label.dart'; |
| 5 | - | ||
| 6 | import 'package:mobile_scanner_example/scanner_error_widget.dart'; | 5 | import 'package:mobile_scanner_example/scanner_error_widget.dart'; |
| 7 | 6 | ||
| 8 | class BarcodeScannerWithScanWindow extends StatefulWidget { | 7 | class BarcodeScannerWithScanWindow extends StatefulWidget { |
| 1 | import 'package:flutter/material.dart'; | 1 | import 'package:flutter/material.dart'; |
| 2 | +import 'package:flutter/services.dart'; | ||
| 2 | import 'package:mobile_scanner_example/barcode_scanner_analyze_image.dart'; | 3 | import 'package:mobile_scanner_example/barcode_scanner_analyze_image.dart'; |
| 3 | import 'package:mobile_scanner_example/barcode_scanner_controller.dart'; | 4 | import 'package:mobile_scanner_example/barcode_scanner_controller.dart'; |
| 4 | import 'package:mobile_scanner_example/barcode_scanner_listview.dart'; | 5 | import 'package:mobile_scanner_example/barcode_scanner_listview.dart'; |
| @@ -8,8 +9,11 @@ import 'package:mobile_scanner_example/barcode_scanner_simple.dart'; | @@ -8,8 +9,11 @@ import 'package:mobile_scanner_example/barcode_scanner_simple.dart'; | ||
| 8 | import 'package:mobile_scanner_example/barcode_scanner_window.dart'; | 9 | import 'package:mobile_scanner_example/barcode_scanner_window.dart'; |
| 9 | import 'package:mobile_scanner_example/barcode_scanner_zoom.dart'; | 10 | import 'package:mobile_scanner_example/barcode_scanner_zoom.dart'; |
| 10 | import 'package:mobile_scanner_example/mobile_scanner_overlay.dart'; | 11 | import 'package:mobile_scanner_example/mobile_scanner_overlay.dart'; |
| 12 | +import 'package:mobile_scanner_example/picklist/picklist_result.dart'; | ||
| 11 | 13 | ||
| 12 | -void main() { | 14 | +void main() async { |
| 15 | + WidgetsFlutterBinding.ensureInitialized(); | ||
| 16 | + await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); | ||
| 13 | runApp( | 17 | runApp( |
| 14 | const MaterialApp( | 18 | const MaterialApp( |
| 15 | title: 'Mobile Scanner Example', | 19 | title: 'Mobile Scanner Example', |
| @@ -91,6 +95,11 @@ class MyHome extends StatelessWidget { | @@ -91,6 +95,11 @@ class MyHome extends StatelessWidget { | ||
| 91 | 'Analyze image from file', | 95 | 'Analyze image from file', |
| 92 | const BarcodeScannerAnalyzeImage(), | 96 | const BarcodeScannerAnalyzeImage(), |
| 93 | ), | 97 | ), |
| 98 | + _buildItem( | ||
| 99 | + context, | ||
| 100 | + 'Picklist mode', | ||
| 101 | + const PicklistResult(), | ||
| 102 | + ), | ||
| 94 | ], | 103 | ], |
| 95 | ), | 104 | ), |
| 96 | ), | 105 | ), |
| 1 | +import 'dart:async'; | ||
| 2 | + | ||
| 3 | +import 'package:flutter/material.dart'; | ||
| 4 | +import 'package:flutter/services.dart'; | ||
| 5 | +import 'package:mobile_scanner/mobile_scanner.dart'; | ||
| 6 | +import 'package:mobile_scanner_example/picklist/classes/detect_collision.dart'; | ||
| 7 | +import 'package:mobile_scanner_example/picklist/widgets/crosshair.dart'; | ||
| 8 | +import 'package:mobile_scanner_example/picklist/widgets/draw_detected_barcodes.dart'; | ||
| 9 | +import 'package:mobile_scanner_example/scanner_error_widget.dart'; | ||
| 10 | + | ||
| 11 | +class BarcodeScannerPicklist extends StatefulWidget { | ||
| 12 | + const BarcodeScannerPicklist({super.key}); | ||
| 13 | + | ||
| 14 | + @override | ||
| 15 | + State<BarcodeScannerPicklist> createState() => _BarcodeScannerPicklistState(); | ||
| 16 | +} | ||
| 17 | + | ||
| 18 | +class _BarcodeScannerPicklistState extends State<BarcodeScannerPicklist> | ||
| 19 | + with WidgetsBindingObserver { | ||
| 20 | + final _mobileScannerController = MobileScannerController(autoStart: false); | ||
| 21 | + StreamSubscription<Object?>? _barcodesSubscription; | ||
| 22 | + | ||
| 23 | + final _scannerDisabled = ValueNotifier(false); | ||
| 24 | + | ||
| 25 | + late final Offset _crosshair; | ||
| 26 | + | ||
| 27 | + bool barcodeDetected = false; | ||
| 28 | + | ||
| 29 | + @override | ||
| 30 | + void didChangeDependencies() { | ||
| 31 | + _crosshair = MediaQuery.sizeOf(context).center(Offset.zero); | ||
| 32 | + super.didChangeDependencies(); | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + @override | ||
| 36 | + void initState() { | ||
| 37 | + SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); | ||
| 38 | + WidgetsBinding.instance.addObserver(this); | ||
| 39 | + _barcodesSubscription = _mobileScannerController.barcodes.listen( | ||
| 40 | + _handleBarcodes, | ||
| 41 | + ); | ||
| 42 | + super.initState(); | ||
| 43 | + unawaited(_mobileScannerController.start()); | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + @override | ||
| 47 | + void didChangeAppLifecycleState(AppLifecycleState state) { | ||
| 48 | + if (!_mobileScannerController.value.isInitialized) { | ||
| 49 | + return; | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + switch (state) { | ||
| 53 | + case AppLifecycleState.detached: | ||
| 54 | + case AppLifecycleState.hidden: | ||
| 55 | + case AppLifecycleState.paused: | ||
| 56 | + return; | ||
| 57 | + case AppLifecycleState.resumed: | ||
| 58 | + _barcodesSubscription = | ||
| 59 | + _mobileScannerController.barcodes.listen(_handleBarcodes); | ||
| 60 | + | ||
| 61 | + unawaited(_mobileScannerController.start()); | ||
| 62 | + case AppLifecycleState.inactive: | ||
| 63 | + unawaited(_barcodesSubscription?.cancel()); | ||
| 64 | + _barcodesSubscription = null; | ||
| 65 | + unawaited(_mobileScannerController.stop()); | ||
| 66 | + } | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + @override | ||
| 70 | + void dispose() { | ||
| 71 | + WidgetsBinding.instance.removeObserver(this); | ||
| 72 | + unawaited(_barcodesSubscription?.cancel()); | ||
| 73 | + _barcodesSubscription = null; | ||
| 74 | + super.dispose(); | ||
| 75 | + _mobileScannerController.dispose(); | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + void _handleBarcodes(BarcodeCapture barcodes) { | ||
| 79 | + if (_scannerDisabled.value) { | ||
| 80 | + return; | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + for (final barcode in barcodes.barcodes) { | ||
| 84 | + if (isOffsetInsideShape( | ||
| 85 | + _crosshair, | ||
| 86 | + barcode.corners, | ||
| 87 | + )) { | ||
| 88 | + if (!barcodeDetected) { | ||
| 89 | + barcodeDetected = true; | ||
| 90 | + Navigator.of(context).pop(barcode); | ||
| 91 | + } | ||
| 92 | + return; | ||
| 93 | + } | ||
| 94 | + } | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | + @override | ||
| 98 | + Widget build(BuildContext context) { | ||
| 99 | + const boxFit = BoxFit.contain; | ||
| 100 | + return PopScope( | ||
| 101 | + onPopInvokedWithResult: (didPop, result) { | ||
| 102 | + if (didPop) { | ||
| 103 | + SystemChrome.setPreferredOrientations([...DeviceOrientation.values]); | ||
| 104 | + } | ||
| 105 | + }, | ||
| 106 | + child: Scaffold( | ||
| 107 | + appBar: AppBar(title: const Text('Picklist scanner')), | ||
| 108 | + backgroundColor: Colors.black, | ||
| 109 | + body: StreamBuilder( | ||
| 110 | + stream: _mobileScannerController.barcodes, | ||
| 111 | + builder: (context, snapshot) { | ||
| 112 | + final barcodes = snapshot.data; | ||
| 113 | + if (barcodes == null) { | ||
| 114 | + debugPrint('ISNULL'); | ||
| 115 | + } | ||
| 116 | + return Listener( | ||
| 117 | + behavior: HitTestBehavior.opaque, | ||
| 118 | + onPointerDown: (_) => _scannerDisabled.value = true, | ||
| 119 | + onPointerUp: (_) => _scannerDisabled.value = false, | ||
| 120 | + onPointerCancel: (_) => _scannerDisabled.value = false, | ||
| 121 | + child: Stack( | ||
| 122 | + fit: StackFit.expand, | ||
| 123 | + children: [ | ||
| 124 | + MobileScanner( | ||
| 125 | + controller: _mobileScannerController, | ||
| 126 | + errorBuilder: (context, error, child) { | ||
| 127 | + return ScannerErrorWidget(error: error); | ||
| 128 | + }, | ||
| 129 | + fit: boxFit, | ||
| 130 | + ), | ||
| 131 | + ...drawDetectedBarcodes( | ||
| 132 | + barcodes: barcodes?.barcodes, | ||
| 133 | + cameraPreviewSize: _mobileScannerController.value.size, | ||
| 134 | + fit: boxFit, | ||
| 135 | + ), | ||
| 136 | + ValueListenableBuilder( | ||
| 137 | + valueListenable: _scannerDisabled, | ||
| 138 | + builder: (context, value, child) { | ||
| 139 | + return Crosshair( | ||
| 140 | + scannerDisabled: value, | ||
| 141 | + ); | ||
| 142 | + }, | ||
| 143 | + ), | ||
| 144 | + ], | ||
| 145 | + ), | ||
| 146 | + ); | ||
| 147 | + }, | ||
| 148 | + ) | ||
| 149 | + // body: Stack( | ||
| 150 | + // fit: StackFit.expand, | ||
| 151 | + // children: [ | ||
| 152 | + // MobileScanner( | ||
| 153 | + // controller: _mobileScannerController, | ||
| 154 | + // errorBuilder: (context, error, child) { | ||
| 155 | + // return ScannerErrorWidget(error: error); | ||
| 156 | + // }, | ||
| 157 | + // fit: boxFit, | ||
| 158 | + // ), | ||
| 159 | + // ...drawDetectedBarcodes( | ||
| 160 | + // controller: _mobileScannerController, | ||
| 161 | + // cameraPreviewSize: _mobileScannerController.value.size, | ||
| 162 | + // fit: boxFit, | ||
| 163 | + // ), | ||
| 164 | + // ...drawDetectedBarcodes( | ||
| 165 | + // controller: _mobileScannerController, | ||
| 166 | + // cameraPreviewSize: _mobileScannerController.value.size, | ||
| 167 | + // fit: boxFit, | ||
| 168 | + // ), | ||
| 169 | + // // barcodes: _mobileScannerController. value. _barcodes.value, | ||
| 170 | + // // cameraPreviewSize: _mobileScannerController.value.size, | ||
| 171 | + // // fit: boxFit, | ||
| 172 | + // // ), | ||
| 173 | + // CustomPaint( | ||
| 174 | + // painter: BarcodeOverlay( | ||
| 175 | + // barcodeCorners: [ | ||
| 176 | + // const Offset(0, 0), | ||
| 177 | + // const Offset(50, 0), | ||
| 178 | + // const Offset(50, 50), | ||
| 179 | + // const Offset(0, 50) | ||
| 180 | + // ], | ||
| 181 | + // barcodeSize: Size(50, 50), | ||
| 182 | + // boxFit: boxFit, | ||
| 183 | + // cameraPreviewSize: _mobileScannerController.value.size, | ||
| 184 | + // ), | ||
| 185 | + // ), | ||
| 186 | + // Crosshair( | ||
| 187 | + // crosshairRectangle: _crosshairRectangle, | ||
| 188 | + // scannerDisabled: _scannerDisabled.value, | ||
| 189 | + // ), | ||
| 190 | + // ], | ||
| 191 | + // ), | ||
| 192 | + , | ||
| 193 | + ), | ||
| 194 | + ); | ||
| 195 | + } | ||
| 196 | +} |
| 1 | +//Some magic created by chatGPT | ||
| 2 | + | ||
| 3 | +import 'package:flutter/material.dart'; | ||
| 4 | + | ||
| 5 | +bool isOffsetInsideShape(Offset point, List<Offset> shape) { | ||
| 6 | + return _isPointInPolygon(shape, point); | ||
| 7 | +} | ||
| 8 | + | ||
| 9 | +bool _isPointInPolygon(List<Offset> polygon, Offset point) { | ||
| 10 | + // Use the ray-casting algorithm for checking if a point is inside a polygon | ||
| 11 | + bool inside = false; | ||
| 12 | + for (int i = 0, j = polygon.length - 1; i < polygon.length; j = i++) { | ||
| 13 | + if ((polygon[i].dy > point.dy) != (polygon[j].dy > point.dy) && | ||
| 14 | + (point.dx < | ||
| 15 | + (polygon[j].dx - polygon[i].dx) * | ||
| 16 | + (point.dy - polygon[i].dy) / | ||
| 17 | + (polygon[j].dy - polygon[i].dy) + | ||
| 18 | + polygon[i].dx)) { | ||
| 19 | + inside = !inside; | ||
| 20 | + } | ||
| 21 | + } | ||
| 22 | + return inside; | ||
| 23 | +} | ||
| 24 | + | ||
| 25 | +// import 'package:flutter/material.dart'; | ||
| 26 | +// | ||
| 27 | +// bool crosshairFullyFitsIntoShape(Rect rect, List<Offset> shape) { | ||
| 28 | +// final List<Offset> rectCorners = [ | ||
| 29 | +// Offset(rect.left, rect.top), | ||
| 30 | +// Offset(rect.right, rect.top), | ||
| 31 | +// Offset(rect.right, rect.bottom), | ||
| 32 | +// Offset(rect.left, rect.bottom), | ||
| 33 | +// ]; | ||
| 34 | +// | ||
| 35 | +// // Check if all rect corners are inside the shape | ||
| 36 | +// for (final Offset corner in rectCorners) { | ||
| 37 | +// if (!_isPointInPolygon(shape, corner)) { | ||
| 38 | +// return false; // If any corner is outside, the rectangle doesn't fit fully | ||
| 39 | +// } | ||
| 40 | +// } | ||
| 41 | +// | ||
| 42 | +// return true; // All corners are inside the shape | ||
| 43 | +// } | ||
| 44 | +// | ||
| 45 | +// bool _isPointInPolygon(List<Offset> polygon, Offset point) { | ||
| 46 | +// // Use the ray-casting algorithm for checking if a point is inside a polygon | ||
| 47 | +// bool inside = false; | ||
| 48 | +// for (int i = 0, j = polygon.length - 1; i < polygon.length; j = i++) { | ||
| 49 | +// if ((polygon[i].dy > point.dy) != (polygon[j].dy > point.dy) && | ||
| 50 | +// (point.dx < | ||
| 51 | +// (polygon[j].dx - polygon[i].dx) * | ||
| 52 | +// (point.dy - polygon[i].dy) / | ||
| 53 | +// (polygon[j].dy - polygon[i].dy) + | ||
| 54 | +// polygon[i].dx)) { | ||
| 55 | +// inside = !inside; | ||
| 56 | +// } | ||
| 57 | +// } | ||
| 58 | +// return inside; | ||
| 59 | +// } | ||
| 60 | +// // import 'package:flutter/material.dart'; | ||
| 61 | +// // | ||
| 62 | +// // bool crosshairTouchesBarcode(Rect rect, List<Offset> shape) { | ||
| 63 | +// // final List<Offset> rectCorners = [ | ||
| 64 | +// // Offset(rect.left, rect.top), | ||
| 65 | +// // Offset(rect.right, rect.top), | ||
| 66 | +// // Offset(rect.right, rect.bottom), | ||
| 67 | +// // Offset(rect.left, rect.bottom), | ||
| 68 | +// // ]; | ||
| 69 | +// // final List<Offset> edges = [shape[0], shape[1], shape[2], shape[3], shape[0]]; | ||
| 70 | +// // | ||
| 71 | +// // // Check edge intersection | ||
| 72 | +// // for (int i = 0; i < edges.length - 1; i++) { | ||
| 73 | +// // for (int j = 0; j < rectCorners.length; j++) { | ||
| 74 | +// // final int next = (j + 1) % rectCorners.length; | ||
| 75 | +// // if (_checkIntersection( | ||
| 76 | +// // edges[i], | ||
| 77 | +// // edges[i + 1], | ||
| 78 | +// // rectCorners[j], | ||
| 79 | +// // rectCorners[next], | ||
| 80 | +// // )) { | ||
| 81 | +// // return true; | ||
| 82 | +// // } | ||
| 83 | +// // } | ||
| 84 | +// // } | ||
| 85 | +// // | ||
| 86 | +// // // Check if any rect corner is inside the shape | ||
| 87 | +// // for (final Offset corner in rectCorners) { | ||
| 88 | +// // if (_isPointInPolygon(shape, corner)) { | ||
| 89 | +// // return true; | ||
| 90 | +// // } | ||
| 91 | +// // } | ||
| 92 | +// // | ||
| 93 | +// // return false; | ||
| 94 | +// // } | ||
| 95 | +// // | ||
| 96 | +// // bool _checkIntersection(Offset p1, Offset p2, Offset p3, Offset p4) { | ||
| 97 | +// // // Calculate the intersection of two line segments | ||
| 98 | +// // double s1X; | ||
| 99 | +// // double s1Y; | ||
| 100 | +// // double s2X; | ||
| 101 | +// // double s2Y; | ||
| 102 | +// // s1X = p2.dx - p1.dx; | ||
| 103 | +// // s1Y = p2.dy - p1.dy; | ||
| 104 | +// // s2X = p4.dx - p3.dx; | ||
| 105 | +// // s2Y = p4.dy - p3.dy; | ||
| 106 | +// // | ||
| 107 | +// // double s; | ||
| 108 | +// // double t; | ||
| 109 | +// // s = (-s1Y * (p1.dx - p3.dx) + s1X * (p1.dy - p3.dy)) / | ||
| 110 | +// // (-s2X * s1Y + s1X * s2Y); | ||
| 111 | +// // t = (s2X * (p1.dy - p3.dy) - s2Y * (p1.dx - p3.dx)) / | ||
| 112 | +// // (-s2X * s1Y + s1X * s2Y); | ||
| 113 | +// // | ||
| 114 | +// // return s >= 0 && s <= 1 && t >= 0 && t <= 1; | ||
| 115 | +// // } | ||
| 116 | +// // | ||
| 117 | +// // bool _isPointInPolygon(List<Offset> polygon, Offset point) { | ||
| 118 | +// // // Ray-casting algorithm for checking if a point is inside a polygon | ||
| 119 | +// // bool inside = false; | ||
| 120 | +// // for (int i = 0, j = polygon.length - 1; i < polygon.length; j = i++) { | ||
| 121 | +// // if ((polygon[i].dy > point.dy) != (polygon[j].dy > point.dy) && | ||
| 122 | +// // (point.dx < | ||
| 123 | +// // (polygon[j].dx - polygon[i].dx) * | ||
| 124 | +// // (point.dy - polygon[i].dy) / | ||
| 125 | +// // (polygon[j].dy - polygon[i].dy) + | ||
| 126 | +// // polygon[i].dx)) { | ||
| 127 | +// // inside = !inside; | ||
| 128 | +// // } | ||
| 129 | +// // } | ||
| 130 | +// // return inside; | ||
| 131 | +// // } |
example/lib/picklist/picklist_result.dart
0 → 100644
| 1 | +import 'package:flutter/material.dart'; | ||
| 2 | +import 'package:mobile_scanner/mobile_scanner.dart'; | ||
| 3 | +import 'package:mobile_scanner_example/picklist/barcode_scanner_picklist.dart'; | ||
| 4 | + | ||
| 5 | +class PicklistResult extends StatefulWidget { | ||
| 6 | + const PicklistResult({super.key}); | ||
| 7 | + | ||
| 8 | + @override | ||
| 9 | + State<PicklistResult> createState() => _PicklistResultState(); | ||
| 10 | +} | ||
| 11 | + | ||
| 12 | +class _PicklistResultState extends State<PicklistResult> { | ||
| 13 | + String barcode = 'Scan Something!'; | ||
| 14 | + | ||
| 15 | + @override | ||
| 16 | + Widget build(BuildContext context) { | ||
| 17 | + return Scaffold( | ||
| 18 | + appBar: AppBar(title: const Text('Picklist mode')), | ||
| 19 | + body: SafeArea( | ||
| 20 | + child: Padding( | ||
| 21 | + padding: const EdgeInsets.symmetric(horizontal: 16.0), | ||
| 22 | + child: Center( | ||
| 23 | + child: Column( | ||
| 24 | + mainAxisAlignment: MainAxisAlignment.center, | ||
| 25 | + children: [ | ||
| 26 | + Text(barcode), | ||
| 27 | + ElevatedButton( | ||
| 28 | + onPressed: () async { | ||
| 29 | + final scannedBarcode = | ||
| 30 | + await Navigator.of(context).push<Barcode>( | ||
| 31 | + MaterialPageRoute( | ||
| 32 | + builder: (context) => const BarcodeScannerPicklist(), | ||
| 33 | + ), | ||
| 34 | + ); | ||
| 35 | + setState( | ||
| 36 | + () { | ||
| 37 | + if (scannedBarcode == null) { | ||
| 38 | + barcode = 'Scan Something!'; | ||
| 39 | + return; | ||
| 40 | + } | ||
| 41 | + if (scannedBarcode.displayValue == null) { | ||
| 42 | + barcode = '>>binary<<'; | ||
| 43 | + return; | ||
| 44 | + } | ||
| 45 | + barcode = scannedBarcode.displayValue!; | ||
| 46 | + }, | ||
| 47 | + ); | ||
| 48 | + }, | ||
| 49 | + child: const Text('Scan'), | ||
| 50 | + ), | ||
| 51 | + ], | ||
| 52 | + ), | ||
| 53 | + ), | ||
| 54 | + ), | ||
| 55 | + ), | ||
| 56 | + ); | ||
| 57 | + } | ||
| 58 | +} |
| 1 | +import 'package:flutter/foundation.dart'; | ||
| 2 | +import 'package:flutter/material.dart'; | ||
| 3 | + | ||
| 4 | +class BarcodeOverlay extends CustomPainter { | ||
| 5 | + BarcodeOverlay({ | ||
| 6 | + required this.barcodeCorners, | ||
| 7 | + required this.barcodeSize, | ||
| 8 | + required this.boxFit, | ||
| 9 | + required this.cameraPreviewSize, | ||
| 10 | + }); | ||
| 11 | + | ||
| 12 | + final List<Offset> barcodeCorners; | ||
| 13 | + final Size barcodeSize; | ||
| 14 | + final BoxFit boxFit; | ||
| 15 | + final Size cameraPreviewSize; | ||
| 16 | + | ||
| 17 | + @override | ||
| 18 | + void paint(Canvas canvas, Size size) { | ||
| 19 | + if (barcodeCorners.isEmpty || | ||
| 20 | + barcodeSize.isEmpty || | ||
| 21 | + cameraPreviewSize.isEmpty) { | ||
| 22 | + return; | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + final adjustedSize = applyBoxFit(boxFit, cameraPreviewSize, size); | ||
| 26 | + | ||
| 27 | + double verticalPadding = size.height - adjustedSize.destination.height; | ||
| 28 | + double horizontalPadding = size.width - adjustedSize.destination.width; | ||
| 29 | + if (verticalPadding > 0) { | ||
| 30 | + verticalPadding = verticalPadding / 2; | ||
| 31 | + } else { | ||
| 32 | + verticalPadding = 0; | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + if (horizontalPadding > 0) { | ||
| 36 | + horizontalPadding = horizontalPadding / 2; | ||
| 37 | + } else { | ||
| 38 | + horizontalPadding = 0; | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + final double ratioWidth; | ||
| 42 | + final double ratioHeight; | ||
| 43 | + | ||
| 44 | + if (!kIsWeb && defaultTargetPlatform == TargetPlatform.iOS) { | ||
| 45 | + ratioWidth = barcodeSize.width / adjustedSize.destination.width; | ||
| 46 | + ratioHeight = barcodeSize.height / adjustedSize.destination.height; | ||
| 47 | + } else { | ||
| 48 | + ratioWidth = cameraPreviewSize.width / adjustedSize.destination.width; | ||
| 49 | + ratioHeight = cameraPreviewSize.height / adjustedSize.destination.height; | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + final List<Offset> adjustedOffset = [ | ||
| 53 | + for (final offset in barcodeCorners) | ||
| 54 | + Offset( | ||
| 55 | + offset.dx / ratioWidth + horizontalPadding, | ||
| 56 | + offset.dy / ratioHeight + verticalPadding, | ||
| 57 | + ), | ||
| 58 | + ]; | ||
| 59 | + | ||
| 60 | + final cutoutPath = Path()..addPolygon(adjustedOffset, true); | ||
| 61 | + | ||
| 62 | + final backgroundPaint = Paint() | ||
| 63 | + ..color = Colors.red.withOpacity(1.0) | ||
| 64 | + ..style = PaintingStyle.fill | ||
| 65 | + ..blendMode = BlendMode.dstOut; | ||
| 66 | + | ||
| 67 | + canvas.drawPath(cutoutPath, backgroundPaint); | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + @override | ||
| 71 | + bool shouldRepaint(covariant CustomPainter oldDelegate) { | ||
| 72 | + return false; | ||
| 73 | + } | ||
| 74 | +} |
example/lib/picklist/widgets/crosshair.dart
0 → 100644
| 1 | +import 'package:flutter/material.dart'; | ||
| 2 | + | ||
| 3 | +class Crosshair extends StatelessWidget { | ||
| 4 | + const Crosshair({ | ||
| 5 | + super.key, | ||
| 6 | + required this.scannerDisabled, | ||
| 7 | + }); | ||
| 8 | + | ||
| 9 | + final bool scannerDisabled; | ||
| 10 | + | ||
| 11 | + @override | ||
| 12 | + Widget build(BuildContext context) { | ||
| 13 | + return Center( | ||
| 14 | + child: Icon( | ||
| 15 | + Icons.close, | ||
| 16 | + color: scannerDisabled ? Colors.green : Colors.red, | ||
| 17 | + ), | ||
| 18 | + ); | ||
| 19 | + } | ||
| 20 | +} |
| 1 | +import 'package:flutter/material.dart'; | ||
| 2 | +import 'package:mobile_scanner/mobile_scanner.dart'; | ||
| 3 | +import 'package:mobile_scanner_example/picklist/widgets/barcode_overlay.dart'; | ||
| 4 | + | ||
| 5 | +List<Widget> drawDetectedBarcodes({ | ||
| 6 | + required List<Barcode>? barcodes, | ||
| 7 | + required Size cameraPreviewSize, | ||
| 8 | + required BoxFit fit, | ||
| 9 | +}) { | ||
| 10 | + final barcodeWidgets = <Widget>[]; | ||
| 11 | + if (barcodes == null || barcodes.isEmpty) { | ||
| 12 | + debugPrint('EMPTY!!!'); | ||
| 13 | + } | ||
| 14 | + if (barcodes != null) { | ||
| 15 | + for (final barcode in barcodes) { | ||
| 16 | + barcodeWidgets.add( | ||
| 17 | + CustomPaint( | ||
| 18 | + painter: BarcodeOverlay( | ||
| 19 | + barcodeCorners: barcode.corners, | ||
| 20 | + barcodeSize: barcode.size, | ||
| 21 | + boxFit: fit, | ||
| 22 | + cameraPreviewSize: cameraPreviewSize, | ||
| 23 | + ), | ||
| 24 | + ), | ||
| 25 | + ); | ||
| 26 | + debugPrint( | ||
| 27 | + 'barcodeCorners => ${barcode.corners.map((e) => 'x: ${e.dx}, y: ${e.dy} ')}, barcodeSize => width: ${barcode.size.width}, height: ${barcode.size.height}, cameraPreviewSize => width: ${cameraPreviewSize.width}, height: ${cameraPreviewSize.height} ', | ||
| 28 | + ); | ||
| 29 | + } | ||
| 30 | + debugPrint(barcodeWidgets.length.toString()); | ||
| 31 | + } | ||
| 32 | + return barcodeWidgets; | ||
| 33 | +} |
-
Please register or login to post a comment