The Paging library provides powerful capabilities for loading and displaying paged data from a larger dataset.
Introduction
The paging library lets your app use both network bandwidth and system resources more efficiently.
When working with large datasets, loading all the data at once can lead to high memory consumption and slow performance. Instead, it’s more efficient to load and display data in smaller chunks as needed.
Project
Create a new Kotlin project with Amper.
dependencies: - androidx.lifecycle:lifecycle-viewmodel-compose:2.8.5 - androidx.paging:paging-compose:3.4.0-alpha04 - org.jetbrains.kotlinx:kotlinx-coroutines-swing:1.10.2PagingSource
The first step is to define a PagingSource implementation to identify the data source. The PagingSource API class includes the load() method, which you override to indicate how to retrieve paged data from the corresponding data source.
PagingSource<Key, Value> has two type parameters: Key and Value. The key defines the identifier used to load the data, and the value is the type of the data itself.
The following example implements a PagingSource that loads pages of items by page number. The Key type is Int and the Value type is Item.
class ItemPagingSource : PagingSource<Int, Item>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Item> {
val start = params.key ?: 0 val range = start.until(start + params.loadSize)
return LoadResult.Page( data = range.map { number -> Item(id = number, name = "Item $number") }, prevKey = when (start) { 0 -> null else -> max(0, range.first - params.loadSize) }, nextKey = range.last + 1 ) }
override fun getRefreshKey(state: PagingState<Int, Item>): Int? {
return state.anchorPosition?.let { anchorPosition -> val anchorPage = state.closestPageToPosition(anchorPosition) anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1) } }
}The LoadParams object contains information about the load operation to be performed. This includes the key to be loaded and the number of items to be loaded.
The [LoadResult] object contains the result of the load operation. LoadResult is a sealed class that takes one of two forms, depending on whether the load() call succeeded:
- If the load is successful, return a
LoadResult.Pageobject. - If the load is not successful, return a
LoadResult.Errorobject.
The PagingSource implementation must also implement a getRefreshKey() method that takes a PagingState object as a parameter.
It returns the key to pass into the load() method when the data is refreshed or invalidated after the initial load. The Paging Library calls this method automatically on subsequent refreshes of the data.
Handle errors
Requests to load data can fail for a number of reasons, especially when loading over a network. Report errors encountered during loading by returning a LoadResult.Error object from the load() method.
For example, you can catch and report loading errors by adding the following to the load() method:
catch (e: IOException) { // IOException for network failures. return LoadResult.Error(e)} catch (e: HttpException) { // HttpException for any non-2xx HTTP status codes. return LoadResult.Error(e)}PagingSource collects and delivers LoadResult.Error objects to the UI so that you can act on them. For more information on exposing the loading state in the UI, see Manage and present loading states.
Pager
The Pager class provides methods that expose a reactive stream of PagingData objects from a PagingSource.
You must provide the instance with a PagingConfig configuration object and a function that tells Pager how to get an instance of your PagingSource implementation:
val pages: Flow<PagingData<Item>> = Pager(PagingConfig(pageSize = 20)) { ItemPagingSource()}.flowLazyPagingItems
LazyPagingItemsis responsible for accessing the data from a Flow of PagingData.
To get an instance of LazyPagingItems use the collectAsLazyPagingItems extension method of Flow with PagingData.
@Composablefun ItemView() {
val pages: Flow<PagingData<Item>> = Pager(PagingConfig(pageSize = 20)) { ItemPagingSource() }.flow
val lazyPagingItems: LazyPagingItems<Item> = pages.collectAsLazyPagingItems() LazyColumn(modifier = Modifier.fillMaxSize()) { items(count = lazyPagingItems.itemCount) { index -> val item = lazyPagingItems[index] Text( text = item?.name ?: "Loading...", fontSize = 15.sp, color = Color.Black ) } }}ViewModel
The cachedIn() operator makes the data stream shareable and caches the loaded data with the provided CoroutineScope.
In most cases, you should use the viewModelScope from a ViewModel to ensure that the data remains cached as long as the ViewModel is alive.
class ItemViewModel : ViewModel() { val pages = Pager(PagingConfig(pageSize = 20)) { ItemPagingSource() }.flow.cachedIn(viewModelScope)}Compose Desktop doesn’t automatically provide a ViewModelStoreOwner like Android does. You need to manually provide one when using ViewModels in Compose Desktop.
val viewModelStoreOwner = object : ViewModelStoreOwner { override val viewModelStore = ViewModelStore()}
CompositionLocalProvider(LocalViewModelStoreOwner provides viewModelStoreOwner) { App()}When running coroutines in a ViewModel, remember that the ViewModel.viewModelScope value is tied to the Dispatchers.Main.immediate value, which might be unavailable on desktop by default. To make ViewModel coroutines work correctly with Compose Multiplatform, add the kotlinx-coroutines-swing dependency to your project.
Releases
Added a new PagingState API closestItemAroundPosition to retrieve the loaded item that is closest to the target position and matches the input predicate. This can be used to generate item-based refresh keys where the ideal anchorable item is around but not at the exact target position.
Pendent
- Paging 3 With (KMP) Kotlin Multiplatform
- Jetpack Compose and collectAsLazyPagingItems
- Paging 3.0: A new library for loading and displaying large data sets
- Pagination in Jetpack Compose: With and Without Paging 3 Library
- 5 steps to use Paging3 library with Jetpack Compose
- A full guide to use Paging-3 Library along with Jetpack compose’s LazyRow, LazyColumn and LazyGrid
- (Deprecated) Android Paging Basics
- Jetpack Compose Pagination: Paging3 vs Alternative Approach
- Jetpack_Compose_Pagination
- beep-beep