Hey Everyone 👋, Today we’re going to learn about Pagination in Flutter. Pagination is considered as one of the best practices while loading a large chunk of data from an API. Pagination offers better performance and a jank free experience to the user.
Json Serializable for automatic serialization-deserialization of the API response.
So let’s get started 🍻
🔌 Prerequisites
Flutter SDK
IDE of choice: VSCode / Intellij Idea / Android Studio
Dart & Flutter Plugins for IDE
🔨 Initial Setup
After creating a fresh flutter project, add the following dependencies in pubspec.yaml.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Delete everything from the main.dart and paste the following content. It has a main method, app routes, and a bloc observer for debugging purposes. Next, we’ll be building the DisplayBeerScreen widget.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
I’ve organized the project files feature-wise. Here, we have got only a single feature i.e. display freshly brewed beers.
Directory Structure
🎹 Coding
Let’s design our BeerRepository. It is going to be a singleton. It contains a method getBeers which requires a page number. Page number will be passed from BeerBloc. I have set the _perPage limit to 10.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
We’ll be creating a model that will map the API response to Dart class (model) using the fromJson method. The model has 5 fields: id, name, tagline, description, and imageUrl. Don’t forget to run flutter pub run build_runner build command to generate the serialization/deserialization code.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Now let’s create business logic component related files & classes: BeerBloc , BeerState , and BeerEvent.
There will be 4 states: Initial , Loading , Success & Error. These are self-explanatory.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Now, let’s design BeerBloc. It will handle BeerFetchEvent and yield an appropriate state to the UI. It’ll maintain the current page number and isFetching boolean flag to prevent duplicate event requests. BeerRepository is injected via BeerBloc constructor. The value of the page is incremented by 1 after the Success state is yielded.
We’ll be yielding BeerInitialState as the Initial State of the UI.
When BeerFetchEvent is delegated to the BeerBloc , first it’ll yield the BeerLoadingState. Then, it’ll call the getBeers method of BeerRepository.
If the return type of response is of type http.Response then status code of the response is checked. If it’s OK , then we’ll parse the JSON using jsonDecode from dart:convert , and map individual objects to BeerModel. Now, these results can be passed to UI by yielding BeerSuccessState.
Otherwise, BeerErrorState is yielded to UI to notify about errors that occurred during API call.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
At last, comes the UI building part. I’ve kept it clean and simple. There are 3 widgets that compose the entire UI: DisplayBeerScreen, BeerBody, and BeerListItem.
DisplayBeerScreen widget displays an AppBar , injects BeerBloc instance via BlocProvider , and renders BeerBody widget.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
BeerBody widget returns a BlocConsumer to build the reactive UI. BeerBody has 2 fields: a list to hold BeerModel s (_beers), and a ScrollController.
listener callback of BlocConsumer conditionally displays the appropriate message using a SnackBar.
builder callback of BlocConsumer conditionally builds the widgets. Let’s understand that logic:
Case (1): If the current state is either initial or loading and the value of _beers is empty. In this case, a progress bar is shown. This condition only occurs when the user opens the app for the first time.
Case (2): If the current state is an error and the value of _beers is empty. In this case, IconButton with retry action is shown. This condition only occurs when the user opens the app for the first time and there is some error due to the internet or any other exception. If the user presses the retry then the value of isFetching field of BeerBloc is set to false.
Case (3): If the current state is a success. In this case, beers (API response) yielded by the BLoC are added to the _beers list & isFetching field of BeerBloc is set to false.
By default, the builder returns a ListView to render the _beers using the BeerListItem widget. ListView ’s controller is set to a ScrollController instance. This controller has a listener which adds a BeerFetchEvent to BeerBloc (to get the response from the next page of the API) if the user has reached the end of the list-view. It also sets isFetching field of BeerBloc is set to true to prevent duplicate event requests.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Finally, there is the BeetListItem widget which shows individual BeerModel data using ExpansionTile , Text , Image.network, and some SizedBox es.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
If you find this post useful, press👏 button as many times as you can and share this post with others. You can leave your feedback/suggestions in the comments 💬 below.
We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.
Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.
Top comments (0)