Infinite Scrolling Pagination in Flutter

Learn how to implement infinite scrolling pagination (also known as lazy loading) in Flutter using the Infinite Scroll Pagination package. By Edson Bueno.

Leave a rating/review
Download materials
Save for later

Forget about the gold at the end of the rainbow. Have you ever wondered what’s at the end of your Instagram feed? Has anyone ever scrolled that far? You could find the password to a secret Swiss bank account, the key to immortality or the answer to the meaning of life. Oh, the possibilities.

Today you’ll not only solve that mystery, but will also master this very same infinite scrolling technique. The technique has many names, including infinite scrolling pagination, endless scrolling pagination, auto-pagination, lazy loading pagination, continuous scroll pagination and progressive loading pagination.

By the end of this tutorial, you’ll know:

  • What infinite scrolling pagination is
  • What its alternatives are
  • Why it’s such a big deal
  • How to go from a non-paginated app to a paginated one
  • How to take that knowledge to any project you work on
Note: This article won’t cover the basics of widgets and networking. For that, please refer to the linked tutorials.

Getting Started

Download the starter project by clicking the Download Materials button at the top or bottom of the tutorial.

In case you didn’t know, has an app for Android and iOS called (drum roll, please): raywenderlich. The thing is, that one is for videos. Today you’ll be working on a low budget version for articles: readwenderlich!

Unzip the downloaded file and open Android Studio 4.1 or later. You can use Visual Studio Code instead, but you might need to tweak some instructions to follow along.

Click on Open an existing Android Studio project and choose the starter folder from your unzipped download.

Now, download your dependencies by double-clicking pubspec.yaml on the left-side panel (1) and then clicking Pub get (2) at the top of your screen.

Instructions on downloading pubspec.yaml dependencies.

Finally, build and run. If everything went OK, you should see something like this:

Starter version of the sample project.

The starter project only fetches the first 20 items for the filtering and sorting options you set. For example, if you filter by Flutter and sort by Most Popular, you’ll see the 20 most popular Flutter articles.

Diving Into the Code

There are many files in the project, but the ones you need to be familiar with are:

  1. lib/ui/preferences/list_preferences.dart: A plain Dart class gathering all the filtering and sorting options the user selected.
  2. lib/ui/list/article_list_view.dart: A widget that receives ListPreferences and uses its information to fetch and display a list of articles. The image below highlights the exact part of the screen controlled by this widget.

ArticleListView widget highlighted on the sample project.

The starter project’s state management approach is setState, because that’s the neutral ground for all Flutter developers. But what you’ll learn here applies to any strategy.

Throughout the tutorial, you won’t change ArticleListView. Instead, you’ll write a substitute for it from scratch.

Rather than a top 20, you want readwenderlich to be a complete catalog of all the articles on For that, you’ll rely on infinite scrolling pagination.

Understanding Pagination

Two guys walk into a bar… just kidding! It’s actually one person, and it’s a woman. You’ll call her the user, while the bar is your app.

The night is just beginning, and you, the bartender, know the user will consume lots of data drinks. What you don’t yet know is how many.

You’re committed to providing the best customer experience, so you start wondering what would be the optimal way of serving your user. You then grab some nearby napkins and start writing out your options:

Napkin sketch of an eager way of serving drinks.

If you think of drinks like batches of items in a list — the so-called pages — the method above is how non-paginated apps work. It’s no surprise that there aren’t bars that serve like this, but unfortunately, this is how most apps work. Which leads you to another option:

Napkin sketch of the conventional lazy way of serving drinks.

If you’re familiar with the above, don’t worry. It’s not only because you’ve been going a lot to bars, but also because that’s how good old web-like pagination works:

Page selection bar from Google Search.

Every time you finish a page, you have to manually ask for another. Good, but still not perfect. It’d be better if you could automate the process.

Automating Pagination

With no luck on your quest for the holy grail of drink service, you’re considering settling for the conventional way… until the customer comes to you with a fantastic idea:

Woman asking a bartender to hand her a drink any time she's lacking one.

That sounds amazing! But you’re cautious, and you don’t want to make a decision before planning everything out on paper a napkin:

Napkin sketch of an automated way of serving drinks.

It’s a done deal! Thank goodness; that was your last napkin!

Luckily, someone did science a favor and recorded the experiment:

Bartender actively serving drinks every time the customer is without one.

This process is the same as infinite scrolling pagination: As the user scrolls down your screen, you watch what’s going on and fetch more data before she hits the bottom of the screen, giving her the smooth experience of scrolling an infinite list.

Look, all the cool kids apps are doing it:

Twitter, Facebook and Instagram using infinite scrolling pagination.

So now that you know what you’ll be doing, it’s time to roll up your sleeves.

Creating a Paginated ArticleListView

Start off by creating a new file inside lib/ui/list called paged_article_list_view.dart. Put the following inside the file:

import 'package:flutter/material.dart';
import 'package:readwenderlich/data/repository.dart';
import 'package:readwenderlich/entities/article.dart';
import 'package:readwenderlich/ui/exception_indicators/empty_list_indicator.dart';
import 'package:readwenderlich/ui/exception_indicators/error_indicator.dart';
import 'package:readwenderlich/ui/list/article_list_item.dart';
import 'package:readwenderlich/ui/preferences/list_preferences.dart';

// 1
class PagedArticleListView extends StatefulWidget {
  const PagedArticleListView({
    // 2
    @required this.repository,
    // 3
    Key key,
  })  : assert(repository != null),
        super(key: key);

  final Repository repository;
  final ListPreferences listPreferences;

  _PagedArticleListViewState createState() => _PagedArticleListViewState();

class _PagedArticleListViewState extends State<PagedArticleListView> {
  // 4
  ListPreferences get _listPreferences => widget.listPreferences;
  // TODO: Instantiate a PagingController.

  // 5
  Widget build(BuildContext context) => const Placeholder();

Here’s what you just did:

  1. This is the widget that will replace the old non-paginated list onscreen. This is your paginated version of ArticleListView.
  2. You’re asking a Repository instance from the enclosing widget. Repository comes with the starter project and helps you communicate with the remote API.
  3. Similarly, you’re now asking a ListPreferences instance from the enclosing widget.
  4. You’re creating a computed property to help you access the ListPreferences received in the previous step without the need to type widget.listPreferences every time.
  5. The Placeholder fills the screen with a giant X while you don’t have the paginated list widget figured out yet. You’ll get back to this shortly.

Now you need to be able to visualize what you just created.