导航描述了用户在应用中移动的方式。用户通常通过点按或点击界面元素与其互动,而应用会通过显示新内容来做出响应。如果用户想要返回上一个内容,可以使用返回手势操作或点按返回按钮。
对导航状态进行建模
对这种行为进行建模的一种便捷方式是使用内容堆栈。当用户向前导航到新内容时,新内容会被推送到堆栈顶部。当用户从该内容中返回时,该内容会从堆栈中弹出,并显示之前的内容。在导航方面,此堆栈通常称为“返回堆栈”,因为它表示用户可以返回的内容。

创建返回堆栈
在 Navigation 3 中,返回堆栈实际上不包含内容。相反,它包含对内容(称为“键”)的引用。键可以是任何类型,但通常是简单的可序列化数据类。使用引用而非内容具有以下优势:
- 只需将键推送到返回堆栈,即可轻松导航。
- 只要键可序列化,就可以将返回堆栈保存到持久性存储空间,从而使其在配置更改和进程终止后继续存在。这一点很重要,因为用户希望能够离开您的应用,稍后再返回该应用,并从上次离开时显示的同一内容继续使用。如需了解详情,请参阅保存返回堆栈。
Navigation 3 API 中的一个关键概念是,您拥有返回堆栈。该库:
- 预期您的返回栈将是受快照状态支持的
List<T>
,其中T
是返回栈keys
的类型。您可以使用Any
,也可以提供自己的更强类型的键。当您看到“推送”或“弹出”这两个术语时,其底层实现是从列表末尾添加或移除项。 - 观察您的返回堆栈,并使用
NavDisplay
在界面中反映其状态。
以下示例展示了如何创建键和返回堆栈,以及如何响应用户导航事件来修改返回堆栈:
// 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
,请创建条目提供程序。这是一个接受 key
并返回相应 key
的 NavEntry
的函数。在创建 NavDisplay
时,它通常定义为 lambda 参数。
您可以通过以下两种方式创建 Entry Provider:直接创建 lambda 函数,或使用 entryProvider
DSL。
直接创建 Entry Provider 函数
您通常使用 when
语句创建一个 Entry Provider 函数,并为每个键创建一个分支。
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
显示返回堆栈
返回堆栈表示应用的导航状态。每当返回堆栈发生变化时,应用界面都应反映新的返回堆栈状态。在 Navigation 3 中,NavDisplay
会观察您的返回堆栈并相应地更新其界面。使用以下参数构造它:
- 您的返回堆栈 - 这应为
SnapshotStateList<T>
类型,其中T
是返回堆栈键的类型。它是可观测的List
,因此当它发生变化时,会触发NavDisplay
的重组。 - 用于将返回堆栈中的键转换为
NavEntry
对象的entryProvider
。 - (可选)为
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
并显示内容。