diff --git a/lib/api.dart b/lib/api.dart index 10964f21..fdcf1d6c 100644 --- a/lib/api.dart +++ b/lib/api.dart @@ -371,7 +371,7 @@ class InvenTreeAPI { } }); } - + bool checkPermission(String role, String permission) { /* * Check if the user has the given role.permission assigned diff --git a/lib/widget/category_display.dart b/lib/widget/category_display.dart index 4454229c..73ebb212 100644 --- a/lib/widget/category_display.dart +++ b/lib/widget/category_display.dart @@ -19,6 +19,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; class CategoryDisplayWidget extends StatefulWidget { @@ -124,8 +125,6 @@ class _CategoryDisplayState extends RefreshableState { List _subcategories = List(); - List _parts = List(); - @override Future onBuild(BuildContext context) async { refresh(); @@ -154,20 +153,6 @@ class _CategoryDisplayState extends RefreshableState { // Update state setState(() {}); }); - - // Request a list of parts under this category - await InvenTreePart().list(context, filters: {"category": "$pk"}).then((var parts) { - _parts.clear(); - - for (var part in parts) { - if (part is InvenTreePart) { - _parts.add(part); - } - } - - // Update state - setState(() {}); - }); } Widget getCategoryDescriptionCard() { @@ -262,32 +247,6 @@ class _CategoryDisplayState extends RefreshableState { return tiles; } - List partTiles() { - List tiles = [ - getCategoryDescriptionCard(), - ListTile( - title: Text( - I18N.of(context).parts, - style: TextStyle(fontWeight: FontWeight.bold) - ), - trailing: _parts.isNotEmpty ? Text("${_parts.length}") : null, - ), - ]; - - if (loading) { - tiles.add(progressIndicator()); - } else if (_parts.length == 0) { - tiles.add(ListTile( - title: Text("No Parts"), - subtitle: Text("No parts available in this category") - )); - } else { - tiles.add(PartList(_parts)); - } - - return tiles; - } - List actionTiles() { List tiles = [ @@ -313,9 +272,7 @@ class _CategoryDisplayState extends RefreshableState { children: detailTiles() ); case 1: - return ListView( - children: partTiles() - ); + return PaginatedPartList(category?.pk ?? null); case 2: return ListView( children: actionTiles() @@ -368,13 +325,67 @@ class SubcategoryList extends StatelessWidget { } -/* - * Builder for displaying a list of Part objects +/** + * Widget for displaying a list of Part objects within a PartCategory display. + * + * Uses server-side pagination for snappy results */ -class PartList extends StatelessWidget { - final List _parts; - PartList(this._parts); +class PaginatedPartList extends StatefulWidget { + + final int categoryId; + + PaginatedPartList(this.categoryId); + + @override + _PaginatedPartListState createState() => _PaginatedPartListState(categoryId); +} + + +class _PaginatedPartListState extends State { + + static const _pageSize = 25; + + final int categoryId; + + _PaginatedPartListState(this.categoryId); + + final PagingController _pagingController = PagingController(firstPageKey: 0); + + @override + void initState() { + _pagingController.addPageRequestListener((pageKey) { + _fetchPage(pageKey); + }); + } + + Future _fetchPage(int pageKey) async { + try { + + final page = await InvenTreePart().listPaginated(_pageSize, pageKey, filters: {"category": "${categoryId}"}); + final isLastPage = page.length < _pageSize; + + // Construct a list of part objects + List parts = []; + + for (var result in page.results) { + if (result is InvenTreePart) { + parts.add(result); + } + } + + if (isLastPage) { + _pagingController.appendLastPage(parts); + } else { + final int nextPageKey = pageKey + page.length; + _pagingController.appendPage(parts, nextPageKey); + } + + } catch (error) { + print("Error! - ${error.toString()}"); + _pagingController.error = error; + } + } void _openPart(BuildContext context, int pk) { // Attempt to load the part information @@ -386,13 +397,7 @@ class PartList extends StatelessWidget { }); } - Widget _build(BuildContext context, int index) { - InvenTreePart part; - - if (index < _parts.length) { - part = _parts[index]; - } - + Widget _buildPart(BuildContext context, InvenTreePart part) { return ListTile( title: Text(part.fullname), subtitle: Text("${part.description}"), @@ -406,15 +411,18 @@ class PartList extends StatelessWidget { _openPart(context, part.pk); }, ); - } @override Widget build(BuildContext context) { - return ListView.separated( - shrinkWrap: true, - physics: ClampingScrollPhysics(), - separatorBuilder: (_, __) => const Divider(height: 3), - itemBuilder: _build, itemCount: _parts.length); + return PagedListView.separated( + pagingController: _pagingController, + builderDelegate: PagedChildBuilderDelegate( + itemBuilder: (context, item, index) { + return _buildPart(context, item); + } + ), + separatorBuilder: (context, index) => const Divider(height: 1), + ); } }