Paging 3 - How to scroll to top of RecyclerView after PagingDataAdapter has finished refreshing AND DiffUtil has finished diffing?

I'm using Paging 3 with RemoteMediator that shows cached data while fetching new data from the network. When I refresh my PagingDataAdapter (by calling refresh() on it) I want my RecyclerView to scroll to the top after the refresh is done. In the codelabs they try to handle this via the loadStateFlow the following way:

lifecycleScope.launch {
    adapter.loadStateFlow
            // Only emit when REFRESH LoadState for RemoteMediator changes.
            .distinctUntilChangedBy { it.refresh }
            // Only react to cases where Remote REFRESH completes i.e., NotLoading.
            .filter { it.refresh is LoadState.NotLoading }
            .collect { binding.list.scrollToPosition(0) }
    }

This indeed does scroll up, but before DiffUtil has finished. This means that if there is actually new data inserted at the top, the RecyclerView will not scroll all the way up.

I know that RecyclerView adapters have an AdapterDataObserver callback where we can get notified when DiffUtil has finished diffing. But this will cause all kinds of race conditions with PREPEND and APPEND loading states of the adapter which also cause DiffUtil to run (but here we don't want to scroll to the top).

One solution that would work would be to pass PagingData.empty() to the PagingDataAdapter and rerun the same query (just calling refresh won't work because the PagingData is now empty and there is nothing to refresh) but I would prefer to keep my old data visible until I know that refresh actually succeeded.


Solution 1:

In cases, such as searching a static content, we can return false inside areItemsTheSame of DiffUtil.ItemCallback as a workaround. I use this also for changing sorting property.