https://kotlinlang.org logo
#compose
Title
# compose
z

zt

06/24/2022, 8:47 PM
I'm using compose destinations for navigation in my app, and when navigating between screens on my bottom bar, it loses the scroll position in a lazy column I have placed. My navigation code:
Copy code
navController.navigate(destination.direction) {
    popUpTo(navController.navGraph.startRoute) {
        saveState = true
    }
    launchSingleTop = true
    restoreState = true
}
j

Joseph Hawkes-Cates

06/24/2022, 9:21 PM
I’m not familiar with destinations, but when you navigate to another view the previous view goes out of composition entirely so the UI state of that lazy column is gone. If you want to retain it, you can store it in the ViewModel for that screen. You can then setup the ViewModel to be restored when you go back to the view and then use the stored position to set the scroll position in your screen.
z

zt

06/24/2022, 9:36 PM
I think it should be saving on its own since internally it uses
rememberSaveable
though I may be wrong
j

Joseph Hawkes-Cates

06/24/2022, 9:42 PM
IIRC, rememberSaveable just saves across configuration changes, but if your composable goes out of composition altogether I don’t think that will save the value
I’m not 100% on that, though
i

Ian Lake

06/24/2022, 10:11 PM
rememberSaveable
also works across being put on the back stack and coming back
1
👍 1
pretty easy to see if that part works by just using a regular
navigate()
call then going back to that first screen. If that works, then there's something wrong with your bottom nav code
z

zt

06/24/2022, 10:15 PM
Yea I think so too, cause in an earlier build of my app it does save the scroll position
So I need to figure out what changed to cause it to behave different
i

Ian Lake

06/24/2022, 10:17 PM
navController.graph.startRoute
is not going to work if you have nested graphs - that's specifically why the documentation uses
navController.graph.findStartDestination().id
to go down as many levels as you have to actually find the first composable destination: https://developer.android.com/jetpack/compose/navigation#bottom-nav
z

zt

06/24/2022, 10:18 PM
I see. I also tried by putting the home destination there instead but same behavior
i

Ian Lake

06/24/2022, 10:22 PM
maybe...try using the code from the docs?
z

zt

06/25/2022, 2:28 AM
I've tried but none of that worked
Alright so i managed to narrow it down for whatever reason having this item at the top of my lazy column is causing it
Copy code
item {
                LazyRow(
                    modifier = Modifier.fillParentMaxWidth(),
                    verticalAlignment = Alignment.CenterVertically
                ) {
                    item {
                        var selected by remember { mutableStateOf(false) }

                        FilterChip(
                            selected = selected,
                            leadingIcon = {
                                Icon(
                                    imageVector = Icons.Default.Explore,
                                    contentDescription = stringResource(R.string.explore)
                                )
                            },
                            selectedIcon = {
                                Icon(
                                    imageVector = Icons.Default.Explore,
                                    contentDescription = stringResource(R.string.explore)
                                )
                            },
                            label = { Text(stringResource(R.string.explore)) },
                            onClick = { selected = !selected }
                        )
                    }
                }
            }
i

Ian Lake

06/26/2022, 1:08 AM
That sounds like something the test I suggested earlier would have detected: https://kotlinlang.slack.com/archives/CJLTWPH7S/p1656108881731389?thread_ts=1656103626.548659&cid=CJLTWPH7S
But yes, if you aren't actually caching your other source of items (i.e., by using stateIn() in a ViewModel to store the last value so that your data is instantly available when you return to that destination rather than asynchronously loading the items each time you go to that destination) then the first recomposition will only have one element in the list when it tries to restore the scroll position, hence you can't scroll down to the Nth element
z

zt

06/26/2022, 1:13 AM
I see, just tested to find that it also happens with an empty item
Copy code
item { }
Copy code
class HomeViewModel(
    private val repository: InnerTubeRepository
) : ViewModel() {
    val videos = Pager(PagingConfig(10)) {
        object : PagingSource<String, DomainVideoPartial>() {
            override suspend fun load(params: LoadParams<String>): LoadResult<String, DomainVideoPartial> {
                return try {
                    val trendingVideosResponse = repository.getTrendingVideos(params.key)

                    LoadResult.Page(
                        data = trendingVideosResponse.videos,
                        prevKey = null,
                        nextKey = trendingVideosResponse.continuation
                    )
                } catch (e: Exception) {
                    e.printStackTrace()
                    LoadResult.Error(e)
                }
            }

            override fun getRefreshKey(state: PagingState<String, DomainVideoPartial>): String? = null
        }
    }.flow.cachedIn(viewModelScope)
}
This is what the viewmodel is right now
88 Views