PagingDataPresenter



The class that connects the UI layer to the underlying Paging operations. Takes input from UI presenters and outputs Paging events (Loads, LoadStateUpdate) in response.

Paging front ends that implement this class will be able to access loaded data, LoadStates, and callbacks from LoadState or Page updates. This class also exposes the PagingDataEvent from a PagingData for custom logic on how to present Loads, Drops, and other Paging events.

For implementation examples, refer to AsyncPagingDataDiffer for RecyclerView, or LazyPagingItems for Compose.

Summary

Public constructors

<T : Any> PagingDataPresenter(
    mainContext: CoroutineContext,
    cachedPagingData: PagingData<T>?
)
Cmn

Public functions

Unit

Add a CombinedLoadStates listener to observe the loading state of the current PagingData.

Cmn
Unit

Add a listener which triggers after the pages presented to the UI are updated, even if the actual items presented don't change.

Cmn
suspend Unit
collectFrom(pagingData: PagingData<T>)
Cmn
operator T?
@MainThread
get(index: @IntRange(from = 0) Int)

Returns the presented item at the specified position, notifying Paging of the item access to trigger any loads necessary to fulfill prefetchDistance.

Cmn
T?
@MainThread
peek(index: @IntRange(from = 0) Int)

Returns the presented item at the specified position, without notifying Paging of the item access that would normally trigger page loads.

Cmn
abstract suspend Unit

Handler for PagingDataEvent emitted by PagingData.

Cmn
Unit

Refresh the data presented by this PagingDataPresenter.

Cmn
Unit

Remove a previously registered CombinedLoadStates listener.

Cmn
Unit

Remove a previously registered listener for updates to presented pages.

Cmn
Unit

Retry any failed load requests that would result in a LoadState.Error update to this PagingDataPresenter.

Cmn
ItemSnapshotList<T>

Returns a new ItemSnapshotList representing the currently presented items, including any placeholders if they are enabled.

Cmn

Public properties

StateFlow<CombinedLoadStates?>

A hot Flow of CombinedLoadStates that emits a snapshot whenever the loading state of the current PagingData changes.

Cmn
Flow<Unit>

A hot Flow that emits after the pages presented to the UI are updated, even if the actual items presented don't change.

Cmn
Int
Cmn

Public constructors

PagingDataPresenter

<T : Any> PagingDataPresenter(
    mainContext: CoroutineContext = Dispatchers.Main,
    cachedPagingData: PagingData<T>? = null
)
Parameters
mainContext: CoroutineContext = Dispatchers.Main

The coroutine context that core Paging operations will run on. Defaults to Dispatchers.Main. Main operations executed within this context include but are not limited to:

  1. flow collection on a PagingData for Loads, LoadStateUpdate etc.

  2. emitting CombinedLoadStates to the loadStateFlow

  3. invoking LoadState and PageUpdate listeners

  4. invoking presentPagingDataEvent

cachedPagingData: PagingData<T>? = null

a PagingData that will initialize this PagingDataPresenter with any LoadStates or loaded data contained within it.

Public functions

addLoadStateListener

fun addLoadStateListener(listener: (CombinedLoadStates) -> Unit): Unit

Add a CombinedLoadStates listener to observe the loading state of the current PagingData.

As new PagingData generations are submitted and displayed, the listener will be notified to reflect the current CombinedLoadStates.

When a new listener is added, it will be immediately called with the current CombinedLoadStates, unless this PagingDataPresenter has not been hooked up to a PagingData yet, and thus has no state to emit.

import androidx.paging.LoadState

val adapter = UserPagingAdapter()
adapter.addLoadStateListener {
    // show a retry button outside the list when refresh hits an error
    retryButton.isVisible = it.refresh is LoadState.Error

    // swipeRefreshLayout displays whether refresh is occurring
    swipeRefreshLayout.isRefreshing = it.refresh is LoadState.Loading

    // show an empty state over the list when loading initially, before items are loaded
    emptyState.isVisible = it.refresh is LoadState.Loading && adapter.itemCount == 0
}
Parameters
listener: (CombinedLoadStates) -> Unit

LoadStates listener to receive updates.

addOnPagesUpdatedListener

fun addOnPagesUpdatedListener(listener: () -> Unit): Unit

Add a listener which triggers after the pages presented to the UI are updated, even if the actual items presented don't change.

An update is triggered from one of the following:

  • collectFrom is called and initial load completes, regardless of any differences in the loaded data

  • A Page is inserted

  • A Page is dropped

Parameters
listener: () -> Unit

called after pages presented are updated.

collectFrom

suspend fun collectFrom(pagingData: PagingData<T>): Unit

get

@MainThread
operator fun get(index: @IntRange(from = 0) Int): T?

Returns the presented item at the specified position, notifying Paging of the item access to trigger any loads necessary to fulfill prefetchDistance.

Parameters
index: @IntRange(from = 0) Int

Index of the presented item to return, including placeholders.

Returns
T?

The presented item at position index, null if it is a placeholder.

peek

@MainThread
fun peek(index: @IntRange(from = 0) Int): T?

Returns the presented item at the specified position, without notifying Paging of the item access that would normally trigger page loads.

Parameters
index: @IntRange(from = 0) Int

Index of the presented item to return, including placeholders.

Returns
T?

The presented item at position index, null if it is a placeholder

presentPagingDataEvent

abstract suspend fun presentPagingDataEvent(event: PagingDataEvent<T>): Unit

Handler for PagingDataEvent emitted by PagingData.

When a PagingData is submitted to this PagingDataPresenter through collectFrom, page loads, drops, or LoadStateUpdates will be emitted to presenters as PagingDataEvent through this method.

Presenter layers that communicate directly with PagingDataPresenter should override this method to handle the PagingDataEvent accordingly. For example by diffing two PagingDataEvent.Refresh lists, or appending the inserted list of data from PagingDataEvent.Prepend or PagingDataEvent.Append.

refresh

fun refresh(): Unit

Refresh the data presented by this PagingDataPresenter.

refresh triggers the creation of a new PagingData with a new instance of PagingSource to represent an updated snapshot of the backing dataset. If a RemoteMediator is set, calling refresh will also trigger a call to RemoteMediator.load with LoadType to allow RemoteMediator to check for updates to the dataset backing PagingSource.

Note: This API is intended for UI-driven refresh signals, such as swipe-to-refresh. Invalidation due repository-layer signals, such as DB-updates, should instead use PagingSource.invalidate.

import androidx.appcompat.app.AppCompatActivity
import androidx.paging.LoadState

class MyActivity : AppCompatActivity() {
    private lateinit var binding: MyActivityBinding
    private val pagingAdapter = UserPagingAdapter()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = MyActivityBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.recyclerView.adapter = pagingAdapter
        pagingAdapter.addLoadStateListener { loadStates ->
            binding.swipeRefreshLayout.isRefreshing = loadStates.refresh is LoadState.Loading
        }

        binding.swipeRefreshLayout.setOnRefreshListener {
            pagingAdapter.refresh()
        }
    }
}
See also
invalidate

removeLoadStateListener

fun removeLoadStateListener(listener: (CombinedLoadStates) -> Unit): Unit

Remove a previously registered CombinedLoadStates listener.

Parameters
listener: (CombinedLoadStates) -> Unit

Previously registered listener.

removeOnPagesUpdatedListener

fun removeOnPagesUpdatedListener(listener: () -> Unit): Unit

Remove a previously registered listener for updates to presented pages.

Parameters
listener: () -> Unit

Previously registered listener.

retry

fun retry(): Unit

Retry any failed load requests that would result in a LoadState.Error update to this PagingDataPresenter.

Unlike refresh, this does not invalidate PagingSource, it only retries failed loads within the same generation of PagingData.

LoadState.Error can be generated from two types of load requests:

snapshot

fun snapshot(): ItemSnapshotList<T>

Returns a new ItemSnapshotList representing the currently presented items, including any placeholders if they are enabled.

Public properties

loadStateFlow

val loadStateFlowStateFlow<CombinedLoadStates?>

A hot Flow of CombinedLoadStates that emits a snapshot whenever the loading state of the current PagingData changes.

This flow is conflated. It buffers the last update to CombinedLoadStates and immediately delivers the current load states on collection, unless this PagingDataPresenter has not been hooked up to a PagingData yet, and thus has no state to emit.

import androidx.lifecycle.lifecycleScope
import androidx.paging.LoadState

val adapter = UserPagingAdapter()
lifecycleScope.launch {
    adapter.loadStateFlow
        .map { it.refresh }
        .distinctUntilChanged()
        .collectLatest {
            // show a retry button outside the list when refresh hits an error
            retryButton.isVisible = it is LoadState.Error

            // swipeRefreshLayout displays whether refresh is occurring
            swipeRefreshLayout.isRefreshing = it is LoadState.Loading

            // show an empty state over the list when loading initially, before items are loaded
            emptyState.isVisible = it is LoadState.Loading && adapter.itemCount == 0
        }
}

onPagesUpdatedFlow

val onPagesUpdatedFlowFlow<Unit>

A hot Flow that emits after the pages presented to the UI are updated, even if the actual items presented don't change.

An update is triggered from one of the following:

  • collectFrom is called and initial load completes, regardless of any differences in the loaded data

  • A Page is inserted

  • A Page is dropped

Note: This is a SharedFlow configured to replay 0 items with a buffer of size 64. If a collector lags behind page updates, it may trigger multiple times for each intermediate update that was presented while your collector was still working. To avoid this behavior, you can conflate this Flow so that you only receive the latest update, which is useful in cases where you are simply updating UI and don't care about tracking the exact number of page updates.

size

val sizeInt
Returns
Int

Total number of presented items, including placeholders.