### Custom Sliver Grid Layout Example with PagedLayoutBuilder Source: https://github.com/edsonbueno/infinite_scroll_pagination/blob/master/example/example.md Demonstrates how to create a custom sliver grid layout using PagedLayoutBuilder. This involves defining builders for completed, loading, and error states, and specifying the layout protocol as sliver. It's useful for complex UI requirements not covered by standard widgets. ```dart PagedLayoutBuilder( layoutProtocol: PagedLayoutProtocol.sliver, state: state, fetchNextPage: fetchNextPage, builderDelegate: builderDelegate, completedListingBuilder: ( context, itemBuilder, itemCount, noMoreItemsIndicatorBuilder, ) => AppendedSliverGrid( sliverGridBuilder: (_, delegate) => SliverGrid( delegate: delegate, gridDelegate: gridDelegate, ), itemBuilder: itemBuilder, itemCount: itemCount, appendixBuilder: noMoreItemsIndicatorBuilder, showAppendixAsGridChild: showNoMoreItemsIndicatorAsGridChild, addAutomaticKeepAlives: addAutomaticKeepAlives, addSemanticIndexes: addSemanticIndexes, addRepaintBoundaries: addRepaintBoundaries, ), loadingListingBuilder: ( context, itemBuilder, itemCount, progressIndicatorBuilder, ) => AppendedSliverGrid( sliverGridBuilder: (_, delegate) => SliverGrid( delegate: delegate, gridDelegate: gridDelegate, ), itemBuilder: itemBuilder, itemCount: itemCount, appendixBuilder: progressIndicatorBuilder, showAppendixAsGridChild: showNewPageProgressIndicatorAsGridChild, addAutomaticKeepAlives: addAutomaticKeepAlives, addSemanticIndexes: addSemanticIndexes, addRepaintBoundaries: addRepaintBoundaries, ), errorListingBuilder: ( context, itemBuilder, itemCount, errorIndicatorBuilder, ) => AppendedSliverGrid( sliverGridBuilder: (_, delegate) => SliverGrid( delegate: delegate, gridDelegate: gridDelegate, ), itemBuilder: itemBuilder, itemCount: itemCount, appendixBuilder: errorIndicatorBuilder, showAppendixAsGridChild: showNewPageErrorIndicatorAsGridChild, addAutomaticKeepAlives: addAutomaticKeepAlives, addSemanticIndexes: addSemanticIndexes, addRepaintBoundaries: addRepaintBoundaries, ), shrinkWrapFirstPageIndicators: shrinkWrapFirstPageIndicators, ); ``` -------------------------------- ### Flutter Bloc Integration for Pagination in Dart Source: https://github.com/edsonbueno/infinite_scroll_pagination/blob/master/example/example.md Shows how to integrate infinite pagination with flutter_bloc. This example defines a Bloc to manage the PagingState and handle fetch events, providing a clean separation of concerns for state management. ```dart sealed class PhotoEvent {} final class FetchNextPhotoPage extends PhotoEvent {} class PhotoBoc extends Bloc> { PhotoBoc() : super(PagingState()) { on((event, emit) { final state = state; if (state.isLoading) return; emit(state.copyWith(isLoading: true, error: null)); try { final newKey = (state.keys?.last ?? 0) + 1; final newItems = await RemoteApi.getPhotos(newKey); final isLastPage = newItems.isEmpty; emit(state.copyWith( pages: [...?state.pages, newItems], keys: [...?state.keys, newKey], hasNextPage: !isLastPage, isLoading: false, )); } catch (error) { emit(state.copyWith( error: error, isLoading: false, )); } }, ); } } ``` ```dart class _ExampleScreenState extends State { final _bloc = PhotoBloc(); @override void dispose() { _bloc.dispose(); super.dispose(); } @override Widget build(BuildContext context) => BlocBuilder>( bloc: _bloc, builder: (context, state) => PagedListView( state: state, fetchNextPage: _bloc.fetchNextPage, builderDelegate: PagedChildBuilderDelegate( itemBuilder: (context, item, index) => ImageListTile(item: item), ), ), ); } ``` -------------------------------- ### Change Invisible Items Threshold in Dart Source: https://github.com/edsonbueno/infinite_scroll_pagination/blob/master/example/example.md Illustrates how to modify the threshold for requesting a new page in `infinite_scroll_pagination`. The `invisibleItemsThreshold` property in `PagedChildBuilderDelegate` allows developers to specify how many items should be invisible before a new page is fetched. The default is 3, and this example sets it to 5. The input is the `PagedListView` with a customized `PagedChildBuilderDelegate`. ```dart PagedListView( state: state, fetchNextPage: fetchNextPage, builderDelegate: PagedChildBuilderDelegate( itemBuilder: (context, item, index) => ImageListTile(item: item), invisibleItemsThreshold: 5, ), ); ``` -------------------------------- ### Position Grid Status Indicators in Dart Source: https://github.com/edsonbueno/infinite_scroll_pagination/blob/master/example/example.md Shows how to control the visibility of status indicators (progress, error, no more items) within a `PagedGridView`. By default, indicators are shown as grid children, but this example demonstrates how to display them below the grid using boolean properties. This is useful for customizing the UI presentation of paged grid layouts. The primary input is the `PagedGridView` widget with specific properties set to `false`. ```dart @override Widget build(BuildContext context) => PagedGridView( showNewPageProgressIndicatorAsGridChild: false, showNewPageErrorIndicatorAsGridChild: false, showNoMoreItemsIndicatorAsGridChild: false, state: state, fetchNextPage: fetchNextPage, gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( childAspectRatio: 100 / 150, crossAxisSpacing: 10, mainAxisSpacing: 10, crossAxisCount: 3, ), builderDelegate: PagedChildBuilderDelegate( itemBuilder: (context, item, index) => ImageListTile(item: item), ), ); ``` -------------------------------- ### Manage Pagination State with PagingController in Dart Source: https://context7.com/edsonbueno/infinite_scroll_pagination/llms.txt The PagingController manages pagination state and handles page fetching. It extends ValueNotifier and acts as a mutex to prevent concurrent fetch operations while providing a declarative API. This example shows how to initialize the controller, fetch pages, access state, refresh, map items, and dispose of the controller. ```dart import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; // Define your item type class Photo { final int id; final String title; final String thumbnailUrl; Photo({required this.id, required this.title, required this.thumbnailUrl}); } // Create controller with integer page keys final pagingController = PagingController( // Define how to get the next page key getNextPageKey: (state) { // Return null when no more pages if (state.lastPageIsEmpty) return null; // Increment page key (assumes keys start at 1) return state.nextIntPageKey; }, // Define how to fetch a page fetchPage: (pageKey) async { final response = await http.get( Uri.parse('https://api.example.com/photos?page=$pageKey&limit=20') ); if (response.statusCode == 200) { final List json = jsonDecode(response.body); return json.map((item) => Photo( id: item['id'], title: item['title'], thumbnailUrl: item['thumbnailUrl'], )).toList(); } throw Exception('Failed to load photos'); }, ); // Fetch the first page pagingController.fetchNextPage(); // Access current state print('Current items: ${pagingController.items}'); print('Is loading: ${pagingController.isLoading}'); print('Has next page: ${pagingController.hasNextPage}'); // Refresh to reset pagination pagingController.refresh(); // Update items by mapping pagingController.mapItems((photo) => Photo(id: photo.id, title: photo.title.toUpperCase(), thumbnailUrl: photo.thumbnailUrl) ); // Always dispose when done pagingController.dispose(); ``` -------------------------------- ### PagingController: Old vs New Initialization (Dart) Source: https://github.com/edsonbueno/infinite_scroll_pagination/blob/master/MIGRATION.md Demonstrates the shift in PagingController initialization from v4 to v5. In v4, a PageRequestListener was added manually. In v5, fetching logic is directly integrated into the constructor via `getNextPageKey` and `fetchPage` parameters. This change simplifies state management and introduces automatic request deduplication. ```dart /// v4 final pagingController = PagingController(firstPageKey: 1); pagingController.addPageRequestListener(fetchPage); // Manual update pagingController.appendPage(newItems, nextPageKey); /// v5 late final pagingController = PagingController( getNextPageKey: (state) => state.lastPageIsEmpty ? null : state.nextIntPageKey, fetchPage: (pageKey) => fetchPage(pageKey), ); // Automatic merging and state update ``` -------------------------------- ### PagedListView: Old vs New Initialization (Dart) Source: https://github.com/edsonbueno/infinite_scroll_pagination/blob/master/MIGRATION.md Illustrates the change in how PagedListView is initialized between v4 and v5. Previously, it directly accepted a PagingController. In v5, it becomes more agnostic, accepting a `PagingState` and a `fetchNextPage` function. This allows for easier integration with various state management solutions. ```dart /// v4 PagedListView.builder( pagingController: pagingController, builderDelegate: PagedChildBuilderDelegate( itemBuilder: (context, item, index) => ImageListTile(item), ), ), /// v5 PagedListView.builder( state: state, fetchNextPage: fetchNextPage, builderDelegate: PagedChildBuilderDelegate( itemBuilder: (context, item, index) => ImageListTile(item), ), ), /// v5 with PagingListener to use PagingController PagingListener( controller: pagingController, builder: (context, state, fetchNextPage) => PagedListView.builder( state: state, fetchNextPage: fetchNextPage, builderDelegate: PagedChildBuilderDelegate( itemBuilder: (context, item, index) => ImageListTile(item), ), ), ), ``` -------------------------------- ### Search/Filter/Sort with Infinite Scroll Pagination in Dart Source: https://github.com/edsonbueno/infinite_scroll_pagination/blob/master/example/example.md Demonstrates how to implement searching, filtering, and sorting with an infinite scroll pagination controller. It uses a `PagingController` and updates the search term to refresh the data, suitable for state management approaches. Dependencies include the `infinite_scroll_pagination` package and a `RemoteApi` for fetching data. The input is a search term string, and the output is a filtered list of photos. ```dart class _ExampleScreenState extends State { String? _searchTerm; late final _pagingController = PagingController( getNextPageKey: (state) => (state.keys?.last ?? 0) + 1, fetchPage: (pageKey) { final results = RemoteApi.getPhotos(pageKey); return _searchTerm == null ? results : results.where((photo) => photo.title.contains(_searchTerm!)).toList(); }, ); void _updateSearchTerm(String searchTerm) { _searchTerm = searchTerm; _pagingController.refresh(); } @override void dispose() { _pagingController.dispose(); super.dispose(); } @override Widget build(BuildContext context) => CustomScrollView( slivers: [ SearchInputSliver(onChanged: _updateSearchTerm), PagingListener( controller: _pagingController, builder: (context, state, fetchNextPage) => PagedSliverList( state: state, fetchNextPage: fetchNextPage, builderDelegate: PagedChildBuilderDelegate( itemBuilder: (context, item, index) => ImageListTile(item: item), ), ), ), ], ); @override void dispose() { _pagingController.dispose(); super.dispose(); } } ``` -------------------------------- ### Customize PagedListView Indicators with Custom Widgets (Dart) Source: https://github.com/edsonbueno/infinite_scroll_pagination/blob/master/example/example.md Demonstrates how to provide custom widgets for different indicator states (error, progress, no items) in a PagedListView. This allows for a tailored user experience beyond the default indicators. ```dart PagedListView( state: state, fetchNextPage: fetchNextPage, builderDelegate: PagedChildBuilderDelegate( itemBuilder: (context, item, index) => ImageListTile(item: item), firstPageErrorIndicatorBuilder: (_) => FirstPageErrorIndicator( error: state.error, onTryAgain: () => fetchNextPage(), ), newPageErrorIndicatorBuilder: (_) => NewPageErrorIndicator( error: state.error, onTryAgain: () => fetchNextPage(), ), firstPageProgressIndicatorBuilder: (_) => FirstPageProgressIndicator(), newPageProgressIndicatorBuilder: (_) => NewPageProgressIndicator(), noItemsFoundIndicatorBuilder: (_) => NoItemsFoundIndicator(), noMoreItemsIndicatorBuilder: (_) => NoMoreItemsIndicator(), ), ); ``` -------------------------------- ### Manage Pagination State with PagingController in Dart Source: https://github.com/edsonbueno/infinite_scroll_pagination/blob/master/example/example.md Demonstrates how to use the PagingController to manage the PagingState for infinite scrolling. It sets up a PagingListener to connect the controller to a PagedListView and defines how to fetch the next page of data. ```dart class _ExampleScreenState extends State { late final _pagingController = PagingController( getNextPageKey: (state) => state.lastPageIsEmpty ? null : state.nextIntPageKey, fetchPage: (pageKey) => RemoteApi.getPhotos(pageKey), ); @override void dispose() { _pagingController.dispose(); super.dispose(); } @override Widget build(BuildContext context) => PagingListener( controller: _pagingController, builder: (context, state, fetchNextPage) => PagedListView( state: state, fetchNextPage: fetchNextPage, builderDelegate: PagedChildBuilderDelegate( itemBuilder: (context, item, index) => ImageListTile(item: item), ), ), ); } ``` -------------------------------- ### Implement Pull-to-Refresh with PagedListView (Dart) Source: https://github.com/edsonbueno/infinite_scroll_pagination/blob/master/example/example.md Demonstrates how to integrate pull-to-refresh functionality with PagedListView by wrapping it in a RefreshIndicator and calling the `refresh` method on the PagingController. ```dart RefreshIndicator( onRefresh: () => Future.sync( () => refresh(), ), child: PagedListView( state: state, fetchNextPage: fetchNextPage, builderDelegate: PagedChildBuilderDelegate( itemBuilder: (context, item, index) => ImageListTile(item: item), ), ), ); ``` -------------------------------- ### Manual Pagination State Management with setState in Dart Source: https://github.com/edsonbueno/infinite_scroll_pagination/blob/master/example/example.md Illustrates manual management of PagingState using Dart's setState for more granular control. This approach involves directly updating the state object when fetching new pages or encountering errors. ```dart class _ExampleScreenState extends State { PagingState _state = PagingState(); void _fetchNextPage() async { if (_state.isLoading) return; setState(() { _state = _state.copyWith(isLoading: true, error: null); }); try { final newKey = (_state.keys?.last ?? 0) + 1; final newItems = await RemoteApi.getPhotos(newKey); final isLastPage = newItems.isEmpty; setState(() { _state = _state.copyWith( pages: [...?_state.pages, newItems], keys: [...?_state.keys, newKey], hasNextPage: !isLastPage, isLoading: false, ); }); } catch (error) { setState(() { _state = _state.copyWith( error: error, isLoading: false, ); }); } } @override Widget build(BuildContext context) => PagedListView( state: _state, fetchNextPage: _fetchNextPage, builderDelegate: PagedChildBuilderDelegate( itemBuilder: (context, item, index) => ImageListTile(item: item), ), ); } ``` -------------------------------- ### Animate PagedListView Status Transitions (Dart) Source: https://github.com/edsonbueno/infinite_scroll_pagination/blob/master/example/example.md Shows how to enable and configure animations for status transitions within a PagedListView. This includes setting a custom transition duration for smoother UI updates. ```dart PagedListView( state: state, fetchNextPage: fetchNextPage, builderDelegate: PagedChildBuilderDelegate( itemBuilder: (context, item, index) => ImageListTile(item: item), animateTransitions: true, // [transitionDuration] has a default value of 250 milliseconds. transitionDuration: const Duration(milliseconds: 500), ), ); ``` -------------------------------- ### Use Slivers for Preceding/Following Items with PagedSliverList (Dart) Source: https://github.com/edsonbueno/infinite_scroll_pagination/blob/master/example/example.md Shows how to use PagedSliverList within a CustomScrollView to include scrollable widgets like headers or footers before or after the main list content. Preceding/following widgets must also be Slivers. ```dart CustomScrollView( slivers: [ SearchInputSliver( onChanged: updateSearchTerm, ), PagedSliverList( state: state, fetchNextPage: fetchNextPage, builderDelegate: PagedChildBuilderDelegate( itemBuilder: (context, item, index) => ImageListTile(item: item), ), ), ], ); ``` -------------------------------- ### Flutter Infinite Scroll Pagination with PagingController Source: https://github.com/edsonbueno/infinite_scroll_pagination/blob/master/README.md This snippet demonstrates the basic usage of the infinite_scroll_pagination package in a Flutter application. It initializes a `PagingController` to manage fetching and displaying paginated data. The controller is configured with a `getNextPageKey` function to determine the next page to fetch and a `fetchPage` function to retrieve data from an API. The `PagedListView` widget then uses this controller to display the items. ```dart class ListViewScreen extends StatefulWidget { const ListViewScreen({super.key}); @override State createState() => _ListViewScreenState(); } class _ListViewScreenState extends State { late final _pagingController = PagingController( getNextPageKey: (state) => state.lastPageIsEmpty ? null : state.nextIntPageKey, fetchPage: (pageKey) => RemoteApi.getPhotos(pageKey), ); @override void dispose() { _pagingController.dispose(); super.dispose(); } @override Widget build(BuildContext context) => PagingListener( controller: _pagingController, builder: (context, state, fetchNextPage) => PagedListView( state: state, fetchNextPage: fetchNextPage, builderDelegate: PagedChildBuilderDelegate( itemBuilder: (context, item, index) => ImageListTile(item: item), ), ), ); } ``` -------------------------------- ### Add Separators to PagedListView (Dart) Source: https://github.com/edsonbueno/infinite_scroll_pagination/blob/master/example/example.md Illustrates how to use the `.separated` constructor for PagedListView to insert custom separator widgets between list items. This is useful for creating visually distinct rows. ```dart PagedListView.separated( state: state, fetchNextPage: fetchNextPage, builderDelegate: PagedChildBuilderDelegate( itemBuilder: (context, item, index) => ImageListTile(item: item), ), separatorBuilder: (context, index) => const Divider(), ); ``` -------------------------------- ### Flutter PagedSliverGrid Product Catalog with Pagination Source: https://context7.com/edsonbueno/infinite_scroll_pagination/llms.txt This Dart code implements a product catalog screen using PagedSliverGrid for infinite scrolling. It fetches product data with pagination, supports category filtering, and integrates into a CustomScrollView with a SliverAppBar. Dependencies include flutter/material.dart and infinite_scroll_pagination/infinite_scroll_pagination.dart. ```dart import 'package:flutter/material.dart'; import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; // Assume Product and ProductApi are defined elsewhere. class Product { final String id; final String name; final String imageUrl; final double price; Product({required this.id, required this.name, required this.imageUrl, required this.price}); } class ProductApi { static Future> fetchProducts(int pageKey, {String? category}) async { // Simulate network delay await Future.delayed(const Duration(seconds: 1)); // Simulate fetching products final List products = List.generate( 20, (index) => Product( id: '$pageKey-$index', name: 'Product ${category ?? 'All'} $pageKey-$index', imageUrl: 'https://via.placeholder.com/150', price: (pageKey * 20 + index) * 1.5, ), ); return products; } } class CategorySelector extends StatelessWidget { final String selected; final ValueChanged onChanged; const CategorySelector({Key? key, required this.selected, required this.onChanged}) : super(key: key); @override Widget build(BuildContext context) { final categories = ['all', 'electronics', 'clothing', 'books']; return Wrap( spacing: 8.0, children: categories.map((cat) => ChoiceChip( label: Text(cat.toUpperCase()), selected: selected == cat, onSelected: (isSelected) => onChanged(cat), )).toList(), ); } } class ProductCatalogScreen extends StatefulWidget { @override State createState() => _ProductCatalogScreenState(); } class _ProductCatalogScreenState extends State { String _selectedCategory = 'all'; late PagingController _pagingController; @override void initState() { super.initState(); _initController(); } void _initController() { _pagingController = PagingController( firstPageKey: 0, // Start from page 0 getNextPageKey: (state) { // Determine the next page key based on the state // For simplicity, assuming pageKey is an integer and increments if (state.pageItems.isEmpty && state.error == null) { // Handle case where first page fetch returns empty but no error return null; } final nextPageKey = state.nextPageKey ?? 0; return nextPageKey + 1; }, fetchPage: (pageKey) => ProductApi.fetchProducts( pageKey, category: _selectedCategory, ), ); // Add listener to handle page errors _pagingController.addErrorListener((error) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Error fetching products: $error')) ); }); } void _onCategoryChanged(String category) { if (_selectedCategory != category) { setState(() => _selectedCategory = category); // Dispose the old controller and initialize a new one to reset pagination _pagingController.dispose(); _initController(); } } @override void dispose() { _pagingController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Product Catalog')), body: PagingListener( controller: _pagingController, builder: (context, state, fetchNextPage) { return CustomScrollView( slivers: [ SliverAppBar( pinned: true, expandedHeight: 120, flexibleSpace: FlexibleSpaceBar( background: Container( alignment: Alignment.bottomLeft, padding: EdgeInsets.all(16), child: CategorySelector( selected: _selectedCategory, onChanged: _onCategoryChanged, ), ), ), ), PagedSliverGrid( state: state, fetchNextPage: fetchNextPage, gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 200, mainAxisSpacing: 10, crossAxisSpacing: 10, childAspectRatio: 0.7, ), builderDelegate: PagedChildBuilderDelegate( itemBuilder: (context, product, index) => Card( child: Column( children: [ Expanded( child: Image.network( product.imageUrl, fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) => const Icon(Icons.error), ), ), Padding( padding: const EdgeInsets.all(8.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( product.name, maxLines: 2, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 4), Text( '$${product.price.toStringAsFixed(2)}', style: Theme.of(context).textTheme.titleMedium, ), ], ), ), ], ), ), firstPageErrorIndicatorBuilder: (_) => Center( child: TextButton( child: const Text('Tap to retry'), onPressed: () => _pagingController.refresh(), ), ), newPageErrorIndicatorBuilder: (_) => const Center(child: Padding(padding: EdgeInsets.all(8.0), child: Text('Error loading more products'))), noItemsFoundIndicatorBuilder: (_) => const Center(child: Text('No products found.')), firstPageProgressIndicatorBuilder: (_) => const Center(child: CircularProgressIndicator()), newPageProgressIndicatorBuilder: (_) => const Center(child: Padding(padding: EdgeInsets.symmetric(vertical: 16.0), child: CircularProgressIndicator())), ), // showNewPageProgressIndicatorAsGridChild: true, // Can be used if needed ), ], ); }, ), ); } } ``` -------------------------------- ### Demonstrate PagingState Extensions in Dart Source: https://context7.com/edsonbueno/infinite_scroll_pagination/llms.txt This Dart code snippet demonstrates the usage of PagingState extensions for operations like accessing flattened items, transforming items using mapItems, filtering items, and accessing controller properties. It utilizes the PagingController to manage pagination state and simulate fetching pages. ```dart import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; // Working with PagingState extensions void demonstrateStateExtensions() { final pagingController = PagingController( getNextPageKey: (state) { // Check if last page was empty if (state.lastPageIsEmpty) { print('Last page was empty, no more pages'); return null; } // For integer keys, use convenience getter final nextKey = state.nextIntPageKey; // Increments last key by 1 print('Next page key: $nextKey'); return nextKey; }, fetchPage: (pageKey) async { // Simulate API call return List.generate(20, (i) => 'Item ${(pageKey - 1) * 20 + i}'); }, ); // Access flattened items (combines all pages) final state = pagingController.value; final allItems = state.items; // Flattens state.pages into single list print('Total items: ${allItems?.length}'); // Transform items using mapItems final uppercaseState = state.mapItems((item) => item.toUpperCase()); print('Transformed items: ${uppercaseState.items}'); // Filter items using filterItems (for computed state only) final filteredState = state.filterItems((item) => item.contains('5')); print('Filtered items: ${filteredState.items}'); // Access controller properties via extensions print('All pages: ${pagingController.pages}'); print('All items: ${pagingController.items}'); print('All keys: ${pagingController.keys}'); print('Current error: ${pagingController.error}'); print('Has next page: ${pagingController.hasNextPage}'); print('Is loading: ${pagingController.isLoading}'); print('Status: ${pagingController.status}'); // Update items via controller pagingController.mapItems((item) => '$item [UPDATED]'); pagingController.dispose(); } ``` -------------------------------- ### Implement PagedListView for Product Listing in Flutter Source: https://context7.com/edsonbueno/infinite_scroll_pagination/llms.txt This Dart code demonstrates how to use PagedListView to implement a product list with search functionality and infinite scrolling. It utilizes the PagingController to manage pagination state, fetch data from an API, and display products in a scrollable and separated list. Error handling and retry mechanisms are included for a better user experience. ```dart import 'package:flutter/material.dart'; import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; class ProductListScreen extends StatefulWidget { @override State createState() => _ProductListScreenState(); } class _ProductListScreenState extends State { String? _searchQuery; late final _pagingController = PagingController( getNextPageKey: (state) => state.lastPageIsEmpty ? null : state.nextIntPageKey, fetchPage: (pageKey) => ProductApi.fetchProducts(pageKey, search: _searchQuery), ); @override void initState() { super.initState(); // Listen for errors to show snackbar _pagingController.addListener(() { if (_pagingController.value.status == PagingStatus.subsequentPageError) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Error loading more items'), action: SnackBarAction( label: 'Retry', onPressed: () => _pagingController.fetchNextPage(), ), ), ); } }); } void _onSearchChanged(String query) { setState(() => _searchQuery = query); _pagingController.refresh(); // Reset pagination with new search } @override void dispose() { _pagingController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: TextField( decoration: InputDecoration(hintText: 'Search products...'), onChanged: _onSearchChanged, ), ), body: RefreshIndicator( onRefresh: () async => _pagingController.refresh(), child: PagingListener( controller: _pagingController, builder: (context, state, fetchNextPage) => PagedListView.separated( state: state, fetchNextPage: fetchNextPage, padding: EdgeInsets.all(16), itemExtent: 80, builderDelegate: PagedChildBuilderDelegate( itemBuilder: (context, product, index) => ProductCard(product: product), firstPageErrorIndicatorBuilder: (context) => ErrorIndicator( error: state.error, onRetry: () => _pagingController.refresh(), ), newPageErrorIndicatorBuilder: (context) => ErrorIndicator( error: state.error, onRetry: fetchNextPage, ), noItemsFoundIndicatorBuilder: (context) => Center( child: Text('No products found'), ), animateTransitions: true, transitionDuration: Duration(milliseconds: 300), ), separatorBuilder: (context, index) => Divider(height: 1), ), ), ), ); } } ``` -------------------------------- ### Flutter PagedGridView for Image Gallery with Pagination Source: https://context7.com/edsonbueno/infinite_scroll_pagination/llms.txt Displays a grid of images with infinite scrolling pagination. It uses PagingController to manage page fetching and PagedGridView to render the items. The UI includes loading indicators, error handling, and a mechanism to fetch new pages when nearing the end of the list. ```dart import 'package:flutter/material.dart'; import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; // Placeholder for ImageItem model and ImageApi service class ImageItem { final String thumbnailUrl; ImageItem({required this.thumbnailUrl}); } class ImageApi { static Future> fetchImages(int pageKey, {required int limit}) async { // Simulate network delay and return dummy data await Future.delayed(Duration(seconds: 1)); return List.generate(limit, (index) => ImageItem(thumbnailUrl: 'https://picsum.photos/seed/$pageKey$index/200/200')); } } class ImageDetailScreen extends StatelessWidget { final ImageItem item; const ImageDetailScreen({Key? key, required this.item}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Image Detail')), body: Center(child: Image.network(item.thumbnailUrl)), ); } } class ImageGalleryScreen extends StatefulWidget { @override State createState() => _ImageGalleryScreenState(); } class _ImageGalleryScreenState extends State { late final _pagingController = PagingController( getNextPageKey: (state) { // Check if we got fewer items than expected (page size is 30) final lastPage = state.pages?.lastOrNull; if (lastPage == null || lastPage.length < 30) return null; return state.nextIntPageKey; }, fetchPage: (pageKey) => ImageApi.fetchImages(pageKey, limit: 30), ); @override void dispose() { _pagingController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Image Gallery')), body: PagingListener( controller: _pagingController, builder: (context, state, fetchNextPage) => PagedGridView( state: state, fetchNextPage: fetchNextPage, padding: EdgeInsets.all(8), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, mainAxisSpacing: 8, crossAxisSpacing: 8, childAspectRatio: 1, ), builderDelegate: PagedChildBuilderDelegate( itemBuilder: (context, item, index) => GestureDetector( onTap: () => Navigator.push( context, MaterialPageRoute( builder: (_) => ImageDetailScreen(item: item), ), ), child: ClipRRect( borderRadius: BorderRadius.circular(8), child: Image.network( item.thumbnailUrl, fit: BoxFit.cover, loadingBuilder: (context, child, progress) { return progress == null ? child : Center(child: CircularProgressIndicator()); }, ), ), ), firstPageProgressIndicatorBuilder: (context) => Center( child: CircularProgressIndicator(), ), noMoreItemsIndicatorBuilder: (context) => Padding( padding: EdgeInsets.all(16), child: Center(child: Text('No more images')), ), invisibleItemsThreshold: 5, // Fetch when 5 items from bottom ), showNewPageProgressIndicatorAsGridChild: true, showNewPageErrorIndicatorAsGridChild: true, showNoMoreItemsIndicatorAsGridChild: true, ), ), ); } } ``` -------------------------------- ### Connect UI to Pagination State with PagingListener in Dart Source: https://context7.com/edsonbueno/infinite_scroll_pagination/llms.txt The PagingListener widget connects a PagingController to the UI, triggering rebuilds when the pagination state changes. It passes the current state and a callback to fetch the next page to its builder function, enabling dynamic UI updates for infinite scrolling lists. ```dart import 'package:flutter/material.dart'; import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; // Assume ApiService.fetchPhotos is defined elsewhere class ApiService { static Future> fetchPhotos(int pageKey) async { // Replace with actual API call await Future.delayed(Duration(seconds: 1)); return List.generate(20, (index) => Photo(id: pageKey * 20 + index, title: 'Photo ${pageKey * 20 + index}', thumbnailUrl: 'https://via.placeholder.com/150')); } } // Define your item type (should match the one in PagingController) class Photo { final int id; final String title; final String thumbnailUrl; Photo({required this.id, required this.title, required this.thumbnailUrl}); } class PhotoListScreen extends StatefulWidget { @override State createState() => _PhotoListScreenState(); } class _PhotoListScreenState extends State { late final PagingController _pagingController; @override void initState() { super.initState(); _pagingController = PagingController( getNextPageKey: (state) => state.lastPageIsEmpty ? null : state.nextIntPageKey, fetchPage: (pageKey) => ApiService.fetchPhotos(pageKey), ); } @override void dispose() { _pagingController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Photos')), body: PagingListener( controller: _pagingController, builder: (context, state, fetchNextPage) { // state is PagingState // fetchNextPage is a callback to load more return PagedListView( state: state, fetchNextPage: fetchNextPage, builderDelegate: PagedChildBuilderDelegate( itemBuilder: (context, item, index) => ListTile( leading: Image.network(item.thumbnailUrl), title: Text(item.title), ), ), ); }, ), ); } } ``` -------------------------------- ### Custom Pagination Logic with PagingController in Flutter Source: https://context7.com/edsonbueno/infinite_scroll_pagination/llms.txt This Flutter widget demonstrates custom pagination logic using PagingController with string keys for cursor-based pagination. It defines custom logic for getNextPageKey based on the last item's ID and uses a PagedListView to display fetched comments. ```dart import 'package:flutter/material.dart'; import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; // Assume Comment and CommentApi are defined elsewhere // class Comment { final String id; final String text; /* ... */ } // class CommentApi { static Future<({List comments, bool hasMore})> fetchComments({required String cursor}) async { /* ... */ } } // class CommentTile extends StatelessWidget { final Comment comment; /* ... */ } // Custom page key logic example class CustomPaginationExample extends StatefulWidget { @override State createState() => _CustomPaginationExampleState(); } class _CustomPaginationExampleState extends State { late final _controller = PagingController( getNextPageKey: (state) { // Custom logic: use cursor-based pagination final pages = state.pages; if (pages == null || pages.isEmpty) return 'initial'; final lastPage = pages.last; if (lastPage.isEmpty) return null; // No more pages // Use last item's ID as next cursor final lastItem = lastPage.last; return 'cursor_${lastItem.id}'; }, fetchPage: (cursor) async { // final response = await CommentApi.fetchComments(cursor: cursor); // return response.comments; // Simulate API call for demonstration: await Future.delayed(const Duration(seconds: 1)); final int pageNum = int.tryParse(cursor?.replaceAll('cursor_', '') ?? '0') ?? 0; return List.generate(10, (i) => Comment(id: '${pageNum + i}', text: 'Comment ${pageNum + i}')); }, ); @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return PagingListener( controller: _controller, builder: (context, state, fetchNextPage) => PagedListView( state: state, fetchNextPage: fetchNextPage, builderDelegate: PagedChildBuilderDelegate( itemBuilder: (context, item, index) => CommentTile(comment: item), ), ), ); } } // Dummy classes for compilation class Comment { final String id; final String text; Comment({required this.id, required this.text}); } class CommentApi { static Future<({List comments, bool hasMore})> fetchComments({required String cursor}) async { await Future.delayed(const Duration(seconds: 1)); final int pageNum = int.tryParse(cursor?.replaceAll('cursor_', '') ?? '0') ?? 0; return (comments: List.generate(10, (i) => Comment(id: '${pageNum + i}', text: 'Comment ${pageNum + i}')), hasMore: true); } } class CommentTile extends StatelessWidget { final Comment comment; const CommentTile({super.key, required this.comment}); @override Widget build(BuildContext context) => ListTile(title: Text(comment.text), subtitle: Text('ID: ${comment.id}')); } ``` -------------------------------- ### PagedPageView for Horizontal Article Swiping (Dart) Source: https://context7.com/edsonbueno/infinite_scroll_pagination/llms.txt Implements a paginated horizontal page view for displaying articles. It uses `PagingController` to manage data fetching and `PagedPageView` to render the scrollable list. The `ArticleApi.fetchArticles` function is assumed to handle the actual data retrieval. ```dart import 'package:flutter/material.dart'; import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; // Assume Article and ArticleApi are defined elsewhere class Article {} class ArticleApi { static Future> fetchArticles(int pageKey, {required int startFrom}) async => []; } class ArticleReaderScreen extends StatefulWidget { final int initialArticleId; const ArticleReaderScreen({required this.initialArticleId}); @override State createState() => _ArticleReaderScreenState(); } class _ArticleReaderScreenState extends State { late final PageController _pageController; late final PagingController _pagingController; int _currentPage = 0; @override void initState() { super.initState(); _pageController = PageController(); _pagingController = PagingController( getNextPageKey: (state) => state.lastPageIsEmpty ? null : state.nextIntPageKey, fetchPage: (pageKey) => ArticleApi.fetchArticles( pageKey, startFrom: widget.initialArticleId, ), ); } @override void dispose() { _pageController.dispose(); _pagingController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Article ${_currentPage + 1}'), actions: [ IconButton( icon: Icon(Icons.refresh), onPressed: () => _pagingController.refresh(), ), ], ), body: PagingListener( controller: _pagingController, builder: (context, state, fetchNextPage) => PagedPageView( state: state, fetchNextPage: fetchNextPage, pageController: _pageController, scrollDirection: Axis.horizontal, onPageChanged: (index) { setState(() => _currentPage = index); }, builderDelegate: PagedChildBuilderDelegate
( itemBuilder: (context, article, index) => SingleChildScrollView( padding: EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( article.title, style: Theme.of(context).textTheme.headlineMedium, ), SizedBox(height: 8), Text( 'By ${article.author}', style: Theme.of(context).textTheme.bodySmall, ), SizedBox(height: 16), Text(article.content), ], ), ), firstPageErrorIndicatorBuilder: (context) => Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text('Failed to load articles'), ElevatedButton( onPressed: () => _pagingController.refresh(), child: Text('Retry'), ), ], ), ), invisibleItemsThreshold: 2, ), ), ), ); } } ```