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.
๐ Demo
๐งฐ Weโll be using,
- Punk API to get some beers ๐.
- BLoC patten for state-management.
- 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.
- 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.
- Iโve organized the project files feature-wise. Here, we have got only a single feature i.e. display freshly brewed beers.
๐น 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.
- 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.
- 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.
- There will be 1 event: BeerFetchEvent.
- 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.
- 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.
- 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.
- Finally, there is the BeetListItem widget which shows individual BeerModel data using ExpansionTile , Text , Image.network, and some SizedBox es.
Thatโs it for this one. Thank you for reading this ๐
You can find the complete source code of this app in this repository.
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.
For any other issues feel free to reach out to me via Twitter: https://twitter.com/piedcipher


Top comments (0)