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

屏幕旋转后如何保留Jetpack Navigation NavHost的状态与回退栈?

解决Compose NavHostController屏幕旋转状态丢失问题

核心错误分析

  1. 强制重复导航:每次Activity重建(包括旋转)都调用navController.navigate(MAIN_PAGE),直接覆盖了恢复后的导航状态,导致回到初始页面。
  2. 错误的状态保存方式:Activity无法直接获取Composable内部创建的NavHostController实例,之前的onSaveInstanceState代码无法正确拿到导航状态完成保存。

正确实现方案

方案1:利用rememberNavController自动状态保存(推荐)

rememberNavController()内部已经集成了状态保存逻辑,只需避免强制重复导航即可:

修改MainActivity代码

class MainActivity : ComponentActivity() {

    private lateinit var viewModel: OurViewModel

    @Inject
    lateinit var ourViewModelFactory: OurViewModelFactory

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val appComponent = DaggerAppComponent.create()
        appComponent.inject(this)
        viewModel = ViewModelProvider(this, ourViewModelFactory).get(OurViewModel::class.java)

        setContent {
            OurTheme {
                val navController = rememberNavController()
                ThisScreenNavigation(navController, viewModel)

                // 仅在首次创建时执行初始导航(可选,NavHost的startDestination已默认处理)
                LaunchedEffect(Unit) {
                    if (savedInstanceState == null) {
                        // 注释原强制导航,NavHost的startDestination已设置为MAIN_PAGE
                        // navController.navigate(MAIN_PAGE)
                    }
                }
            }
        }
    }
}

保持ThisScreenNavigation代码不变

@Composable
fun ThisScreenNavigation(navController: NavHostController, viewModel : OurViewModel) {
    NavHost(navController, startDestination = MAIN_PAGE) {
        composable(route = MAIN_PAGE) {
            MainPage(navController, viewModel)
        }
        composable(route = SECOND_PAGE) {
            SecondPage(navController, viewModel)
        }
    }
}

方案2:通过ViewModel手动保存导航状态(适合复杂场景)

如果需要更灵活的状态控制,可以将导航状态保存在ViewModel中(ViewModel旋转后存活):

修改ViewModel代码

class OurViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() {
    private val NAV_STATE_KEY = "nav_controller_state"

    // 保存导航状态
    fun saveNavState(state: Bundle) {
        savedStateHandle.set(NAV_STATE_KEY, state)
    }

    // 获取保存的导航状态
    fun getSavedNavState(): Bundle? {
        return savedStateHandle.get(NAV_STATE_KEY)
    }
}

修改ThisScreenNavigation代码

@Composable
fun ThisScreenNavigation(navController: NavHostController, viewModel : OurViewModel) {
    // 恢复导航状态
    LaunchedEffect(Unit) {
        viewModel.getSavedNavState()?.let {
            navController.restoreState(it)
        }
    }

    // 监听导航变化,实时保存状态
    DisposableEffect(navController) {
        val listener = NavController.OnDestinationChangedListener { _, _, _ ->
            navController.saveState()?.let {
                viewModel.saveNavState(it)
            }
        }
        navController.addOnDestinationChangedListener(listener)

        onDispose {
            navController.removeOnDestinationChangedListener(listener)
        }
    }

    NavHost(navController, startDestination = MAIN_PAGE) {
        composable(route = MAIN_PAGE) {
            MainPage(navController, viewModel)
        }
        composable(route = SECOND_PAGE) {
            SecondPage(navController, viewModel)
        }
    }
}

MainActivity代码同方案1

移除强制的navController.navigate(MAIN_PAGE),仅在首次创建时可选执行。

关键注意事项

  • 不要在每次重组时调用navController.navigate(),否则会覆盖恢复后的状态。
  • rememberNavController()依赖Compose的状态保存机制,确保你的Composable树正确关联到Activity的生命周期。
  • 若使用ViewModel保存状态,需确保ViewModel通过SavedStateHandle持久化状态,避免进程被杀后状态丢失。

内容的提问来源于stack exchange,提问作者Ali Has

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 19:31:19