Easy Paginated Lists In Flutter

David Djordjevic
Prime Holding JSC
Published in
5 min readApr 12, 2021

As developers, we know that time is precious. For that reason, we try to make tools that will help other developers solve particular problems and shorten development time. Some of the most common issues encountered in almost every app are how to refresh the data and display it in certain amounts (or as we devs say — to paginate it). So in this article, we will look at the new rx_bloc_list package, which helps implement thеse features with minimal effort.

Sample app with data pagination and pull-to-refresh feature using the rx_bloc_list package

Who is the villain here?

When working with apps that deal with large volumes of data, a good approach is to have an API that will allow us to fetch data in smaller amounts. By doing this, we improve the user experience by fetching “pages” of a certain size, allowing for smaller and faster server response. In this way, the presentation of the data is less overwhelming for the user and it improves the navigation between pages.

Showcase of pagination using pages on the bottom of Google’s search page
Pagination implemented with the use of pages

There are many good examples of how pagination can be used, such as Google search results and page selection at the bottom of the page. One of the most common uses can be seen when implementing a news feed on social media platforms. Presenting all the data at once would not make much sense, so by implementing infinite scrolling we can bring data consumption down to manageable levels.

Example of pagination implemented as infinite scrolling on Twitter newsfeed
Pagination implemented as an infinite-scroll

Nowadays we are surrounded by data that is constantly changing and being replaced by new. Accordingly, users are constantly looking for new content, so providing the tools to access it would be a smart choice. The most logical way would be to allow the user to refresh the data. This can be achieved in multiple ways, but the mobile way to do it would be through a pull-to-refresh implementation.

From the above, we can see that data pagination and data refreshing are common features that go hand in hand. Reworking a solution containing these features can take time, especially in new projects. To tackle this, we’ve created the rx_bloc_list package, which helps you easily implement these features, giving you all the customizations you need.

The rx_bloc_list hero

The rx_bloc_list package is part of the RxBloc ecosystem. This means that we can take advantage of it along with the powerful RxBlocs and other rx_bloc packages. So how do we use rx_bloc_list in our app?

After including the package in pubspec.yamland importing it you can start writing code. Similarly to other rx_bloc widgets, we start by defining which bloc and data model we are using for our RxPaginatedBuilder widget:

RxPaginatedBuilder<UserBlocType,User>.withRefreshIndicator(
state: (bloc) => bloc.states.paginatedList,
onBottomScrolled: (bloc) => bloc.events.loadPage(),
onRefresh: (bloc) async {
bloc.events.loadPage(reset: true);
return bloc.states.refreshDone;
},
...
)

We start by defining which blocstate we listen to (Note: you can find an example of the bloc implementation here). Every time a change happens in that state, the widget will rebuild.

The onBottomScrolled callback is required as it executes every time the user reaches the end of the list. You can use it to load the next page or have your own custom logic.

Similar to the previous one, the onRefresh callback is run once the user tries to refresh the page. It is worth noting that this parameter is absent from the widget using the default constructor (check the wrapperBuilder parameter instead).

However, we’re not done yet! The only remaining thing to do is to define how we are presenting the data. For that we have several build parameters which we can specify:

RxPaginatedBuilder<UserBlocType,User>.withRefreshIndicator(
state: (bloc) => bloc.states.paginatedList,
onBottomScrolled: (bloc) => bloc.events.loadPage(),
onRefresh: (bloc) async {
bloc.events.loadPage(reset: true);
return bloc.states.refreshDone;
},
buildLoading: (context, list, bloc) => YourProgressIndicator(),
buildError: (context, list, bloc) =>
YourErrorWidget(error: list.error!),
buildSuccess: (context, list, bloc) => ListView.builder(
itemBuilder: (context, index) {
final user = list.getItem(index);

if (user == null) {
// We're loading more data, show a loading indicator
return YourProgressIndicator();
}

return YourListTile(user: user);
},
itemCount: list.itemCount),
)

While the data is being loaded for the very first time, we can define which widget will be displayed by using the buildLoading parameter. It accepts a callback that has to return a widget, giving you access to other things such as the build context, the actual list of data, and the BLoC.

Similarly, the buildError parameter lets you define a widget that will be shown if an error occurs. Perhaps you want to display a retry button if the user has no internet connection or something went wrong. Or you may even want to show a snack bar. The buildError parameter can be used for that and more.

Every time the data has been fetched, it can be presented using the buildSuccess parameter. Like with the previous two build parameters, you have access to the build context, a list of paginated data and the BLoC.

The heroes’ apprentice

Once you have defined these parameters, you’re all set up. Easy as that! But now you may ask: how is the RxPaginatedBuilder unique in what it does?

The RxPaginatedBuilder uses internally PaginatedList instead of just a List type. With PaginatedList we can take advantage of the best of both worlds, pagination logic and lists all in one. It provides common extensions that facilitate the pagination of data while eliminating the need for additional model-specific logic.

So to answer the previous question, the combination of the PaginatedList and RxPaginatedBuilder is what makes rx_bloc_list stand out. While the RxPaginatedBuilder is presenting the data to the front-end, the PaginatedList handles all the necessary logic behind the scenes. This gives the developers the needed tools allowing them to focus more on their app.

Initial loading, infinite-scrolling, and refreshing examples

Final thoughts

I hope this short article gives you some idea of how you can use the rx_bloc_list package in your favor. You can find the full example here.

If you have a suggestion or an improvement, feel free to write us in the official rx_bloc repo issue tracker. We’d like to hear from you!

Stay curious!

Related articles

  1. Building complex apps in Flutter through the power of reactive programming
  2. Introducing rx_bloc ecosystem — Part 1
  3. Introducing rx_bloc: Part 2
  4. Introducing flutter_rx_bloc: Part 3
  5. RxBloc Intellij Plugin

--

--