导航描述了用户在应用中移动的方式。用户通常通过点按或点击来与 UI 元素交互,应用则通过显示新内容进行响应。如果用户想返回到之前的内容,他们可以使用返回手势或点按返回按钮。
建模导航状态
建模这种行为的一种便捷方式是使用内容堆栈。当用户向 前 导航到新内容时,新内容会被推到堆栈顶部。当他们从该内容 返回 时,新内容会从堆栈中弹出,并显示之前的内容。在导航术语中,此堆栈通常被称为 返回堆栈,因为它代表用户 可以返回 到的内容。

创建返回堆栈
在 Navigation 3 中,返回堆栈实际上不包含内容。相反,它包含 对内容的引用,称为 键。键可以是任何类型,但通常是简单的、可序列化的数据类。使用引用而不是内容具有以下优点:
- 通过将键推入返回堆栈即可轻松导航。
- 只要键是可序列化的,返回堆栈就可以保存到持久存储中,从而使其能够在配置更改和进程终止后继续存在。这一点很重要,因为用户希望离开您的应用后,稍后返回时能够从上次离开的地方继续,并显示相同的内容。有关更多信息,请参阅保存返回堆栈。
Navigation 3 API 中的一个关键概念是您拥有返回堆栈。该库
- 期望您的返回堆栈是快照状态支持的
List<T>
,其中T
是您的返回堆栈keys
的类型。您可以使用Any
,或者提供您自己的、更强类型的键。当您看到“推入”或“弹出”这些术语时,底层实现是从列表的末尾添加或删除项。 - 观察您的返回堆栈,并使用
NavDisplay
在 UI 中反映其状态。
以下示例展示了如何创建键和返回堆栈,以及如何响应用户导航事件修改返回堆栈
// Define keys that will identify content data object ProductList data class ProductDetail(val id: String) @Composable fun MyApp() { // Create a back stack, specifying the key the app should start with val backStack = remember { mutableStateListOf<Any>(ProductList) } // Supply your back stack to a NavDisplay so it can reflect changes in the UI // ...more on this below... // Push a key onto the back stack (navigate forward), the navigation library will reflect the change in state backStack.add(ProductDetail(id = "ABC")) // Pop a key off the back stack (navigate back), the navigation library will reflect the change in state backStack.removeLastOrNull() }
将键解析为内容
在 Navigation 3 中,内容使用 NavEntry
进行建模,它是一个包含可组合函数的类。它代表一个 目标 - 用户可以 导航到 和 返回 的单个内容片段。
一个 NavEntry
还可以包含元数据——关于内容的信息。此元数据可以由容器对象(如 NavDisplay
)读取,以帮助它们决定如何显示 NavEntry
的内容。例如,元数据可用于覆盖特定 NavEntry
的默认动画。NavEntry metadata
是将 String
键映射到 Any
值的映射,提供多功能数据存储。
要将 key
转换为 NavEntry
,请创建一个 entryProvider
。这是一个接受 key
并返回该 key
对应的 NavEntry
的函数。它通常在创建 NavDisplay
时定义为 lambda 参数。
有两种创建 entryProvider
的方法:直接创建 lambda 函数,或使用 entryProvider
DSL。
直接创建 entryProvider
函数
您通常使用 when
语句创建一个 entryProvider
函数,其中每个键都有一个分支。
entryProvider = { key -> when (key) { is ProductList -> NavEntry(key) { Text("Product List") } is ProductDetail -> NavEntry( key, metadata = mapOf("extraDataKey" to "extraDataValue") ) { Text("Product ${key.id} ") } else -> { NavEntry(Unit) { Text(text = "Invalid Key: $it") } } } }
使用 entryProvider
DSL
entryProvider
DSL 可以简化您的 lambda 函数,因为它避免了对每个键类型进行测试并为每个键构造 NavEntry
的需要。为此,请使用 entryProvider
构建器函数。如果找不到键,它还包括默认回退行为(抛出错误)。
entryProvider = entryProvider { entry<ProductList> { Text("Product List") } entry<ProductDetail>( metadata = mapOf("extraDataKey" to "extraDataValue") ) { key -> Text("Product ${key.id} ") } }
请注意以下代码段中的内容:
entry
用于定义具有给定类型和可组合内容的NavEntry
entry
接受metadata
参数来设置NavEntry.metadata
显示返回堆栈
返回堆栈表示您的应用的导航状态。每当返回堆栈更改时,应用 UI 都应反映新的返回堆栈状态。在 Navigation 3 中,NavDisplay
会观察您的返回堆栈并相应地更新其 UI。使用以下参数构造它:
- 您的返回堆栈——这应该是
SnapshotStateList<T>
类型,其中T
是您的返回堆栈键的类型。它是一个可观察的List
,以便在更改时触发NavDisplay
的重新组合。 - 一个
entryProvider
,用于将返回堆栈中的键转换为NavEntry
。 - 可选地,为
onBack
参数提供一个 lambda。当用户触发返回事件时,会调用此函数。
以下示例展示了如何创建 NavDisplay
。
data object Home data class Product(val id: String) @Composable fun NavExample() { val backStack = remember { mutableStateListOf<Any>(Home) } NavDisplay( backStack = backStack, onBack = { backStack.removeLastOrNull() }, entryProvider = { key -> when (key) { is Home -> NavEntry(key) { ContentGreen("Welcome to Nav3") { Button(onClick = { backStack.add(Product("123")) }) { Text("Click to navigate") } } } is Product -> NavEntry(key) { ContentBlue("Product ${key.id} ") } else -> NavEntry(Unit) { Text("Unknown route") } } } ) }
默认情况下,NavDisplay
在单个窗格布局中显示返回堆栈中最顶部的 NavEntry
。以下录屏显示了此应用运行情况:

NavDisplay
在两个目标下的默认行为。整合
以下图表显示了 Navigation 3 中各个对象之间的数据流:

导航事件启动更改。键会根据用户交互被添加到返回堆栈或从返回堆栈中移除。
返回堆栈状态更改触发内容检索。
NavDisplay
(一个渲染返回堆栈的可组合项)会观察返回堆栈。在其默认配置中,它会在单个窗格布局中显示最顶部的返回堆栈条目。当返回堆栈上的顶部键更改时,NavDisplay
会使用此键从入口提供程序请求相应的内容。入口提供程序提供内容。入口提供程序是一个将键解析为
NavEntry
的函数。NavDisplay
收到键后,入口提供程序会提供关联的NavEntry
,其中包含键和内容。显示内容。
NavDisplay
接收NavEntry
并显示内容。