mirror of
				https://github.com/inventree/inventree-app.git
				synced 2025-10-31 13:25:40 +00:00 
			
		
		
		
	Barcode Scanning Updates (#448)
* Add new setting for controlling manual barcode scan * Adds ability to pause and resume scanning with button - Camera is still "live" during this * Add UI elements * Change scan setting - "Continuous" scanning - Enabled by default * Update release notes * Scanner updates - Use "hold to pause" in continuous scan - Use "tap to pause" in single scan * Improve barcode scanning options - Allow tap-to-pause or hold-to-pause - More obvious user interactions * Ensure consistent icon placement * Remove separate setting for barcode pause mode
This commit is contained in:
		| @@ -1,5 +1,8 @@ | ||||
| import "dart:io"; | ||||
| import "package:flutter/material.dart"; | ||||
| import "package:font_awesome_flutter/font_awesome_flutter.dart"; | ||||
| import "package:inventree/app_colors.dart"; | ||||
| import "package:inventree/preferences.dart"; | ||||
|  | ||||
| import "package:qr_code_scanner/qr_code_scanner.dart"; | ||||
|  | ||||
| @@ -13,30 +16,54 @@ import "package:inventree/barcode/controller.dart"; | ||||
|  * Under the hood it uses the qr_code_scanner package. | ||||
|  */ | ||||
| class CameraBarcodeController extends InvenTreeBarcodeController { | ||||
|  | ||||
|   const CameraBarcodeController(BarcodeHandler handler, {Key? key}) : super(handler, key: key); | ||||
|   const CameraBarcodeController(BarcodeHandler handler, {Key? key}) | ||||
|       : super(handler, key: key); | ||||
|  | ||||
|   @override | ||||
|   State<StatefulWidget> createState() => _CameraBarcodeControllerState(); | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
| class _CameraBarcodeControllerState extends InvenTreeBarcodeControllerState { | ||||
|  | ||||
|   _CameraBarcodeControllerState() : super(); | ||||
|  | ||||
|   QRViewController? _controller; | ||||
|  | ||||
|   bool flash_status = false; | ||||
|  | ||||
|   bool single_scanning = false; | ||||
|   bool scanning_paused = false; | ||||
|  | ||||
|   Future<void> _loadSettings() async { | ||||
|     bool _single = await InvenTreeSettingsManager() | ||||
|         .getBool(INV_BARCODE_SCAN_SINGLE, false); | ||||
|  | ||||
|     if (mounted) { | ||||
|       setState(() { | ||||
|         single_scanning = _single; | ||||
|         scanning_paused = false; | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /* Callback function when the Barcode scanner view is initially created */ | ||||
|   void _onViewCreated(BuildContext context, QRViewController controller) { | ||||
|     _controller = controller; | ||||
|  | ||||
|     controller.scannedDataStream.listen((barcode) { | ||||
|       handleBarcodeData(barcode.code); | ||||
|       if (!scanning_paused) { | ||||
|         handleBarcodeData(barcode.code).then((value) => { | ||||
|               // If in single-scanning mode, pause after successful scan | ||||
|               if (single_scanning && mounted) | ||||
|                 { | ||||
|                   setState(() { | ||||
|                     scanning_paused = true; | ||||
|                   }) | ||||
|                 } | ||||
|             }); | ||||
|       } | ||||
|     }); | ||||
|  | ||||
|     _loadSettings(); | ||||
|   } | ||||
|  | ||||
|   // In order to get hot reload to work we need to pause the camera if the platform | ||||
| @@ -71,7 +98,6 @@ class _CameraBarcodeControllerState extends InvenTreeBarcodeControllerState { | ||||
|  | ||||
|   @override | ||||
|   Future<void> resumeScan() async { | ||||
|  | ||||
|     // Do not attempt to resume if the widget is not mounted | ||||
|     if (!mounted) { | ||||
|       return; | ||||
| @@ -97,17 +123,25 @@ class _CameraBarcodeControllerState extends InvenTreeBarcodeControllerState { | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     Widget actionIcon = | ||||
|         FaIcon(FontAwesomeIcons.circlePause, color: COLOR_WARNING, size: 64); | ||||
|  | ||||
|     if (scanning_paused) { | ||||
|       actionIcon = | ||||
|           FaIcon(FontAwesomeIcons.circlePlay, color: COLOR_ACTION, size: 64); | ||||
|     } | ||||
|  | ||||
|     String info_text = scanning_paused ? L10().barcodeScanPaused : L10().barcodeScanPause; | ||||
|  | ||||
|     return Scaffold( | ||||
|         appBar: AppBar( | ||||
|           title: Text(L10().scanBarcode), | ||||
|           actions: [ | ||||
|             IconButton( | ||||
|               icon: Icon(Icons.flip_camera_android), | ||||
|               onPressed: () { | ||||
|                 _controller?.flipCamera(); | ||||
|               } | ||||
|             ), | ||||
|                 icon: Icon(Icons.flip_camera_android), | ||||
|                 onPressed: () { | ||||
|                   _controller?.flipCamera(); | ||||
|                 }), | ||||
|             IconButton( | ||||
|               icon: flash_status ? Icon(Icons.flash_off) : Icon(Icons.flash_on), | ||||
|               onPressed: () { | ||||
| @@ -117,44 +151,71 @@ class _CameraBarcodeControllerState extends InvenTreeBarcodeControllerState { | ||||
|             ) | ||||
|           ], | ||||
|         ), | ||||
|         body: Stack( | ||||
|           children: <Widget>[ | ||||
|             Column( | ||||
|               children: [ | ||||
|                 Expanded( | ||||
|                   child: QRView( | ||||
|         body: GestureDetector( | ||||
|             onTapDown: (details) async { | ||||
|               setState(() { | ||||
|                 scanning_paused = !scanning_paused; | ||||
|               }); | ||||
|             }, | ||||
|             onLongPressEnd: (details) async { | ||||
|               if (mounted) { | ||||
|                 setState(() { | ||||
|                   scanning_paused = false; | ||||
|                 }); | ||||
|               } | ||||
|             }, | ||||
|             child: Stack( | ||||
|               children: <Widget>[ | ||||
|                 Column(children: [ | ||||
|                   Expanded( | ||||
|                       child: QRView( | ||||
|                     key: barcodeControllerKey, | ||||
|                     onQRViewCreated: (QRViewController controller) { | ||||
|                       _onViewCreated(context, controller); | ||||
|                     }, | ||||
|                     overlay: QrScannerOverlayShape( | ||||
|                       borderColor: Colors.red, | ||||
|                       borderColor: | ||||
|                           scanning_paused ? COLOR_WARNING : COLOR_ACTION, | ||||
|                       borderRadius: 10, | ||||
|                       borderLength: 30, | ||||
|                       borderWidth: 10, | ||||
|                       cutOutSize: 300, | ||||
|                     ), | ||||
|                   ) | ||||
|                 ) | ||||
|               ] | ||||
|             ), | ||||
|             Center( | ||||
|                 child: Column( | ||||
|                     children: [ | ||||
|                       Spacer(), | ||||
|                       Padding( | ||||
|                         child: Text(widget.handler.getOverlayText(context), | ||||
|                           style: TextStyle( | ||||
|                               fontWeight: FontWeight.bold, | ||||
|                               color: Colors.white), | ||||
|                         ), | ||||
|                         padding: EdgeInsets.all(20), | ||||
|                   )) | ||||
|                 ]), | ||||
|                 Center( | ||||
|                     child: Column(children: [ | ||||
|                   Padding( | ||||
|                       child: Text( | ||||
|                         widget.handler.getOverlayText(context), | ||||
|                         style: TextStyle( | ||||
|                           fontSize: 16, | ||||
|                             fontWeight: FontWeight.bold, color: Colors.white), | ||||
|                       ), | ||||
|                     ] | ||||
|                 ) | ||||
|             ) | ||||
|           ], | ||||
|         ) | ||||
|     ); | ||||
|                       padding: EdgeInsets.all(25)), | ||||
|                   Padding( | ||||
|                     child: CircularProgressIndicator( | ||||
|                         value: scanning_paused ? 0 : null), | ||||
|                     padding: EdgeInsets.all(40), | ||||
|                   ), | ||||
|                   Spacer(), | ||||
|                   SizedBox( | ||||
|                     child: Center( | ||||
|                       child: actionIcon, | ||||
|                     ), | ||||
|                     width: 100, | ||||
|                     height: 150, | ||||
|                   ), | ||||
|                   Padding( | ||||
|                     child: Text(info_text, | ||||
|                         textAlign: TextAlign.center, | ||||
|                         style: TextStyle( | ||||
|                           color: Colors.white, | ||||
|                         )), | ||||
|                     padding: EdgeInsets.all(25), | ||||
|                   ), | ||||
|                 ])) | ||||
|               ], | ||||
|             ))); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -115,6 +115,12 @@ | ||||
|   "barcodeReceivePart": "Scan barcode to receive part", | ||||
|   "@barcodeReceivePart": {}, | ||||
|  | ||||
|   "barcodeScanPaused": "Barcode scanning paused", | ||||
|   "@barodeScanPaused": {}, | ||||
|  | ||||
|   "barcodeScanPause": "Tap or hold to pause scanning", | ||||
|   "@barcodeScanPause": {}, | ||||
|  | ||||
|   "barcodeScanAssign": "Scan to assign barcode", | ||||
|   "@barcodeScanAssign": {}, | ||||
|  | ||||
| @@ -139,6 +145,12 @@ | ||||
|   "barcodeScanLocation": "Scan stock location", | ||||
|   "@barcodeScanLocation": {}, | ||||
|  | ||||
|   "barcodeScanSingle": "Single Scan Mode", | ||||
|   "@barcodeScanSingle": {}, | ||||
|  | ||||
|   "barcodeScanSingleDetail": "Pause barcode scanner after each scan", | ||||
|   "@barcodeScanSingleDetail": {}, | ||||
|  | ||||
|   "barcodeScanIntoLocationSuccess": "Scanned into location", | ||||
|   "@barcodeScanIntoLocationSuccess": {}, | ||||
|  | ||||
|   | ||||
| @@ -42,6 +42,7 @@ const String INV_STRICT_HTTPS = "strictHttps"; | ||||
| // Barcode settings | ||||
| const String INV_BARCODE_SCAN_DELAY = "barcodeScanDelay"; | ||||
| const String INV_BARCODE_SCAN_TYPE = "barcodeScanType"; | ||||
| const String INV_BARCODE_SCAN_SINGLE = "barcodeScanSingle"; | ||||
|  | ||||
| // Barcode scanner types | ||||
| const int BARCODE_CONTROLLER_CAMERA = 0; | ||||
|   | ||||
| @@ -18,6 +18,7 @@ class _InvenTreeBarcodeSettingsState extends State<InvenTreeBarcodeSettingsWidge | ||||
|  | ||||
|  int barcodeScanDelay = 500; | ||||
|  int barcodeScanType = BARCODE_CONTROLLER_CAMERA; | ||||
|  bool barcodeScanSingle = false; | ||||
|  | ||||
|  final TextEditingController _barcodeScanDelayController = TextEditingController(); | ||||
|  | ||||
| @@ -30,6 +31,7 @@ class _InvenTreeBarcodeSettingsState extends State<InvenTreeBarcodeSettingsWidge | ||||
|   Future<void> loadSettings() async { | ||||
|     barcodeScanDelay = await InvenTreeSettingsManager().getValue(INV_BARCODE_SCAN_DELAY, 500) as int; | ||||
|     barcodeScanType = await InvenTreeSettingsManager().getValue(INV_BARCODE_SCAN_TYPE, BARCODE_CONTROLLER_CAMERA) as int; | ||||
|     barcodeScanSingle = await InvenTreeSettingsManager().getBool(INV_BARCODE_SCAN_SINGLE, false); | ||||
|  | ||||
|     if (mounted) { | ||||
|       setState(() { | ||||
| @@ -153,6 +155,20 @@ class _InvenTreeBarcodeSettingsState extends State<InvenTreeBarcodeSettingsWidge | ||||
|                 }, | ||||
|               ), | ||||
|             ), | ||||
|             ListTile( | ||||
|               title: Text(L10().barcodeScanSingle), | ||||
|               subtitle: Text(L10().barcodeScanSingleDetail), | ||||
|               leading: Icon(Icons.barcode_reader), | ||||
|               trailing: Switch( | ||||
|                 value: barcodeScanSingle, | ||||
|                 onChanged: (bool v) { | ||||
|                   InvenTreeSettingsManager().setValue(INV_BARCODE_SCAN_SINGLE, v); | ||||
|                   setState(() { | ||||
|                     barcodeScanSingle = v; | ||||
|                   }); | ||||
|                 }, | ||||
|               ), | ||||
|             ) | ||||
|           ], | ||||
|         ) | ||||
|       ) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user