Reputation: 356
I am aware of the remember lazy list state and it works fine
setContent {
Test(myList) // Call Test with a dummy list
}
@Composable
fun Test(data: List<Int>){
val state = rememberLazyListState()
LazyColumn(state = state) {
items(data){ item ->Text("$item")}
}
}
It will remember scroll position and after every rotation and change configuration it will be the same
But whenever I try to catch data from database and use some method like collectAsState
it doesn't work and it seem an issue
setContent{
val myList by viewModel.getList.collectAsState(initial = listOf())
Test(myList)
}
Upvotes: 13
Views: 5723
Reputation: 3708
/**
* 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
}
Usage:
LazyColumn(
state = rememberForeverLazyListState(key = "Overview")
)
Click here to check the original answer.
Taking inspiration from the previous answer I created a similar remember
method to save the ScrollState
.
import androidx.compose.foundation.ScrollState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.saveable.rememberSaveable
/**
* Static field, contains all scroll values
*/
private val SaveMap = mutableMapOf<String, ScrollKeyParams>()
private data class ScrollKeyParams(
val value: Int
)
/**
* Save scroll state on all time.
* @param key value for comparing screen
* @param initial see [ScrollState.value]
*/
@Composable
fun rememberForeverScrollState(
key: String,
initial: Int = 0
): ScrollState {
val scrollState = rememberSaveable(saver = ScrollState.Saver) {
val scrollValue: Int = SaveMap[key]?.value ?: initial
SaveMap[key] = ScrollKeyParams(scrollValue)
return@rememberSaveable ScrollState(scrollValue)
}
DisposableEffect(Unit) {
onDispose {
SaveMap[key] = ScrollKeyParams(scrollState.value)
}
}
return scrollState
}
For usage:
val scrollState = rememberForeverScrollState("history_screen")
Column(
modifier = modifier
.fillMaxSize()
.verticalScroll(scrollState)
) {
...
}
Unfortunately for now there's not a native way to do so, but you can use this code:
val listState = rememberLazyListState()
listState
has 3 methods:
firstVisibleItemIndex
firstVisibleItemScrollOffset
isScrollInProgress
All of them are State()
so you will always get the data as it updates. For example, if you start scrolling the list, isScrollInProgress
will change from false
to true
.
SAVE AND RESTORE STATE
val listState: LazyListState = rememberLazyListState(viewModel.index, viewModel.offset)
LaunchedEffect(key1 = listState.isScrollInProgress) {
if (!listState.isScrollInProgress) {
viewModel.index = listState.firstVisibleItemIndex
viewModel.offset = listState.firstVisibleItemScrollOffset
}
}
Upvotes: 8