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

Android Jetpack Compose页面切换时保存滚动位置的问题(无需ViewModel)

Android Jetpack Compose页面切换时保存滚动位置的问题(无需ViewModel)

我帮你分析下问题出在哪,再给你一个不用ViewModel的简单解决方案:

问题根源

你当前用rememberSaveable在ContentScreen内部保存ScrollState,但当你从ContentScreen返回WelcomeScreen时,ContentScreen对应的Composable实例会被销毁,它的rememberSaveable状态也会跟着被清除——因为rememberSaveable的作用域是当前Composable对应的导航栈条目(NavBackStackEntry),一旦这个条目被弹出栈,绑定的状态就没了。所以再次进入ContentScreen时,会重新初始化ScrollState到0,回到顶部。

解决方案:提升状态作用域

我们只需要把滚动状态的保存位置提升到比NavHost更持久的作用域(也就是MainScreen的作用域,只要MainScreen存在,这个状态就不会被销毁),然后把状态传递给ContentScreen即可,完全不用ViewModel:

1. 修改MainScreen,在父作用域保存滚动状态

把ScrollState的创建和保存移到MainScreen里,这样它的生命周期和整个主页面绑定:

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MainScreen(
    navController: NavHostController = rememberNavController(),
) {
    // 把滚动状态提升到MainScreen作用域,用rememberSaveable持久化
    val contentScrollState = rememberSaveable(saver = ScrollState.Saver) {
        ScrollState(initial = 0)
    }

    Scaffold(
    ) { innerPadding ->
        NavHost(
            navController = navController,
            startDestination = WelcomeDestination.route,
        ) {
            composable(route = WelcomeDestination.route) {
                WelcomeScreen(
                    onNavigationButtonClick = { navigationDestination: NavigationDestination ->
                        navController.navigate(navigationDestination.route)
                    },
                    modifier = Modifier
                        .padding(innerPadding)
                        .fillMaxSize()
                        .wrapContentSize(Alignment.Center)
                )
            }
            composable(route = ContentDestination.route) {
                ContentScreen(
                    scrollState = contentScrollState, // 把状态传递给ContentScreen
                    modifier = Modifier
                        .padding(innerPadding)
                        .fillMaxSize()
                        .wrapContentSize(Alignment.Center)
                )
            }
        }
    }
}

2. 修改ContentScreen,接收外部传入的状态

不再在ContentScreen内部创建ScrollState,直接使用从父组件传入的已保存状态:

@Composable
fun ContentScreen(
    scrollState: ScrollState, // 接收外部传入的滚动状态
    modifier: Modifier
) {
    Box(modifier = modifier.verticalScroll(scrollState)) {
        Text(LONG_TEXT)
    }
}

效果验证

现在你再测试:进入ContentScreen滚动到下方,返回WelcomeScreen,再进入ContentScreen,就能回到之前的滚动位置了。

另外补充个小细节:如果你的App有多个需要独立保存滚动位置的页面场景,也可以用NavBackStackEntry的savedStateHandle来存储,但这个场景下提升作用域是最简单直接的方式。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 11:13:08