Compose 导航返回栈管理:多页面状态恢复避坑
## Compose 导航返回栈:状态恢复与页面切换问题
Compose 导航返回栈:状态恢复与页面切换最常见的问题,不是导航本身跳不过去,而是切页回来后状态丢了、列表滚动位置没了,或者多层页面返回时把不该销毁的 ViewModel 和 UI 状态一起清掉。很多团队一开始只盯 NavHost 写法,真正的坑却在返回栈和状态恢复边界。
## 方案
先把页面级状态、共享状态和一次性事件拆开;再把导航参数与 saveableStateHolder 对齐;最后补一层返回栈恢复策略和调试日志。这个顺序的价值在于,能先把最常见的“切回列表白屏”“详情页返回状态错乱”“底部导航多栈互相污染”压下来。继续往下做时,建议把每个页面需要恢复的状态显式列出来,而不是默认指望 remember 自己兜住一切。
如果项目里已经有底部导航、多 back stack 或深链路跳转,最好再补一层页面恢复用例表。因为 Compose 导航问题真正难的不是第一次写通,而是复杂页面切换叠起来之后还能不能稳定回到正确状态。只要返回栈规则和恢复策略没锁清楚,后面问题一定会重复出现。
## 示例代码
1. 导航与状态恢复
NavHost(navController, startDestination = "home") {
composable("home") { HomeScreen() }
composable("detail/{id}") { backStackEntry ->
val id = backStackEntry.arguments?.getString("id")
DetailScreen(id = id.orEmpty())
}
}
2. 调试命令
adb logcat -d | grep -i NavHost
adb shell dumpsys activity top
3. saveableStateHolder
val stateHolder = rememberSaveableStateHolder()
stateHolder.SaveableStateProvider(key = "home") {
HomeScreen()
}
## 注意点
先盯三类:一是把一次性事件当长期状态保存;二是底部导航多返回栈没有隔离;三是导航参数变化后旧状态被错误复用。很多 UI 切换异常不是导航组件错,而是状态边界定义错。
## 报错与排查
1. 返回后状态丢失
先查需要恢复的状态是页面级还是共享级,再看是不是用了 remember 而不是 rememberSaveable,或者 ViewModel scope 跟页面范围不匹配。
adb logcat -d | grep -i savedstate
2. 页面切换串状态
如果 A 页面改过的筛选条件跑到 B 页面去了,先查多返回栈有没有隔离,再查 key 是否唯一。不要一开始就把锅甩给 Compose。
fun routeKey(route: String, id: String): String = "$route-$id"
## 可运行片段
1. 状态对象
data class NavState(
val route: String,
val restored: Boolean,
val scrollIndex: Int
)
2. 演示入口
fun printNavState() {
println(NavState("home", true, 12))
}
3. 验证命令
adb logcat -d | grep -i NavHost
adb shell dumpsys activity top
## 结论
Compose 导航返回栈:状态恢复与页面切换这类问题,关键不是多写几个 remember,而是先把页面状态、返回栈和恢复规则钉死。只要这三层清楚,复杂页面切换也能稳定落地。
