Scrollable lists in Jetpack Compose, such as LazyColumn and LazyRow,
can display large amounts of data efficiently. However, configuration changes,
such as device rotation or folding/unfolding a foldable device, can reset the
state of a list, forcing users to again scroll to their previous position.
Best practices
Scrollable lists should maintain their state—in particular, scroll position—and the state of their individual list elements (like text inputs) during all configuration changes to provide a seamless user experience.
Ingredients
rememberLazyListState(): Creates and remembers aLazyListStatethat is used to control and observe the scroll position of the list. It automatically persists across configuration changes.rememberSaveable: Remembers the state value across configuration changes, useful for preserving individual list element states (like text field inputs).
Steps
In Jetpack Compose, preserving the scroll position of a list is handled
automatically when you use a scroll state. To preserve the state of individual,
complex list elements (such as TextField inputs), use rememberSaveable.
1. Preserve list scroll state
To save and restore the scroll position of your list across configuration
changes, create a LazyListState using rememberLazyListState() and pass
it as the state parameter of your scrollable list, such as a LazyColumn.
Compose automatically persists the scroll position for you.
2. Preserve individual list element state
If your list contains stateful elements (for example, items with text fields), the state of those individual elements can be lost when they are scrolled off-screen and recycled, or during configuration changes.
To preserve individual element state, use rememberSaveable inside the list
item scope. This ensures that when the item is recomposed or scrolled back into
view, its state is restored.
Here is a complete, compilable implementation demonstrating both scroll state and item state restoration:
// Scroll state is automatically saved and restored by rememberLazyListState val listState = rememberLazyListState() LazyColumn(state = listState) { items(100) { index -> // Use rememberSaveable to preserve individual item state (e.g., text input) var text by rememberSaveable { mutableStateOf("") } TextField( value = text, onValueChange = { text = it }, label = { Text("Item $index") } ) } }
Results
Your scrollable list is now able to restore its scroll position and the state of every element in the list.