Two related regressions occur when navigating back to the Home screen from the Song Presenter, Dafts, History and Settings:
- Content reloads from scratch. The song list visibly re-fetches and shows the shimmer skeleton loader briefly before re-rendering, even though nothing has changed.
- Scroll position resets to the top. Even when the reload does not trigger, the search tab's
LazyColumn scrolls back to item 0 rather than returning to the position the user was at before tapping a song.
Steps to reproduce (reload)
- Open the app. Wait for songs to load.
- Tap any song to enter the Presenter.
- Press back.
- Observe: the shimmer loader briefly appears and songs reload.
Steps to reproduce (scroll reset)
- Open the app and scroll down past ~10 songs in the search tab.
- Tap a song to open the Presenter.
- Press back.
- Observe: the list is back at the top instead of where you left off.
Expected behaviour
- Returning to the Home screen should show the search tab exactly as it was left — same scroll position, same query, same results — with no reload.
- The
dataFetched guard should be sufficient to prevent a re-fetch, but the LazyListState scroll position should also survive the navigation round-trip.
Root cause (technical)
There are two separate causes:
1. fetchData() is called again on recomposition via LaunchedEffect(Unit)
HomeScreen.kt calls viewModel.fetchData() inside a LaunchedEffect(Unit). The HomeViewModel has a dataFetched boolean guard to prevent double-loading, which should work because the ViewModel survives the back stack. However, if the HomeViewModel is being re-created on return (e.g. due to how hiltViewModel() scopes work with the nav graph), the guard resets to false and a full reload fires. The uiState transitions back through UiState.Loading, which HomeScreen maps to HomeSkeletonScreen(), causing the visible shimmer flash.
2. LazyListState is created with rememberLazyListState() and is not survived across navigation
In HomeSearch.kt, the list state is declared as:
val listState = rememberLazyListState()
rememberLazyListState() is tied to the composition lifetime of HomeSearch. When the user navigates to the Presenter, the HomeSearch composable leaves the composition (the NavHost replaces it). On return, HomeSearch recomposes from scratch and rememberLazyListState() returns a new instance at position 0, losing the previous scroll position.
Two related regressions occur when navigating back to the Home screen from the Song Presenter, Dafts, History and Settings:
LazyColumnscrolls back to item 0 rather than returning to the position the user was at before tapping a song.Steps to reproduce (reload)
Steps to reproduce (scroll reset)
Expected behaviour
dataFetchedguard should be sufficient to prevent a re-fetch, but theLazyListStatescroll position should also survive the navigation round-trip.Root cause (technical)
There are two separate causes:
1.
fetchData()is called again on recomposition viaLaunchedEffect(Unit)HomeScreen.ktcallsviewModel.fetchData()inside aLaunchedEffect(Unit). TheHomeViewModelhas adataFetchedboolean guard to prevent double-loading, which should work because the ViewModel survives the back stack. However, if theHomeViewModelis being re-created on return (e.g. due to howhiltViewModel()scopes work with the nav graph), the guard resets tofalseand a full reload fires. TheuiStatetransitions back throughUiState.Loading, whichHomeScreenmaps toHomeSkeletonScreen(), causing the visible shimmer flash.2.
LazyListStateis created withrememberLazyListState()and is not survived across navigationIn
HomeSearch.kt, the list state is declared as:rememberLazyListState()is tied to the composition lifetime ofHomeSearch. When the user navigates to the Presenter, theHomeSearchcomposable leaves the composition (theNavHostreplaces it). On return,HomeSearchrecomposes from scratch andrememberLazyListState()returns a new instance at position 0, losing the previous scroll position.