I had a chance to work with the Paging 3 library, along with Jetpack Compose. This guide shares some of the basics of the paging library, along with a guide on how to implement it in any android app that is using Jetpack Compose.
So I chose to build a demo news feed app to test the paging library. This is the app.
For those of you interested in skipping the article, the GitHub link is here. So let’s begin!
I have used Hilt and KSP (Kotlin Symbol Processing — similar to KAPT) for injecting our data layer with our UI layer. The other libraries used here are Coil for displaying contact URI image.
I have also used Gradle’s version catalogs to declare dependencies in the app.
You can checkout my other articles on version catalogs and Hilt to get a deeper understanding of what is required here. For now, I’ll just share the relevant files used to add these dependencies.
We also need to create a custom Application
class and annotate it with @HiltAndroidApp
and add it to our Manifest file.
Now that we have our dependencies, we need to create a model class that holds our news api data.
Now, we should be able to create our rest api service class using Retrofit.
We also create a Repository
class that simply fetches the list of news feed from the api service. We can use Hilt
to inject the api service to our repository.
In the Paging 3 library, the data source concept is implemented using either PagingSource
or RemoteMediator
.
PagingSource
is the core class that serves as the data source in the Paging3 library. It defines how the data is loaded for a paginated list. It fetches data incrementally (page by page). It supports bidirectional paging (both forward and backward) and handles errors and retries for data loading.
A PagingSource
implementation involves overriding the load()
function, which loads the data for a given page.
If we need to handle both local and remote data sources together (e.g., caching data in Room while fetching it from a network), we can use RemoteMediator
alongside PagingSource
.
In this app, we are only using NewsDataSource
.
This is pretty straightforward. The view model will be responsible for creating the Pager along with its configurations and send it to the UI so it can observe the data changes.
A Pager
is a class that is responsible for incrementally pulling chunks of data from the PagingSource
as requested by the UI. Since the Pager
requires access to the PagingSource
, it is typically created in the data layer where the PagingSource
is defined. The second thing needed to construct a Pager
is the PagingConfig
which defines parameters that govern how the Pager
will fetch data.
Our MainActivity
looks like this:
In our activity class, we simply call a compose function called HomeScreen()
. The HomeScreen
composable collects the news from the ViewModel
using the collectAsLazyPagingItems()
. This provides us with a list of LazyPagingItems<Article>
object. This object includes the list of news items we want to display, along with the current load state of the paging data.
What is LoadState? LoadState
represents the state of data loading in Paging 3. It provides information about whether a particular load operation is currently in progress, completed successfully, or failed.
LoadState
objects can have three forms:
You can access these states for different loading scenarios such as initial loading (LoadType.REFRESH
), appending more data (LoadType.APPEND
), or refreshing(LoadType.PREPEND
).
We can use the loadState.refresh
to check if the LoadState
is loading, in error state or is successful and update the UI accordingly.
Note: I am using a custom animation here for my pager implementation. You can find that composable here.
And that’s pretty much it!