2
0
mirror of https://github.com/inventree/inventree-app.git synced 2025-04-28 05:26:47 +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:
Oliver 2023-11-14 07:39:06 +11:00 committed by GitHub
parent 20127c6090
commit 8cb5dd20f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 134 additions and 41 deletions

View File

@ -2,8 +2,11 @@
--- ---
- Adds support for Sales Orders - Adds support for Sales Orders
- Adds option to pause and resume barcode scanning with camera
- Adds option for "single shot" barcode scanning with camera
- Fixes bug when removing entire quantity of a stock item - Fixes bug when removing entire quantity of a stock item
### 0.13.0 - October 2023 ### 0.13.0 - October 2023
--- ---

View File

@ -1,5 +1,8 @@
import "dart:io"; import "dart:io";
import "package:flutter/material.dart"; 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"; import "package:qr_code_scanner/qr_code_scanner.dart";
@ -13,31 +16,55 @@ import "package:inventree/barcode/controller.dart";
* Under the hood it uses the qr_code_scanner package. * Under the hood it uses the qr_code_scanner package.
*/ */
class CameraBarcodeController extends InvenTreeBarcodeController { class CameraBarcodeController extends InvenTreeBarcodeController {
const CameraBarcodeController(BarcodeHandler handler, {Key? key})
const CameraBarcodeController(BarcodeHandler handler, {Key? key}) : super(handler, key: key); : super(handler, key: key);
@override @override
State<StatefulWidget> createState() => _CameraBarcodeControllerState(); State<StatefulWidget> createState() => _CameraBarcodeControllerState();
} }
class _CameraBarcodeControllerState extends InvenTreeBarcodeControllerState { class _CameraBarcodeControllerState extends InvenTreeBarcodeControllerState {
_CameraBarcodeControllerState() : super(); _CameraBarcodeControllerState() : super();
QRViewController? _controller; QRViewController? _controller;
bool flash_status = false; 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 */ /* Callback function when the Barcode scanner view is initially created */
void _onViewCreated(BuildContext context, QRViewController controller) { void _onViewCreated(BuildContext context, QRViewController controller) {
_controller = controller; _controller = controller;
controller.scannedDataStream.listen((barcode) { 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 // In order to get hot reload to work we need to pause the camera if the platform
// is android, or resume the camera if the platform is iOS. // is android, or resume the camera if the platform is iOS.
@ -71,7 +98,6 @@ class _CameraBarcodeControllerState extends InvenTreeBarcodeControllerState {
@override @override
Future<void> resumeScan() async { Future<void> resumeScan() async {
// Do not attempt to resume if the widget is not mounted // Do not attempt to resume if the widget is not mounted
if (!mounted) { if (!mounted) {
return; return;
@ -97,6 +123,15 @@ class _CameraBarcodeControllerState extends InvenTreeBarcodeControllerState {
@override @override
Widget build(BuildContext context) { 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( return Scaffold(
appBar: AppBar( appBar: AppBar(
@ -106,8 +141,7 @@ class _CameraBarcodeControllerState extends InvenTreeBarcodeControllerState {
icon: Icon(Icons.flip_camera_android), icon: Icon(Icons.flip_camera_android),
onPressed: () { onPressed: () {
_controller?.flipCamera(); _controller?.flipCamera();
} }),
),
IconButton( IconButton(
icon: flash_status ? Icon(Icons.flash_off) : Icon(Icons.flash_on), icon: flash_status ? Icon(Icons.flash_off) : Icon(Icons.flash_on),
onPressed: () { onPressed: () {
@ -117,10 +151,22 @@ class _CameraBarcodeControllerState extends InvenTreeBarcodeControllerState {
) )
], ],
), ),
body: Stack( body: GestureDetector(
onTapDown: (details) async {
setState(() {
scanning_paused = !scanning_paused;
});
},
onLongPressEnd: (details) async {
if (mounted) {
setState(() {
scanning_paused = false;
});
}
},
child: Stack(
children: <Widget>[ children: <Widget>[
Column( Column(children: [
children: [
Expanded( Expanded(
child: QRView( child: QRView(
key: barcodeControllerKey, key: barcodeControllerKey,
@ -128,33 +174,48 @@ class _CameraBarcodeControllerState extends InvenTreeBarcodeControllerState {
_onViewCreated(context, controller); _onViewCreated(context, controller);
}, },
overlay: QrScannerOverlayShape( overlay: QrScannerOverlayShape(
borderColor: Colors.red, borderColor:
scanning_paused ? COLOR_WARNING : COLOR_ACTION,
borderRadius: 10, borderRadius: 10,
borderLength: 30, borderLength: 30,
borderWidth: 10, borderWidth: 10,
cutOutSize: 300, cutOutSize: 300,
), ),
) ))
) ]),
]
),
Center( Center(
child: Column( child: Column(children: [
children: [
Spacer(),
Padding( Padding(
child: Text(widget.handler.getOverlayText(context), child: Text(
widget.handler.getOverlayText(context),
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 16,
color: Colors.white), fontWeight: FontWeight.bold, color: Colors.white),
), ),
padding: EdgeInsets.all(20), 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),
),
]))
], ],
) )));
);
} }
} }

View File

@ -115,6 +115,12 @@
"barcodeReceivePart": "Scan barcode to receive part", "barcodeReceivePart": "Scan barcode to receive part",
"@barcodeReceivePart": {}, "@barcodeReceivePart": {},
"barcodeScanPaused": "Barcode scanning paused",
"@barodeScanPaused": {},
"barcodeScanPause": "Tap or hold to pause scanning",
"@barcodeScanPause": {},
"barcodeScanAssign": "Scan to assign barcode", "barcodeScanAssign": "Scan to assign barcode",
"@barcodeScanAssign": {}, "@barcodeScanAssign": {},
@ -139,6 +145,12 @@
"barcodeScanLocation": "Scan stock location", "barcodeScanLocation": "Scan stock location",
"@barcodeScanLocation": {}, "@barcodeScanLocation": {},
"barcodeScanSingle": "Single Scan Mode",
"@barcodeScanSingle": {},
"barcodeScanSingleDetail": "Pause barcode scanner after each scan",
"@barcodeScanSingleDetail": {},
"barcodeScanIntoLocationSuccess": "Scanned into location", "barcodeScanIntoLocationSuccess": "Scanned into location",
"@barcodeScanIntoLocationSuccess": {}, "@barcodeScanIntoLocationSuccess": {},

View File

@ -42,6 +42,7 @@ const String INV_STRICT_HTTPS = "strictHttps";
// Barcode settings // Barcode settings
const String INV_BARCODE_SCAN_DELAY = "barcodeScanDelay"; const String INV_BARCODE_SCAN_DELAY = "barcodeScanDelay";
const String INV_BARCODE_SCAN_TYPE = "barcodeScanType"; const String INV_BARCODE_SCAN_TYPE = "barcodeScanType";
const String INV_BARCODE_SCAN_SINGLE = "barcodeScanSingle";
// Barcode scanner types // Barcode scanner types
const int BARCODE_CONTROLLER_CAMERA = 0; const int BARCODE_CONTROLLER_CAMERA = 0;

View File

@ -18,6 +18,7 @@ class _InvenTreeBarcodeSettingsState extends State<InvenTreeBarcodeSettingsWidge
int barcodeScanDelay = 500; int barcodeScanDelay = 500;
int barcodeScanType = BARCODE_CONTROLLER_CAMERA; int barcodeScanType = BARCODE_CONTROLLER_CAMERA;
bool barcodeScanSingle = false;
final TextEditingController _barcodeScanDelayController = TextEditingController(); final TextEditingController _barcodeScanDelayController = TextEditingController();
@ -30,6 +31,7 @@ class _InvenTreeBarcodeSettingsState extends State<InvenTreeBarcodeSettingsWidge
Future<void> loadSettings() async { Future<void> loadSettings() async {
barcodeScanDelay = await InvenTreeSettingsManager().getValue(INV_BARCODE_SCAN_DELAY, 500) as int; barcodeScanDelay = await InvenTreeSettingsManager().getValue(INV_BARCODE_SCAN_DELAY, 500) as int;
barcodeScanType = await InvenTreeSettingsManager().getValue(INV_BARCODE_SCAN_TYPE, BARCODE_CONTROLLER_CAMERA) 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) { if (mounted) {
setState(() { 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;
});
},
),
)
], ],
) )
) )

View File

@ -1,7 +1,7 @@
name: inventree name: inventree
description: InvenTree stock management description: InvenTree stock management
version: 0.13.0+76 version: 0.14.0+77
environment: environment:
sdk: ">=2.19.5 <3.13.0" sdk: ">=2.19.5 <3.13.0"