Paging 3 (Infinite Scrolling)
PagingSource, Pager & collectAsLazyPagingItems
Loading thousands of rows at once is wasteful. Paging 3 fetches data in pages as the user scrolls, exposes load states (loading/error/append), and integrates directly with `LazyColumn`.
๐ Explanation
A `PagingSource` knows how to load a single page given a key and returns the prev/next keys so Paging can walk forwards and backwards. `Pager` turns that source into a cold `Flow<PagingData>`; `cachedIn(viewModelScope)` keeps loaded pages across configuration changes. In the UI, `collectAsLazyPagingItems()` gives you an object you drive from `LazyColumn` โ items are fetched just before they scroll into view, and `getRefreshKey` lets Paging resume near the user's current position after a refresh.
Note: Code execution is not available for Android/Kotlin in the browser. Use Android Studio to run and test the app.
import androidx.paging.Pagerimport androidx.paging.PagingConfigimport androidx.paging.PagingSourceimport androidx.paging.PagingStateimport androidx.paging.compose.collectAsLazyPagingItemsimport androidx.paging.compose.itemKeyimport androidx.compose.foundation.lazy.LazyColumnimport androidx.compose.foundation.lazy.itemsimport kotlinx.coroutines.flow.Flow// 1. A PagingSource describes how to load ONE pageclass ArticleSource(private val api: ArticleApi) : PagingSource<Int, Article>() {override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Article> = try {val page = params.key ?: 1val articles = api.getArticles(page = page, size = params.loadSize)LoadResult.Page(data = articles,prevKey = if (page == 1) null else page - 1,nextKey = if (articles.isEmpty()) null else page + 1)} catch (e: Exception) {LoadResult.Error(e)}// Used to restart paging after a process death / refreshoverride fun getRefreshKey(state: PagingState<Int, Article>): Int? =state.anchorPosition?.let { anchor ->state.closestPageToPosition(anchor)?.prevKey?.plus(1)}}// 2. The ViewModel exposes a Flow<PagingData>class FeedViewModel(api: ArticleApi) : ViewModel() {val articles: Flow<PagingData<Article>> = Pager(config = PagingConfig(pageSize = 20, prefetchDistance = 5)) { ArticleSource(api) }.flow.cachedIn(viewModelScope)}// 3. The UI consumes pages lazily@Composablefun FeedScreen(viewModel: FeedViewModel) {val items = viewModel.articles.collectAsLazyPagingItems()LazyColumn {items(count = items.itemCount,key = items.itemKey { it.id }) { index ->val article = items[index] // null while a placeholder loadsif (article != null) ArticleRow(article)}}}