mirror of
https://github.com/inventree/inventree-app.git
synced 2025-04-29 14:06:47 +00:00
Implementing a generic "ordering" option configuration for paginated list widget
This commit is contained in:
parent
9c4f6710ff
commit
c878f37ec2
@ -1,8 +1,9 @@
|
|||||||
|
|
||||||
|
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
|
import "package:font_awesome_flutter/font_awesome_flutter.dart";
|
||||||
|
|
||||||
import "package:inventree/api.dart";
|
import "package:inventree/api.dart";
|
||||||
|
import "package:inventree/api_form.dart";
|
||||||
import "package:inventree/helpers.dart";
|
import "package:inventree/helpers.dart";
|
||||||
import "package:inventree/inventree/bom.dart";
|
import "package:inventree/inventree/bom.dart";
|
||||||
import "package:inventree/l10.dart";
|
import "package:inventree/l10.dart";
|
||||||
@ -15,6 +16,7 @@ import "package:inventree/widget/part_detail.dart";
|
|||||||
import "package:inventree/widget/refreshable_state.dart";
|
import "package:inventree/widget/refreshable_state.dart";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Widget for displaying a list of BomItems for the specified 'parent' Part instance
|
* Widget for displaying a list of BomItems for the specified 'parent' Part instance
|
||||||
*/
|
*/
|
||||||
@ -30,7 +32,7 @@ class BomList extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class _BomListState extends RefreshableState<BomList> {
|
class _BomListState extends PaginatedState<BomList> {
|
||||||
|
|
||||||
_BomListState(this.parent);
|
_BomListState(this.parent);
|
||||||
|
|
||||||
@ -39,6 +41,15 @@ class _BomListState extends RefreshableState<BomList> {
|
|||||||
@override
|
@override
|
||||||
String getAppBarTitle(BuildContext context) => L10().billOfMaterials;
|
String getAppBarTitle(BuildContext context) => L10().billOfMaterials;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get prefix => "bom_";
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, String> get orderingOptions => {
|
||||||
|
"quantity": L10().quantity,
|
||||||
|
"part": L10().part,
|
||||||
|
};
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget getBody(BuildContext context) {
|
Widget getBody(BuildContext context) {
|
||||||
return PaginatedBomList({
|
return PaginatedBomList({
|
||||||
|
@ -3,9 +3,129 @@ import "package:flutter/material.dart";
|
|||||||
import "package:font_awesome_flutter/font_awesome_flutter.dart";
|
import "package:font_awesome_flutter/font_awesome_flutter.dart";
|
||||||
import "package:infinite_scroll_pagination/infinite_scroll_pagination.dart";
|
import "package:infinite_scroll_pagination/infinite_scroll_pagination.dart";
|
||||||
|
|
||||||
|
import "package:inventree/api_form.dart";
|
||||||
|
import "package:inventree/l10.dart";
|
||||||
|
|
||||||
import "package:inventree/inventree/model.dart";
|
import "package:inventree/inventree/model.dart";
|
||||||
import "package:inventree/inventree/sentry.dart";
|
import "package:inventree/inventree/sentry.dart";
|
||||||
import "package:inventree/l10.dart";
|
import "package:inventree/preferences.dart";
|
||||||
|
|
||||||
|
import "package:inventree/widget/refreshable_state.dart";
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generic widget class for displaying a "paginated list".
|
||||||
|
* Provides some basic functionality for adjusting ordering and filtering options
|
||||||
|
*/
|
||||||
|
abstract class PaginatedState<T extends StatefulWidget> extends RefreshableState<T> {
|
||||||
|
|
||||||
|
// Prefix for storing and loading pagination options
|
||||||
|
String get prefix => "prefix_";
|
||||||
|
|
||||||
|
// Ordering options for this paginated state (override in implementing class)
|
||||||
|
Map<String, String> get orderingOptions => {};
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Widget> getAppBarActions(BuildContext context) {
|
||||||
|
List<Widget> actions = [];
|
||||||
|
|
||||||
|
// If ordering options have been provided
|
||||||
|
if (orderingOptions.isNotEmpty) {
|
||||||
|
actions.add(IconButton(
|
||||||
|
icon: FaIcon(FontAwesomeIcons.sort),
|
||||||
|
onPressed: () => _updateFilters(context),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the selected ordering "field" for this list widget
|
||||||
|
Future<String> orderingField() async {
|
||||||
|
dynamic field = await InvenTreeSettingsManager().getValue("${prefix}ordering_field", null);
|
||||||
|
|
||||||
|
if (field != null) {
|
||||||
|
return field.toString();
|
||||||
|
} else if (orderingOptions.isNotEmpty) {
|
||||||
|
// By default, return the first specified key
|
||||||
|
return orderingOptions.keys.first;
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the selected ordering "order" ("+" or "-") for this list widget
|
||||||
|
Future<String> orderingOrder() async {
|
||||||
|
dynamic order = await InvenTreeSettingsManager().getValue("${prefix}ordering_order", "+");
|
||||||
|
|
||||||
|
return order == "+" ? "+" : "-";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the (configurable) filters for this paginated list
|
||||||
|
Future<void> _updateFilters(BuildContext context) async {
|
||||||
|
|
||||||
|
// Retrieve stored setting
|
||||||
|
dynamic _field = await orderingField();
|
||||||
|
dynamic _order = await orderingOrder();
|
||||||
|
|
||||||
|
// Construct the 'ordering' options
|
||||||
|
List<Map<String, dynamic>> _opts = [];
|
||||||
|
|
||||||
|
orderingOptions.forEach((k, v) => _opts.add({
|
||||||
|
"value": k.toString(),
|
||||||
|
"display_name": v.toString()
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (_field == null && _opts.isNotEmpty) {
|
||||||
|
_field = _opts.first["value"];
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> fields = {
|
||||||
|
"ordering_field": {
|
||||||
|
"type": "choice",
|
||||||
|
"label": "Ordering Field",
|
||||||
|
"required": true,
|
||||||
|
"choices": _opts,
|
||||||
|
"value": _field,
|
||||||
|
},
|
||||||
|
"ordering_order": {
|
||||||
|
"type": "choice",
|
||||||
|
"label": "Ordering Direction",
|
||||||
|
"required": true,
|
||||||
|
"value": _order,
|
||||||
|
"choices": [
|
||||||
|
{
|
||||||
|
"value": "+",
|
||||||
|
"display_name": "Ascending",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "-",
|
||||||
|
"display_name": "Descending",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
launchApiForm(
|
||||||
|
context,
|
||||||
|
"...filtering...",
|
||||||
|
"",
|
||||||
|
fields,
|
||||||
|
icon: FontAwesomeIcons.checkCircle,
|
||||||
|
onSuccess: (Map<String, dynamic> data) async {
|
||||||
|
|
||||||
|
// Extract data from the processed form
|
||||||
|
String f = (data["ordering_field"] ?? _field) as String;
|
||||||
|
String o = (data["ordering_order"] ?? _order) as String;
|
||||||
|
|
||||||
|
// Save values to settings
|
||||||
|
await InvenTreeSettingsManager().setValue("${prefix}ordering_field", f);
|
||||||
|
await InvenTreeSettingsManager().setValue("${prefix}ordering_order", o);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class PaginatedSearchState<T extends StatefulWidget> extends State<T> {
|
class PaginatedSearchState<T extends StatefulWidget> extends State<T> {
|
||||||
@ -21,6 +141,10 @@ class PaginatedSearchState<T extends StatefulWidget> extends State<T> {
|
|||||||
|
|
||||||
int resultCount = 0;
|
int resultCount = 0;
|
||||||
|
|
||||||
|
// List of variables by which the list can be "ordered".
|
||||||
|
// Override in any implementing sub-class
|
||||||
|
List<String> orderingFilters = [];
|
||||||
|
|
||||||
// Text controller
|
// Text controller
|
||||||
final TextEditingController searchController = TextEditingController();
|
final TextEditingController searchController = TextEditingController();
|
||||||
|
|
||||||
|
12
pubspec.yaml
12
pubspec.yaml
@ -10,11 +10,11 @@ dependencies:
|
|||||||
|
|
||||||
audioplayers: ^0.20.1 # Play audio files
|
audioplayers: ^0.20.1 # Play audio files
|
||||||
cached_network_image: ^3.2.0 # Download and cache remote images
|
cached_network_image: ^3.2.0 # Download and cache remote images
|
||||||
camera: ^0.9.4 # Camera
|
camera: ^0.9.4 # Camera
|
||||||
cupertino_icons: ^1.0.3
|
cupertino_icons: ^1.0.3
|
||||||
datetime_picker_formfield: ^2.0.0 # Date / time picker
|
datetime_picker_formfield: ^2.0.0 # Date / time picker
|
||||||
device_info_plus: ^3.2.2 # Information about the device
|
device_info_plus: ^3.2.2 # Information about the device
|
||||||
dropdown_search: ^0.6.3 # Dropdown autocomplete form fields
|
dropdown_search: ^0.6.3 # Dropdown autocomplete form fields
|
||||||
file_picker: ^4.5.1 # Select files from the device
|
file_picker: ^4.5.1 # Select files from the device
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
@ -28,14 +28,14 @@ dependencies:
|
|||||||
infinite_scroll_pagination: ^3.1.0 # Let the server do all the work!
|
infinite_scroll_pagination: ^3.1.0 # Let the server do all the work!
|
||||||
intl: ^0.17.0
|
intl: ^0.17.0
|
||||||
one_context: ^1.1.0 # Dialogs without requiring context
|
one_context: ^1.1.0 # Dialogs without requiring context
|
||||||
open_file: ^3.2.1 # Open local files
|
open_file: ^3.2.1 # Open local files
|
||||||
package_info_plus: ^1.0.4 # App information introspection
|
package_info_plus: ^1.0.4 # App information introspection
|
||||||
path: ^1.8.0
|
path: ^1.8.0
|
||||||
path_provider: ^2.0.2 # Local file storage
|
path_provider: ^2.0.2 # Local file storage
|
||||||
qr_code_scanner: ^0.7.0 # Barcode scanning
|
qr_code_scanner: ^0.7.0 # Barcode scanning
|
||||||
sembast: ^3.1.0+2 # NoSQL data storage
|
sembast: ^3.1.0+2 # NoSQL data storage
|
||||||
sentry_flutter: ^6.4.0 # Error reporting
|
sentry_flutter: ^6.4.0 # Error reporting
|
||||||
url_launcher: ^6.0.9 # Open link in system browser
|
url_launcher: ^6.0.9 # Open link in system browser
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_launcher_icons: ^0.9.0
|
flutter_launcher_icons: ^0.9.0
|
||||||
|
Loading…
x
Reference in New Issue
Block a user