mirror of
https://github.com/inventree/inventree-app.git
synced 2025-04-27 21:16:48 +00:00
Barcode scanner updates (#562)
* Add BUILDING.md * Replace scaning library - Out with qr_code_scanner - In with flutter_zxing * Update specs for jdk / kotlin / gradle - NFI what this all means? * Refactor barcode scanning widget * Refactor barcode overlay * Add handlers * Update release notes * Fix AppBar color * Enhance attachment widget * remove unused import * Improved icon * Select theme from main drawer
This commit is contained in:
parent
4151aeb8e1
commit
d4cff1a5b9
4
.github/workflows/android.yaml
vendored
4
.github/workflows/android.yaml
vendored
@ -19,7 +19,7 @@ jobs:
|
|||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: '11'
|
java-version: '17'
|
||||||
- name: Setup Flutter
|
- name: Setup Flutter
|
||||||
uses: subosito/flutter-action@v2
|
uses: subosito/flutter-action@v2
|
||||||
with:
|
with:
|
||||||
@ -29,7 +29,7 @@ jobs:
|
|||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
uses: gradle/gradle-build-action@v2.4.2
|
uses: gradle/gradle-build-action@v2.4.2
|
||||||
with:
|
with:
|
||||||
gradle-version: 7.6
|
gradle-version: 8.5
|
||||||
- name: Collect Translation Files
|
- name: Collect Translation Files
|
||||||
run: |
|
run: |
|
||||||
cd lib/l10n
|
cd lib/l10n
|
||||||
|
64
BUILDING.md
Normal file
64
BUILDING.md
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
## InvenTree App Development
|
||||||
|
|
||||||
|
For developers looking to contribute to the project, we use Flutter for app development. The project has been tested in Android Studio (on both Windows and Mac) and also VSCode.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
To build the app from source, you will need the following tools installed on your system:
|
||||||
|
|
||||||
|
- Android Studio (with Flutter and Dart plugins)
|
||||||
|
|
||||||
|
### iOS Development
|
||||||
|
|
||||||
|
For iOS development, you will need a Mac system with XCode installed.
|
||||||
|
|
||||||
|
### Java Version
|
||||||
|
|
||||||
|
Some versions of Android Studio ship with a built-in version of the Java JDK. However, the InvenTree app requires [JDK 17](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html) to be installed.
|
||||||
|
|
||||||
|
If you see any errors related to JDK version mismatch, download and install the correct version of the JDK (from the link above) and update your Android Studio settings to point to the correct JDK location:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
flutter config --jdk-dir /path/to/jdk
|
||||||
|
```
|
||||||
|
|
||||||
|
## Invoke Tasks
|
||||||
|
|
||||||
|
We use the [invoke](https://www.pyinvoke.org) to run some core tasks - you will need python and invoke installed on your local system.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
Initial project setup (after you have installed all required dev tools) is as follows:
|
||||||
|
|
||||||
|
Generate initial translation files:
|
||||||
|
|
||||||
|
```
|
||||||
|
invoke translate
|
||||||
|
```
|
||||||
|
|
||||||
|
Install required flutter packages:
|
||||||
|
```
|
||||||
|
flutter pub get
|
||||||
|
```
|
||||||
|
|
||||||
|
You should now be ready to debug on a connected or emulated device!
|
||||||
|
|
||||||
|
## Building Release Versions
|
||||||
|
|
||||||
|
Building release versions for target platforms (either android or iOS) is simplified using invoke:
|
||||||
|
|
||||||
|
### Android
|
||||||
|
|
||||||
|
Build Android release:
|
||||||
|
|
||||||
|
```
|
||||||
|
invoke android
|
||||||
|
```
|
||||||
|
|
||||||
|
### iOS
|
||||||
|
|
||||||
|
Build iOS release:
|
||||||
|
|
||||||
|
```
|
||||||
|
invoke ios
|
||||||
|
```
|
39
README.md
39
README.md
@ -31,41 +31,4 @@ User documentation for the InvenTree mobile app can be found [within the InvenTr
|
|||||||
|
|
||||||
## Developer Documentation
|
## Developer Documentation
|
||||||
|
|
||||||
For developers looking to contribute to the project, we use Flutter for app development. The project has been tested in Android Studio (on both Windows and Mac) and also VSCode.
|
Refer to the [build instructions](BUILDING.md) for information on how to build the app from source.
|
||||||
|
|
||||||
### Invoke Tasks
|
|
||||||
|
|
||||||
We use the [invoke](https://www.pyinvoke.org) to run some core tasks - you will need python and invoke installed on your local system.
|
|
||||||
|
|
||||||
### Getting Started
|
|
||||||
|
|
||||||
Initial project setup (after you have installed all required dev tools) is as follows:
|
|
||||||
|
|
||||||
Generate initial translation files:
|
|
||||||
|
|
||||||
```
|
|
||||||
invoke translate
|
|
||||||
```
|
|
||||||
|
|
||||||
Install required flutter packages:
|
|
||||||
```
|
|
||||||
flutter pub get
|
|
||||||
```
|
|
||||||
|
|
||||||
You should now be ready to debug on a connected or emulated device!
|
|
||||||
|
|
||||||
### Building Release Versions
|
|
||||||
|
|
||||||
Building release versions for target platforms (either android or iOS) is simplified using invoke:
|
|
||||||
|
|
||||||
Build Android release:
|
|
||||||
|
|
||||||
```
|
|
||||||
invoke android
|
|
||||||
```
|
|
||||||
|
|
||||||
Build iOS release:
|
|
||||||
|
|
||||||
```
|
|
||||||
invoke ios
|
|
||||||
```
|
|
||||||
|
@ -31,6 +31,16 @@ if (keystorePropertiesFile.exists()) {
|
|||||||
android {
|
android {
|
||||||
compileSdkVersion 34
|
compileSdkVersion 34
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_17
|
||||||
|
targetCompatibility JavaVersion.VERSION_17
|
||||||
|
}
|
||||||
|
|
||||||
|
// If using Kotlin
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = JavaVersion.VERSION_17
|
||||||
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
main.java.srcDirs += 'src/main/kotlin'
|
main.java.srcDirs += 'src/main/kotlin'
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-all.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
### 0.17.0 - November 2024
|
### 0.17.0 - December 2024
|
||||||
---
|
---
|
||||||
|
|
||||||
|
- Improved barcode scanning with new scanning library
|
||||||
- Enhanced home-screen display using grid-view
|
- Enhanced home-screen display using grid-view
|
||||||
- Improvements for image uploading
|
- Improvements for image uploading
|
||||||
- Provide "upload image" shortcut on Purchase Order detail view
|
- Provide "upload image" shortcut on Purchase Order detail view
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import "dart:io";
|
import "dart:math";
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.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/preferences.dart";
|
import "package:inventree/preferences.dart";
|
||||||
|
|
||||||
import "package:qr_code_scanner/qr_code_scanner.dart";
|
import "package:flutter_zxing/flutter_zxing.dart";
|
||||||
|
|
||||||
import "package:inventree/l10.dart";
|
import "package:inventree/l10.dart";
|
||||||
|
|
||||||
@ -26,197 +26,234 @@ class CameraBarcodeController extends InvenTreeBarcodeController {
|
|||||||
class _CameraBarcodeControllerState extends InvenTreeBarcodeControllerState {
|
class _CameraBarcodeControllerState extends InvenTreeBarcodeControllerState {
|
||||||
_CameraBarcodeControllerState() : super();
|
_CameraBarcodeControllerState() : super();
|
||||||
|
|
||||||
QRViewController? _controller;
|
|
||||||
|
|
||||||
bool flash_status = false;
|
bool flash_status = false;
|
||||||
|
|
||||||
|
int scan_delay = 500;
|
||||||
bool single_scanning = false;
|
bool single_scanning = false;
|
||||||
bool scanning_paused = false;
|
bool scanning_paused = false;
|
||||||
|
|
||||||
|
String scanned_code = "";
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_loadSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Load the barcode scanning settings
|
||||||
|
*/
|
||||||
Future<void> _loadSettings() async {
|
Future<void> _loadSettings() async {
|
||||||
bool _single = await InvenTreeSettingsManager()
|
bool _single = await InvenTreeSettingsManager()
|
||||||
.getBool(INV_BARCODE_SCAN_SINGLE, false);
|
.getBool(INV_BARCODE_SCAN_SINGLE, false);
|
||||||
|
|
||||||
|
int _delay = await InvenTreeSettingsManager()
|
||||||
|
.getValue(INV_BARCODE_SCAN_DELAY, 500) as int;
|
||||||
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
|
scan_delay = _delay;
|
||||||
single_scanning = _single;
|
single_scanning = _single;
|
||||||
scanning_paused = false;
|
scanning_paused = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Callback function when the Barcode scanner view is initially created */
|
|
||||||
void _onViewCreated(BuildContext context, QRViewController controller) {
|
|
||||||
_controller = controller;
|
|
||||||
|
|
||||||
controller.scannedDataStream.listen((barcode) {
|
|
||||||
if (!scanning_paused) {
|
|
||||||
handleBarcodeData(barcode.code).then((value) => {
|
|
||||||
// If in single-scanning mode, pause after successful scan
|
|
||||||
if (single_scanning && mounted)
|
|
||||||
{
|
|
||||||
setState(() {
|
|
||||||
scanning_paused = true;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
_loadSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
// In order to get hot reload to work we need to pause the camera if the platform
|
|
||||||
// is android, or resume the camera if the platform is iOS.
|
|
||||||
@override
|
|
||||||
void reassemble() {
|
|
||||||
super.reassemble();
|
|
||||||
|
|
||||||
if (mounted) {
|
|
||||||
if (Platform.isAndroid) {
|
|
||||||
_controller!.pauseCamera();
|
|
||||||
}
|
|
||||||
|
|
||||||
_controller!.resumeCamera();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_controller?.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> pauseScan() async {
|
Future<void> pauseScan() async {
|
||||||
try {
|
|
||||||
await _controller?.pauseCamera();
|
|
||||||
} on CameraException {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> resumeScan() async {
|
|
||||||
// Do not attempt to resume if the widget is not mounted
|
|
||||||
if (!mounted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await _controller?.resumeCamera();
|
|
||||||
} on CameraException {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Toggle the status of the camera flash
|
|
||||||
Future<void> updateFlashStatus() async {
|
|
||||||
final bool? status = await _controller?.getFlashStatus();
|
|
||||||
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
flash_status = status != null && status;
|
scanning_paused = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Future<void> resumeScan() async {
|
||||||
Widget actionIcon =
|
if (mounted) {
|
||||||
Icon(TablerIcons.player_pause, color: COLOR_WARNING, size: 64);
|
setState(() {
|
||||||
|
scanning_paused = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Callback function when a barcode is scanned
|
||||||
|
*/
|
||||||
|
void _onScanSuccess(Code? code) {
|
||||||
|
|
||||||
if (scanning_paused) {
|
if (scanning_paused) {
|
||||||
actionIcon =
|
return;
|
||||||
Icon(TablerIcons.player_play, color: COLOR_ACTION, size: 64);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String barcode_data = code?.text ?? "";
|
||||||
|
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
scanned_code = barcode_data;
|
||||||
|
scanning_paused = barcode_data.isNotEmpty;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (barcode_data.isNotEmpty) {
|
||||||
|
handleBarcodeData(barcode_data).then((_) {
|
||||||
|
if (!single_scanning && mounted) {
|
||||||
|
// Resume next scan
|
||||||
|
setState(() {
|
||||||
|
scanning_paused = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Build the barcode scanner overlay
|
||||||
|
*/
|
||||||
|
FixedScannerOverlay BarcodeOverlay(BuildContext context) {
|
||||||
|
|
||||||
|
// Note: Copied from reader_widget.dart:ReaderWidget.build
|
||||||
|
final Size size = MediaQuery.of(context).size;
|
||||||
|
final double cropSize = min(size.width, size.height) * 0.5;
|
||||||
|
|
||||||
|
return FixedScannerOverlay(
|
||||||
|
borderColor: scanning_paused ? COLOR_WARNING : COLOR_ACTION,
|
||||||
|
overlayColor: Colors.black45,
|
||||||
|
borderRadius: 1,
|
||||||
|
borderLength: 15,
|
||||||
|
borderWidth: 8,
|
||||||
|
cutOutSize: cropSize,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Build the barcode reader widget
|
||||||
|
*/
|
||||||
|
Widget BarcodeReader(BuildContext context) {
|
||||||
|
|
||||||
|
return ReaderWidget(
|
||||||
|
onScan: _onScanSuccess,
|
||||||
|
isMultiScan: false,
|
||||||
|
tryHarder: true,
|
||||||
|
tryInverted: true,
|
||||||
|
tryRotate: true,
|
||||||
|
showGallery: false,
|
||||||
|
scanDelay: Duration(milliseconds: scan_delay),
|
||||||
|
resolution: ResolutionPreset.high,
|
||||||
|
lensDirection: CameraLensDirection.back,
|
||||||
|
flashOnIcon: const Icon(Icons.flash_on),
|
||||||
|
flashOffIcon: const Icon(Icons.flash_off),
|
||||||
|
toggleCameraIcon: const Icon(TablerIcons.camera_rotate),
|
||||||
|
actionButtonsBackgroundBorderRadius:
|
||||||
|
BorderRadius.circular(40),
|
||||||
|
scannerOverlay: BarcodeOverlay(context),
|
||||||
|
actionButtonsBackgroundColor: Colors.black.withOpacity(0.7),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget topCenterOverlay() {
|
||||||
|
return SafeArea(
|
||||||
|
child: Align(
|
||||||
|
alignment: Alignment.topCenter,
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(10),
|
||||||
|
child: Text(
|
||||||
|
widget.handler.getOverlayText(context),
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget bottomCenterOverlay() {
|
||||||
|
|
||||||
String info_text = scanning_paused ? L10().barcodeScanPaused : L10().barcodeScanPause;
|
String info_text = scanning_paused ? L10().barcodeScanPaused : L10().barcodeScanPause;
|
||||||
|
|
||||||
return Scaffold(
|
return SafeArea(
|
||||||
appBar: AppBar(
|
child: Align(
|
||||||
backgroundColor: COLOR_APP_BAR,
|
alignment: Alignment.bottomCenter,
|
||||||
title: Text(L10().scanBarcode),
|
child: Padding(
|
||||||
actions: [
|
padding: EdgeInsets.all(10),
|
||||||
IconButton(
|
child: Text(
|
||||||
icon: Icon(Icons.flip_camera_android),
|
scanned_code.isNotEmpty ? scanned_code : info_text,
|
||||||
onPressed: () {
|
textAlign: TextAlign.center,
|
||||||
_controller?.flipCamera();
|
style: TextStyle(
|
||||||
}),
|
color: Colors.white,
|
||||||
IconButton(
|
fontSize: 16,
|
||||||
icon: flash_status ? Icon(Icons.flash_off) : Icon(Icons.flash_on),
|
fontWeight: FontWeight.bold
|
||||||
onPressed: () {
|
)
|
||||||
_controller?.toggleFlash();
|
),
|
||||||
updateFlashStatus();
|
)
|
||||||
},
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Display an overlay at the bottom right of the screen
|
||||||
|
*/
|
||||||
|
Widget bottomRightOverlay() {
|
||||||
|
return SafeArea(
|
||||||
|
child: Align(
|
||||||
|
alignment: Alignment.bottomRight,
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(10),
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(40),
|
||||||
|
child: ColoredBox(
|
||||||
|
color: Colors.black45,
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: scanning_paused ? [] : [
|
||||||
|
CircularProgressIndicator(
|
||||||
|
value: null
|
||||||
|
)
|
||||||
|
// actionIcon,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
backgroundColor: COLOR_APP_BAR,
|
||||||
|
title: Text(L10().scanBarcode),
|
||||||
|
),
|
||||||
|
body: GestureDetector(
|
||||||
|
onTap: () async {
|
||||||
|
setState(() {
|
||||||
|
scanning_paused = !scanning_paused;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Stack(
|
||||||
|
children: <Widget>[
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: BarcodeReader(context)
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
topCenterOverlay(),
|
||||||
|
bottomCenterOverlay(),
|
||||||
|
bottomRightOverlay(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: GestureDetector(
|
),
|
||||||
onTapDown: (details) async {
|
);
|
||||||
setState(() {
|
|
||||||
scanning_paused = !scanning_paused;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onLongPressEnd: (details) async {
|
|
||||||
if (mounted) {
|
|
||||||
setState(() {
|
|
||||||
scanning_paused = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Stack(
|
|
||||||
children: <Widget>[
|
|
||||||
Column(children: [
|
|
||||||
Expanded(
|
|
||||||
child: QRView(
|
|
||||||
key: barcodeControllerKey,
|
|
||||||
onQRViewCreated: (QRViewController controller) {
|
|
||||||
_onViewCreated(context, controller);
|
|
||||||
},
|
|
||||||
overlay: QrScannerOverlayShape(
|
|
||||||
borderColor:
|
|
||||||
scanning_paused ? COLOR_WARNING : COLOR_ACTION,
|
|
||||||
borderRadius: 10,
|
|
||||||
borderLength: 30,
|
|
||||||
borderWidth: 10,
|
|
||||||
cutOutSize: 300,
|
|
||||||
),
|
|
||||||
))
|
|
||||||
]),
|
|
||||||
Center(
|
|
||||||
child: Column(children: [
|
|
||||||
Padding(
|
|
||||||
child: Text(
|
|
||||||
widget.handler.getOverlayText(context),
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.bold, color: Colors.white),
|
|
||||||
),
|
|
||||||
padding: EdgeInsets.all(25)),
|
|
||||||
Padding(
|
|
||||||
child: CircularProgressIndicator(
|
|
||||||
value: scanning_paused ? 0 : null),
|
|
||||||
padding: EdgeInsets.all(40),
|
|
||||||
),
|
|
||||||
Spacer(),
|
|
||||||
SizedBox(
|
|
||||||
child: Center(
|
|
||||||
child: actionIcon,
|
|
||||||
),
|
|
||||||
width: 100,
|
|
||||||
height: 150,
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
child: Text(info_text,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
)),
|
|
||||||
padding: EdgeInsets.all(25),
|
|
||||||
),
|
|
||||||
]))
|
|
||||||
],
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,7 @@ class BarcodeHandler {
|
|||||||
Future<void> processBarcode(String barcode,
|
Future<void> processBarcode(String barcode,
|
||||||
{String url = "barcode/",
|
{String url = "barcode/",
|
||||||
Map<String, dynamic> extra_data = const {}}) async {
|
Map<String, dynamic> extra_data = const {}}) async {
|
||||||
|
|
||||||
debug("Scanned barcode data: '${barcode}'");
|
debug("Scanned barcode data: '${barcode}'");
|
||||||
|
|
||||||
barcode = barcode.trim();
|
barcode = barcode.trim();
|
||||||
|
@ -274,6 +274,12 @@
|
|||||||
"damaged": "Damaged",
|
"damaged": "Damaged",
|
||||||
"@damaged": {},
|
"@damaged": {},
|
||||||
|
|
||||||
|
"colorScheme": "Color Scheme",
|
||||||
|
"@colorScheme": {},
|
||||||
|
|
||||||
|
"colorSchemeDetail": "Select color scheme",
|
||||||
|
"@colorSchemeDetail": {},
|
||||||
|
|
||||||
"darkMode": "Dark Mode",
|
"darkMode": "Dark Mode",
|
||||||
"@darkMode": {},
|
"@darkMode": {},
|
||||||
|
|
||||||
|
@ -174,7 +174,7 @@ class _InvenTreeAppSettingsState extends State<InvenTreeAppSettingsWidget> {
|
|||||||
ListTile(
|
ListTile(
|
||||||
title: Text(L10().darkMode),
|
title: Text(L10().darkMode),
|
||||||
subtitle: Text(L10().darkModeEnable),
|
subtitle: Text(L10().darkModeEnable),
|
||||||
leading: Icon(TablerIcons.moon),
|
leading: Icon(TablerIcons.sun_moon),
|
||||||
trailing: Switch(
|
trailing: Switch(
|
||||||
value: darkMode,
|
value: darkMode,
|
||||||
onChanged: (bool value) {
|
onChanged: (bool value) {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.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/l10.dart";
|
import "package:inventree/l10.dart";
|
||||||
import "package:inventree/preferences.dart";
|
import "package:inventree/preferences.dart";
|
||||||
@ -39,7 +40,10 @@ class _InvenTreePurchaseOrderSettingsState extends State<InvenTreePurchaseOrderS
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(title: Text(L10().purchaseOrderSettings)),
|
appBar: AppBar(
|
||||||
|
title: Text(L10().purchaseOrderSettings),
|
||||||
|
backgroundColor: COLOR_APP_BAR,
|
||||||
|
),
|
||||||
body: Container(
|
body: Container(
|
||||||
child: ListView(
|
child: ListView(
|
||||||
children: [
|
children: [
|
||||||
|
@ -3,6 +3,7 @@ import "package:flutter/material.dart";
|
|||||||
import "package:flutter_tabler_icons/flutter_tabler_icons.dart";
|
import "package:flutter_tabler_icons/flutter_tabler_icons.dart";
|
||||||
|
|
||||||
import "package:inventree/l10.dart";
|
import "package:inventree/l10.dart";
|
||||||
|
import "package:inventree/app_colors.dart";
|
||||||
import "package:inventree/preferences.dart";
|
import "package:inventree/preferences.dart";
|
||||||
|
|
||||||
|
|
||||||
@ -39,7 +40,10 @@ class _InvenTreeSalesOrderSettingsState extends State<InvenTreeSalesOrderSetting
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(title: Text(L10().salesOrderSettings)),
|
appBar: AppBar(
|
||||||
|
title: Text(L10().salesOrderSettings),
|
||||||
|
backgroundColor: COLOR_APP_BAR,
|
||||||
|
),
|
||||||
body: Container(
|
body: Container(
|
||||||
child: ListView(
|
child: ListView(
|
||||||
children: [
|
children: [
|
||||||
|
@ -266,6 +266,7 @@ class _InvenTreeSelectServerState extends State<InvenTreeSelectServerWidget> {
|
|||||||
key: _loginKey,
|
key: _loginKey,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(L10().profileSelect),
|
title: Text(L10().profileSelect),
|
||||||
|
backgroundColor: COLOR_APP_BAR,
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(TablerIcons.circle_plus),
|
icon: Icon(TablerIcons.circle_plus),
|
||||||
|
@ -208,11 +208,8 @@ class _AttachmentWidgetState extends RefreshableState<AttachmentWidget> {
|
|||||||
|
|
||||||
if (tiles.isEmpty) {
|
if (tiles.isEmpty) {
|
||||||
tiles.add(ListTile(
|
tiles.add(ListTile(
|
||||||
|
leading: Icon(TablerIcons.file_x, color: COLOR_WARNING),
|
||||||
title: Text(L10().attachmentNone),
|
title: Text(L10().attachmentNone),
|
||||||
subtitle: Text(
|
|
||||||
L10().attachmentNoneDetail,
|
|
||||||
style: TextStyle(fontStyle: FontStyle.italic),
|
|
||||||
),
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import "package:adaptive_theme/adaptive_theme.dart";
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:flutter_tabler_icons/flutter_tabler_icons.dart";
|
import "package:flutter_tabler_icons/flutter_tabler_icons.dart";
|
||||||
|
|
||||||
@ -185,6 +186,28 @@ class InvenTreeDrawer extends StatelessWidget {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
tiles.add(Divider());
|
||||||
|
|
||||||
|
bool darkMode = AdaptiveTheme.of(context).mode.isDark;
|
||||||
|
|
||||||
|
tiles.add(
|
||||||
|
ListTile(
|
||||||
|
onTap: () {
|
||||||
|
AdaptiveTheme.of(context).toggleThemeMode();
|
||||||
|
_closeDrawer();
|
||||||
|
},
|
||||||
|
title: Text(L10().colorScheme),
|
||||||
|
subtitle: Text(L10().colorSchemeDetail),
|
||||||
|
leading: Icon(
|
||||||
|
TablerIcons.sun_moon,
|
||||||
|
color: COLOR_ACTION
|
||||||
|
),
|
||||||
|
trailing: Icon(
|
||||||
|
darkMode ? TablerIcons.moon : TablerIcons.sun,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
tiles.add(
|
tiles.add(
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(L10().settings),
|
title: Text(L10().settings),
|
||||||
|
@ -101,7 +101,7 @@ mixin BaseWidgetProperties {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Icons.barcode_reader, color: COLOR_ACTION),
|
icon: Icon(TablerIcons.barcode, color: COLOR_ACTION),
|
||||||
iconSize: iconSize,
|
iconSize: iconSize,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (InvenTreeAPI().checkConnection()) {
|
if (InvenTreeAPI().checkConnection()) {
|
||||||
|
32
pubspec.lock
32
pubspec.lock
@ -210,10 +210,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: cli_util
|
name: cli_util
|
||||||
sha256: "66f86e916d285c1a93d3b79587d94bd71984a66aac4ff74e524cfa7877f1395c"
|
sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.5"
|
version: "0.4.2"
|
||||||
clock:
|
clock:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -391,10 +391,10 @@ packages:
|
|||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: flutter_launcher_icons
|
name: flutter_launcher_icons
|
||||||
sha256: ce0e501cfc258907842238e4ca605e74b7fd1cdf04b3b43e86c43f3e40a1592c
|
sha256: "619817c4b65b322b5104b6bb6dfe6cda62d9729bd7ad4303ecc8b4e690a67a77"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.11.0"
|
version: "0.14.1"
|
||||||
flutter_localizations:
|
flutter_localizations:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -466,6 +466,14 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_zxing:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_zxing
|
||||||
|
sha256: "5b2670f151a6d96643204ff3a781e073739c23a91ef5fc39742bf13fb8287b4c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.8.2"
|
||||||
frontend_server_client:
|
frontend_server_client:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -510,10 +518,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image
|
name: image
|
||||||
sha256: "8e9d133755c3e84c73288363e6343157c383a0c6c56fc51afcc5d4d7180306d6"
|
sha256: f31d52537dc417fdcde36088fdf11d191026fd5e4fae742491ebd40e5a8bea7d
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.3.0"
|
version: "4.3.0"
|
||||||
image_picker:
|
image_picker:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -750,10 +758,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: package_info_plus
|
name: package_info_plus
|
||||||
sha256: a75164ade98cb7d24cfd0a13c6408927c6b217fa60dee5a7ff5c116a58f28918
|
sha256: da8d9ac8c4b1df253d1a328b7bf01ae77ef132833479ab40763334db13b91cce
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.0.2"
|
version: "8.1.1"
|
||||||
package_info_plus_platform_interface:
|
package_info_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -858,14 +866,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.4"
|
version: "2.1.4"
|
||||||
qr_code_scanner:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: qr_code_scanner
|
|
||||||
sha256: f23b68d893505a424f0bd2e324ebea71ed88465d572d26bb8d2e78a4749591fd
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.1"
|
|
||||||
rxdart:
|
rxdart:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -27,22 +27,22 @@ 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
|
||||||
|
flutter_zxing: ^1.8.2 # Barcode scanning
|
||||||
http: ^1.2.2
|
http: ^1.2.2
|
||||||
image_picker: ^1.1.2 # 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.19.0
|
intl: ^0.19.0
|
||||||
one_context: ^4.0.0 # Dialogs without requiring context
|
one_context: ^4.0.0 # Dialogs without requiring context
|
||||||
open_filex: ^4.5.0 # Open local files
|
open_filex: ^4.5.0 # Open local files
|
||||||
package_info_plus: ^8.0.2 # App information introspection
|
package_info_plus: ^8.1.1 # App information introspection
|
||||||
path: ^1.9.0
|
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
|
|
||||||
sembast: ^3.6.0 # NoSQL data storage
|
sembast: ^3.6.0 # NoSQL data storage
|
||||||
sentry_flutter: 8.9.0 # Error reporting
|
sentry_flutter: 8.9.0 # Error reporting
|
||||||
url_launcher: ^6.3.0 # 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.14.1
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
lint: ^2.1.2
|
lint: ^2.1.2
|
||||||
|
Loading…
x
Reference in New Issue
Block a user