FlutterArtist ListView Infinite Scroll Pagination Example
Infinite Scroll pagination is a application design technique where content loads dynamically as the user scrolls down a page, rather than being divided into discrete pages like traditional pagination. Instead of clicking through page numbers, users experience a continuous stream of content without interruptions. This method is often used on applications with large amounts of media-heavy content or where the goal is to encourage users to explore continuously, like social media platforms.

Example scenario: Demo48a
This example displays a list of Countries in a ListView. When the user scrolls to the last element, the system automatically fetches more data and appends it to the current list.
No ADS
Related examples:FlutterArtist Demo:
- Download FlutterArtist Demo
- Flutter ListView tutorial with examples
2. Country48aShelf
The structure of Country48aShelf is straightforward, consisting of a single Block. Note the pageable parameter in BlockConfig, which allows you to set the maximum number of ITEM(s) to be loaded per request.
country48a_shelf.dart
class Country48aShelf extends Shelf {
@override
ShelfStructure defineShelfStructure() {
return ShelfStructure(
filterModels: {},
blocks: [
Country48aBlock(
name: Country48aBlock.blkName,
description: null,
config: BlockConfig(
pageable: Pageable(pageSize: 20),
),
filterModelName: null,
formModel: null,
childBlocks: [],
),
],
);
}
Country48aBlock findCountry48aBlock() {
return findBlock(Country48aBlock.blkName) as Country48aBlock;
}
}3. Country48aListItemsView
The key to this technique lies in using NotificationListener to capture the ScrollEndNotification. When the user finishes scrolling and the scroll position reaches the bottom, we trigger the data loading process.
NotificationListener<ScrollEndNotification>(
onNotification: _onScrollEndNotification,
child: ListView(
padding: const EdgeInsets.fromLTRB(0, 0, 8, 0),
children: block.items
.map(
(item) => Country48aListItem(
countryInfo: item,
selected: block.isCurrentItem(item),
onCountryPressed: _onCountry48aPressed,
),
)
.toList(),
),
)No ADS

This function checks if the user has reached the end of the list and if there are more pages available to fetch.
_onScrollEndNotification()
bool _onScrollEndNotification(ScrollEndNotification notification) {
PaginationInfo? pagination = block.paginationInfo;
if (pagination == null) {
return false;
}
if (notification.metrics.pixels == notification.metrics.maxScrollExtent &&
pagination.currentPage < pagination.totalPages) {
_loadMore();
return true;
}
return false;
}Utilizing the queryMore() method, which automatically increments the currentPage and merges the new results into the Block's current list.
_loadMore()
Future<void> _loadMore() async {
await block.queryMore();
}Full code:
country48a_list_items_view.dart
class Country48aListItemsView extends BlockItemsView<Country48aBlock> {
const Country48aListItemsView({required super.block, super.key});
@override
Widget buildContent(BuildContext context) {
return NotificationListener<ScrollEndNotification>(
onNotification: _onScrollEndNotification,
child: ListView(
padding: const EdgeInsets.fromLTRB(0, 0, 8, 0),
children: block.items
.map(
(item) => Country48aListItem(
countryInfo: item,
selected: block.isCurrentItem(item),
onCountryPressed: _onCountry48aPressed,
),
)
.toList(),
),
);
}
bool _onScrollEndNotification(ScrollEndNotification notification) {
PaginationInfo? pagination = block.paginationInfo;
if (pagination == null) {
return false;
}
if (notification.metrics.pixels == notification.metrics.maxScrollExtent &&
pagination.currentPage < pagination.totalPages) {
_loadMore();
return true;
}
return false;
}
Future<void> _loadMore() async {
await block.queryMore();
}
void _onCountry48aPressed(CountryInfo countryInfo) {
// Write Log:
FlutterArtist.codeFlowLogger.addMethodCall(
ownerClassInstance: this,
currentStackTrace: StackTrace.current,
parameters: null,
);
block.refreshItemAndSetAsCurrent(item: countryInfo, navigate: null);
}
}4. Using Sentinel Widget
Besides listening to scroll events via NotificationListener, there is another more elegant and modern technique: the Sentinel Trigger. Instead of calculating scroll coordinates, we place a special Widget at the end of the list. When this Widget appears in the ViewPort, it automatically triggers the data loading command.
Implementation Concept:
We utilize ListView.builder and append a virtual element to the end of the list as long as there is more data to fetch.
ListView.builder
ListView.builder(
itemCount: items.length + (hasMore ? 1 : 0),
itemBuilder: (context, index) {
if (index < items.length) {
return CountryListItem(countryInfo: items[index]);
}
// Sentinel Widget
return _buildSentinelTrigger();
},
)_buildSentinelTrigger()
Widget _buildSentinelTrigger() {
return Builder(
builder: (context) {
// Automatically trigger the query when this widget is rendered.
Future.microtask(() => block.queryMore());
// Display a custom loading indicator here.
return const CustomLoadingIndicator();
},
);
}Demo48b

This technique keeps your source code extremely clean and maintainable as Flutter automatically manages when to fire the "trigger."
The complete source code for this modern approach is available in the Demo48b example.
No ADS
FlutterArtist
- Basic concepts in Flutter Artist
- FlutterArtist Block ex1
- FlutterArtist Filter Example
- FlutterArtist FilterModel MultiOptFilterCriterion ex1
- FlutterArtist FilterInput Example 1
- FlutterArtist Form ex1
- The idea of designing filter models in FlutterArtist
- FlutterArtist FormModel.patchFormFields() Ex1
- FlutterArtist BlockQuickItemUpdateAction Example
- FlutterArtist BlockNumberPagination Ex1
- FlutterArtist GridView Infinite Scroll Example
- FlutterArtist BlockQuickMultiItemCreationAction Example
- FlutterArtist ListView Infinite Scroll Pagination Example
- FlutterArtist Pagination
- FlutterArtist Sort DropdownSortPanel Example
- FlutterArtist Dio
- FlutterArtist BlockBackendAction Example
- FlutterArtist BackgroundWebDownloadAction Example
- FlutterArtist StorageBackendAction ex1
- FlutterArtist Block External Shelf Event ex1
- FlutterArtist Filter FormBuilderMultiDropDown Ex1
- FlutterArtist Master-detail Blocks ex1
- FlutterArtist Scalar ex1
- FlutterArtist Pagination Davi table Infinite Scroll Ex1
- FlutterArtist Filter Tree FormBuilderField ex1
- FlutterArtist Filter FormBuilderRadioGroup ex1
- FlutterArtist Form Parent-child MultiOptFormProp ex1
- FlutterArtist Manual Sorting ReorderableGridView Example
- FlutterArtist Manual Sorting ReorderableListView
- FlutterArtist Scalar External Shelf Event ex1
- FlutterArtist Code Flow Viewer
- FlutterArtist Log Viewer
- FlutterArtist config
- FlutterArtist StorageStructure
- FlutterArtist Debug Storage Viewer
- FlutterArtist DebugMenu
- FlutterArtist Debug UI Components Viewer
- FlutterArtist Debug Shelf Structure Viewer
- FlutterArtist Context Provider Views
- FlutterArtist FilterModelStructure ex1
- FlutterArtist FilterModelStructure ex2
- FlutterArtist FilterModelStructure ex3
- FlutterArtist Internal Shelf Event ex1
- FlutterArtist Deferring External Shelf Events (Ex1)
- FlutterArtist DropdownSortPanel
Show More

