Paging 3

Data and storage Mid-level Kotlin 2.0+ API 24+ Reviewed By Best Latest Apps

Pick the fix

Rotating the screen restarts the paged list from the top and refetches page 1. Best fix?

Options
Hint 1 of 1

Where are loaded pages kept?

Choose an answer, then open Show answer below to check it.

Show answer

Answer: A Call .cachedIn(viewModelScope) on the pager flow in the ViewModel

Correct. Loaded pages are cached for the ViewModel's lifetime.

Why not the others?
  • B Build the Pager inside the composable. That makes it worse: a new Pager per composition.
  • C Increase pageSize. Fewer pages, same restart.
  • D Use collectAsState instead. Paging needs collectAsLazyPagingItems, and caching is the issue.

30-second answer

Paging 3 loads data in pages as the user scrolls. A PagingSource loads one page by key; Pager(...).flow exposes Flow<PagingData<T>>, which you cachedIn(viewModelScope) and collect with collectAsLazyPagingItems() in Compose. For offline-first, a RemoteMediator fetches pages from the network into Room, and Room's own PagingSource feeds the UI.

Practise saying it · 35 s. No recording, just a timer

Explain Paging 3.

Say your answer out loud, then check which of these points you covered:

  • PagingSource loads pages
  • Pager flow with cachedIn
  • collectAsLazyPagingItems
  • RemoteMediator with Room for offline

Deep dive

Room can generate a PagingSource from a DAO query returning PagingSource<Int, T>.

cachedIn keeps loaded pages across rotation and shares them between collectors.

LoadState exposes loading and error states for prepend, append and refresh.

Transform with map and insertSeparators on PagingData.

A pager in a ViewModel Compile-checked · Kotlin 2.4.21
@Dao
interface FeedDao {
    @Query("SELECT * FROM ArticleRow ORDER BY id")
    fun paging(): PagingSource<Int, ArticleRow>
}

@Entity data class ArticleRow(@PrimaryKey val id: Long, val title: String)

class PagedFeedViewModel(dao: FeedDao) : ViewModel() {
    val feed: Flow<PagingData<ArticleRow>> =
        Pager(PagingConfig(pageSize = 30)) { dao.paging() }.flow.cachedIn(viewModelScope)
}

Common mistakes

  • Forgetting cachedIn, so rotation reloads from page one.
  • Building a new Pager on every recomposition.
  • Ignoring LoadState and showing nothing on errors.

Senior take & follow-ups

Explain the RemoteMediator + Room combo; it's the senior answer.

Follow-up questions

Think of your answer first, then open the hint.

  1. What does RemoteMediator do?

    Show hint

    Loads network pages into the database when the local data runs out.

  2. Why cachedIn?

    Show hint

    So loaded pages survive configuration changes and aren't refetched.

Similar on iOS: Lazy loading iOS apps hand-roll paging; Android has a library for it.

Suggest a fix if something here is wrong or out of date.