I'm trying to save/remember LazyColumn scroll position when I navigate away from one composable screen to another. Even if I pass a rememberLazyListState to a LazyColumn the scroll position is not saved after I get back to my first composable screen. Can someone help me out?
@ExperimentalMaterialApi
@Composable
fun DisplayTasks(
tasks: List<Task>,
navigateToTaskScreen: (Int) -> Unit
) {
val listState = rememberLazyListState()
LazyColumn(state = listState) {
itemsIndexed(
items = tasks,
key = { _, task ->
task.id
}
) { _, task ->
LazyColumnItem(
toDoTask = task,
navigateToTaskScreen = navigateToTaskScreen
)
}
}
}
/**
* Static field, contains all scroll values
*/
private val SaveMap = mutableMapOf<String, KeyParams>()
private data class KeyParams(
val params: String = "",
val index: Int,
val scrollOffset: Int
)
/**
* Save scroll state on all time.
* @param key value for comparing screen
* @param params arguments for find different between equals screen
* @param initialFirstVisibleItemIndex see [LazyListState.firstVisibleItemIndex]
* @param initialFirstVisibleItemScrollOffset see [LazyListState.firstVisibleItemScrollOffset]
*/
@Composable
fun rememberForeverLazyListState(
key: String,
params: String = "",
initialFirstVisibleItemIndex: Int = 0,
initialFirstVisibleItemScrollOffset: Int = 0
): LazyListState {
val scrollState = rememberSaveable(saver = LazyListState.Saver) {
var savedValue = SaveMap[key]
if (savedValue?.params != params) savedValue = null
val savedIndex = savedValue?.index ?: initialFirstVisibleItemIndex
val savedOffset = savedValue?.scrollOffset ?: initialFirstVisibleItemScrollOffset
LazyListState(
savedIndex,
savedOffset
)
}
DisposableEffect(Unit) {
onDispose {
val lastIndex = scrollState.firstVisibleItemIndex
val lastOffset = scrollState.firstVisibleItemScrollOffset
SaveMap[key] = KeyParams(params, lastIndex, lastOffset)
}
}
return scrollState
}
example of use
LazyColumn(
state = rememberForeverLazyListState(key = "Overview")
)
Well if you literally want to save it, you must store it is something like a viewmodel where it remains preserved. The remember
ed stuff only lasts till the Composable gets destroyed. If you navigate to another screen, the previous Composables are destroyed and along with them, the scroll state
@Composable
fun persistedScrollState(viewModel: ParentViewModel): ScrollState {
val scrollState = rememberScrollState(viewModel.scrollPosition)
DisposableEffect(key1 = null) {
onDispose {
viewModel.scrollPosition = scrollState.value
}
}
return scrollState
}
Above I defined a helper function to persist scroll state when a composable is disposed of. All that is needed is a ViewModel with a scroll position variable.
Hope this helps someone!
Column(modifier = Modifier
.fillMaxSize()
.verticalScroll(
persistedScrollState(viewModel = viewModel)
) {
//Your content here
}
@Composable
fun persistedLazyScrollState(viewModel: YourViewModel): LazyListState {
val scrollState = rememberLazyListState(viewModel.firstVisibleItemIdx, viewModel.firstVisibleItemOffset)
DisposableEffect(key1 = null) {
onDispose {
viewModel.firstVisibleItemIdx = scrollState.firstVisibleItemIndex
viewModel.firstVisibleItemOffset = scrollState.firstVisibleItemScrollOffset
}
}
return scrollState
}
}
Above I defined a helper function to persist scroll state when a composable is disposed of. All that is needed is a ViewModel with variables for firstVisibleItemIdx and firstVisibleItemOffet.
Column(modifier = Modifier
.fillMaxSize()
.verticalScroll(
persistedScrollState(viewModel = viewModel)
) {
//Your content here
}
The LazyColumn
should save scroll position out of the box when navigating to next screen. If it doesn't work, this may be a bug described here ( issue tracker ). Basically check if the list becomes empty when changing screens, eg because you observe a cold Flow or LiveData (so the initial value is used).
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.