mirror of
				https://github.com/inventree/inventree-app.git
				synced 2025-11-04 07:15:46 +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:
		
							
								
								
									
										2
									
								
								.github/workflows/android.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/android.yaml
									
									
									
									
										vendored
									
									
								
							@@ -23,7 +23,7 @@ jobs:
 | 
			
		||||
    - name: Setup Flutter
 | 
			
		||||
      uses: subosito/flutter-action@v2
 | 
			
		||||
      with:
 | 
			
		||||
        flutter-version: '3.13.0'
 | 
			
		||||
        flutter-version: '3.24.3'
 | 
			
		||||
        channel: 'stable'
 | 
			
		||||
    - run: flutter --version
 | 
			
		||||
    - name: Setup Gradle
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/ci.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/ci.yaml
									
									
									
									
										vendored
									
									
								
							@@ -36,7 +36,7 @@ jobs:
 | 
			
		||||
      - name: Setup Flutter 
 | 
			
		||||
        uses: subosito/flutter-action@v2
 | 
			
		||||
        with:
 | 
			
		||||
          flutter-version: '3.13.0'
 | 
			
		||||
          flutter-version: '3.24.3'
 | 
			
		||||
      - name: Collect Translation Files
 | 
			
		||||
        run: |
 | 
			
		||||
          cd lib/l10n
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/ios.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/ios.yaml
									
									
									
									
										vendored
									
									
								
							@@ -25,7 +25,7 @@ jobs:
 | 
			
		||||
    - name: Setup Flutter
 | 
			
		||||
      uses: subosito/flutter-action@v2
 | 
			
		||||
      with:
 | 
			
		||||
        flutter-version: '3.13.0'
 | 
			
		||||
        flutter-version: '3.24.3'
 | 
			
		||||
        channel: 'stable'
 | 
			
		||||
    - name: Collect Translation Files
 | 
			
		||||
      run: |
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,9 @@
 | 
			
		||||
plugins {
 | 
			
		||||
    id "com.android.application"
 | 
			
		||||
    id "kotlin-android"
 | 
			
		||||
    id "dev.flutter.flutter-gradle-plugin"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
def localProperties = new Properties()
 | 
			
		||||
def localPropertiesFile = rootProject.file('local.properties')
 | 
			
		||||
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')
 | 
			
		||||
if (flutterVersionCode == null) {
 | 
			
		||||
    flutterVersionCode = '1'
 | 
			
		||||
@@ -21,10 +22,6 @@ if (flutterVersionName == null) {
 | 
			
		||||
    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 keystorePropertiesFile = rootProject.file('key.properties')
 | 
			
		||||
if (keystorePropertiesFile.exists()) {
 | 
			
		||||
@@ -85,5 +82,4 @@ dependencies {
 | 
			
		||||
    androidTestImplementation 'com.android.support:multidex:2.0.1'
 | 
			
		||||
    implementation "androidx.core:core:1.9.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 {
 | 
			
		||||
    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()
 | 
			
		||||
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
 | 
			
		||||
if (pluginsFile.exists()) {
 | 
			
		||||
    pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
 | 
			
		||||
    repositories {
 | 
			
		||||
        google()
 | 
			
		||||
        mavenCentral()
 | 
			
		||||
        gradlePluginPortal()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
plugins.each { name, path ->
 | 
			
		||||
    def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
 | 
			
		||||
    include ":$name"
 | 
			
		||||
    project(":$name").projectDir = pluginDirectory
 | 
			
		||||
plugins {
 | 
			
		||||
    id "dev.flutter.flutter-plugin-loader" version "1.0.0"
 | 
			
		||||
    id "com.android.application" version "7.4.1" apply false
 | 
			
		||||
    id "org.jetbrains.kotlin.android" version "1.8.10" apply false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
include ":app"
 | 
			
		||||
@@ -21,6 +21,6 @@
 | 
			
		||||
  <key>CFBundleVersion</key>
 | 
			
		||||
  <string>1.0</string>
 | 
			
		||||
  <key>MinimumOSVersion</key>
 | 
			
		||||
  <string>11.0</string>
 | 
			
		||||
  <string>12.0</string>
 | 
			
		||||
</dict>
 | 
			
		||||
</plist>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
# 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.
 | 
			
		||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
 | 
			
		||||
@@ -39,7 +39,7 @@ post_install do |installer|
 | 
			
		||||
  installer.pods_project.targets.each do |target|
 | 
			
		||||
    flutter_additional_ios_build_settings(target)
 | 
			
		||||
    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
 | 
			
		||||
 
 | 
			
		||||
@@ -92,6 +92,7 @@
 | 
			
		||||
				3B8B22940C363C2F0DDB698A /* Frameworks */,
 | 
			
		||||
			);
 | 
			
		||||
			sourceTree = "<group>";
 | 
			
		||||
			tabWidth = 5;
 | 
			
		||||
		};
 | 
			
		||||
		97C146EF1CF9000F007C117D /* Products */ = {
 | 
			
		||||
			isa = PBXGroup;
 | 
			
		||||
@@ -167,7 +168,7 @@
 | 
			
		||||
			isa = PBXProject;
 | 
			
		||||
			attributes = {
 | 
			
		||||
				BuildIndependentTargetsInParallel = YES;
 | 
			
		||||
				LastUpgradeCheck = 1430;
 | 
			
		||||
				LastUpgradeCheck = 1510;
 | 
			
		||||
				ORGANIZATIONNAME = "The Chromium Authors";
 | 
			
		||||
				TargetAttributes = {
 | 
			
		||||
					97C146ED1CF9000F007C117D = {
 | 
			
		||||
@@ -271,11 +272,9 @@
 | 
			
		||||
				"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
 | 
			
		||||
				"${BUILT_PRODUCTS_DIR}/DKImagePickerController/DKImagePickerController.framework",
 | 
			
		||||
				"${BUILT_PRODUCTS_DIR}/DKPhotoGallery/DKPhotoGallery.framework",
 | 
			
		||||
				"${BUILT_PRODUCTS_DIR}/FMDB/FMDB.framework",
 | 
			
		||||
				"${BUILT_PRODUCTS_DIR}/MTBBarcodeScanner/MTBBarcodeScanner.framework",
 | 
			
		||||
				"${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework",
 | 
			
		||||
				"${BUILT_PRODUCTS_DIR}/Sentry/Sentry.framework",
 | 
			
		||||
				"${BUILT_PRODUCTS_DIR}/SentryPrivate/SentryPrivate.framework",
 | 
			
		||||
				"${BUILT_PRODUCTS_DIR}/SwiftyGif/SwiftyGif.framework",
 | 
			
		||||
				"${BUILT_PRODUCTS_DIR}/audioplayers_darwin/audioplayers_darwin.framework",
 | 
			
		||||
				"${BUILT_PRODUCTS_DIR}/camera_avfoundation/camera_avfoundation.framework",
 | 
			
		||||
@@ -295,11 +294,9 @@
 | 
			
		||||
			outputPaths = (
 | 
			
		||||
				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKImagePickerController.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}/SDWebImage.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}/audioplayers_darwin.framework",
 | 
			
		||||
				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/camera_avfoundation.framework",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<Scheme
 | 
			
		||||
   LastUpgradeVersion = "1430"
 | 
			
		||||
   LastUpgradeVersion = "1510"
 | 
			
		||||
   version = "1.3">
 | 
			
		||||
   <BuildAction
 | 
			
		||||
      parallelizeBuildables = "YES"
 | 
			
		||||
 
 | 
			
		||||
@@ -1393,7 +1393,7 @@ class InvenTreeAPI {
 | 
			
		||||
  // Find the current locale code for the running app
 | 
			
		||||
  String get currentLocale {
 | 
			
		||||
 | 
			
		||||
    if (OneContext.hasContext) {
 | 
			
		||||
    if (hasContext()) {
 | 
			
		||||
      // Try to get app context
 | 
			
		||||
      BuildContext? context = OneContext().context;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,22 +1,29 @@
 | 
			
		||||
import "package:adaptive_theme/adaptive_theme.dart";
 | 
			
		||||
import "package:flutter/material.dart";
 | 
			
		||||
import "package:inventree/helpers.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
 | 
			
		||||
Color get COLOR_ACTION {
 | 
			
		||||
 | 
			
		||||
  // OneContext might not have context, e.g. in testing
 | 
			
		||||
  if (!OneContext.hasContext) {
 | 
			
		||||
    return Colors.lightBlue;
 | 
			
		||||
  if (!hasContext()) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  BuildContext? context = OneContext().context;
 | 
			
		||||
 | 
			
		||||
  if (context != null) {
 | 
			
		||||
    return Theme.of(context).indicatorColor;
 | 
			
		||||
  if (context == null) {
 | 
			
		||||
    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 {
 | 
			
		||||
    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_SUCCESS = Color.fromRGBO(100, 200, 75, 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_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()),
 | 
			
		||||
                        )
 | 
			
		||||
                      ],
 | 
			
		||||
                    )
 | 
			
		||||
            );
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -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();
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 | 
			
		||||
        if (result && OneContext.hasContext) {
 | 
			
		||||
        if (result && hasContext()) {
 | 
			
		||||
          OneContext().pop();
 | 
			
		||||
        }
 | 
			
		||||
        return;
 | 
			
		||||
 
 | 
			
		||||
@@ -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(
 | 
			
		||||
 
 | 
			
		||||
@@ -39,13 +39,32 @@ bool debugContains(String msg, {bool raiseAssert = true}) {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!result) {
 | 
			
		||||
    print("Debug does not contain expected string: '${msg}'");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (raiseAssert) {
 | 
			
		||||
 | 
			
		||||
    assert(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
 | 
			
		||||
 */
 | 
			
		||||
@@ -83,7 +102,7 @@ Future<void> playAudioFile(String path) async {
 | 
			
		||||
  // Debug message for unit testing
 | 
			
		||||
  debug("Playing audio file: '${path}'");
 | 
			
		||||
 | 
			
		||||
  if (!OneContext.hasContext) {
 | 
			
		||||
  if (!hasContext()) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -117,21 +136,13 @@ String renderCurrency(double? amount, String currency, {int decimals = 2}) {
 | 
			
		||||
 | 
			
		||||
  if (currency.isEmpty) return "-";
 | 
			
		||||
 | 
			
		||||
  CurrencyFormatterSettings backupSettings = CurrencyFormatterSettings(
 | 
			
		||||
    symbol: "\$",
 | 
			
		||||
    symbolSide: SymbolSide.left,
 | 
			
		||||
  );
 | 
			
		||||
  CurrencyFormat fmt = CurrencyFormat.fromCode(currency.toLowerCase()) ?? CurrencyFormat.usd;
 | 
			
		||||
 | 
			
		||||
  String value = CurrencyFormatter.format(
 | 
			
		||||
    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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
import "dart:io";
 | 
			
		||||
 | 
			
		||||
import "package:device_info_plus/device_info_plus.dart";
 | 
			
		||||
import "package:inventree/helpers.dart";
 | 
			
		||||
import "package:one_context/one_context.dart";
 | 
			
		||||
import "package:package_info_plus/package_info_plus.dart";
 | 
			
		||||
import "package:sentry_flutter/sentry_flutter.dart";
 | 
			
		||||
@@ -129,16 +130,16 @@ Future<bool> sentryReportMessage(String message, {Map<String, String>? context})
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Sentry.configureScope((scope) {
 | 
			
		||||
    scope.setExtra("server", server_info);
 | 
			
		||||
    scope.setExtra("app", app_info);
 | 
			
		||||
    scope.setExtra("device", device_info);
 | 
			
		||||
    scope.setContexts("server", server_info);
 | 
			
		||||
    scope.setContexts("app", app_info);
 | 
			
		||||
    scope.setContexts("device", device_info);
 | 
			
		||||
 | 
			
		||||
    if (context != null) {
 | 
			
		||||
      scope.setExtra("context", context);
 | 
			
		||||
      scope.setContexts("context", context);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Catch stacktrace data if possible
 | 
			
		||||
    scope.setExtra("stacktrace", StackTrace.current.toString());
 | 
			
		||||
    scope.setContexts("stacktrace", StackTrace.current.toString());
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
@@ -203,7 +204,7 @@ Future<void> sentryReportError(String source, dynamic error, StackTrace? stackTr
 | 
			
		||||
  // Ensure we pass the 'source' of the error
 | 
			
		||||
  context["source"] = source;
 | 
			
		||||
 | 
			
		||||
  if (OneContext.hasContext) {
 | 
			
		||||
  if (hasContext()) {
 | 
			
		||||
    final ctx = OneContext().context;
 | 
			
		||||
 | 
			
		||||
    if (ctx != null) {
 | 
			
		||||
@@ -213,10 +214,10 @@ Future<void> sentryReportError(String source, dynamic error, StackTrace? stackTr
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Sentry.configureScope((scope) {
 | 
			
		||||
    scope.setExtra("server", server_info);
 | 
			
		||||
    scope.setExtra("app", app_info);
 | 
			
		||||
    scope.setExtra("device", device_info);
 | 
			
		||||
    scope.setExtra("context", context);
 | 
			
		||||
    scope.setContexts("server", server_info);
 | 
			
		||||
    scope.setContexts("app", app_info);
 | 
			
		||||
    scope.setContexts("device", device_info);
 | 
			
		||||
    scope.setContexts("context", context);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  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:flutter/material.dart";
 | 
			
		||||
 | 
			
		||||
import "package:inventree/helpers.dart";
 | 
			
		||||
 | 
			
		||||
// Shortcut function to reduce boilerplate!
 | 
			
		||||
I18N L10()
 | 
			
		||||
{
 | 
			
		||||
  if (OneContext.hasContext) {
 | 
			
		||||
    BuildContext? _ctx = OneContext().context;
 | 
			
		||||
  // Testing mode - ignore context
 | 
			
		||||
  if (!hasContext()) {
 | 
			
		||||
    return I18NEn();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    if (_ctx != null) {
 | 
			
		||||
      I18N? i18n = I18N.of(_ctx);
 | 
			
		||||
  BuildContext? _ctx = OneContext().context;
 | 
			
		||||
 | 
			
		||||
      if (i18n != null) {
 | 
			
		||||
        return i18n;
 | 
			
		||||
      }
 | 
			
		||||
  if (_ctx != null) {
 | 
			
		||||
    I18N? i18n = I18N.of(_ctx);
 | 
			
		||||
 | 
			
		||||
    if (i18n != null) {
 | 
			
		||||
      return i18n;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -162,13 +162,13 @@ class InvenTreeAppState extends State<StatefulWidget> {
 | 
			
		||||
    return AdaptiveTheme(
 | 
			
		||||
      light: ThemeData(
 | 
			
		||||
        brightness: Brightness.light,
 | 
			
		||||
        primarySwatch: Colors.lightBlue,
 | 
			
		||||
        secondaryHeaderColor: Colors.blueGrey
 | 
			
		||||
        colorSchemeSeed: Colors.lightBlueAccent,
 | 
			
		||||
        useMaterial3: true,
 | 
			
		||||
      ),
 | 
			
		||||
      dark: ThemeData(
 | 
			
		||||
        brightness: Brightness.dark,
 | 
			
		||||
        primarySwatch: Colors.lightBlue,
 | 
			
		||||
        secondaryHeaderColor: Colors.blueGrey,
 | 
			
		||||
        colorSchemeSeed: Colors.blue,
 | 
			
		||||
        useMaterial3: true,
 | 
			
		||||
      ),
 | 
			
		||||
      initial: savedThemeMode ?? AdaptiveThemeMode.light,
 | 
			
		||||
      builder: (light, dark) =>  MaterialApp(
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,6 @@ import "dart:ui";
 | 
			
		||||
 | 
			
		||||
import "package:inventree/l10n/supported_locales.dart";
 | 
			
		||||
import "package:path_provider/path_provider.dart";
 | 
			
		||||
import "package:sembast/sembast.dart";
 | 
			
		||||
import "package:sembast/sembast_io.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(
 | 
			
		||||
    builder: (BuildContext context) {
 | 
			
		||||
      return AlertDialog(
 | 
			
		||||
@@ -63,6 +67,10 @@ Future<void> confirmationDialog(String title, String text, {Color? color, IconDa
 | 
			
		||||
  String _accept = acceptText ?? L10().ok;
 | 
			
		||||
  String _reject = rejectText ?? L10().cancel;
 | 
			
		||||
 | 
			
		||||
  if (!hasContext()) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  OneContext().showDialog(
 | 
			
		||||
    builder: (BuildContext context) {
 | 
			
		||||
      return AlertDialog(
 | 
			
		||||
@@ -176,6 +184,10 @@ Future<void> showErrorDialog(String title, {String description = "", APIResponse
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!hasContext()) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  OneContext().showDialog(
 | 
			
		||||
    builder: (context) => SimpleDialog(
 | 
			
		||||
      title: ListTile(
 | 
			
		||||
@@ -196,7 +208,7 @@ Future<void> showErrorDialog(String title, {String description = "", APIResponse
 | 
			
		||||
 */
 | 
			
		||||
Future<void> showServerError(String url, String title, String description) async {
 | 
			
		||||
 | 
			
		||||
  if (!OneContext.hasContext) {
 | 
			
		||||
  if (!hasContext()) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,7 @@ void showSnackIcon(String text, {IconData? icon, Function()? onAction, bool? suc
 | 
			
		||||
  debug("showSnackIcon: '${text}'");
 | 
			
		||||
 | 
			
		||||
  // Escape quickly if we do not have context
 | 
			
		||||
  if (!OneContext.hasContext) {
 | 
			
		||||
  if (!hasContext()) {
 | 
			
		||||
    // Debug message for unit testing
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -301,12 +301,16 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
 | 
			
		||||
    // Serial number field is not required here
 | 
			
		||||
    fields.remove("serial");
 | 
			
		||||
 | 
			
		||||
    Map<String, dynamic> data = {};
 | 
			
		||||
 | 
			
		||||
    if (location != null) {
 | 
			
		||||
      data["location"] = location!.pk;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    InvenTreeStockItem().createForm(
 | 
			
		||||
        context,
 | 
			
		||||
        L10().stockItemCreate,
 | 
			
		||||
        data: {
 | 
			
		||||
          "location": location != null ? location!.pk : null,
 | 
			
		||||
        },
 | 
			
		||||
        data: data,
 | 
			
		||||
        fields: fields,
 | 
			
		||||
        onSuccess: (result) async {
 | 
			
		||||
          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:
 | 
			
		||||
  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
 | 
			
		||||
  camera: ^0.10.3                         # Camera
 | 
			
		||||
  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
 | 
			
		||||
  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
 | 
			
		||||
  file_picker: ^5.3.1                     # Select files from the device
 | 
			
		||||
  file_picker: ^8.1.2                     # Select files from the device
 | 
			
		||||
  flutter:
 | 
			
		||||
    sdk: flutter
 | 
			
		||||
  flutter_cache_manager: ^3.3.0
 | 
			
		||||
@@ -27,19 +27,19 @@ dependencies:
 | 
			
		||||
  flutter_overlay_loader: ^2.0.0          # Overlay screen support
 | 
			
		||||
  flutter_speed_dial: ^6.2.0              # Speed dial / FAB implementation
 | 
			
		||||
  flutter_tabler_icons: ^1.35.0
 | 
			
		||||
  http: ^0.13.6
 | 
			
		||||
  image_picker: ^1.0.8                    # Select or take photos
 | 
			
		||||
  http: ^1.2.2
 | 
			
		||||
  image_picker: ^1.1.2                    # Select or take photos
 | 
			
		||||
  infinite_scroll_pagination: ^4.0.0      # Let the server do all the work!
 | 
			
		||||
  intl: ^0.18.0
 | 
			
		||||
  one_context: ^2.1.0                     # Dialogs without requiring context
 | 
			
		||||
  open_filex: ^4.4.0                      # Open local files
 | 
			
		||||
  package_info_plus: ^3.0.2               # App information introspection
 | 
			
		||||
  path: ^1.8.2
 | 
			
		||||
  intl: ^0.19.0
 | 
			
		||||
  one_context: ^4.0.0                     # Dialogs without requiring context
 | 
			
		||||
  open_filex: ^4.5.0                      # Open local files
 | 
			
		||||
  package_info_plus: ^8.0.2               # App information introspection
 | 
			
		||||
  path: ^1.9.0
 | 
			
		||||
  path_provider: ^2.1.3                   # Local file storage
 | 
			
		||||
  qr_code_scanner: ^1.0.1                 # Barcode scanning
 | 
			
		||||
  sembast: ^3.6.0                         # NoSQL data storage
 | 
			
		||||
  sentry_flutter: 7.9.0                   # Error reporting
 | 
			
		||||
  url_launcher: ^6.2.4                    # Open link in system browser
 | 
			
		||||
  sentry_flutter: 8.9.0                   # Error reporting
 | 
			
		||||
  url_launcher: ^6.3.0                    # Open link in system browser
 | 
			
		||||
 | 
			
		||||
dev_dependencies:
 | 
			
		||||
  flutter_launcher_icons: ^0.11.0
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@ void main() {
 | 
			
		||||
    await simulateKeyDownEvent(LogicalKeyboardKey.keyA);
 | 
			
		||||
    await simulateKeyDownEvent(LogicalKeyboardKey.keyB);
 | 
			
		||||
    await simulateKeyDownEvent(LogicalKeyboardKey.keyC);
 | 
			
		||||
    await simulateKeyDownEvent(LogicalKeyboardKey.enter);
 | 
			
		||||
    await simulateKeyDownEvent(LogicalKeyboardKey.enter, character: "\n");
 | 
			
		||||
 | 
			
		||||
    // Check debug output
 | 
			
		||||
    debugContains("scanned: abc");
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user