简体   繁体   中英

Remember LazyColumn Scroll Position - Jetpack Compose

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM