I found a solution!
@Composable
fun Sample(data: Flow<PagingData<Something>>):
val listState: LazyListState = rememberLazyListState()
val items: LazyPagingItems<Something> = data.collectAsLazyPagingItems()
when {
items.itemCount == 0 -> LoadingScreen()
else -> {
LazyColumn(state = listState, ...) {
...
}
}
}
...
I just found out what the issue is when using Paging
.
The reason the list scroll position is not remembered with Paging
when navigating boils down to what happens below the hood.
It looks like this:
- Composable with
LazyColumn
is created.
- We asynchronously request our list data from the pager. Current pager list item count = 0.
- The UI draws a lazyColumn with 0 items.
- The pager responds with data, e.g. 10 items, and the UI is recomposed to show them.
- User scrolls e.g. all the way down and clicks the bottom item, which navigates them elsewhere.
- User navigates back using e.g. the back button.
- Uh oh. Due to navigation, our composable with
LazyColumn
is recomposed. We start again with asynchronously requesting pager data. Note: pager item count = 0 again!
rememberLazyListState
is evaluated, and it tells the UI that the user scrolled down all the way, so it now should go back to the same offset, e.g. to the fifth item.
This is the point where the UI screams in wild confusion, as the pager has 0 items, so the lazyColumn has 0 items.
The UI cannot handle the scroll offset to the fifth item. The scroll position is set to just show from item 0, as there are only 0 items.
What happens next:
- The pager responds that there are e.g. 10 items again, causing another recomposition.
- After recomposition, we see our list again, with scroll position starting on item 0.
To confirm this is the case with your code, add a simple log statement just above the LazyColumn
call:
Log.w("TEST", "List state recompose. " +
"first_visible=${listState.firstVisibleItemIndex}, " +
"offset=${listState.firstVisibleItemScrollOffset}, " +
"amount items=${items.itemCount}")
You should see, upon navigating back, a log line stating the exact same first_visible
and offset
, but with amount items=0
.
The line directly after that will show that first_visible
and offset
are reset to 0
.
My solution works, because it skips using the listState
until the pager has loaded the data.
Once loaded, the correct values still reside in the listState
, and the scroll position is correctly restored.
Source: https://issuetracker.google.com/issues/177245496