Maintain/Save/Restore scroll position when returning to a ListView
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Users notice immediately when a long Android ListView jumps back to the top after they open a detail screen and return. ListView does not automatically preserve the exact visual position in every navigation flow, so the reliable solution is to save the first visible row plus its top offset and restore both when the list comes back.
What Needs To Be Saved
To restore the visible position accurately, you usually need two values:
- the adapter position of the first visible row
- the pixel offset of that row from the top of the
ListView
If you save only the row index, the list may come back to the correct item but still look slightly off because the top offset is lost.
Save And Restore In A Fragment
A practical Kotlin pattern looks like this:
The critical part is setSelectionFromTop, not just setSelection, because it restores both row position and pixel offset.
Why post Matters
Restoration usually needs to happen after the adapter is attached and the view has completed layout. That is why listView.post is so useful:
If you restore too early, Android may ignore the request or overwrite it during the next layout pass.
Returning From A Detail Screen
When the user opens a detail screen and navigates back, the fragment instance may survive or its view may be recreated. The safer design is to store the scroll state somewhere that survives view recreation, such as a ViewModel.
Then save into the ViewModel before leaving and restore from it when the list screen returns.
When The Data Changes
Scroll restoration works best when the dataset is stable. If rows were inserted, removed, or re-sorted while the user was away, an old position may now refer to different content.
If the list uses stable IDs, saving the item identity can be better than saving only the adapter position. Then you can find that item after reload and restore near it instead of trusting a stale index.
Common Pitfalls
The biggest mistake is saving only firstVisiblePosition and ignoring the top offset. That usually restores the right item but not the exact visible location.
Another common issue is calling setSelectionFromTop before the adapter is attached or before layout is complete. The restore call then appears to do nothing.
Developers also often forget that asynchronous data reloads can reorder the list before restoration happens. If the dataset changed, the saved index may no longer point to the same logical item.
Finally, getChildAt(0) can return null when there are no visible children. Guard that case before reading the top offset.
Summary
- Save both the first visible row index and its top pixel offset.
- Restore with
setSelectionFromTop, not justsetSelection. - Perform restoration after the adapter is attached and layout has completed.
- Use
savedInstanceStateor aViewModelso the state survives navigation and recreation. - If the dataset can change, prefer restoring by stable item identity when possible.

