From 6dad1f2b2537141a28aeefca4c6769311e58d0b5 Mon Sep 17 00:00:00 2001 From: Oliver Date: Mon, 4 Oct 2021 00:20:06 +1100 Subject: [PATCH] Refactor part list --- lib/widget/category_display.dart | 169 +------------------------- lib/widget/part_list.dart | 201 +++++++++++++++++++++++++++++++ lib/widget/search.dart | 14 ++- 3 files changed, 214 insertions(+), 170 deletions(-) create mode 100644 lib/widget/part_list.dart diff --git a/lib/widget/category_display.dart b/lib/widget/category_display.dart index 3fa0d7d5..86856156 100644 --- a/lib/widget/category_display.dart +++ b/lib/widget/category_display.dart @@ -4,6 +4,7 @@ import "package:inventree/app_colors.dart"; import "package:inventree/app_settings.dart"; import "package:inventree/inventree/part.dart"; import "package:inventree/inventree/sentry.dart"; +import 'package:inventree/widget/part_list.dart'; import "package:inventree/widget/progress.dart"; import "package:inventree/l10.dart"; @@ -387,171 +388,3 @@ class SubcategoryList extends StatelessWidget { itemBuilder: _build, itemCount: _categories.length); } } - - -/* - * Widget for displaying a list of Part objects within a PartCategory display. - * - * Uses server-side pagination for snappy results - */ - -class PaginatedPartList extends StatefulWidget { - - const PaginatedPartList(this.filters, {this.onTotalChanged}); - - final Map filters; - - final Function(int)? onTotalChanged; - - @override - _PaginatedPartListState createState() => _PaginatedPartListState(filters, onTotalChanged); -} - - -class _PaginatedPartListState extends State { - - _PaginatedPartListState(this.filters, this.onTotalChanged); - - static const _pageSize = 25; - - String _searchTerm = ""; - - Function(int)? onTotalChanged; - - final Map filters; - - final PagingController _pagingController = PagingController(firstPageKey: 0); - - @override - void initState() { - _pagingController.addPageRequestListener((pageKey) { - _fetchPage(pageKey); - }); - - super.initState(); - } - - @override - void dispose() { - _pagingController.dispose(); - super.dispose(); - } - - int resultCount = 0; - - Future _fetchPage(int pageKey) async { - try { - - Map params = filters; - - params["search"] = _searchTerm; - - final bool cascade = await InvenTreeSettingsManager().getBool("partSubcategory", true); - - params["cascade"] = "${cascade}"; - - final page = await InvenTreePart().listPaginated(_pageSize, pageKey, filters: params); - int pageLength = page?.length ?? 0; - int pageCount = page?.count ?? 0; - - final isLastPage = pageLength < _pageSize; - - // Construct a list of part objects - List parts = []; - - if (page != null) { - for (var result in page.results) { - if (result is InvenTreePart) { - parts.add(result); - } - } - } - - if (isLastPage) { - _pagingController.appendLastPage(parts); - } else { - final int nextPageKey = pageKey + pageLength; - _pagingController.appendPage(parts, nextPageKey); - } - - if (onTotalChanged != null) { - onTotalChanged!(pageCount); - } - - setState(() { - resultCount = pageCount; - }); - - } catch (error, stackTrace) { - print("Error! - ${error.toString()}"); - _pagingController.error = error; - - sentryReportError(error, stackTrace); - } - } - - void _openPart(BuildContext context, int pk) { - // Attempt to load the part information - InvenTreePart().get(pk).then((var part) { - if (part is InvenTreePart) { - - Navigator.push(context, MaterialPageRoute(builder: (context) => PartDetailWidget(part))); - } - }); - } - - Widget _buildPart(BuildContext context, InvenTreePart part) { - return ListTile( - title: Text(part.fullname), - subtitle: Text("${part.description}"), - trailing: Text("${part.inStockString}"), - leading: InvenTreeAPI().getImage( - part.thumbnail, - width: 40, - height: 40, - ), - onTap: () { - _openPart(context, part.pk); - }, - ); - } - - final TextEditingController searchController = TextEditingController(); - - void updateSearchTerm() { - - _searchTerm = searchController.text; - _pagingController.refresh(); - } - - @override - Widget build(BuildContext context) { - return Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - PaginatedSearchWidget(searchController, updateSearchTerm, resultCount), - Expanded( - child: CustomScrollView( - shrinkWrap: true, - physics: ClampingScrollPhysics(), - scrollDirection: Axis.vertical, - slivers: [ - PagedSliverList.separated( - pagingController: _pagingController, - builderDelegate: PagedChildBuilderDelegate( - itemBuilder: (context, item, index) { - return _buildPart(context, item); - }, - noItemsFoundIndicatorBuilder: (context) { - return NoResultsWidget(L10().partNoResults); - }, - ), - separatorBuilder: (context, index) => const Divider(height: 1), - ) - ], - ) - ) - ], - ); - } -} diff --git a/lib/widget/part_list.dart b/lib/widget/part_list.dart new file mode 100644 index 00000000..88725877 --- /dev/null +++ b/lib/widget/part_list.dart @@ -0,0 +1,201 @@ +import 'package:flutter/material.dart'; +import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; +import 'package:inventree/inventree/part.dart'; +import 'package:inventree/inventree/sentry.dart'; +import 'package:inventree/widget/paginator.dart'; +import 'package:inventree/widget/part_detail.dart'; +import 'package:inventree/widget/refreshable_state.dart'; + +import '../api.dart'; +import '../app_settings.dart'; +import '../l10.dart'; + + +class PartList extends StatefulWidget { + + const PartList(this.filters); + + final Map filters; + + @override + _PartListState createState() => _PartListState(filters); +} + + +class _PartListState extends RefreshableState { + + _PartListState(this.filters); + + final Map filters; + + @override + String getAppBarTitle(BuildContext context) => L10().parts; + + @override + Widget getBody(BuildContext context) { + return PaginatedPartList(filters); + } + +} + + +class PaginatedPartList extends StatefulWidget { + + const PaginatedPartList(this.filters, {this.onTotalChanged}); + + final Map filters; + + final Function(int)? onTotalChanged; + + @override + _PaginatedPartListState createState() => _PaginatedPartListState(filters, onTotalChanged); +} + + +class _PaginatedPartListState extends State { + + _PaginatedPartListState(this.filters, this.onTotalChanged); + + static const _pageSize = 25; + + String _searchTerm = ""; + + Function(int)? onTotalChanged; + + final Map filters; + + final PagingController _pagingController = PagingController(firstPageKey: 0); + + @override + void initState() { + _pagingController.addPageRequestListener((pageKey) { + _fetchPage(pageKey); + }); + + super.initState(); + } + + @override + void dispose() { + _pagingController.dispose(); + super.dispose(); + } + + int resultCount = 0; + + Future _fetchPage(int pageKey) async { + try { + + Map params = filters; + + params["search"] = _searchTerm; + + final bool cascade = await InvenTreeSettingsManager().getBool("partSubcategory", true); + + params["cascade"] = "${cascade}"; + + final page = await InvenTreePart().listPaginated(_pageSize, pageKey, filters: params); + int pageLength = page?.length ?? 0; + int pageCount = page?.count ?? 0; + + final isLastPage = pageLength < _pageSize; + + // Construct a list of part objects + List parts = []; + + if (page != null) { + for (var result in page.results) { + if (result is InvenTreePart) { + parts.add(result); + } + } + } + + if (isLastPage) { + _pagingController.appendLastPage(parts); + } else { + final int nextPageKey = pageKey + pageLength; + _pagingController.appendPage(parts, nextPageKey); + } + + if (onTotalChanged != null) { + onTotalChanged!(pageCount); + } + + setState(() { + resultCount = pageCount; + }); + + } catch (error, stackTrace) { + print("Error! - ${error.toString()}"); + _pagingController.error = error; + + sentryReportError(error, stackTrace); + } + } + + void _openPart(BuildContext context, int pk) { + // Attempt to load the part information + InvenTreePart().get(pk).then((var part) { + if (part is InvenTreePart) { + + Navigator.push(context, MaterialPageRoute(builder: (context) => PartDetailWidget(part))); + } + }); + } + + Widget _buildPart(BuildContext context, InvenTreePart part) { + return ListTile( + title: Text(part.fullname), + subtitle: Text("${part.description}"), + trailing: Text("${part.inStockString}"), + leading: InvenTreeAPI().getImage( + part.thumbnail, + width: 40, + height: 40, + ), + onTap: () { + _openPart(context, part.pk); + }, + ); + } + + final TextEditingController searchController = TextEditingController(); + + void updateSearchTerm() { + + _searchTerm = searchController.text; + _pagingController.refresh(); + } + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + PaginatedSearchWidget(searchController, updateSearchTerm, resultCount), + Expanded( + child: CustomScrollView( + shrinkWrap: true, + physics: ClampingScrollPhysics(), + scrollDirection: Axis.vertical, + slivers: [ + PagedSliverList.separated( + pagingController: _pagingController, + builderDelegate: PagedChildBuilderDelegate( + itemBuilder: (context, item, index) { + return _buildPart(context, item); + }, + noItemsFoundIndicatorBuilder: (context) { + return NoResultsWidget(L10().partNoResults); + }, + ), + separatorBuilder: (context, index) => const Divider(height: 1), + ) + ], + ) + ) + ], + ); + } +} \ No newline at end of file diff --git a/lib/widget/search.dart b/lib/widget/search.dart index c522c747..b1e93b47 100644 --- a/lib/widget/search.dart +++ b/lib/widget/search.dart @@ -3,6 +3,7 @@ import "dart:async"; import 'package:inventree/inventree/company.dart'; import 'package:inventree/inventree/purchase_order.dart'; import "package:inventree/widget/part_detail.dart"; +import 'package:inventree/widget/part_list.dart'; import "package:inventree/widget/progress.dart"; import 'package:inventree/widget/purchase_order_list.dart'; import 'package:inventree/widget/refreshable_state.dart'; @@ -161,7 +162,7 @@ class _SearchDisplayState extends RefreshableState { onSearchTextChanged(text); }, ), - trailing: IconButton( + leading: IconButton( icon: FaIcon(FontAwesomeIcons.backspace, color: Colors.red), onPressed: () { searchController.clear(); @@ -184,7 +185,16 @@ class _SearchDisplayState extends RefreshableState { leading: FaIcon(FontAwesomeIcons.shapes), trailing: Text("${nPartResults}"), onTap: () { - // Show part results + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => PartList( + { + "original_search": query + } + ) + ) + ); } ) );