You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetpack Compose返回列表页时保持滚动位置问题求助

问题分析与解决方案

问题原因

  1. 滚动状态未持久化:默认情况下LazyVerticalGrid的滚动状态仅在当前Composition中临时保留,从详情页返回时,列表页Composable重组,滚动状态丢失,导致页面回到顶部。
  2. 数据重复请求(潜在):尽管你通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 04:54:57