A simple workaround for the infinite scroll (Pagination) in Flutter ListView!
We face many requirements in our daily flutter app development for infinite scrolling (pagination)in ListView.
We start creating ListView then create a list of widgets and set scroll listener and set error or failure scenarios and then lastly, stop the pagination when data loading is completed.
What if I tell you that you have one widget in which you just need to pass your callback with data which will be called when user reach to the end of the list.
So I’ll first mention how we can implement pagination with a straight forward way and then we’ll look for some better approach.
So one initial note to mobile developers that in Android or iOS we were reusing one single view in the list with different data, for eg. if in one viewport there are 7 items visible then, view objects will be created for the same to be reused.
But in Flutter, architecture is designed in a manner that whenever data is updated all view components(or you can say widget) will be recreated.
Widget _createListView(List<NewsResponseItem> itemList) {
ScrollController _scrollController = ScrollController();
_scrollController.addListener(() {
if (_scrollController.position.maxScrollExtent ==
_scrollController.position.pixels) {
if (!isLoading) {
isLoading = !isLoading;
// Perform event when user reach at the end of list (e.g. do Api call)
}
}
});
return ListView.builder(
controller: _scrollController,
itemCount: itemList.length,
itemBuilder: (BuildContext context, int index) =>
NewsListItem(itemList[index]));
}
Widget _getPage(EventModel model) {
isLoading = false;
if (model.progress) {
// Return progress widget if item list is empty else return same list widget to rebuild
if (itemList.isEmpty) {
return ProgressIndicatorWidget();
} else {
return _createListView(itemList);
}
} else if (model.response != null) {
// Add/remove null value to show/hide progress bar and update list widget with newer items.
if (itemList.contains(null)) {
itemList.remove(null);
}
itemList.addAll(model.response);
itemList.add(null);
return _createListView(itemList);
} else {
// Show error message when unable to fetch items.
if (itemList.isEmpty) {
return ErrorTextWidget(model.error);
} else {
return _createListView(itemList);
}
}
}
Widget _rowWidget(BuildContext context, NewsResponseItem newsResponseItem) {
if (newsResponseItem == null) {
return Center(
child: ProgressIndicatorWidget(),
);
} else {
return Material(
child: InkWell(
onTap: () {
_navigateToWebViewScreen(context, newsResponseItem);
},
child: Padding(
padding: EdgeInsets.only(top: 16, bottom: 16, left: 10, right: 10),
child: Container(
decoration: BoxDecoration(
color: Colors.grey[50],
boxShadow: [BoxShadow(color: Colors.grey, blurRadius: 10)],
border: Border.all(color: Colors.grey[400], width: 1.0),
borderRadius: BorderRadius.circular(5)),
child: Padding(
padding: EdgeInsets.all(10),
child: Text(
newsResponseItem.title,
style: TextStyle(fontSize: 18),
),
),
),
),
),
);
}
}
You can get the full source for above implementation from here
Now let’s head up to some smart workaround.
So I figured out some common and boilerplate code and made a generalized solution and created a library for that named Flutter Pagination Helper.
Here are the steps to implement pagination in flutter
dependencies:
flutter_pagination_helper: ^1.0.0+5
class CustomProgressWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: PaginatedListWidget(
progressWidget: Center(
child: Text("Loading..."),
),
itemListCallback: OnScrollCallback()),
),
);
}
}
where progressWidget is an optional parameter to assign your custom progress widget and itemListCallback is required parameter in which you need to provide the call back for data in the form of Future.
class OnScrollCallback<T extends Widget> extends ItemListCallback {
@override
Future<EventModel<T>> getItemList() {
// TODO: implement getItemList
return null;
}
}
Where EventModel is model which stores different states such as progress bar visibility, data, and error message as below.
class EventModel<T extends Widget> {
final bool progress;
final List<T> data;
final String error;
final bool stopLoading;
EventModel({this.progress, this.data, this.error, this.stopLoading});
}
And your output for the list with default progress widget will be as bellow.
And the output for a list with custom progress widget will be as bellow.
For the major requirement of Flutter pagination ListView, Flutter Pagination Helper will be a more suitable option and for more specification, you can go with the first option.
You can find the full source for the library at
Have a product idea?
Talk to our experts to see how you can turn it
into an engaging, sustainable digital product.