mirror of
				https://github.com/inventree/inventree-app.git
				synced 2025-10-31 05:15:42 +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:
		| @@ -5,15 +5,20 @@ import "package:font_awesome_flutter/font_awesome_flutter.dart"; | ||||
| import "package:inventree/api.dart"; | ||||
| import "package:inventree/app_colors.dart"; | ||||
| import "package:inventree/barcode.dart"; | ||||
| import "package:inventree/l10.dart"; | ||||
|  | ||||
| import "package:inventree/inventree/stock.dart"; | ||||
| import "package:inventree/widget/progress.dart"; | ||||
|  | ||||
| import "package:inventree/widget/location_list.dart"; | ||||
| import "package:inventree/widget/refreshable_state.dart"; | ||||
| import "package:inventree/widget/snacks.dart"; | ||||
| import "package:inventree/widget/stock_detail.dart"; | ||||
| import "package:inventree/l10.dart"; | ||||
| import "package:inventree/widget/stock_list.dart"; | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Widget for displaying detail view for a single StockLocation instance | ||||
|  */ | ||||
| class LocationDisplayWidget extends StatefulWidget { | ||||
|  | ||||
|   LocationDisplayWidget(this.location, {Key? key}) : super(key: key); | ||||
| @@ -32,6 +37,8 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> { | ||||
|  | ||||
|   final InvenTreeStockLocation? location; | ||||
|  | ||||
|   bool showFilterOptions = false; | ||||
|  | ||||
|   @override | ||||
|   String getAppBarTitle(BuildContext context) { return L10().stockLocation; } | ||||
|  | ||||
| @@ -103,19 +110,6 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> { | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   List<InvenTreeStockLocation> _sublocations = []; | ||||
|  | ||||
|   String _locationFilter = ""; | ||||
|  | ||||
|   List<InvenTreeStockLocation> get sublocations { | ||||
|      | ||||
|     if (_locationFilter.isEmpty || _sublocations.isEmpty) { | ||||
|       return _sublocations; | ||||
|     } else { | ||||
|       return _sublocations.where((loc) => loc.filter(_locationFilter)).toList(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Future<void> onBuild(BuildContext context) async { | ||||
|     refresh(context); | ||||
| @@ -124,8 +118,6 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> { | ||||
|   @override | ||||
|   Future<void> request(BuildContext context) async { | ||||
|  | ||||
|     int pk = location?.pk ?? -1; | ||||
|  | ||||
|     // Reload location information | ||||
|     if (location != null) { | ||||
|       final bool result = await location!.reload(); | ||||
| @@ -135,17 +127,6 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> { | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     // Request a list of sub-locations under this one | ||||
|     await InvenTreeStockLocation().list(filters: {"parent": "$pk"}).then((var locs) { | ||||
|       _sublocations.clear(); | ||||
|  | ||||
|       for (var loc in locs) { | ||||
|         if (loc is InvenTreeStockLocation) { | ||||
|           _sublocations.add(loc); | ||||
|         } | ||||
|       } | ||||
|     }); | ||||
|  | ||||
|     setState(() {}); | ||||
|   } | ||||
|  | ||||
| @@ -214,7 +195,11 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> { | ||||
|     if (location == null) { | ||||
|       return Card( | ||||
|         child: ListTile( | ||||
|           title: Text(L10().stockTopLevel), | ||||
|           title: Text( | ||||
|             L10().stockTopLevel, | ||||
|             style: TextStyle(fontStyle: FontStyle.italic) | ||||
|           ), | ||||
|           leading: FaIcon(FontAwesomeIcons.boxes), | ||||
|         ) | ||||
|       ); | ||||
|     } else { | ||||
| @@ -223,7 +208,7 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> { | ||||
|         ListTile( | ||||
|           title: Text("${location!.name}"), | ||||
|           subtitle: Text("${location!.description}"), | ||||
|           trailing: Text("${location!.itemcount}"), | ||||
|           leading: FaIcon(FontAwesomeIcons.boxes), | ||||
|         ), | ||||
|       ]; | ||||
|  | ||||
| @@ -286,20 +271,15 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> { | ||||
|  | ||||
|   Widget getSelectedWidget(int index) { | ||||
|  | ||||
|     // Construct filters for paginated stock list | ||||
|     Map<String, String> filters = {}; | ||||
|  | ||||
|     if (location != null) { | ||||
|       filters["location"] = "${location!.pk}"; | ||||
|     } | ||||
|  | ||||
|     switch (index) { | ||||
|       case 0: | ||||
|         return ListView( | ||||
|         return Column( | ||||
|           children: detailTiles(), | ||||
|         ); | ||||
|       case 1: | ||||
|         return PaginatedStockItemList(filters); | ||||
|         return Column( | ||||
|           children: stockTiles(), | ||||
|         ); | ||||
|       case 2: | ||||
|         return ListView( | ||||
|           children: ListTile.divideTiles( | ||||
| @@ -317,8 +297,8 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> { | ||||
|     return getSelectedWidget(tabIndex); | ||||
|   } | ||||
|  | ||||
|  | ||||
| List<Widget> detailTiles() { | ||||
|   // Construct the "details" panel | ||||
|   List<Widget> detailTiles() { | ||||
|     List<Widget> tiles = [ | ||||
|       locationDescriptionCard(), | ||||
|       ListTile( | ||||
| @@ -326,27 +306,61 @@ List<Widget> detailTiles() { | ||||
|           L10().sublocations, | ||||
|           style: TextStyle(fontWeight: FontWeight.bold), | ||||
|         ), | ||||
|         trailing: sublocations.isNotEmpty ? Text("${sublocations.length}") : null, | ||||
|       ), | ||||
|     ]; | ||||
|  | ||||
|     if (loading) { | ||||
|       tiles.add(progressIndicator()); | ||||
|     } else if (_sublocations.isNotEmpty) { | ||||
|       tiles.add(SublocationList(_sublocations)); | ||||
|     } else { | ||||
|       tiles.add(ListTile( | ||||
|         title: Text(L10().sublocationNone), | ||||
|         subtitle: Text( | ||||
|             L10().sublocationNoneDetail, | ||||
|             style: TextStyle(fontStyle: FontStyle.italic) | ||||
|         trailing: GestureDetector( | ||||
|           child: FaIcon(FontAwesomeIcons.filter), | ||||
|           onTap: () async { | ||||
|             setState(() { | ||||
|               showFilterOptions = !showFilterOptions; | ||||
|             }); | ||||
|           }, | ||||
|         ) | ||||
|       )); | ||||
|     } | ||||
|       ), | ||||
|       Expanded( | ||||
|         child: PaginatedStockLocationList( | ||||
|           { | ||||
|             "parent": location?.pk.toString() ?? "null", | ||||
|           }, | ||||
|           showFilterOptions, | ||||
|         ), | ||||
|         flex: 10, | ||||
|       ) | ||||
|     ]; | ||||
|  | ||||
|     return tiles; | ||||
|   } | ||||
|  | ||||
|   // Construct the "stock" panel | ||||
|   List<Widget> stockTiles() { | ||||
|  | ||||
|     Map<String, String> filters = { | ||||
|       "location": location?.pk.toString() ?? "null", | ||||
|     }; | ||||
|  | ||||
|     return [ | ||||
|       locationDescriptionCard(includeActions: false), | ||||
|       ListTile( | ||||
|         title: Text( | ||||
|           L10().stock, | ||||
|           style: TextStyle(fontWeight: FontWeight.bold), | ||||
|         ), | ||||
|         trailing: GestureDetector( | ||||
|           child: FaIcon(FontAwesomeIcons.filter), | ||||
|           onTap: () async { | ||||
|             setState(() { | ||||
|               showFilterOptions = !showFilterOptions; | ||||
|             }); | ||||
|           }, | ||||
|         ), | ||||
|       ), | ||||
|       Expanded( | ||||
|         child: PaginatedStockItemList( | ||||
|           filters, | ||||
|           showFilterOptions, | ||||
|         ), | ||||
|         flex: 10, | ||||
|       ) | ||||
|     ]; | ||||
|   } | ||||
|  | ||||
|   List<Widget> actionTiles() { | ||||
|     List<Widget> tiles = []; | ||||
| @@ -452,48 +466,4 @@ List<Widget> detailTiles() { | ||||
|  | ||||
|     return tiles; | ||||
|   } | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| class SublocationList extends StatelessWidget { | ||||
|  | ||||
|   const SublocationList(this._locations); | ||||
|  | ||||
|   final List<InvenTreeStockLocation> _locations; | ||||
|  | ||||
|   void _openLocation(BuildContext context, int pk) { | ||||
|  | ||||
|     InvenTreeStockLocation().get(pk).then((var loc) { | ||||
|       if (loc is InvenTreeStockLocation) { | ||||
|  | ||||
|         Navigator.push(context, MaterialPageRoute(builder: (context) => LocationDisplayWidget(loc))); | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   Widget _build(BuildContext context, int index) { | ||||
|     InvenTreeStockLocation loc = _locations[index]; | ||||
|  | ||||
|     return ListTile( | ||||
|       title: Text("${loc.name}"), | ||||
|       subtitle: Text("${loc.description}"), | ||||
|       trailing: Text("${loc.itemcount}"), | ||||
|       onTap: () { | ||||
|         _openLocation(context, loc.pk); | ||||
|       }, | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return ListView.separated( | ||||
|         shrinkWrap: true, | ||||
|         physics: ClampingScrollPhysics(), | ||||
|         itemBuilder: _build, | ||||
|         separatorBuilder: (_, __) => const Divider(height: 3), | ||||
|         itemCount: _locations.length | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user