mirror of
https://github.com/inventree/inventree-app.git
synced 2025-04-27 21:16:48 +00:00
Update Requirements (#541)
* 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:
parent
29948e5809
commit
d990508237
2
.github/workflows/android.yaml
vendored
2
.github/workflows/android.yaml
vendored
@ -23,7 +23,7 @@ jobs:
|
|||||||
- name: Setup Flutter
|
- name: Setup Flutter
|
||||||
uses: subosito/flutter-action@v2
|
uses: subosito/flutter-action@v2
|
||||||
with:
|
with:
|
||||||
flutter-version: '3.13.0'
|
flutter-version: '3.24.3'
|
||||||
channel: 'stable'
|
channel: 'stable'
|
||||||
- run: flutter --version
|
- run: flutter --version
|
||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
|
2
.github/workflows/ci.yaml
vendored
2
.github/workflows/ci.yaml
vendored
@ -36,7 +36,7 @@ jobs:
|
|||||||
- name: Setup Flutter
|
- name: Setup Flutter
|
||||||
uses: subosito/flutter-action@v2
|
uses: subosito/flutter-action@v2
|
||||||
with:
|
with:
|
||||||
flutter-version: '3.13.0'
|
flutter-version: '3.24.3'
|
||||||
- name: Collect Translation Files
|
- name: Collect Translation Files
|
||||||
run: |
|
run: |
|
||||||
cd lib/l10n
|
cd lib/l10n
|
||||||
|
2
.github/workflows/ios.yaml
vendored
2
.github/workflows/ios.yaml
vendored
@ -25,7 +25,7 @@ jobs:
|
|||||||
- name: Setup Flutter
|
- name: Setup Flutter
|
||||||
uses: subosito/flutter-action@v2
|
uses: subosito/flutter-action@v2
|
||||||
with:
|
with:
|
||||||
flutter-version: '3.13.0'
|
flutter-version: '3.24.3'
|
||||||
channel: 'stable'
|
channel: 'stable'
|
||||||
- name: Collect Translation Files
|
- name: Collect Translation Files
|
||||||
run: |
|
run: |
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
|
plugins {
|
||||||
|
id "com.android.application"
|
||||||
|
id "kotlin-android"
|
||||||
|
id "dev.flutter.flutter-gradle-plugin"
|
||||||
|
}
|
||||||
|
|
||||||
def localProperties = new Properties()
|
def localProperties = new Properties()
|
||||||
def localPropertiesFile = rootProject.file('local.properties')
|
def localPropertiesFile = rootProject.file('local.properties')
|
||||||
if (localPropertiesFile.exists()) {
|
if (localPropertiesFile.exists()) {
|
||||||
@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def flutterRoot = localProperties.getProperty('flutter.sdk')
|
|
||||||
if (flutterRoot == null) {
|
|
||||||
throw new FileNotFoundException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
|
|
||||||
}
|
|
||||||
|
|
||||||
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||||
if (flutterVersionCode == null) {
|
if (flutterVersionCode == null) {
|
||||||
flutterVersionCode = '1'
|
flutterVersionCode = '1'
|
||||||
@ -21,10 +22,6 @@ if (flutterVersionName == null) {
|
|||||||
flutterVersionName = '1.0'
|
flutterVersionName = '1.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'com.android.application'
|
|
||||||
apply plugin: 'kotlin-android'
|
|
||||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
|
||||||
|
|
||||||
def keystoreProperties = new Properties()
|
def keystoreProperties = new Properties()
|
||||||
def keystorePropertiesFile = rootProject.file('key.properties')
|
def keystorePropertiesFile = rootProject.file('key.properties')
|
||||||
if (keystorePropertiesFile.exists()) {
|
if (keystorePropertiesFile.exists()) {
|
||||||
@ -85,5 +82,4 @@ dependencies {
|
|||||||
androidTestImplementation 'com.android.support:multidex:2.0.1'
|
androidTestImplementation 'com.android.support:multidex:2.0.1'
|
||||||
implementation "androidx.core:core:1.9.0"
|
implementation "androidx.core:core:1.9.0"
|
||||||
implementation 'androidx.appcompat:appcompat:1.6.0'
|
implementation 'androidx.appcompat:appcompat:1.6.0'
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,3 @@
|
|||||||
buildscript {
|
|
||||||
|
|
||||||
ext.kotlin_version = '1.8.10'
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
google()
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
classpath 'com.android.tools.build:gradle:7.4.1'
|
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
|
@ -1,15 +1,25 @@
|
|||||||
include ':app'
|
pluginManagement {
|
||||||
|
def flutterSdkPath = {
|
||||||
|
def properties = new Properties()
|
||||||
|
file("local.properties").withInputStream { properties.load(it) }
|
||||||
|
def flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||||
|
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
|
||||||
|
return flutterSdkPath
|
||||||
|
}()
|
||||||
|
|
||||||
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
|
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
|
||||||
|
|
||||||
def plugins = new Properties()
|
repositories {
|
||||||
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
|
google()
|
||||||
if (pluginsFile.exists()) {
|
mavenCentral()
|
||||||
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
|
gradlePluginPortal()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins.each { name, path ->
|
plugins {
|
||||||
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
|
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||||
include ":$name"
|
id "com.android.application" version "7.4.1" apply false
|
||||||
project(":$name").projectDir = pluginDirectory
|
id "org.jetbrains.kotlin.android" version "1.8.10" apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
include ":app"
|
@ -21,6 +21,6 @@
|
|||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1.0</string>
|
<string>1.0</string>
|
||||||
<key>MinimumOSVersion</key>
|
<key>MinimumOSVersion</key>
|
||||||
<string>11.0</string>
|
<string>12.0</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Uncomment this line to define a global platform for your projects
|
# Uncomment this line to define a global platform for your projects
|
||||||
platform :ios, '11.0'
|
platform :ios, '12.0'
|
||||||
|
|
||||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||||
@ -39,7 +39,7 @@ post_install do |installer|
|
|||||||
installer.pods_project.targets.each do |target|
|
installer.pods_project.targets.each do |target|
|
||||||
flutter_additional_ios_build_settings(target)
|
flutter_additional_ios_build_settings(target)
|
||||||
target.build_configurations.each do |config|
|
target.build_configurations.each do |config|
|
||||||
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '11.0'
|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -92,6 +92,7 @@
|
|||||||
3B8B22940C363C2F0DDB698A /* Frameworks */,
|
3B8B22940C363C2F0DDB698A /* Frameworks */,
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
tabWidth = 5;
|
||||||
};
|
};
|
||||||
97C146EF1CF9000F007C117D /* Products */ = {
|
97C146EF1CF9000F007C117D /* Products */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
@ -167,7 +168,7 @@
|
|||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
BuildIndependentTargetsInParallel = YES;
|
BuildIndependentTargetsInParallel = YES;
|
||||||
LastUpgradeCheck = 1430;
|
LastUpgradeCheck = 1510;
|
||||||
ORGANIZATIONNAME = "The Chromium Authors";
|
ORGANIZATIONNAME = "The Chromium Authors";
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
97C146ED1CF9000F007C117D = {
|
97C146ED1CF9000F007C117D = {
|
||||||
@ -271,11 +272,9 @@
|
|||||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
|
||||||
"${BUILT_PRODUCTS_DIR}/DKImagePickerController/DKImagePickerController.framework",
|
"${BUILT_PRODUCTS_DIR}/DKImagePickerController/DKImagePickerController.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/DKPhotoGallery/DKPhotoGallery.framework",
|
"${BUILT_PRODUCTS_DIR}/DKPhotoGallery/DKPhotoGallery.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/FMDB/FMDB.framework",
|
|
||||||
"${BUILT_PRODUCTS_DIR}/MTBBarcodeScanner/MTBBarcodeScanner.framework",
|
"${BUILT_PRODUCTS_DIR}/MTBBarcodeScanner/MTBBarcodeScanner.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework",
|
"${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/Sentry/Sentry.framework",
|
"${BUILT_PRODUCTS_DIR}/Sentry/Sentry.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/SentryPrivate/SentryPrivate.framework",
|
|
||||||
"${BUILT_PRODUCTS_DIR}/SwiftyGif/SwiftyGif.framework",
|
"${BUILT_PRODUCTS_DIR}/SwiftyGif/SwiftyGif.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/audioplayers_darwin/audioplayers_darwin.framework",
|
"${BUILT_PRODUCTS_DIR}/audioplayers_darwin/audioplayers_darwin.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/camera_avfoundation/camera_avfoundation.framework",
|
"${BUILT_PRODUCTS_DIR}/camera_avfoundation/camera_avfoundation.framework",
|
||||||
@ -295,11 +294,9 @@
|
|||||||
outputPaths = (
|
outputPaths = (
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKImagePickerController.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKImagePickerController.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKPhotoGallery.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKPhotoGallery.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FMDB.framework",
|
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MTBBarcodeScanner.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MTBBarcodeScanner.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Sentry.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Sentry.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SentryPrivate.framework",
|
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyGif.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyGif.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/audioplayers_darwin.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/audioplayers_darwin.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/camera_avfoundation.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/camera_avfoundation.framework",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "1430"
|
LastUpgradeVersion = "1510"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
@ -1393,7 +1393,7 @@ class InvenTreeAPI {
|
|||||||
// Find the current locale code for the running app
|
// Find the current locale code for the running app
|
||||||
String get currentLocale {
|
String get currentLocale {
|
||||||
|
|
||||||
if (OneContext.hasContext) {
|
if (hasContext()) {
|
||||||
// Try to get app context
|
// Try to get app context
|
||||||
BuildContext? context = OneContext().context;
|
BuildContext? context = OneContext().context;
|
||||||
|
|
||||||
|
@ -1,22 +1,29 @@
|
|||||||
|
import "package:adaptive_theme/adaptive_theme.dart";
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
|
import "package:inventree/helpers.dart";
|
||||||
import "package:one_context/one_context.dart";
|
import "package:one_context/one_context.dart";
|
||||||
|
|
||||||
const Color COLOR_GRAY_LIGHT = Color.fromRGBO(150, 150, 150, 1);
|
bool isDarkMode() {
|
||||||
|
|
||||||
// Return an "action" color based on the current theme
|
if (!hasContext()) {
|
||||||
Color get COLOR_ACTION {
|
return false;
|
||||||
|
|
||||||
// OneContext might not have context, e.g. in testing
|
|
||||||
if (!OneContext.hasContext) {
|
|
||||||
return Colors.lightBlue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BuildContext? context = OneContext().context;
|
BuildContext? context = OneContext().context;
|
||||||
|
|
||||||
if (context != null) {
|
if (context == null) {
|
||||||
return Theme.of(context).indicatorColor;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return AdaptiveTheme.of(context).brightness == Brightness.dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return an "action" color based on the current theme
|
||||||
|
Color get COLOR_ACTION {
|
||||||
|
if (isDarkMode()) {
|
||||||
|
return Colors.lightBlueAccent;
|
||||||
} else {
|
} else {
|
||||||
return Colors.lightBlue;
|
return Colors.blue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,3 +31,4 @@ const Color COLOR_WARNING = Color.fromRGBO(250, 150, 50, 1);
|
|||||||
const Color COLOR_DANGER = Color.fromRGBO(200, 50, 75, 1);
|
const Color COLOR_DANGER = Color.fromRGBO(200, 50, 75, 1);
|
||||||
const Color COLOR_SUCCESS = Color.fromRGBO(100, 200, 75, 1);
|
const Color COLOR_SUCCESS = Color.fromRGBO(100, 200, 75, 1);
|
||||||
const Color COLOR_PROGRESS = Color.fromRGBO(50, 100, 200, 1);
|
const Color COLOR_PROGRESS = Color.fromRGBO(50, 100, 200, 1);
|
||||||
|
const Color COLOR_GRAY_LIGHT = Color.fromRGBO(150, 150, 150, 1);
|
||||||
|
@ -2,6 +2,7 @@ import "package:flutter/material.dart";
|
|||||||
|
|
||||||
import "package:flutter_speed_dial/flutter_speed_dial.dart";
|
import "package:flutter_speed_dial/flutter_speed_dial.dart";
|
||||||
import "package:flutter_tabler_icons/flutter_tabler_icons.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/inventree/sales_order.dart";
|
||||||
import "package:inventree/preferences.dart";
|
import "package:inventree/preferences.dart";
|
||||||
import "package:inventree/widget/order/sales_order_detail.dart";
|
import "package:inventree/widget/order/sales_order_detail.dart";
|
||||||
@ -46,17 +47,20 @@ Future<void> barcodeFailure(String msg, dynamic extra) async {
|
|||||||
msg,
|
msg,
|
||||||
success: false,
|
success: false,
|
||||||
onAction: () {
|
onAction: () {
|
||||||
OneContext().showDialog(
|
if (hasContext()) {
|
||||||
builder: (BuildContext context) => SimpleDialog(
|
OneContext().showDialog(
|
||||||
title: Text(L10().barcodeError),
|
builder: (BuildContext context) =>
|
||||||
children: <Widget>[
|
SimpleDialog(
|
||||||
ListTile(
|
title: Text(L10().barcodeError),
|
||||||
title: Text(L10().responseData),
|
children: <Widget>[
|
||||||
subtitle: Text(extra.toString())
|
ListTile(
|
||||||
)
|
title: Text(L10().responseData),
|
||||||
]
|
subtitle: Text(extra.toString())
|
||||||
)
|
)
|
||||||
);
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -277,17 +281,20 @@ class BarcodeScanHandler extends BarcodeHandler {
|
|||||||
success: false,
|
success: false,
|
||||||
onAction: () {
|
onAction: () {
|
||||||
|
|
||||||
OneContext().showDialog(
|
if (hasContext()) {
|
||||||
builder: (BuildContext context) => SimpleDialog(
|
OneContext().showDialog(
|
||||||
title: Text(L10().unknownResponse),
|
builder: (BuildContext context) =>
|
||||||
children: <Widget>[
|
SimpleDialog(
|
||||||
ListTile(
|
title: Text(L10().unknownResponse),
|
||||||
title: Text(L10().responseData),
|
children: <Widget>[
|
||||||
subtitle: Text(data.toString()),
|
ListTile(
|
||||||
)
|
title: Text(L10().responseData),
|
||||||
],
|
subtitle: Text(data.toString()),
|
||||||
)
|
)
|
||||||
);
|
],
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
|
import "package:inventree/helpers.dart";
|
||||||
import "package:one_context/one_context.dart";
|
import "package:one_context/one_context.dart";
|
||||||
|
|
||||||
import "package:inventree/preferences.dart";
|
import "package:inventree/preferences.dart";
|
||||||
@ -43,7 +44,7 @@ class InvenTreeBarcodeControllerState extends State<InvenTreeBarcodeController>
|
|||||||
* Barcode data should be passed as a string
|
* Barcode data should be passed as a string
|
||||||
*/
|
*/
|
||||||
Future<void> handleBarcodeData(String? data) async {
|
Future<void> handleBarcodeData(String? data) async {
|
||||||
|
|
||||||
// Check that the data is valid, and this view is still mounted
|
// Check that the data is valid, and this view is still mounted
|
||||||
if (!mounted || data == null || data.isEmpty) {
|
if (!mounted || data == null || data.isEmpty) {
|
||||||
return;
|
return;
|
||||||
@ -58,7 +59,11 @@ class InvenTreeBarcodeControllerState extends State<InvenTreeBarcodeController>
|
|||||||
processingBarcode = true;
|
processingBarcode = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
BuildContext? context = OneContext.hasContext ? OneContext().context : null;
|
BuildContext? context;
|
||||||
|
|
||||||
|
if (hasContext()) {
|
||||||
|
context = OneContext.hasContext ? OneContext().context : null;
|
||||||
|
}
|
||||||
|
|
||||||
showLoadingOverlay(context);
|
showLoadingOverlay(context);
|
||||||
await pauseScan();
|
await pauseScan();
|
||||||
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
@ -42,7 +42,7 @@ class BarcodeScanStockLocationHandler extends BarcodeHandler {
|
|||||||
|
|
||||||
final bool result = await onLocationScanned(_loc);
|
final bool result = await onLocationScanned(_loc);
|
||||||
|
|
||||||
if (result && OneContext.hasContext) {
|
if (result && hasContext()) {
|
||||||
OneContext().pop();
|
OneContext().pop();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
|
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter/services.dart";
|
||||||
import "package:flutter_tabler_icons/flutter_tabler_icons.dart";
|
import "package:flutter_tabler_icons/flutter_tabler_icons.dart";
|
||||||
|
|
||||||
import "package:inventree/app_colors.dart";
|
import "package:inventree/app_colors.dart";
|
||||||
import "package:inventree/barcode/controller.dart";
|
import "package:inventree/barcode/controller.dart";
|
||||||
import "package:inventree/barcode/handler.dart";
|
import "package:inventree/barcode/handler.dart";
|
||||||
import "package:inventree/barcode/flutter_barcode_listener.dart";
|
|
||||||
import "package:inventree/l10.dart";
|
import "package:inventree/l10.dart";
|
||||||
import "package:inventree/helpers.dart";
|
import "package:inventree/helpers.dart";
|
||||||
|
|
||||||
@ -31,6 +32,12 @@ class _WedgeBarcodeControllerState extends InvenTreeBarcodeControllerState {
|
|||||||
|
|
||||||
bool get scanning => mounted && canScan;
|
bool get scanning => mounted && canScan;
|
||||||
|
|
||||||
|
final FocusNode _focusNode = FocusNode();
|
||||||
|
|
||||||
|
List<String> _scannedCharacters = [];
|
||||||
|
|
||||||
|
DateTime? _lastScanTime;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> pauseScan() async {
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
||||||
@ -66,8 +112,9 @@ class _WedgeBarcodeControllerState extends InvenTreeBarcodeControllerState {
|
|||||||
Spacer(flex: 5),
|
Spacer(flex: 5),
|
||||||
Icon(TablerIcons.barcode, size: 64),
|
Icon(TablerIcons.barcode, size: 64),
|
||||||
Spacer(flex: 5),
|
Spacer(flex: 5),
|
||||||
BarcodeKeyboardListener(
|
KeyboardListener(
|
||||||
useKeyDownEvent: true,
|
autofocus: true,
|
||||||
|
focusNode: _focusNode,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
child: CircularProgressIndicator(
|
child: CircularProgressIndicator(
|
||||||
color: scanning ? COLOR_ACTION : COLOR_PROGRESS
|
color: scanning ? COLOR_ACTION : COLOR_PROGRESS
|
||||||
@ -75,13 +122,16 @@ class _WedgeBarcodeControllerState extends InvenTreeBarcodeControllerState {
|
|||||||
width: 64,
|
width: 64,
|
||||||
height: 64,
|
height: 64,
|
||||||
),
|
),
|
||||||
onBarcodeScanned: (String barcode) {
|
onKeyEvent: (event) {
|
||||||
debug("scanned: ${barcode}");
|
handleKeyEvent(event);
|
||||||
if (scanning) {
|
|
||||||
// Process the barcode data
|
|
||||||
handleBarcodeData(barcode);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
// onBarcodeScanned: (String barcode) {
|
||||||
|
// debug("scanned: ${barcode}");
|
||||||
|
// if (scanning) {
|
||||||
|
// // Process the barcode data
|
||||||
|
// handleBarcodeData(barcode);
|
||||||
|
// }
|
||||||
|
// },
|
||||||
),
|
),
|
||||||
Spacer(flex: 5),
|
Spacer(flex: 5),
|
||||||
Padding(
|
Padding(
|
||||||
|
@ -39,13 +39,32 @@ bool debugContains(String msg, {bool raiseAssert = true}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
print("Debug does not contain expected string: '${msg}'");
|
||||||
|
}
|
||||||
|
|
||||||
if (raiseAssert) {
|
if (raiseAssert) {
|
||||||
|
|
||||||
assert(result);
|
assert(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool isTesting() {
|
||||||
|
return Platform.environment.containsKey("FLUTTER_TEST");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasContext() {
|
||||||
|
try {
|
||||||
|
return !isTesting() && OneContext.hasContext;
|
||||||
|
} catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Display a debug message if we are in testing mode, or running in debug mode
|
* Display a debug message if we are in testing mode, or running in debug mode
|
||||||
*/
|
*/
|
||||||
@ -83,7 +102,7 @@ Future<void> playAudioFile(String path) async {
|
|||||||
// Debug message for unit testing
|
// Debug message for unit testing
|
||||||
debug("Playing audio file: '${path}'");
|
debug("Playing audio file: '${path}'");
|
||||||
|
|
||||||
if (!OneContext.hasContext) {
|
if (!hasContext()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,21 +136,13 @@ String renderCurrency(double? amount, String currency, {int decimals = 2}) {
|
|||||||
|
|
||||||
if (currency.isEmpty) return "-";
|
if (currency.isEmpty) return "-";
|
||||||
|
|
||||||
CurrencyFormatterSettings backupSettings = CurrencyFormatterSettings(
|
CurrencyFormat fmt = CurrencyFormat.fromCode(currency.toLowerCase()) ?? CurrencyFormat.usd;
|
||||||
symbol: "\$",
|
|
||||||
symbolSide: SymbolSide.left,
|
|
||||||
);
|
|
||||||
|
|
||||||
String value = CurrencyFormatter.format(
|
String value = CurrencyFormatter.format(
|
||||||
amount,
|
amount,
|
||||||
CurrencyFormatter.majors[currency.toLowerCase()] ?? backupSettings
|
fmt
|
||||||
);
|
);
|
||||||
|
|
||||||
// If we were not able to determine the currency
|
|
||||||
if (!CurrencyFormatter.majors.containsKey(currency.toLowerCase())) {
|
|
||||||
value += " ${currency}";
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import "dart:io";
|
import "dart:io";
|
||||||
|
|
||||||
import "package:device_info_plus/device_info_plus.dart";
|
import "package:device_info_plus/device_info_plus.dart";
|
||||||
|
import "package:inventree/helpers.dart";
|
||||||
import "package:one_context/one_context.dart";
|
import "package:one_context/one_context.dart";
|
||||||
import "package:package_info_plus/package_info_plus.dart";
|
import "package:package_info_plus/package_info_plus.dart";
|
||||||
import "package:sentry_flutter/sentry_flutter.dart";
|
import "package:sentry_flutter/sentry_flutter.dart";
|
||||||
@ -129,16 +130,16 @@ Future<bool> sentryReportMessage(String message, {Map<String, String>? context})
|
|||||||
}
|
}
|
||||||
|
|
||||||
Sentry.configureScope((scope) {
|
Sentry.configureScope((scope) {
|
||||||
scope.setExtra("server", server_info);
|
scope.setContexts("server", server_info);
|
||||||
scope.setExtra("app", app_info);
|
scope.setContexts("app", app_info);
|
||||||
scope.setExtra("device", device_info);
|
scope.setContexts("device", device_info);
|
||||||
|
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
scope.setExtra("context", context);
|
scope.setContexts("context", context);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Catch stacktrace data if possible
|
// Catch stacktrace data if possible
|
||||||
scope.setExtra("stacktrace", StackTrace.current.toString());
|
scope.setContexts("stacktrace", StackTrace.current.toString());
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -203,7 +204,7 @@ Future<void> sentryReportError(String source, dynamic error, StackTrace? stackTr
|
|||||||
// Ensure we pass the 'source' of the error
|
// Ensure we pass the 'source' of the error
|
||||||
context["source"] = source;
|
context["source"] = source;
|
||||||
|
|
||||||
if (OneContext.hasContext) {
|
if (hasContext()) {
|
||||||
final ctx = OneContext().context;
|
final ctx = OneContext().context;
|
||||||
|
|
||||||
if (ctx != null) {
|
if (ctx != null) {
|
||||||
@ -213,10 +214,10 @@ Future<void> sentryReportError(String source, dynamic error, StackTrace? stackTr
|
|||||||
}
|
}
|
||||||
|
|
||||||
Sentry.configureScope((scope) {
|
Sentry.configureScope((scope) {
|
||||||
scope.setExtra("server", server_info);
|
scope.setContexts("server", server_info);
|
||||||
scope.setExtra("app", app_info);
|
scope.setContexts("app", app_info);
|
||||||
scope.setExtra("device", device_info);
|
scope.setContexts("device", device_info);
|
||||||
scope.setExtra("context", context);
|
scope.setContexts("context", context);
|
||||||
});
|
});
|
||||||
|
|
||||||
Sentry.captureException(error, stackTrace: stackTrace).catchError((error) {
|
Sentry.captureException(error, stackTrace: stackTrace).catchError((error) {
|
||||||
|
19
lib/l10.dart
19
lib/l10.dart
@ -4,18 +4,23 @@ import "package:flutter_gen/gen_l10n/app_localizations_en.dart";
|
|||||||
import "package:one_context/one_context.dart";
|
import "package:one_context/one_context.dart";
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
|
|
||||||
|
import "package:inventree/helpers.dart";
|
||||||
|
|
||||||
// Shortcut function to reduce boilerplate!
|
// Shortcut function to reduce boilerplate!
|
||||||
I18N L10()
|
I18N L10()
|
||||||
{
|
{
|
||||||
if (OneContext.hasContext) {
|
// Testing mode - ignore context
|
||||||
BuildContext? _ctx = OneContext().context;
|
if (!hasContext()) {
|
||||||
|
return I18NEn();
|
||||||
|
}
|
||||||
|
|
||||||
if (_ctx != null) {
|
BuildContext? _ctx = OneContext().context;
|
||||||
I18N? i18n = I18N.of(_ctx);
|
|
||||||
|
|
||||||
if (i18n != null) {
|
if (_ctx != null) {
|
||||||
return i18n;
|
I18N? i18n = I18N.of(_ctx);
|
||||||
}
|
|
||||||
|
if (i18n != null) {
|
||||||
|
return i18n;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,13 +162,13 @@ class InvenTreeAppState extends State<StatefulWidget> {
|
|||||||
return AdaptiveTheme(
|
return AdaptiveTheme(
|
||||||
light: ThemeData(
|
light: ThemeData(
|
||||||
brightness: Brightness.light,
|
brightness: Brightness.light,
|
||||||
primarySwatch: Colors.lightBlue,
|
colorSchemeSeed: Colors.lightBlueAccent,
|
||||||
secondaryHeaderColor: Colors.blueGrey
|
useMaterial3: true,
|
||||||
),
|
),
|
||||||
dark: ThemeData(
|
dark: ThemeData(
|
||||||
brightness: Brightness.dark,
|
brightness: Brightness.dark,
|
||||||
primarySwatch: Colors.lightBlue,
|
colorSchemeSeed: Colors.blue,
|
||||||
secondaryHeaderColor: Colors.blueGrey,
|
useMaterial3: true,
|
||||||
),
|
),
|
||||||
initial: savedThemeMode ?? AdaptiveThemeMode.light,
|
initial: savedThemeMode ?? AdaptiveThemeMode.light,
|
||||||
builder: (light, dark) => MaterialApp(
|
builder: (light, dark) => MaterialApp(
|
||||||
|
@ -3,7 +3,6 @@ import "dart:ui";
|
|||||||
|
|
||||||
import "package:inventree/l10n/supported_locales.dart";
|
import "package:inventree/l10n/supported_locales.dart";
|
||||||
import "package:path_provider/path_provider.dart";
|
import "package:path_provider/path_provider.dart";
|
||||||
import "package:sembast/sembast.dart";
|
|
||||||
import "package:sembast/sembast_io.dart";
|
import "package:sembast/sembast_io.dart";
|
||||||
import "package:path/path.dart";
|
import "package:path/path.dart";
|
||||||
|
|
||||||
|
@ -31,6 +31,10 @@ Future<void> choiceDialog(String title, List<Widget> items, {Function? onSelecte
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!hasContext()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
OneContext().showDialog(
|
OneContext().showDialog(
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
@ -63,6 +67,10 @@ Future<void> confirmationDialog(String title, String text, {Color? color, IconDa
|
|||||||
String _accept = acceptText ?? L10().ok;
|
String _accept = acceptText ?? L10().ok;
|
||||||
String _reject = rejectText ?? L10().cancel;
|
String _reject = rejectText ?? L10().cancel;
|
||||||
|
|
||||||
|
if (!hasContext()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
OneContext().showDialog(
|
OneContext().showDialog(
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
@ -176,6 +184,10 @@ Future<void> showErrorDialog(String title, {String description = "", APIResponse
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!hasContext()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
OneContext().showDialog(
|
OneContext().showDialog(
|
||||||
builder: (context) => SimpleDialog(
|
builder: (context) => SimpleDialog(
|
||||||
title: ListTile(
|
title: ListTile(
|
||||||
@ -196,7 +208,7 @@ Future<void> showErrorDialog(String title, {String description = "", APIResponse
|
|||||||
*/
|
*/
|
||||||
Future<void> showServerError(String url, String title, String description) async {
|
Future<void> showServerError(String url, String title, String description) async {
|
||||||
|
|
||||||
if (!OneContext.hasContext) {
|
if (!hasContext()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ void showSnackIcon(String text, {IconData? icon, Function()? onAction, bool? suc
|
|||||||
debug("showSnackIcon: '${text}'");
|
debug("showSnackIcon: '${text}'");
|
||||||
|
|
||||||
// Escape quickly if we do not have context
|
// Escape quickly if we do not have context
|
||||||
if (!OneContext.hasContext) {
|
if (!hasContext()) {
|
||||||
// Debug message for unit testing
|
// Debug message for unit testing
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -301,12 +301,16 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
|
|||||||
// Serial number field is not required here
|
// Serial number field is not required here
|
||||||
fields.remove("serial");
|
fields.remove("serial");
|
||||||
|
|
||||||
|
Map<String, dynamic> data = {};
|
||||||
|
|
||||||
|
if (location != null) {
|
||||||
|
data["location"] = location!.pk;
|
||||||
|
}
|
||||||
|
|
||||||
InvenTreeStockItem().createForm(
|
InvenTreeStockItem().createForm(
|
||||||
context,
|
context,
|
||||||
L10().stockItemCreate,
|
L10().stockItemCreate,
|
||||||
data: {
|
data: data,
|
||||||
"location": location != null ? location!.pk : null,
|
|
||||||
},
|
|
||||||
fields: fields,
|
fields: fields,
|
||||||
onSuccess: (result) async {
|
onSuccess: (result) async {
|
||||||
Map<String, dynamic> data = result as Map<String, dynamic>;
|
Map<String, dynamic> data = result as Map<String, dynamic>;
|
||||||
|
549
pubspec.lock
549
pubspec.lock
File diff suppressed because it is too large
Load Diff
26
pubspec.yaml
26
pubspec.yaml
@ -8,15 +8,15 @@ environment:
|
|||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
adaptive_theme: ^3.3.0 # Theme management (e.g. dark mode)
|
adaptive_theme: ^3.3.0 # Theme management (e.g. dark mode)
|
||||||
audioplayers: ^4.1.0 # Play audio files
|
audioplayers: ^6.1.0 # Play audio files
|
||||||
cached_network_image: ^3.3.1 # Download and cache remote images
|
cached_network_image: ^3.3.1 # Download and cache remote images
|
||||||
camera: ^0.10.3 # Camera
|
camera: ^0.10.3 # Camera
|
||||||
cupertino_icons: ^1.0.8
|
cupertino_icons: ^1.0.8
|
||||||
currency_formatter: ^2.0.1
|
currency_formatter: ^2.2.1 # Currency formatting
|
||||||
datetime_picker_formfield: ^2.0.1 # Date / time picker
|
datetime_picker_formfield: ^2.0.1 # Date / time picker
|
||||||
device_info_plus: ^8.2.2 # Information about the device
|
device_info_plus: ^10.1.2 # Information about the device
|
||||||
dropdown_search: ^5.0.6 # Dropdown autocomplete form fields
|
dropdown_search: ^5.0.6 # Dropdown autocomplete form fields
|
||||||
file_picker: ^5.3.1 # Select files from the device
|
file_picker: ^8.1.2 # Select files from the device
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_cache_manager: ^3.3.0
|
flutter_cache_manager: ^3.3.0
|
||||||
@ -27,19 +27,19 @@ dependencies:
|
|||||||
flutter_overlay_loader: ^2.0.0 # Overlay screen support
|
flutter_overlay_loader: ^2.0.0 # Overlay screen support
|
||||||
flutter_speed_dial: ^6.2.0 # Speed dial / FAB implementation
|
flutter_speed_dial: ^6.2.0 # Speed dial / FAB implementation
|
||||||
flutter_tabler_icons: ^1.35.0
|
flutter_tabler_icons: ^1.35.0
|
||||||
http: ^0.13.6
|
http: ^1.2.2
|
||||||
image_picker: ^1.0.8 # Select or take photos
|
image_picker: ^1.1.2 # Select or take photos
|
||||||
infinite_scroll_pagination: ^4.0.0 # Let the server do all the work!
|
infinite_scroll_pagination: ^4.0.0 # Let the server do all the work!
|
||||||
intl: ^0.18.0
|
intl: ^0.19.0
|
||||||
one_context: ^2.1.0 # Dialogs without requiring context
|
one_context: ^4.0.0 # Dialogs without requiring context
|
||||||
open_filex: ^4.4.0 # Open local files
|
open_filex: ^4.5.0 # Open local files
|
||||||
package_info_plus: ^3.0.2 # App information introspection
|
package_info_plus: ^8.0.2 # App information introspection
|
||||||
path: ^1.8.2
|
path: ^1.9.0
|
||||||
path_provider: ^2.1.3 # Local file storage
|
path_provider: ^2.1.3 # Local file storage
|
||||||
qr_code_scanner: ^1.0.1 # Barcode scanning
|
qr_code_scanner: ^1.0.1 # Barcode scanning
|
||||||
sembast: ^3.6.0 # NoSQL data storage
|
sembast: ^3.6.0 # NoSQL data storage
|
||||||
sentry_flutter: 7.9.0 # Error reporting
|
sentry_flutter: 8.9.0 # Error reporting
|
||||||
url_launcher: ^6.2.4 # Open link in system browser
|
url_launcher: ^6.3.0 # Open link in system browser
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_launcher_icons: ^0.11.0
|
flutter_launcher_icons: ^0.11.0
|
||||||
|
@ -21,7 +21,7 @@ void main() {
|
|||||||
await simulateKeyDownEvent(LogicalKeyboardKey.keyA);
|
await simulateKeyDownEvent(LogicalKeyboardKey.keyA);
|
||||||
await simulateKeyDownEvent(LogicalKeyboardKey.keyB);
|
await simulateKeyDownEvent(LogicalKeyboardKey.keyB);
|
||||||
await simulateKeyDownEvent(LogicalKeyboardKey.keyC);
|
await simulateKeyDownEvent(LogicalKeyboardKey.keyC);
|
||||||
await simulateKeyDownEvent(LogicalKeyboardKey.enter);
|
await simulateKeyDownEvent(LogicalKeyboardKey.enter, character: "\n");
|
||||||
|
|
||||||
// Check debug output
|
// Check debug output
|
||||||
debugContains("scanned: abc");
|
debugContains("scanned: abc");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user