2
0
mirror of https://github.com/inventree/inventree-app.git synced 2025-04-29 05:56:47 +00:00

Stock display now uses infinite scroll

This commit is contained in:
Oliver Walters 2021-03-01 13:17:57 +11:00
parent 64e544c043
commit d9a92216d2
2 changed files with 143 additions and 29 deletions

View File

@ -346,6 +346,8 @@ class _PaginatedPartListState extends State<PaginatedPartList> {
static const _pageSize = 25; static const _pageSize = 25;
String _searchTerm;
final int categoryId; final int categoryId;
_PaginatedPartListState(this.categoryId); _PaginatedPartListState(this.categoryId);
@ -357,12 +359,28 @@ class _PaginatedPartListState extends State<PaginatedPartList> {
_pagingController.addPageRequestListener((pageKey) { _pagingController.addPageRequestListener((pageKey) {
_fetchPage(pageKey); _fetchPage(pageKey);
}); });
super.initState();
}
@override
void dispose() {
_pagingController.dispose();
super.dispose();
} }
Future<void> _fetchPage(int pageKey) async { Future<void> _fetchPage(int pageKey) async {
try { try {
final page = await InvenTreePart().listPaginated(_pageSize, pageKey, filters: {"category": "${categoryId}"}); Map<String, String> filters = {
"category": "${categoryId}",
};
if (_searchTerm != null && _searchTerm.isNotEmpty) {
filters["search"] = _searchTerm;
}
final page = await InvenTreePart().listPaginated(_pageSize, pageKey, filters: filters);
final isLastPage = page.length < _pageSize; final isLastPage = page.length < _pageSize;
// Construct a list of part objects // Construct a list of part objects
@ -413,8 +431,34 @@ class _PaginatedPartListState extends State<PaginatedPartList> {
); );
} }
void updateSearchTerm(String searchTerm) {
_searchTerm = searchTerm;
_pagingController.refresh();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return CustomScrollView(
slivers: <Widget>[
// TODO: Introduce searching within the list
/*
SliverToBoxAdapter(child: TextField(
onChanged: updateSearchTerm,
)
),
*/
PagedSliverList.separated(
pagingController: _pagingController,
builderDelegate: PagedChildBuilderDelegate<InvenTreePart>(
itemBuilder: (context, item, index) {
return _buildPart(context, item);
}
),
separatorBuilder: (context, index) => const Divider(height: 1),
),
]
);
return PagedListView<int, InvenTreePart>.separated( return PagedListView<int, InvenTreePart>.separated(
pagingController: _pagingController, pagingController: _pagingController,
builderDelegate: PagedChildBuilderDelegate<InvenTreePart>( builderDelegate: PagedChildBuilderDelegate<InvenTreePart>(

View File

@ -16,6 +16,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
class LocationDisplayWidget extends StatefulWidget { class LocationDisplayWidget extends StatefulWidget {
@ -159,18 +160,6 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
}); });
setState(() {}); setState(() {});
await InvenTreeStockItem().list(context, filters: {"location": "$pk"}).then((var items) {
_items.clear();
for (var item in items) {
if (item is InvenTreeStockItem) {
_items.add(item);
}
}
});
setState(() {});
} }
Widget locationDescriptionCard() { Widget locationDescriptionCard() {
@ -240,9 +229,7 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
children: detailTiles(), children: detailTiles(),
); );
case 1: case 1:
return ListView( return PaginatedStockList(location?.pk ?? null);
children: stockTiles(),
);
case 2: case 2:
return ListView( return ListView(
children: ListTile.divideTiles( children: ListTile.divideTiles(
@ -300,6 +287,8 @@ List<Widget> detailTiles() {
) )
]; ];
/*
if (loading) { if (loading) {
tiles.add(progressIndicator()); tiles.add(progressIndicator());
} else if (_items.length > 0) { } else if (_items.length > 0) {
@ -310,6 +299,7 @@ List<Widget> detailTiles() {
subtitle: Text("No stock items available in this location") subtitle: Text("No stock items available in this location")
)); ));
} }
*/
return tiles; return tiles;
} }
@ -403,10 +393,83 @@ class SublocationList extends StatelessWidget {
} }
} }
class StockList extends StatelessWidget { /**
final List<InvenTreeStockItem> _items; * Widget for displaying a list of stock items within a stock location.
*
* Users server-side pagination for snappy results
*/
StockList(this._items); class PaginatedStockList extends StatefulWidget {
final int locationId;
PaginatedStockList(this.locationId);
@override
_PaginatedStockListState createState() => _PaginatedStockListState(locationId);
}
class _PaginatedStockListState extends State<PaginatedStockList> {
static const _pageSize = 25;
String _searchTerm;
final int locationId;
_PaginatedStockListState(this.locationId);
final PagingController<int, InvenTreeStockItem> _pagingController = PagingController(firstPageKey: 0);
@override
void initState() {
_pagingController.addPageRequestListener((pageKey) {
_fetchPage(pageKey);
});
super.initState();
}
@override
void dispose() {
_pagingController.dispose();
super.dispose();
}
Future<void> _fetchPage(int pageKey) async {
try {
Map<String, String> filters = {
"location": "${locationId}"
};
if (_searchTerm != null && _searchTerm.isNotEmpty) {
filters["search"] = "${_searchTerm}";
}
final page = await InvenTreeStockItem().listPaginated(_pageSize, pageKey, filters: filters);
final isLastPage = page.length < _pageSize;
// Construct a list of stock item objects
List<InvenTreeStockItem> items = [];
for (var result in page.results) {
if (result is InvenTreeStockItem) {
items.add(result);
}
}
if (isLastPage) {
_pagingController.appendLastPage(items);
} else {
final int nextPageKey = pageKey + page.length;
_pagingController.appendPage(items, nextPageKey);
}
} catch (error) {
_pagingController.error = error;
}
}
void _openItem(BuildContext context, int pk) { void _openItem(BuildContext context, int pk) {
InvenTreeStockItem().get(context, pk).then((var item) { InvenTreeStockItem().get(context, pk).then((var item) {
@ -416,9 +479,7 @@ class StockList extends StatelessWidget {
}); });
} }
Widget _build(BuildContext context, int index) { Widget _buildItem(BuildContext context, InvenTreeStockItem item) {
InvenTreeStockItem item = _items[index];
return ListTile( return ListTile(
title: Text("${item.partName}"), title: Text("${item.partName}"),
subtitle: Text("${item.partDescription}"), subtitle: Text("${item.partDescription}"),
@ -437,11 +498,20 @@ class StockList extends StatelessWidget {
} }
@override @override
Widget build(BuildContext context) { Widget build (BuildContext context) {
return ListView.separated( return CustomScrollView(
shrinkWrap: true, slivers: <Widget>[
physics: ClampingScrollPhysics(), // TODO - Search input
separatorBuilder: (_, __) => const Divider(height: 3), PagedSliverList.separated(
itemBuilder: _build, itemCount: _items.length); pagingController: _pagingController,
builderDelegate: PagedChildBuilderDelegate<InvenTreeStockItem>(
itemBuilder: (context, item, index) {
return _buildItem(context, item);
}
),
separatorBuilder: (context, item) => const Divider(height: 1),
)
]
);
} }
} }