Reputation: 3115
I have a LazyColumn
that contains multiple LazyRow
. In old terms, nested RecyclerView.
My problem is, LazyColumn
does not restore scroll state when move to a new composable (Different tab). But inner LazyRow
s restore their states.
For example, open home screen, scroll to bottom then scroll LazyRow
to end then open a different tab and back to home tab again. LazyColumn
starts from top (does not restore state) but the last LazyRow
restore it's scroll state.
HomeScreen that contains LazyColumn
@Composable
fun HomeScreen(
homeViewModel: HomeViewModel = hiltViewModel()
) {
val scrollState = rememberLazyListState()
LazyColumn(contentPadding = PaddingValues(vertical = 8.dp),
horizontalAlignment = Alignment.Start,
verticalArrangement = Arrangement.spacedBy(8.dp),
state = scrollState,
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colors.background)
) {
items(5) {
TopRatedProducts(homeViewModel = homeViewModel)
}
}
}
TopRatedProducts that contains LazyRow
@Composable
fun TopRatedProducts(
homeViewModel: HomeViewModel = hiltViewModel()
) {
val topRatedProducts by rememberFlowWithLifecycle(homeViewModel.topRatedProducts)
.collectAsState(initial = emptyList())
LazyRow(
contentPadding = PaddingValues(horizontal = 8.dp), // Space between start and end
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp), // Space between items
modifier = Modifier
.background(Color.Green)
) {
items(topRatedProducts) {
ProductCardItem(item = it)
}
}
}
How can I restore LazyColumn
scroll state?
Upvotes: 4
Views: 4354
Reputation: 3571
Try the following at the end of your HomeScreen()
function:
LaunchedEffect(scrollState.firstVisibleItemScrollOffset) {
scrollState.scrollToItem(
scrollState.firstVisibleItemIndex,
scrollState.firstVisibleItemScrollOffset
)
}
Upvotes: 0
Reputation: 9036
You should mention ViewCompositionStrategy when you want this behaviour with ComposeView in Fragments.
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return ComposeView(requireContext()).apply {
// Dispose the Composition when viewLifecycleOwner is destroyed
setViewCompositionStrategy(
ViewCompositionStrategy.DisposeOnLifecycleDestroyed(viewLifecycleOwner)
)
setContent {
// content
}
}
}
From docs: By default, Compose disposes of the Composition whenever the view becomes detached from a window. Compose UI View types such as ComposeView and AbstractComposeView use a ViewCompositionStrategy that defines this behavior.
By default, Compose uses the DisposeOnDetachedFromWindow strategy. However, this default value might be undesirable in some situations when the Compose UI View types are used in:
Fragments. The Composition must follow the fragment's view lifecycle for Compose UI View types to save state.
Transitions. Anytime the Compose UI View is used as part of a transition, it will be detached from its window when the transition starts instead of when the transition ends, thus causing your composable to dispose of its state while it is still on screen.
RecyclerView view holders, or your own lifecycle-managed custom View
Docs link: https://developer.android.com/jetpack/compose/interop/interop-apis#composition-strategy
Upvotes: 4