Jetpack Compose返回列表页时保持滚动位置问题求助
问题分析与解决方案
问题原因
- 滚动状态未持久化:默认情况下
LazyVerticalGrid的滚动状态仅在当前Composition中临时保留,从详情页返回时,列表页Composable重组,滚动状态丢失,导致页面回到顶部。 - 数据重复请求(潜在):尽管你通过
cachedIn(viewModelScope)缓存了Paging数据,但如果MovieListViewModel被意外销毁(比如导航栈异常),会重新创建ViewModel并发起新请求。不过结合你的代码,hiltViewModel()绑定到导航栈条目,ViewModel不会在返回时销毁,所以核心问题还是滚动状态丢失。
解决办法
不需要手动记录滚动索引,Compose提供了更优雅的自动状态保存方案,同时确保Paging数据缓存稳定。
步骤1:持久化滚动状态
修改VerticalGridButtons函数,添加可持久化的LazyListState,让滚动位置在返回时自动恢复:
@Composable fun VerticalGridButtons( movieDataList: LazyPagingItems<MovieData>, navigationCallback: (Int) -> Unit, ) { // 用rememberSaveable+LazyListState.Saver持久化滚动状态 val listState = rememberSaveable(saver = LazyListState.Saver) { LazyListState() } LazyVerticalGrid( state = listState, // 将状态绑定到网格组件 columns = GridCells.Fixed(2), contentPadding = PaddingValues( start = 8.dp, end = 8.dp, bottom = 8.dp, top = 100.dp ), ) { items( movieDataList.itemCount ) { index -> movieDataList[index]?.let { MenuItemTile(it, navigationCallback) } } } // TODO: Add error handling }
步骤2:确保Paging数据缓存稳定
你的MovieListViewModel中已经通过cachedIn(viewModelScope)实现了数据缓存,这会让数据在ViewModel生命周期内保持缓存,避免返回时重新发起网络请求。如果仍出现重复请求,检查两点:
- 确认
MoviesListRepository中的getDiscoverMoviesPage()返回的PagingSource没有被频繁调用invalidate()。 - 确保ViewModel没有被意外销毁(配置变更时ViewModel默认会保留,除非手动处理了销毁逻辑)。
额外优化:改用底部弹窗(可选)
如果改成底部弹窗展示详情,列表页不会被导航栈弹出,滚动状态和数据都不会丢失,实现更简单:
@Composable fun MovieListScreen() { val viewModel: MovieListViewModel = hiltViewModel() val movieDataList = viewModel.getMovieListPage().collectAsLazyPagingItems() val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden) val scope = rememberCoroutineScope() var selectedMovieId by remember { mutableStateOf<Int?>(null) } ModalBottomSheetLayout( sheetState = sheetState, sheetContent = { selectedMovieId?.let { id -> MovieDetailsScreen(id = id) } } ) { Box( modifier = Modifier .fillMaxSize() .background( brush = Brush.verticalGradient( colors = listOf( SplashGradientStart, SplashGradientEnd ) ) ), contentAlignment = Alignment.TopCenter ) { VerticalGridButtons(movieDataList = movieDataList) { id -> selectedMovieId = id scope.launch { sheetState.show() } } } } }
内容的提问来源于stack exchange,提问作者greysqrl
相关产品推荐
相关产品推荐

