mirror of
https://github.com/inventree/inventree-app.git
synced 2025-06-16 04:05:28 +00:00
List refactor (#179)
* Catch paginator bug if widget is disposed before request returns * Refactoring paginated query widget - Add option to enable / disable search filters * Major refactor of paginated search widget - Learned something new.. a state can access widget.<attribute> - THIS CHANGES EVERTHING * Preferences: Add code for tri-state values - Also improve unit testing for preferences code * Allow boolean form fields to be optionally tristate * paginator: Allow custom boolean filters * Remove outdated filtering preferences * Refactor filter options - Allow specification of more detailed options * Add custom filters for "part" list * filter tweaks * Remove legacy "SublocationList" widget * Add filtering option for locationlist * Updates for stock location widget * Refactor category display widget * More widget refactoring * Update main search widget * Fix unit tests * Improve filtering on BOM display page
This commit is contained in:
@ -1,14 +1,15 @@
|
||||
import "package:flutter/material.dart";
|
||||
|
||||
import "package:font_awesome_flutter/font_awesome_flutter.dart";
|
||||
|
||||
import "package:inventree/api.dart";
|
||||
import "package:inventree/app_colors.dart";
|
||||
import "package:inventree/inventree/part.dart";
|
||||
import "package:inventree/widget/part_list.dart";
|
||||
import "package:inventree/widget/progress.dart";
|
||||
import "package:inventree/widget/snacks.dart";
|
||||
import "package:inventree/l10.dart";
|
||||
|
||||
import "package:inventree/inventree/part.dart";
|
||||
|
||||
import "package:inventree/widget/category_list.dart";
|
||||
import "package:inventree/widget/part_list.dart";
|
||||
import "package:inventree/widget/snacks.dart";
|
||||
import "package:inventree/widget/part_detail.dart";
|
||||
import "package:inventree/widget/refreshable_state.dart";
|
||||
|
||||
@ -28,6 +29,8 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
|
||||
|
||||
_CategoryDisplayState(this.category);
|
||||
|
||||
bool showFilterOptions = false;
|
||||
|
||||
@override
|
||||
String getAppBarTitle(BuildContext context) => L10().partCategory;
|
||||
|
||||
@ -73,8 +76,6 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
|
||||
// The local InvenTreePartCategory object
|
||||
final InvenTreePartCategory? category;
|
||||
|
||||
List<InvenTreePartCategory> _subcategories = [];
|
||||
|
||||
@override
|
||||
Future<void> onBuild(BuildContext context) async {
|
||||
refresh(context);
|
||||
@ -83,8 +84,6 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
|
||||
@override
|
||||
Future<void> request(BuildContext context) async {
|
||||
|
||||
int pk = category?.pk ?? -1;
|
||||
|
||||
// Update the category
|
||||
if (category != null) {
|
||||
final bool result = await category?.reload() ?? false;
|
||||
@ -93,27 +92,17 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
}
|
||||
|
||||
// Request a list of sub-categories under this one
|
||||
await InvenTreePartCategory().list(filters: {"parent": "$pk"}).then((var cats) {
|
||||
_subcategories.clear();
|
||||
|
||||
for (var cat in cats) {
|
||||
if (cat is InvenTreePartCategory) {
|
||||
_subcategories.add(cat);
|
||||
}
|
||||
}
|
||||
|
||||
// Update state
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
|
||||
Widget getCategoryDescriptionCard({bool extra = true}) {
|
||||
if (category == null) {
|
||||
return Card(
|
||||
child: ListTile(
|
||||
title: Text(L10().partCategoryTopLevel)
|
||||
leading: FaIcon(FontAwesomeIcons.shapes),
|
||||
title: Text(
|
||||
L10().partCategoryTopLevel,
|
||||
style: TextStyle(fontStyle: FontStyle.italic),
|
||||
)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
@ -182,7 +171,9 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
|
||||
);
|
||||
}
|
||||
|
||||
// Construct the "details" panel
|
||||
List<Widget> detailTiles() {
|
||||
|
||||
List<Widget> tiles = <Widget>[
|
||||
getCategoryDescriptionCard(),
|
||||
ListTile(
|
||||
@ -190,27 +181,62 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
|
||||
L10().subcategories,
|
||||
style: TextStyle(fontWeight: FontWeight.bold)
|
||||
),
|
||||
trailing: _subcategories.isNotEmpty ? Text("${_subcategories.length}") : null,
|
||||
trailing: GestureDetector(
|
||||
child: FaIcon(FontAwesomeIcons.filter),
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
showFilterOptions = !showFilterOptions;
|
||||
});
|
||||
},
|
||||
)
|
||||
),
|
||||
Expanded(
|
||||
child: PaginatedPartCategoryList(
|
||||
{
|
||||
"parent": category?.pk.toString() ?? "null"
|
||||
},
|
||||
showFilterOptions,
|
||||
),
|
||||
flex: 10,
|
||||
)
|
||||
];
|
||||
|
||||
if (loading) {
|
||||
tiles.add(progressIndicator());
|
||||
} else if (_subcategories.isEmpty) {
|
||||
tiles.add(ListTile(
|
||||
title: Text(L10().noSubcategories),
|
||||
subtitle: Text(
|
||||
L10().noSubcategoriesAvailable,
|
||||
style: TextStyle(fontStyle: FontStyle.italic)
|
||||
)
|
||||
));
|
||||
} else {
|
||||
tiles.add(SubcategoryList(_subcategories));
|
||||
}
|
||||
|
||||
return tiles;
|
||||
}
|
||||
|
||||
// Construct the "parts" panel
|
||||
List<Widget> partsTiles() {
|
||||
|
||||
Map<String, String> filters = {
|
||||
"category": category?.pk.toString() ?? "null",
|
||||
};
|
||||
|
||||
return [
|
||||
getCategoryDescriptionCard(extra: false),
|
||||
ListTile(
|
||||
title: Text(
|
||||
L10().parts,
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
trailing: GestureDetector(
|
||||
child: FaIcon(FontAwesomeIcons.filter),
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
showFilterOptions = !showFilterOptions;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: PaginatedPartList(
|
||||
filters,
|
||||
showFilterOptions,
|
||||
),
|
||||
flex: 10,
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
Future<void> _newCategory(BuildContext context) async {
|
||||
|
||||
int pk = category?.pk ?? -1;
|
||||
@ -323,14 +349,12 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
|
||||
|
||||
switch (tabIndex) {
|
||||
case 0:
|
||||
return ListView(
|
||||
children: detailTiles()
|
||||
return Column(
|
||||
children: detailTiles()
|
||||
);
|
||||
case 1:
|
||||
return PaginatedPartList(
|
||||
{
|
||||
"category": "${category?.pk ?? 'null'}"
|
||||
},
|
||||
return Column(
|
||||
children: partsTiles()
|
||||
);
|
||||
case 2:
|
||||
return ListView(
|
||||
@ -341,47 +365,3 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Builder for displaying a list of PartCategory objects
|
||||
*/
|
||||
class SubcategoryList extends StatelessWidget {
|
||||
|
||||
const SubcategoryList(this._categories);
|
||||
|
||||
final List<InvenTreePartCategory> _categories;
|
||||
|
||||
void _openCategory(BuildContext context, int pk) {
|
||||
|
||||
// Attempt to load the sub-category.
|
||||
InvenTreePartCategory().get(pk).then((var cat) {
|
||||
if (cat is InvenTreePartCategory) {
|
||||
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => CategoryDisplayWidget(cat)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Widget _build(BuildContext context, int index) {
|
||||
InvenTreePartCategory cat = _categories[index];
|
||||
|
||||
return ListTile(
|
||||
title: Text("${cat.name}"),
|
||||
subtitle: Text("${cat.description}"),
|
||||
trailing: Text("${cat.partcount}"),
|
||||
onTap: () {
|
||||
_openCategory(context, cat.pk);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: ClampingScrollPhysics(),
|
||||
separatorBuilder: (_, __) => const Divider(height: 3),
|
||||
itemBuilder: _build, itemCount: _categories.length);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user