Compare commits

...

19 commits

Author SHA1 Message Date
HendrikRauh
6aeae91c80 deps(update) 2026-03-29 17:43:30 +02:00
Hendrik Rauh
5f792e0956
Merge pull request #7 from HendrikRauh/master
v22.7
2026-03-29 16:10:17 +02:00
Hendrik Rauh
84e8ccc43a
Merge pull request #6 from inventree/master
Some checks failed
Android / build (push) Has been cancelled
CI / test (push) Has been cancelled
iOS / build (push) Has been cancelled
update to version 22.7
2026-03-29 16:08:47 +02:00
Oliver
83e97c56a2
Shipment fix (#802)
* Fix broken URL for SalesOrderShipment

* Bump version and update release notes
2026-03-29 15:08:19 +11:00
Oliver
ec9a7176a2
[bug] Fix for testable items (#796)
- Show test results
2026-03-09 21:43:53 +11:00
Oliver
7dcd35e56c
Bump version number (#794) 2026-03-03 21:04:46 +11:00
Oliver
a87a7d6228
Fix default location for "cascade" filter (#793) 2026-03-03 20:10:06 +11:00
Oliver
0d68762252
Bump version to 0.22.4 (#790) 2026-03-01 10:03:04 +11:00
Oliver
a90fdd4323
[bug] Fix boolean fields (#788)
- Use Switch field in API forms
- Retain custom tri-state filter button
- Closes https://github.com/inventree/inventree-app/issues/785
2026-02-28 15:47:07 +11:00
Oliver
d45ed1f838
[bug] Fix fetching of related field values (#789)
- Fixes https://github.com/inventree/inventree-app/issues/784
2026-02-28 15:47:01 +11:00
Oliver
a38ec4cf0d
Fix URLs (#783)
* Fix URLs

- Remove leading slash
- Closes https://github.com/inventree/inventree-app/issues/780

* Bump release notes
2026-02-27 23:26:14 +11:00
Oliver
5339d6acc0
Check server state (#782) 2026-02-27 23:03:25 +11:00
Oliver
958067a3f9
Bump version (#781) 2026-02-27 22:03:22 +11:00
Oliver
a88a102a50
Update barcode scanner (#779) 2026-02-27 18:03:06 +11:00
Oliver
04f98559fc
Fix bool fields (#778)
* Improved UX for boolean fields

- Use segmented button
- Allow tristate
- Improved filtering options

* Bug fix for null filter values

* Prevent null filters from being sent to the server

* Update release notes
2026-02-27 13:55:08 +11:00
Oliver
286daf2567
Extract location data when scanning barcode (#774)
- Closes https://github.com/inventree/inventree-app/issues/772
2026-02-22 13:57:08 +11:00
Oliver
91bface77f
Update Android direct download link in README (#771) 2026-02-17 07:55:19 +11:00
Oliver
05c59260f1
[bug] Label fix (#768)
* [Bug] Fix label printing URL

- Closes https://github.com/inventree/inventree-app/issues/767

* Bump release notes and version
2026-02-10 18:52:40 +11:00
Hendrik Rauh
6380930ac7
Merge pull request #1 from HendrikRauh/login-with-token
Some checks failed
Android / build (push) Has been cancelled
CI / test (push) Has been cancelled
iOS / build (push) Has been cancelled
Implement token-based login method
2026-02-08 18:14:37 +01:00
20 changed files with 263 additions and 52 deletions

View file

@ -27,7 +27,7 @@ Download and install from the [Apple App Store](https://apps.apple.com/au/app/in
### Direct Download (Android) ### Direct Download (Android)
We provide direct downloads for Android users - view our [download page via polar.sh](https://polar.sh/inventree/products/299bf0d5-af88-4e0f-becf-c007ad37ecf2) We provide direct downloads for Android users - view our [download page via polar.sh](https://buy.polar.sh/polar_cl_UnGILJ0c7P3hQrOrJs127oyLTTDOTHKrnqfCg30XtBI)
## User Documentation ## User Documentation

View file

@ -30,7 +30,8 @@ if (keystorePropertiesFile.exists()) {
android { android {
namespace "inventree.inventree_app" namespace "inventree.inventree_app"
compileSdkVersion 35 ndkVersion "26.3.11579264"
compileSdkVersion 36
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_17 sourceCompatibility JavaVersion.VERSION_17
@ -56,7 +57,7 @@ android {
defaultConfig { defaultConfig {
applicationId "inventree.inventree_app" applicationId "inventree.inventree_app"
minSdkVersion 21 minSdkVersion 23
targetSdkVersion 35 targetSdkVersion 35
versionCode flutterVersionCode.toInteger() versionCode flutterVersionCode.toInteger()
versionName flutterVersionName versionName flutterVersionName

View file

@ -19,7 +19,7 @@ pluginManagement {
plugins { plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0" id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "8.6.0" apply false id "com.android.application" version "8.6.0" apply false
id "org.jetbrains.kotlin.android" version "1.9.25" apply false id "org.jetbrains.kotlin.android" version "2.3.10" apply false
} }
include ":app" include ":app"

View file

@ -1,3 +1,39 @@
## 0.22.7 - March 2026
---
- Bug fix for loading sales order shipments
## 0.22.6 - March 2026
---
- Fix for displaying stock item test results
## 0.22.5 - March 2026
---
- Fix default value for "cascade" filter in PartCategory list view
- Fix default value for "cascade" filter in StockLocation list view
## 0.22.4 - March 2026
---
- Adds button to check server connection
- Fixes bug fetching sales order shipments
- Fix for boolean fields in API forms
## 0.22.3 - February 2026
---
- Auto-fill location data when receiving item via barcode scan
- Visual improvements for boolean form fields
- Add support for tri-state boolean form fields
- Bug fixes for refreshing list view data
## 0.22.2 - February 2026
---
- Bug fix for label printing, which used improperly formatted URL
## 0.22.1 - February 2026 ## 0.22.1 - February 2026
--- ---

6
flake.lock generated
View file

@ -20,11 +20,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1769170682, "lastModified": 1774386573,
"narHash": "sha256-oMmN1lVQU0F0W2k6OI3bgdzp2YOHWYUAw79qzDSjenU=", "narHash": "sha256-4hAV26quOxdC6iyG7kYaZcM3VOskcPUrdCQd/nx8obc=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "c5296fdd05cfa2c187990dd909864da9658df755", "rev": "46db2e09e1d3f113a13c0d7b81e2f221c63b8ce9",
"type": "github" "type": "github"
}, },
"original": { "original": {

View file

@ -33,15 +33,17 @@
androidSdk = androidSdk =
(pkgs.androidenv.composeAndroidPackages { (pkgs.androidenv.composeAndroidPackages {
platformVersions = [ platformVersions = [
"36"
"35" "35"
"34" "34"
]; ];
buildToolsVersions = [ buildToolsVersions = [
"36.0.0"
"35.0.0" "35.0.0"
"34.0.0" "34.0.0"
]; ];
includeNDK = true; includeNDK = true;
ndkVersions = [ "26.1.10909125" ]; ndkVersions = [ "26.3.11579264" ];
cmakeVersions = [ "3.22.1" ]; cmakeVersions = [ "3.22.1" ];
}).androidsdk; }).androidsdk;

1
ios/.gitignore vendored
View file

@ -22,6 +22,7 @@ Flutter/Generated.xcconfig
Flutter/ephemeral/ Flutter/ephemeral/
Flutter/app.flx Flutter/app.flx
Flutter/app.zip Flutter/app.zip
Flutter/ephemeral/
Flutter/flutter_assets/ Flutter/flutter_assets/
Flutter/flutter_export_environment.sh Flutter/flutter_export_environment.sh
ServiceDefinitions.json ServiceDefinitions.json

View file

@ -411,7 +411,7 @@ class InvenTreeAPI {
* 5. Request information on available plugins * 5. Request information on available plugins
*/ */
Future<bool> _connectToServer() async { Future<bool> _connectToServer() async {
if (!await _checkServer()) { if (!await checkServer()) {
return false; return false;
} }
@ -451,8 +451,8 @@ class InvenTreeAPI {
* Check that the remote server is available. * Check that the remote server is available.
* Ping the api/ endpoint, which does not require user authentication * Ping the api/ endpoint, which does not require user authentication
*/ */
Future<bool> _checkServer() async { Future<bool> checkServer({String? server}) async {
String address = profile?.server ?? ""; String address = server ?? profile?.server ?? "";
if (address.isEmpty) { if (address.isEmpty) {
showSnackIcon( showSnackIcon(
@ -463,8 +463,10 @@ class InvenTreeAPI {
return false; return false;
} }
if (!address.endsWith("/")) { String url = _makeUrl("/api/", base: address);
address = address + "/";
if (!url.endsWith("/")) {
url = url + "/";
} }
// Cache the "strictHttps" setting, so we can use it later without async requirement // Cache the "strictHttps" setting, so we can use it later without async requirement
@ -474,7 +476,7 @@ class InvenTreeAPI {
debug("Connecting to ${apiUrl}"); debug("Connecting to ${apiUrl}");
APIResponse response = await get("", expectedStatusCode: 200); APIResponse response = await get(url, expectedStatusCode: 200);
if (!response.successful()) { if (!response.successful()) {
debug("Server returned invalid response: ${response.statusCode}"); debug("Server returned invalid response: ${response.statusCode}");

View file

@ -275,7 +275,7 @@ class APIFormField {
return; return;
} }
String url = api_url + "/" + value.toString() + "/"; String url = api_url + value.toString() + "/";
final APIResponse response = await InvenTreeAPI().get(url, params: filters); final APIResponse response = await InvenTreeAPI().get(url, params: filters);
@ -293,6 +293,8 @@ class APIFormField {
return _constructString(); return _constructString();
case "boolean": case "boolean":
return _constructBoolean(); return _constructBoolean();
case "boolean filter":
return _constructBooleanFilter();
case "related field": case "related field":
return _constructRelatedField(); return _constructRelatedField();
case "integer": case "integer":
@ -874,25 +876,109 @@ class APIFormField {
// Construct a boolean input element // Construct a boolean input element
Widget _constructBoolean() { Widget _constructBoolean() {
bool? initial_value; bool v = false;
if (value is bool || value == null) { if (value is bool) {
initial_value = value as bool?; v = value as bool;
} else { } else {
String vs = value.toString().toLowerCase(); v = false;
initial_value = ["1", "true", "yes"].contains(vs);
} }
return CheckBoxField( return ListTile(
label: label, title: Text(label),
labelStyle: _labelStyle(), subtitle: Text(helpText),
helperText: helpText, contentPadding: EdgeInsets.zero,
helperStyle: _helperStyle(), trailing: Switch(
initial: initial_value, value: v,
tristate: (getParameter("tristate") ?? false) as bool, onChanged: (val) {
onSaved: (val) { setFieldValue(val);
setFieldValue(val); },
}, ),
);
}
// Construct a tri-state boolean filter element
Widget _constructBooleanFilter() {
String initial_value = "null";
bool allow_null = (getParameter("tristate") ?? false) as bool;
if (value is bool) {
initial_value = value.toString().toLowerCase();
} else if (value == null) {
if (allow_null) {
initial_value = "null";
} else {
initial_value = "false";
}
} else {
// Not a boolean value - may be a string
if (["1", "true", "yes"].contains(value.toString().toLowerCase())) {
initial_value = "true";
} else if ([
"0",
"false",
"no",
].contains(value.toString().toLowerCase())) {
initial_value = "false";
} else if (allow_null) {
initial_value = "null";
} else {
initial_value = "false";
}
}
List<ButtonSegment<String>> buttons = [];
if ((getParameter("tristate") ?? false) as bool) {
buttons.add(
ButtonSegment<String>(
value: "null",
icon: Icon(TablerIcons.minus, color: COLOR_GRAY_LIGHT),
),
);
}
buttons.add(
ButtonSegment<String>(
value: "false",
icon: Icon(TablerIcons.x, color: COLOR_DANGER),
),
);
buttons.add(
ButtonSegment<String>(
value: "true",
icon: Icon(TablerIcons.check, color: COLOR_SUCCESS),
),
);
return ListTile(
title: Text(label),
subtitle: Text(helpText),
contentPadding: EdgeInsets.zero,
trailing: SegmentedButton<String>(
segments: buttons,
selected: {initial_value},
showSelectedIcon: false,
multiSelectionEnabled: false,
style: SegmentedButton.styleFrom(
padding: EdgeInsets.all(0),
// minimumSize: MaterialStateProperty.all(Size(0, 0)),
// tapTargetSize: MaterialTapTargetSize.shrinkWrap,
visualDensity: VisualDensity.compact,
),
onSelectionChanged: (Set<String> selection) {
String element = selection.first;
if (element == "null" && allow_null) {
setFieldValue(null);
} else if (element == "true") {
setFieldValue(true);
} else {
setFieldValue(false);
}
},
),
); );
} }
@ -1168,7 +1254,9 @@ class APIFormWidgetState extends State<APIFormWidget> {
// Callback for when a field value is changed // Callback for when a field value is changed
// Default implementation does nothing, // Default implementation does nothing,
// but custom form implementations may override this function // but custom form implementations may override this function
void onValueChanged(String field, dynamic value) {} void onValueChanged(String field, dynamic value) {
setState(() {});
}
Future<void> handleSuccess( Future<void> handleSuccess(
Map<String, dynamic> submittedData, Map<String, dynamic> submittedData,
@ -1394,7 +1482,7 @@ class APIFormWidgetState extends State<APIFormWidget> {
if (field.isSimple) { if (field.isSimple) {
// Simple top-level field data // Simple top-level field data
data[field.name] = field.data["value"]; data[field.name] = field.data["value"] ?? field.defaultValue;
} else { } else {
// Not so simple... (WHY DID I MAKE THE API SO COMPLEX?) // Not so simple... (WHY DID I MAKE THE API SO COMPLEX?)
if (field.parent.isNotEmpty) { if (field.parent.isNotEmpty) {

View file

@ -154,9 +154,12 @@ class POAllocateBarcodeHandler extends BarcodeHandler {
return onBarcodeUnknown(data); return onBarcodeUnknown(data);
} }
// Extract field data from the returned result
dynamic supplier_part = data["supplierpart"]; dynamic supplier_part = data["supplierpart"];
dynamic location = data["location"];
int supplier_part_pk = -1; int supplier_part_pk = -1;
int location_pk = -1;
if (supplier_part is Map<String, dynamic>) { if (supplier_part is Map<String, dynamic>) {
supplier_part_pk = (supplier_part["pk"] ?? -1) as int; supplier_part_pk = (supplier_part["pk"] ?? -1) as int;
@ -164,6 +167,10 @@ class POAllocateBarcodeHandler extends BarcodeHandler {
return onBarcodeUnknown(data); return onBarcodeUnknown(data);
} }
if (location is Map<String, dynamic>) {
location_pk = (location["pk"] ?? -1) as int;
}
// Dispose of the barcode scanner // Dispose of the barcode scanner
if (OneContext.hasContext) { if (OneContext.hasContext) {
OneContext().pop(); OneContext().pop();
@ -177,6 +184,10 @@ class POAllocateBarcodeHandler extends BarcodeHandler {
fields["part"]?["hidden"] = false; fields["part"]?["hidden"] = false;
fields["part"]?["value"] = supplier_part_pk; fields["part"]?["value"] = supplier_part_pk;
if (location_pk > 0) {
fields["location"]?["value"] = location_pk;
}
InvenTreePOLineItem().createForm( InvenTreePOLineItem().createForm(
context, context,
L10().lineItemAdd, L10().lineItemAdd,

View file

@ -270,9 +270,9 @@ class InvenTreeSalesOrderShipment extends InvenTreeModel {
InvenTreeSalesOrderShipment.fromJson(json); InvenTreeSalesOrderShipment.fromJson(json);
@override @override
String get URL => "/order/so/shipment/"; String get URL => "order/so/shipment/";
String get SHIP_SHIPMENT_URL => "/order/so/shipment/${pk}/ship/"; String get SHIP_SHIPMENT_URL => "order/so/shipment/${pk}/ship/";
@override @override
Future<Object?> goToDetailPage(BuildContext context) async { Future<Object?> goToDetailPage(BuildContext context) async {
@ -345,7 +345,7 @@ class InvenTreeSalesOrderAllocation extends InvenTreeModel {
InvenTreeSalesOrderAllocation.fromJson(json); InvenTreeSalesOrderAllocation.fromJson(json);
@override @override
String get URL => "/order/so-allocation/"; String get URL => "order/so-allocation/";
@override @override
List<String> get rolesRequired => ["sales_order"]; List<String> get rolesRequired => ["sales_order"];

View file

@ -294,6 +294,9 @@
"confirmScanDetail": "Confirm stock transfer details when scanning barcodes", "confirmScanDetail": "Confirm stock transfer details when scanning barcodes",
"@confirmScanDetail": {}, "@confirmScanDetail": {},
"connectionCheck": "Check Connection",
"@connectionCheck": {},
"connectionRefused": "Connection Refused", "connectionRefused": "Connection Refused",
"@connectionRefused": {}, "@connectionRefused": {},

View file

@ -7,7 +7,7 @@ import "package:inventree/l10.dart";
import "package:inventree/widget/progress.dart"; import "package:inventree/widget/progress.dart";
import "package:inventree/widget/snacks.dart"; import "package:inventree/widget/snacks.dart";
const String PRINT_LABEL_URL = "api/label/print/"; const String PRINT_LABEL_URL = "label/print/";
/* /*
* Custom form handler for label printing. * Custom form handler for label printing.
@ -45,6 +45,8 @@ class LabelFormWidgetState extends APIFormWidgetState {
if (field == "plugin") { if (field == "plugin") {
onPluginChanged(value.toString()); onPluginChanged(value.toString());
} }
super.onValueChanged(field, value);
} }
@override @override

View file

@ -298,6 +298,9 @@ class _ProfileEditState extends State<ProfileEditWidget> {
String name = ""; String name = "";
String server = ""; String server = "";
bool? serverStatus;
bool serverChecking = false;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@ -411,6 +414,49 @@ class _ProfileEditState extends State<ProfileEditWidget> {
return null; return null;
}, },
), ),
Divider(),
SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
label: Text(L10().connectionCheck),
icon: serverStatus == true
? Icon(TablerIcons.circle_check, color: COLOR_SUCCESS)
: serverStatus == false
? Icon(TablerIcons.circle_x, color: COLOR_DANGER)
: Icon(TablerIcons.question_mark, color: COLOR_WARNING),
onPressed: serverChecking
? null
: () async {
if (serverChecking) {
return;
}
if (!formKey.currentState!.validate()) {
return;
}
if (mounted) {
setState(() {
serverStatus = null;
serverChecking = true;
});
}
formKey.currentState!.save();
InvenTreeAPI().checkServer(server: server).then((
result,
) {
if (mounted) {
setState(() {
serverStatus = result;
serverChecking = false;
});
}
});
},
),
),
], ],
), ),
padding: EdgeInsets.all(16), padding: EdgeInsets.all(16),

View file

@ -92,9 +92,10 @@ abstract class PaginatedSearchState<T extends PaginatedSearchWidget>
// Skip null values // Skip null values
if (value == null) { if (value == null) {
continue; f[k] = "null";
} else {
f[k] = value.toString();
} }
f[k] = value.toString();
} }
return f; return f;
@ -206,7 +207,7 @@ abstract class PaginatedSearchState<T extends PaginatedSearchWidget>
} }
Map<String, dynamic> filter = { Map<String, dynamic> filter = {
"type": "boolean", "type": "boolean filter",
"display_name": label, "display_name": label,
"label": label, "label": label,
"help_text": help_text, "help_text": help_text,
@ -341,7 +342,16 @@ abstract class PaginatedSearchState<T extends PaginatedSearchWidget>
Map<String, String> f = await constructFilters(); Map<String, String> f = await constructFilters();
if (f.isNotEmpty) { if (f.isNotEmpty) {
params.addAll(f); for (String k in f.keys) {
// Remove any existing filter keys
dynamic value = f[k];
if (value == null || value == "null") {
params.remove(k);
} else {
params[k] = value.toString();
}
}
} }
final page = await requestPage(_pageSize, pageKey, params); final page = await requestPage(_pageSize, pageKey, params);

View file

@ -54,7 +54,7 @@ class _PaginatedPartCategoryListState
@override @override
Map<String, Map<String, dynamic>> get filterOptions => { Map<String, Map<String, dynamic>> get filterOptions => {
"cascade": { "cascade": {
"default": false, "default": true,
"label": L10().includeSubcategories, "label": L10().includeSubcategories,
"help_text": L10().includeSubcategoriesDetail, "help_text": L10().includeSubcategoriesDetail,
"tristate": false, "tristate": false,

View file

@ -66,6 +66,7 @@ class _PaginatedStockLocationListState
"label": L10().includeSublocations, "label": L10().includeSublocations,
"help_text": L10().includeSublocationsDetail, "help_text": L10().includeSublocationsDetail,
"tristate": false, "tristate": false,
"default": true,
}, },
}; };

View file

@ -225,7 +225,7 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
// Request part information // Request part information
part = await InvenTreePart().get(widget.item.partId) as InvenTreePart?; part = await InvenTreePart().get(widget.item.partId) as InvenTreePart?;
stockShowTests &= part?.isTrackable ?? false; stockShowTests &= part?.isTestable ?? false;
// Request default location // Request default location
int? defaultLocationId = part?.defaultLocation; int? defaultLocationId = part?.defaultLocation;

View file

@ -637,6 +637,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.5" version: "1.0.5"
jni:
dependency: transitive
description:
name: jni
sha256: d2c361082d554d4593c3012e26f6b188f902acd291330f13d6427641a92b3da1
url: "https://pub.dev"
source: hosted
version: "0.14.2"
js: js:
dependency: transitive dependency: transitive
description: description:
@ -737,10 +745,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: mobile_scanner name: mobile_scanner
sha256: "54005bdea7052d792d35b4fef0f84ec5ddc3a844b250ecd48dc192fb9b4ebc95" sha256: c92c26bf2231695b6d3477c8dcf435f51e28f87b1745966b1fe4c47a286171ce
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "7.0.1" version: "7.2.0"
node_preamble: node_preamble:
dependency: transitive dependency: transitive
description: description:
@ -921,18 +929,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: sentry name: sentry
sha256: "599701ca0693a74da361bc780b0752e1abc98226cf5095f6b069648116c896bb" sha256: "605ad1f6f1ae5b72018cbe8fc20f490fa3bd53e58882e5579566776030d8c8c1"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "8.14.2" version: "9.14.0"
sentry_flutter: sentry_flutter:
dependency: "direct main" dependency: "direct main"
description: description:
name: sentry_flutter name: sentry_flutter
sha256: "5ba2cf40646a77d113b37a07bd69f61bb3ec8a73cbabe5537b05a7c89d2656f8" sha256: "7fd0fb80050c1f6a77ae185bda997a76d384326d6777cf5137a6c38952c4ac7d"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "8.14.2" version: "9.14.0"
shared_preferences: shared_preferences:
dependency: transitive dependency: transitive
description: description:

View file

@ -1,7 +1,7 @@
name: inventree name: inventree
description: InvenTree stock management description: InvenTree stock management
version: 0.22.1+110 version: 0.22.7+116
environment: environment:
sdk: ^3.8.1 sdk: ^3.8.1
@ -28,19 +28,19 @@ dependencies:
flutter_markdown: ^0.6.19 # Rendering markdown flutter_markdown: ^0.6.19 # Rendering markdown
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.43.0 flutter_tabler_icons: ^1.43.0 # Tabler icons
http: ^1.4.0 http: ^1.4.0
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.20.2 intl: ^0.20.2
mobile_scanner: ^7.0.1 # Barcode scanning support mobile_scanner: ^7.2.0 # Barcode scanning support
one_context: ^4.0.0 # Dialogs without requiring context one_context: ^4.0.0 # Dialogs without requiring context
open_filex: ^4.7.0 # Open local files open_filex: ^4.7.0 # Open local files
package_info_plus: ^8.1.1 # App information introspection package_info_plus: ^8.1.1 # App information introspection
path: ^1.9.0 path: ^1.9.0
path_provider: ^2.1.5 # Local file storage path_provider: ^2.1.5 # Local file storage
sembast: ^3.6.0 # NoSQL data storage sembast: ^3.6.0 # NoSQL data storage
sentry_flutter: 8.14.2 # Error reporting sentry_flutter: ^9.14.0 # Error reporting
url_launcher: ^6.3.1 # Open link in system browser url_launcher: ^6.3.1 # Open link in system browser
wakelock_plus: ^1.3.2 # Prevent device from sleeping wakelock_plus: ^1.3.2 # Prevent device from sleeping