Navigation 3:自有 Back Stack · AndroidX 源码指南
AAndroidX 源码指南
Navigation
应用导航 · androidx.navigation

Navigation 3:自有 Back Stack2.10.0-beta01

用 NavKey、NavBackStack、entryProvider、NavDisplay 组合导航状态与显示策略。

最后更新 2026-08-01

最小模型

@Serializable object Home : NavKey
@Serializable data class Detail(val id: String) : NavKey

val backStack = rememberNavBackStack(Home)

NavDisplay(
    backStack = backStack,
    onBack = { backStack.removeAt(backStack.lastIndex) },
    entryProvider = entryProvider {
        entry<Home> {
            HomeScreen { id -> backStack.add(Detail(id)) }
        }
        entry<Detail> { route ->
            DetailScreen(route.id)
        }
    },
)

核心思想:导航状态就是可观察的列表

  • 前进 = backStack.add(navKey)
  • 返回 = backStack.removeAt(lastIndex)
  • NavDisplay 要求 back stack 非空——根 entry 不能被普通返回删除。
@Serializable object Home : NavKey
@Serializable data class Profile(val id: String) : NavKey
@Serializable data class Settings(val tab: Int = 0) : NavKey
  • NavKey 是导航目标的身份标识(类似 Nav2 的 route)。
  • 可以是 object(无参数)或 data class(带参数)。
  • 参数要小而可序列化(ID、枚举),完整对象由目标页自己拉取。
backStack.add(key)                    // 压栈(前进)
backStack.addAll(listOf(a, b))        // 批量压栈
backStack.removeAt(backStack.lastIndex)  // 弹栈(返回)
backStack.removeLastOrNull()          // 安全弹栈
backStack[0] = key                    // 替换栈底(如 tab 切换)
场景操作
普通前进add
系统返回removeAt(lastIndex)
底部导航切换替换/重排(保留各 tab 的栈)
回到根移除到只剩根

Decorator:状态能力

NavDisplay(
    entryDecorators = listOf(
        rememberSaveableStateHolderNavEntryDecorator(),  // 保存 rememberSaveable
        rememberViewModelStoreNavEntryDecorator(),       // 每 entry 独立 ViewModel
    ),
    ...
)
Decorator能力
rememberSaveableStateHolderNavEntryDecoratorentry 切换时保存/恢复 UI 状态
rememberViewModelStoreNavEntryDecorator每个 entry 独立 ViewModelStore(back stack 弹出才销毁)

没有 ViewModel decorator:所有 entry 共享外层 ViewModelStore,返回后再进入会拿到旧 ViewModel。

NavDisplay 一次显示 back stack 的一个 entry(栈顶)。布局策略:

NavDisplay(
    backStack = backStack,
    onBack = ...,
    entryProvider = ...,
    modifier = Modifier.fillMaxSize(),
)
  • 单页面:直接 NavDisplay
  • 底部导航 + 内容:用 Scaffold + NavigationBar,NavDisplay 放内容区,tab 切换操作 backStack。
  • 多栏(大屏):不同 window size 下把同一 backStack 渲染成不同布局。

与 Meme 项目的实际用法

// Meme 的 MainDisplay 模式:NavDisplay + 手动 backStack
val backStack = remember { mutableStateListOf<Any>(Main) }

NavDisplay(
    entryDecorators = listOf(
        rememberSaveableStateHolderNavEntryDecorator(),
        rememberViewModelStoreNavEntryDecorator(),
    ),
    backStack = backStack,
    onBack = { backStack.removeLastOrNull() },
    entryProvider = entryProvider {
        entry<Main> { MainScreen() }
        entry<Image> { ImageScreen(...) }
        entry<Meme> { MemeScreen() }
    },
)

常见陷阱

  • 根 entry 被 pop:back stack 空 → 崩溃或黑屏;onBack 要保护根。
  • 忘 ViewModel decorator:entry 间 ViewModel 串扰。
  • 参数塞大对象:不可序列化 → 崩溃;只传 ID。
  • 手动管理不清理:无限 add 不 remove → 内存增长(用 removeAt 配合系统返回)。

复制即用

import androidx.navigation3.NavDisplay
import androidx.navigation3.NavKey
import androidx.navigation3.entryProvider
import androidx.navigation3.rememberNavBackStack
import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator
import androidx.navigation3.runtime.rememberViewModelStoreNavEntryDecorator

@Serializable object Home : NavKey
@Serializable data class Detail(val id: String) : NavKey

@Composable
fun AppNavigation() {
    val backStack = rememberNavBackStack(Home)

    NavDisplay(
        backStack = backStack,
        onBack = { if (backStack.size > 1) backStack.removeAt(backStack.lastIndex) },
        entryDecorators = listOf(
            rememberSaveableStateHolderNavEntryDecorator(),
            rememberViewModelStoreNavEntryDecorator(),
        ),
        entryProvider = entryProvider {
            entry<Home> {
                HomeScreen(onOpen = { id -> backStack.add(Detail(id)) })
            }
            entry<Detail> { route ->
                DetailScreen(route.id)
            }
        },
    )
}

要点

  • Nav3 = NavKey 列表 + NavDisplay + entryProvider + decorator。
  • 前进 add、返回 removeAt;根 entry 保护。
  • ViewModel/saveable 状态靠 decorator;缺了会串扰或丢失。
  • 应用拥有栈,NavDisplay 只负责显示栈顶。

相关页面