2
0
mirror of https://github.com/inventree/inventree-app.git synced 2025-06-15 19:55:27 +00:00

Update Requirements (#541)
Some checks failed
Android / build (push) Has been cancelled
CI / test (push) Has been cancelled
iOS / build (push) Has been cancelled

* Update package requiremenst

* github workflow updates

* ios build updates

* Theme adjustments

* Further updates

* Fix typo

* Deprecated imperative apply of Flutter's Gradle plugins

Ref: https://docs.flutter.dev/release/breaking-changes/flutter-gradle-plugin-apply

* Refactor wedge scanner

* Add context checks

* Adjust behaviour if testing

* Further refactoring

* Moar checks

* Logic fix

* Fix for wedge scanner test

* Fix for barcode processing

* Fix

* Yet another fix
This commit is contained in:
Oliver
2024-10-01 12:25:11 +10:00
committed by GitHub
parent 29948e5809
commit d990508237
28 changed files with 519 additions and 582 deletions

View File

@ -2,6 +2,7 @@ import "package:flutter/material.dart";
import "package:flutter_speed_dial/flutter_speed_dial.dart";
import "package:flutter_tabler_icons/flutter_tabler_icons.dart";
import "package:inventree/helpers.dart";
import "package:inventree/inventree/sales_order.dart";
import "package:inventree/preferences.dart";
import "package:inventree/widget/order/sales_order_detail.dart";
@ -46,17 +47,20 @@ Future<void> barcodeFailure(String msg, dynamic extra) async {
msg,
success: false,
onAction: () {
OneContext().showDialog(
builder: (BuildContext context) => SimpleDialog(
title: Text(L10().barcodeError),
children: <Widget>[
ListTile(
title: Text(L10().responseData),
subtitle: Text(extra.toString())
)
]
)
);
if (hasContext()) {
OneContext().showDialog(
builder: (BuildContext context) =>
SimpleDialog(
title: Text(L10().barcodeError),
children: <Widget>[
ListTile(
title: Text(L10().responseData),
subtitle: Text(extra.toString())
)
]
)
);
}
}
);
}
@ -277,17 +281,20 @@ class BarcodeScanHandler extends BarcodeHandler {
success: false,
onAction: () {
OneContext().showDialog(
builder: (BuildContext context) => SimpleDialog(
title: Text(L10().unknownResponse),
children: <Widget>[
ListTile(
title: Text(L10().responseData),
subtitle: Text(data.toString()),
)
],
)
);
if (hasContext()) {
OneContext().showDialog(
builder: (BuildContext context) =>
SimpleDialog(
title: Text(L10().unknownResponse),
children: <Widget>[
ListTile(
title: Text(L10().responseData),
subtitle: Text(data.toString()),
)
],
)
);
}
}
);
}

View File

@ -1,4 +1,5 @@
import "package:flutter/material.dart";
import "package:inventree/helpers.dart";
import "package:one_context/one_context.dart";
import "package:inventree/preferences.dart";
@ -43,7 +44,7 @@ class InvenTreeBarcodeControllerState extends State<InvenTreeBarcodeController>
* Barcode data should be passed as a string
*/
Future<void> handleBarcodeData(String? data) async {
// Check that the data is valid, and this view is still mounted
if (!mounted || data == null || data.isEmpty) {
return;
@ -58,7 +59,11 @@ class InvenTreeBarcodeControllerState extends State<InvenTreeBarcodeController>
processingBarcode = true;
});
BuildContext? context = OneContext.hasContext ? OneContext().context : null;
BuildContext? context;
if (hasContext()) {
context = OneContext.hasContext ? OneContext().context : null;
}
showLoadingOverlay(context);
await pauseScan();

View File

@ -1,175 +0,0 @@
/*
* Custom keyboard listener which allows the app to act as a keyboard "wedge",
* and intercept barcodes from any compatible scanner.
*
* Note: This code was copied from https://github.com/fuadreza/flutter_barcode_listener/blob/master/lib/flutter_barcode_listener.dart
*
* If that code becomes available on pub.dev, we can remove this file and reference that library
*/
import "dart:async";
import "package:flutter/material.dart";
import "package:flutter/services.dart";
typedef BarcodeScannedCallback = void Function(String barcode);
/// This widget will listen for raw PHYSICAL keyboard events
/// even when other controls have primary focus.
/// It will buffer all characters coming in specifed `bufferDuration` time frame
/// that end with line feed character and call callback function with result.
/// Keep in mind this widget will listen for events even when not visible.
/// Windows seems to be using the [RawKeyDownEvent] instead of the
/// [RawKeyUpEvent], this behaviour can be managed by setting [useKeyDownEvent].
class BarcodeKeyboardListener extends StatefulWidget {
/// This widget will listen for raw PHYSICAL keyboard events
/// even when other controls have primary focus.
/// It will buffer all characters coming in specifed `bufferDuration` time frame
/// that end with line feed character and call callback function with result.
/// Keep in mind this widget will listen for events even when not visible.
const BarcodeKeyboardListener(
{Key? key,
/// Child widget to be displayed.
required this.child,
/// Callback to be called when barcode is scanned.
required Function(String) onBarcodeScanned,
/// When experiencing issueswith empty barcodes on Windows,
/// set this value to true. Default value is `false`.
this.useKeyDownEvent = false,
/// Maximum time between two key events.
/// If time between two key events is longer than this value
/// previous keys will be ignored.
Duration bufferDuration = hundredMs})
: _onBarcodeScanned = onBarcodeScanned,
_bufferDuration = bufferDuration,
super(key: key);
final Widget child;
final BarcodeScannedCallback _onBarcodeScanned;
final Duration _bufferDuration;
final bool useKeyDownEvent;
@override
_BarcodeKeyboardListenerState createState() => _BarcodeKeyboardListenerState(
_onBarcodeScanned, _bufferDuration, useKeyDownEvent);
}
const Duration aSecond = Duration(seconds: 1);
const Duration hundredMs = Duration(milliseconds: 100);
const String lineFeed = "\n";
class _BarcodeKeyboardListenerState extends State<BarcodeKeyboardListener> {
_BarcodeKeyboardListenerState(this._onBarcodeScannedCallback,
this._bufferDuration, this._useKeyDownEvent) {
RawKeyboard.instance.addListener(_keyBoardCallback);
_keyboardSubscription =
_controller.stream.where((char) => char != null).listen(onKeyEvent);
}
List<String> _scannedChars = [];
DateTime? _lastScannedCharCodeTime;
late StreamSubscription<String?> _keyboardSubscription;
final BarcodeScannedCallback _onBarcodeScannedCallback;
final Duration _bufferDuration;
final _controller = StreamController<String?>();
final bool _useKeyDownEvent;
bool _isShiftPressed = false;
void onKeyEvent(String? char) {
//remove any pending characters older than bufferDuration value
checkPendingCharCodesToClear();
_lastScannedCharCodeTime = DateTime.now();
if (char == lineFeed) {
_onBarcodeScannedCallback.call(_scannedChars.join());
resetScannedCharCodes();
} else {
//add character to list of scanned characters;
_scannedChars.add(char!);
}
}
void checkPendingCharCodesToClear() {
if (_lastScannedCharCodeTime != null) {
if (_lastScannedCharCodeTime!
.isBefore(DateTime.now().subtract(_bufferDuration))) {
resetScannedCharCodes();
}
}
}
void resetScannedCharCodes() {
_lastScannedCharCodeTime = null;
_scannedChars = [];
}
void addScannedCharCode(String charCode) {
_scannedChars.add(charCode);
}
void _keyBoardCallback(RawKeyEvent keyEvent) {
if (keyEvent.logicalKey.keyId > 255 &&
keyEvent.data.logicalKey != LogicalKeyboardKey.enter &&
keyEvent.data.logicalKey != LogicalKeyboardKey.shiftLeft) return;
if ((!_useKeyDownEvent && keyEvent is RawKeyUpEvent) ||
(_useKeyDownEvent && keyEvent is RawKeyDownEvent)) {
if (keyEvent.data is RawKeyEventDataAndroid) {
if (keyEvent.data.logicalKey == LogicalKeyboardKey.shiftLeft) {
_isShiftPressed = true;
} else {
if (_isShiftPressed) {
_isShiftPressed = false;
_controller.sink.add(String.fromCharCode(
((keyEvent.data) as RawKeyEventDataAndroid).codePoint).toUpperCase());
} else {
_controller.sink.add(String.fromCharCode(
((keyEvent.data) as RawKeyEventDataAndroid).codePoint));
}
}
} else if (keyEvent.data is RawKeyEventDataFuchsia) {
_controller.sink.add(String.fromCharCode(
((keyEvent.data) as RawKeyEventDataFuchsia).codePoint));
} else if (keyEvent.data.logicalKey == LogicalKeyboardKey.enter) {
_controller.sink.add(lineFeed);
} else if (keyEvent.data is RawKeyEventDataWeb) {
_controller.sink.add(((keyEvent.data) as RawKeyEventDataWeb).keyLabel);
} else if (keyEvent.data is RawKeyEventDataLinux) {
_controller.sink
.add(((keyEvent.data) as RawKeyEventDataLinux).keyLabel);
} else if (keyEvent.data is RawKeyEventDataWindows) {
_controller.sink.add(String.fromCharCode(
((keyEvent.data) as RawKeyEventDataWindows).keyCode));
} else if (keyEvent.data is RawKeyEventDataMacOs) {
_controller.sink
.add(((keyEvent.data) as RawKeyEventDataMacOs).characters);
} else if (keyEvent.data is RawKeyEventDataIos) {
_controller.sink
.add(((keyEvent.data) as RawKeyEventDataIos).characters);
} else {
_controller.sink.add(keyEvent.character);
}
}
}
@override
Widget build(BuildContext context) {
return widget.child;
}
@override
void dispose() {
_keyboardSubscription.cancel();
_controller.close();
RawKeyboard.instance.removeListener(_keyBoardCallback);
super.dispose();
}
}

View File

@ -42,7 +42,7 @@ class BarcodeScanStockLocationHandler extends BarcodeHandler {
final bool result = await onLocationScanned(_loc);
if (result && OneContext.hasContext) {
if (result && hasContext()) {
OneContext().pop();
}
return;

View File

@ -1,11 +1,12 @@
import "package:flutter/material.dart";
import "package:flutter/services.dart";
import "package:flutter_tabler_icons/flutter_tabler_icons.dart";
import "package:inventree/app_colors.dart";
import "package:inventree/barcode/controller.dart";
import "package:inventree/barcode/handler.dart";
import "package:inventree/barcode/flutter_barcode_listener.dart";
import "package:inventree/l10.dart";
import "package:inventree/helpers.dart";
@ -31,6 +32,12 @@ class _WedgeBarcodeControllerState extends InvenTreeBarcodeControllerState {
bool get scanning => mounted && canScan;
final FocusNode _focusNode = FocusNode();
List<String> _scannedCharacters = [];
DateTime? _lastScanTime;
@override
Future<void> pauseScan() async {
@ -51,6 +58,45 @@ class _WedgeBarcodeControllerState extends InvenTreeBarcodeControllerState {
}
}
// Callback for a single key press / scan
void handleKeyEvent(KeyEvent event) {
if (!scanning) {
return;
}
// Look only for key-down events
if (event is! KeyDownEvent) {
return;
}
// Ignore events without a character code
if (event.character == null) {
return;
}
DateTime now = DateTime.now();
// Throw away old characters
if (_lastScanTime == null || _lastScanTime!.isBefore(now.subtract(Duration(milliseconds: 250)))) {
_scannedCharacters.clear();
}
_lastScanTime = now;
if (event.character == "\n") {
if (_scannedCharacters.isNotEmpty) {
// Debug output required for unit testing
debug("scanned: ${_scannedCharacters.join()}");
handleBarcodeData(_scannedCharacters.join());
}
_scannedCharacters.clear();
} else {
_scannedCharacters.add(event.character!);
}
}
@override
Widget build(BuildContext context) {
@ -66,8 +112,9 @@ class _WedgeBarcodeControllerState extends InvenTreeBarcodeControllerState {
Spacer(flex: 5),
Icon(TablerIcons.barcode, size: 64),
Spacer(flex: 5),
BarcodeKeyboardListener(
useKeyDownEvent: true,
KeyboardListener(
autofocus: true,
focusNode: _focusNode,
child: SizedBox(
child: CircularProgressIndicator(
color: scanning ? COLOR_ACTION : COLOR_PROGRESS
@ -75,13 +122,16 @@ class _WedgeBarcodeControllerState extends InvenTreeBarcodeControllerState {
width: 64,
height: 64,
),
onBarcodeScanned: (String barcode) {
debug("scanned: ${barcode}");
if (scanning) {
// Process the barcode data
handleBarcodeData(barcode);
}
onKeyEvent: (event) {
handleKeyEvent(event);
},
// onBarcodeScanned: (String barcode) {
// debug("scanned: ${barcode}");
// if (scanning) {
// // Process the barcode data
// handleBarcodeData(barcode);
// }
// },
),
Spacer(flex: 5),
Padding(