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 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
### 0.13.0 - October 2023
---

View File

@ -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),
),
]))
],
)));
}
}

View File

@ -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": {},

View File

@ -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;

View File

@ -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;
});
},
),
)
],
)
)

View File

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