Closed davoutuk closed 5 months ago
I face the same issue.
but after grouping when I add a list of groups using this method( appendPage) method. it shows multiple times.
Resolved.
instead of using appendPage i set pagingController.value. below is a screenshot.
Group Method
List<VisaItemHeader> group({required List<VisaItem> data}) {
List<VisaItemHeader> list = [];
Map<String, List<VisaItem>> map =
groupBy(data, (VisaItem obj) => obj.visaGroupId ?? "");
map.forEach((key, value) {
list.add(VisaItemHeader(key, value));
});
return list;
}
@davoutuk have you found any possible solution ?
Resolved.
instead of using appendPage i set pagingController.value. below is a screenshot.
Group Method
List<VisaItemHeader> group({required List<VisaItem> data}) { List<VisaItemHeader> list = []; Map<String, List<VisaItem>> map = groupBy(data, (VisaItem obj) => obj.visaGroupId ?? ""); map.forEach((key, value) { list.add(VisaItemHeader(key, value)); }); return list; }
This replaces the previous page with the new page group... Which isn't what we want
I've found a solution making use of @NALAWALAMURTUZA grouping method. I modified it to fit my code below.
List<TransactionItemHeader> group({required List<Datum> data}) {
List<TransactionItemHeader> list = [];
Map<String, List<Datum>> map = groupBy(
data,
(Datum obj) => Validators.dateTimeToString(DateTime(
obj.pubDate!.year, obj.pubDate!.month, obj.pubDate!.day))!);
map.forEach((key, value) {
list.add(TransactionItemHeader(headerTitle: key, transactionData: value));
});
return list;
}
I did not make use of PageController.value as he did however. Doing that causes the whole page history to be replaced, which is not what I want in a scrolling view.
What I did to stop the duplication of a group, is this:
Future<void> fetchPaginationPage({required int pageKey}) async {
const pageSize = 20;
try {
final transactionsList = await fetchAllTransactionsPagination(
authToken: getIt<UserDependencies>().getAuthToken()!,
merchantTribeRef: getIt<UserDependencies>().getMerchantTribeRef()!,
startDate: DateTime(2008),
endDate: DateTime(
DateTime.now().year, DateTime.now().month, DateTime.now().day),
transactionType: TransactionType.AllFiat,
pageNumber: pageKey);
final isLastPage = transactionsList.length < pageSize;
final transactionItemsSoFar = pagingController.itemList;
final groupedTransactionList = group(data: transactionsList);
if (transactionItemsSoFar != null) {
TransactionItemHeader? temp;
for (var groupedElement in groupedTransactionList) {
temp = transactionItemsSoFar.firstWhereOrNull(
(element) => element.headerTitle == groupedElement.headerTitle);
if (temp != null) {
break;
}
}
if (temp != null) {
var temp2 = groupedTransactionList.firstWhereOrNull(
(element) => temp!.headerTitle == element.headerTitle);
if (temp2 != null) {
var newTransactionItemHeader = TransactionItemHeader(
headerTitle: temp2.headerTitle,
transactionData: [
...temp.transactionData,
...temp2.transactionData
]);
var tempIndex = transactionItemsSoFar.indexWhere((element) =>
newTransactionItemHeader.headerTitle == element.headerTitle);
pagingController.itemList![tempIndex] = newTransactionItemHeader;
groupedTransactionList.removeAt(0);
}
}
}
// pagingController.
if (isLastPage) {
pagingController.appendLastPage(groupedTransactionList);
// pagingController.value = PagingState(
// nextPageKey: null, itemList: group(data: transactionsList));
} else {
final nextPageKey = pageKey + 1;
pagingController.appendPage(groupedTransactionList, nextPageKey);
// pagingController.value = PagingState(
// nextPageKey: nextPageKey, itemList: group(data: transactionsList));
}
} catch (e) {
pagingController.error = e;
}
}
So far, it works pretty good and seamless.
for an actual grouped list, I have used the grouped_list package and created a PagedGroupedListView:
import 'package:flutter/material.dart';
import 'package:grouped_list/sliver_grouped_list.dart';
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
import 'package:sliver_tools/sliver_tools.dart';
class PagedGroupedListView<PageKeyType, ItemType, SortType>
extends BoxScrollView {
const PagedGroupedListView({
super.key,
required this.pagingController,
required this.builderDelegate,
this.shrinkWrapFirstPageIndicators = false,
required this.groupBy,
this.groupComparator,
this.groupSeparatorBuilder,
this.groupHeaderBuilder,
this.itemComparator,
this.order = GroupedListOrder.ASC,
this.sort = true,
this.separator = const SizedBox.shrink(),
super.scrollDirection,
super.reverse,
super.controller,
super.primary,
super.physics,
super.shrinkWrap,
super.padding,
super.cacheExtent,
super.semanticChildCount,
super.dragStartBehavior,
super.keyboardDismissBehavior,
super.restorationId,
super.clipBehavior,
});
/// Matches [PagedLayoutBuilder.pagingController].
final PagingController<PageKeyType, ItemType> pagingController;
/// Matches [PagedLayoutBuilder.builderDelegate].
final PagedChildBuilderDelegate<ItemType> builderDelegate;
/// Matches [PagedLayoutBuilder.shrinkWrapFirstPageIndicators].
final bool shrinkWrapFirstPageIndicators;
/// Matches [SliverGroupedListView.groupBy].
final SortType Function(ItemType element) groupBy;
/// Matches [SliverGroupedListView.groupComparator].
final int Function(SortType value1, SortType value2)? groupComparator;
/// Matches [SliverGroupedListView.itemComparator].
final int Function(ItemType element1, ItemType element2)? itemComparator;
/// Matches [SliverGroupedListView.groupSeparatorBuilder].
final Widget Function(SortType value)? groupSeparatorBuilder;
/// Matches [SliverGroupedListView.groupHeaderBuilder].
final Widget Function(ItemType element)? groupHeaderBuilder;
/// Matches [SliverGroupedListView.order].
final GroupedListOrder order;
/// Matches [SliverGroupedListView.sort].
final bool sort;
/// Matches [SliverGroupedListView.separator].
final Widget separator;
@override
Widget buildChildLayout(BuildContext context) {
return PagedSliverGroupedListView(
pagingController: pagingController,
builderDelegate: builderDelegate,
shrinkWrapFirstPageIndicators: shrinkWrapFirstPageIndicators,
groupBy: groupBy,
groupComparator: groupComparator,
groupSeparatorBuilder: groupSeparatorBuilder,
groupHeaderBuilder: groupHeaderBuilder,
itemComparator: itemComparator,
order: order,
sort: sort,
separator: separator,
);
}
}
class PagedSliverGroupedListView<PageKeyType, ItemType, SortType>
extends StatelessWidget {
const PagedSliverGroupedListView({
super.key,
required this.pagingController,
required this.builderDelegate,
this.shrinkWrapFirstPageIndicators = false,
required this.groupBy,
this.groupComparator,
this.groupSeparatorBuilder,
this.groupHeaderBuilder,
this.itemComparator,
this.order = GroupedListOrder.ASC,
this.sort = true,
this.separator = const SizedBox.shrink(),
});
/// Matches [PagedLayoutBuilder.pagingController].
final PagingController<PageKeyType, ItemType> pagingController;
/// Matches [PagedLayoutBuilder.builderDelegate].
final PagedChildBuilderDelegate<ItemType> builderDelegate;
/// Matches [PagedLayoutBuilder.shrinkWrapFirstPageIndicators].
final bool shrinkWrapFirstPageIndicators;
/// Matches [SliverGroupedListView.groupBy].
final SortType Function(ItemType element) groupBy;
/// Matches [SliverGroupedListView.groupComparator].
final int Function(SortType value1, SortType value2)? groupComparator;
/// Matches [SliverGroupedListView.itemComparator].
final int Function(ItemType element1, ItemType element2)? itemComparator;
/// Matches [SliverGroupedListView.groupSeparatorBuilder].
final Widget Function(SortType value)? groupSeparatorBuilder;
/// Matches [SliverGroupedListView.groupHeaderBuilder].
final Widget Function(ItemType element)? groupHeaderBuilder;
/// Matches [SliverGroupedListView.order].
final GroupedListOrder order;
/// Matches [SliverGroupedListView.sort].
final bool sort;
/// Matches [SliverGroupedListView.separator].
final Widget separator;
@override
Widget build(BuildContext context) {
Widget buildLayout(
IndexedWidgetBuilder itemBuilder,
int itemCount, {
WidgetBuilder? statusIndicatorBuilder,
}) =>
MultiSliver(
children: [
SliverGroupedListView<ItemType, SortType>(
key: key,
elements: pagingController.itemList!,
groupBy: groupBy,
groupComparator: groupComparator,
groupSeparatorBuilder: groupSeparatorBuilder,
groupHeaderBuilder: groupHeaderBuilder,
indexedItemBuilder: (context, item, index) =>
itemBuilder(context, index),
itemComparator: itemComparator,
order: order,
sort: sort,
separator: separator,
),
if (statusIndicatorBuilder != null)
SliverToBoxAdapter(
child: statusIndicatorBuilder(context),
)
],
);
return PagedLayoutBuilder<PageKeyType, ItemType>(
layoutProtocol: PagedLayoutProtocol.sliver,
pagingController: pagingController,
builderDelegate: builderDelegate,
shrinkWrapFirstPageIndicators: shrinkWrapFirstPageIndicators,
completedListingBuilder: (
context,
itemBuilder,
itemCount,
noMoreItemsIndicatorBuilder,
) =>
buildLayout(
itemBuilder,
itemCount,
statusIndicatorBuilder: noMoreItemsIndicatorBuilder,
),
loadingListingBuilder: (
context,
itemBuilder,
itemCount,
progressIndicatorBuilder,
) =>
buildLayout(
itemBuilder,
itemCount,
statusIndicatorBuilder: progressIndicatorBuilder,
),
errorListingBuilder: (
context,
itemBuilder,
itemCount,
errorIndicatorBuilder,
) =>
buildLayout(
itemBuilder,
itemCount,
statusIndicatorBuilder: errorIndicatorBuilder,
),
);
}
}
Hi @clragon , Could you please show a usage example?
You can read how to use the grouped list on the grouped list package page: https://pub.dev/packages/grouped_list
for an actual grouped list, I have used the grouped_list package and created a PagedGroupedListView:
import 'package:flutter/material.dart'; import 'package:grouped_list/sliver_grouped_list.dart'; import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; import 'package:sliver_tools/sliver_tools.dart'; class PagedGroupedListView<PageKeyType, ItemType, SortType> extends BoxScrollView { const PagedGroupedListView({ super.key, required this.pagingController, required this.builderDelegate, this.shrinkWrapFirstPageIndicators = false, required this.groupBy, this.groupComparator, this.groupSeparatorBuilder, this.groupHeaderBuilder, this.itemComparator, this.order = GroupedListOrder.ASC, this.sort = true, this.separator = const SizedBox.shrink(), super.scrollDirection, super.reverse, super.controller, super.primary, super.physics, super.shrinkWrap, super.padding, super.cacheExtent, super.semanticChildCount, super.dragStartBehavior, super.keyboardDismissBehavior, super.restorationId, super.clipBehavior, }); /// Matches [PagedLayoutBuilder.pagingController]. final PagingController<PageKeyType, ItemType> pagingController; /// Matches [PagedLayoutBuilder.builderDelegate]. final PagedChildBuilderDelegate<ItemType> builderDelegate; /// Matches [PagedLayoutBuilder.shrinkWrapFirstPageIndicators]. final bool shrinkWrapFirstPageIndicators; /// Matches [SliverGroupedListView.groupBy]. final SortType Function(ItemType element) groupBy; /// Matches [SliverGroupedListView.groupComparator]. final int Function(SortType value1, SortType value2)? groupComparator; /// Matches [SliverGroupedListView.itemComparator]. final int Function(ItemType element1, ItemType element2)? itemComparator; /// Matches [SliverGroupedListView.groupSeparatorBuilder]. final Widget Function(SortType value)? groupSeparatorBuilder; /// Matches [SliverGroupedListView.groupHeaderBuilder]. final Widget Function(ItemType element)? groupHeaderBuilder; /// Matches [SliverGroupedListView.order]. final GroupedListOrder order; /// Matches [SliverGroupedListView.sort]. final bool sort; /// Matches [SliverGroupedListView.separator]. final Widget separator; @override Widget buildChildLayout(BuildContext context) { return PagedSliverGroupedListView( pagingController: pagingController, builderDelegate: builderDelegate, shrinkWrapFirstPageIndicators: shrinkWrapFirstPageIndicators, groupBy: groupBy, groupComparator: groupComparator, groupSeparatorBuilder: groupSeparatorBuilder, groupHeaderBuilder: groupHeaderBuilder, itemComparator: itemComparator, order: order, sort: sort, separator: separator, ); } } class PagedSliverGroupedListView<PageKeyType, ItemType, SortType> extends StatelessWidget { const PagedSliverGroupedListView({ super.key, required this.pagingController, required this.builderDelegate, this.shrinkWrapFirstPageIndicators = false, required this.groupBy, this.groupComparator, this.groupSeparatorBuilder, this.groupHeaderBuilder, this.itemComparator, this.order = GroupedListOrder.ASC, this.sort = true, this.separator = const SizedBox.shrink(), }); /// Matches [PagedLayoutBuilder.pagingController]. final PagingController<PageKeyType, ItemType> pagingController; /// Matches [PagedLayoutBuilder.builderDelegate]. final PagedChildBuilderDelegate<ItemType> builderDelegate; /// Matches [PagedLayoutBuilder.shrinkWrapFirstPageIndicators]. final bool shrinkWrapFirstPageIndicators; /// Matches [SliverGroupedListView.groupBy]. final SortType Function(ItemType element) groupBy; /// Matches [SliverGroupedListView.groupComparator]. final int Function(SortType value1, SortType value2)? groupComparator; /// Matches [SliverGroupedListView.itemComparator]. final int Function(ItemType element1, ItemType element2)? itemComparator; /// Matches [SliverGroupedListView.groupSeparatorBuilder]. final Widget Function(SortType value)? groupSeparatorBuilder; /// Matches [SliverGroupedListView.groupHeaderBuilder]. final Widget Function(ItemType element)? groupHeaderBuilder; /// Matches [SliverGroupedListView.order]. final GroupedListOrder order; /// Matches [SliverGroupedListView.sort]. final bool sort; /// Matches [SliverGroupedListView.separator]. final Widget separator; @override Widget build(BuildContext context) { Widget buildLayout( IndexedWidgetBuilder itemBuilder, int itemCount, { WidgetBuilder? statusIndicatorBuilder, }) => MultiSliver( children: [ SliverGroupedListView<ItemType, SortType>( key: key, elements: pagingController.itemList!, groupBy: groupBy, groupComparator: groupComparator, groupSeparatorBuilder: groupSeparatorBuilder, groupHeaderBuilder: groupHeaderBuilder, indexedItemBuilder: (context, item, index) => itemBuilder(context, index), itemComparator: itemComparator, order: order, sort: sort, separator: separator, ), if (statusIndicatorBuilder != null) SliverToBoxAdapter( child: statusIndicatorBuilder(context), ) ], ); return PagedLayoutBuilder<PageKeyType, ItemType>( layoutProtocol: PagedLayoutProtocol.sliver, pagingController: pagingController, builderDelegate: builderDelegate, shrinkWrapFirstPageIndicators: shrinkWrapFirstPageIndicators, completedListingBuilder: ( context, itemBuilder, itemCount, noMoreItemsIndicatorBuilder, ) => buildLayout( itemBuilder, itemCount, statusIndicatorBuilder: noMoreItemsIndicatorBuilder, ), loadingListingBuilder: ( context, itemBuilder, itemCount, progressIndicatorBuilder, ) => buildLayout( itemBuilder, itemCount, statusIndicatorBuilder: progressIndicatorBuilder, ), errorListingBuilder: ( context, itemBuilder, itemCount, errorIndicatorBuilder, ) => buildLayout( itemBuilder, itemCount, statusIndicatorBuilder: errorIndicatorBuilder, ), ); } }
This doesn't work at all. The groupBy function of the list view, should expose a single item of the list as a parameter. Eg: if I have a List of String, the group by should expose a single string item. The code above, exposes the entire list of string which breaks the functionality sadly
Any suggestions on how I can introduce a grouping display of items in an infinite list?
By grouping, I mean adding a title to a sublist of items, as shown by the 'language' grouping in the screenshot below