Paging Library for Android With Kotlin: Creating Infinite Lists

In this tutorial, you’ll build a simple Reddit clone that loads pages of information gradually into an infinite list using Paging 3.0 and Room. By Harun Wangereka.

4.8 (15) · 2 Reviews

Download materials
Save for later
Share
Update note: Harun Wangereka updated this tutorial. Alex Sullivan wrote the original.

Lists are an essential part of mobile development. A list is a great option when you have a lot of information to display, and it contains items of similar type.

Some lists don’t have a fixed size. There’s often too much information to display all at once. For example, think about your Facebook feed. If Facebook loaded and displayed every post, image or Like every time you opened it, the list would never finish loading.

Instead, Facebook uses infinitely loading lists, as do most apps with large datasets. In an infinitely loading list, you download only as much data as the user will immediately consume. Then you load more data as the user scrolls farther in the list.

Implementing infinitely loading lists is an arduous process. To make it easier, Google provides a Paging library. It includes several core classes to help you create progressively loading lists. These classes abstract the logic that determines when to load new items. The Paging library also provides tie-ins to other core architecture components. These include the LiveData streaming library and the Room storage library.

In this tutorial, you’ll build a simple Reddit clone. It uses the Android Paging library to load information into an infinite list, page by page. During this process you’ll learn several important skills:

  • Implement the Paging 3.0 library.
  • Add headers and footers in infinitely loading lists.
  • Learn how to use Room with the Paging Library.

Now it’s time to get to work!

Getting Started

Start by downloading the materials for this tutorial using the Download Materials button at the top or bottom of this tutorial. Fire up Android Studio 4 or later, and import the starter project.

You’ll see that there’s a networking package with several model classes. It also contains a RedditService class that provides the Reddit API.The project also includes several empty classes that you’ll flesh out as you work through the tutorial.

Now, build and run. You’ll see an empty screen.

Paging Library Reddit Clone

To begin populating the screen, you’ll use the Reddit API which returns pages of Reddit posts.

Defining a PagingSource

Your first task is to create a PagingSource that will pull down Reddit information. A PagingSource is a class that manages the loading of data in small sets/pages for your list. In the data from the Reddit API, you receive a list of Reddit posts corresponding to a single page via the dist key, which is known as the a one-page key.

Head over to repositories/RedditPagingSource.kt:

class RedditPagingSource(private val redditService: RedditService) :
    // 1 & 2
    PagingSource<String, RedditPost>() {

    override suspend fun load(params: LoadParams<String>): 
        LoadResult<String, RedditPost> {
            TODO("not implemented")
    }
}

As you can see above, PagingSource has two parameters:

  1. String: Reddit API provides before and after keys, which tell the API how it is going to fetch the previous and next pages of data.
  2. RedditPost: The information received from the Reddit API i.e a list of Reddit posts of type RedditPost.

At the same time, notice that the load method is not yet implemented. It accepts LoadParams as an argument, which is a sealed Kotlin class that keeps information related to the load operation. It does so via its below class properties:

  • loadSize: Number of items you’re requesting.
  • placeholdersEnabled: Boolean value that indicates whether or not you’re using a placeholder.

In the next section, you’ll add the logic to fetch data from the Reddit API.

Fetching Reddit Posts From the Network

While still inside repositories/RedditPagingSource.kt, replace the TODO() with the following code:

return try {
    // 1 
    val response = redditService.fetchPosts(loadSize = params.loadSize)
    // 2
    val listing = response.body()?.data
    val redditPosts = listing?.children?.map { it.data }
    // 3
    LoadResult.Page(
        // 4
        redditPosts ?: listOf(),
        // 5
        listing?.before,
        listing?.after
    )
} catch (exception: IOException) { // 6
    return LoadResult.Error(exception)
} catch (exception: HttpException) {
    return LoadResult.Error(exception)
}

Here’s what is happening here:

  1. RedditService is used to fetch the list of posts from the Reddit API, passing loadSize as a parameter, via LoadParams.
  2. Get the list of posts from the body of the response.
  3. Create an instance of LoadResult.Page by providing all arguments.
  4. Pass in the list of reddit posts. If the case when API call didn’t return any RedditPosts, you pass an empty list.
  5. Pass in the before and after keys you received in the response body from the Reddit API.
  6. Finally, two catch blocks handle exceptions and return LoadResult.Error.

The code block above returns LoadResult, which is another sealed class that can take the following forms:

  • When the network call executes successfully, load() returns LoadResult.Page.
  • If an error occurred when executing the network call, load() returns LoadResult.Error.

When the IDE prompts you, make sure you add the imports to resolve the errors.

Next, you’ll use this RedditPagingSource to get list of posts.

Fetching Posts From the PagingSource

An advantage of the Paging 3.0 library is that its components conform to the Model-View-ViewModel (MVVM) architecture. RedditPagingSource acts as a data source, corresponding to the Model layer in the MVVM.

Navigate to repositories/RedditRepo.kt. Add the following code block to the class:

// 1
private val redditService = RedditClient.getClient().create(RedditService::class.java)

// 2
fun fetchPosts(): Flow<PagingData<RedditPost>> {
    // 3
    return Pager(
        PagingConfig(pageSize = 40, enablePlaceholders = false)
    ) {
        RedditPagingSource(redditService)
    }.flow
}

Here,

  • PagingConfig, defines how PagingSource should load the data. pageSize specifies how many items to load in a single page. enablePlaceholders indicates whether you want to display placeholders when loading the data. Here you set it to false.
  • The second parameter is a trailing lambda that returns a PagingSource. In this case, you use RedditPagingSource with the redditService you created earlier.
  1. You create a reference to RedditService to download list of posts from the Reddit API
  2. You return an instance of Pager class, which is used to fetch a stream of data from PagingSource. The Paging library supports several return types. These include Flow, LiveData, and the RxJava Flowable and Observable. In this tutorial, you’ll use Flow from Kotlin coroutines.
  3. Pager takes two parameters:
Note: If you’re not familiar with Flow, take a look at this Kotlin Flow for Android: Getting Started tutorial.

Finally, when the IDE prompts you, make sure you add the imports to resolve the errors. In case of importing Flow, choose the one from Kotlin Coroutines.

In the next section, you’ll make use of fetchPosts() from the ViewModel to trigger the API call for fetching list of posts from Reddit API.