Paginate API results with BLoC in Flutter
Tags: dart, flutter, httprequest, bloc
Published: 2020-09-16 15:18:56

---
title: Paginate API results with BLoC in Flutter
published: true
date: 2020-09-16 13:02:05 UTC
tags: dart,flutter,httprequest,bloc
canonical_url: 
---

![](https://cdn-images-1.medium.com/max/1024/0*czaFDiRDfxMYFNq5)<figcaption>Photo by <a href="https://unsplash.com/@christinhumephoto?utm_source=medium&amp;utm_medium=referral">Christin Hume</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption>

Hey Everyone 👋, Today we’re going to learn about Pagination in [Flutter](https://flutter.dev/). 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

![](https://cdn-images-1.medium.com/max/600/1*MVFw7FPkNIkWnYlNaRh0FA.gif)

### 🧰 We’ll be using,

- [Punk API](https://punkapi.com/documentation/v2) to get some beers 😋.
- [BLoC](https://pub.dev/packages/flutter_bloc) patten for state-management.
- [Json Serializable](https://pub.dev/packages/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.

{% gist https://gist.github.com/piedcipher/9eed8232312b45c9332332b9e47d2f7a %}

- 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.

{% gist https://gist.github.com/piedcipher/bbce3fea2179cd38a5095aaeca79ba4c %}

- I’ve organized the project files feature-wise. Here, we have got only a single feature i.e. display freshly brewed beers.

![](https://cdn-images-1.medium.com/max/319/1*NbFvzMiZEVtwZ5N8GcFlnA.png)<figcaption><strong>Directory Structure</strong></figcaption>

### 🎹 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.

{% gist https://gist.github.com/piedcipher/5df7564c6cf9feb58422f193a3cf13b3 %}

- 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.

{% gist https://gist.github.com/piedcipher/e4485b1cd59a4f35ec2ebc0dffb89b57 %}

- 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.

{% gist https://gist.github.com/piedcipher/684c8177931b5eb3b40383e9eda95d80 %}

- There will be 1 event: **BeerFetchEvent**.

{% gist https://gist.github.com/piedcipher/8d3f921daa92bd54e6dce3ecb6531190 %}

- 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.

{% gist https://gist.github.com/piedcipher/52a9d70b8f77de014c0db8bbef338f7d %}

- 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.

{% gist https://gist.github.com/piedcipher/740f7e6d8316b4b8ccd25603d6de3a1c %}

- **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.

{% gist https://gist.github.com/piedcipher/6d038059369804726ff49eaf1e44dd6a %}

- Finally, there is the **BeetListItem** widget which shows individual **BeerModel** data using **ExpansionTile** , **Text** , **Image.network,** and some **SizedBox** es.

{% gist https://gist.github.com/piedcipher/e1d58c214aeef770470ea3a85a0b1df3 %}

### That’s it for this one. Thank you for reading this 💙

**_You can find the complete source code of this app in this repository._**

[piedcipher/beer-app](https://github.com/piedcipher/beer-app)

**_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](https://twitter.com/piedcipher)