最初的Jetpack Navigation库(有时也称为Nav2,因为它已经是主版本2)最初于2018年发布,早于Compose。虽然它很好地实现了最初的目标,但它在处理现代Compose模式时存在一些局限性。
一个关键的限制是,返回栈的状态只能间接观察。这意味着可能存在两个不同的数据源,从而导致应用程序状态不一致。此外,Nav2的NavHost被设计成只能显示单个目标页面——即返回栈顶部的目标页面——并填充可用空间。现在各种折叠屏等大屏设备越来越多,这使得实现能够同时显示多个内容窗格的自适应布局变得困难,例如在大屏幕上显示列表详情布局。
Nav3的设计理念
Nav3 的设计理念旨在提供更大的灵活性和开发者控制权:
- 自由掌控返回栈:开发者拥有并控制返回栈。它是一个简单的列表,由Compose状态提供支持。具体来说,Nav3要求返回栈是SnapshotStateList类型,其中T可以是任何类型。可以通过添加或删除项(T)来进行导航,状态更改会被Nav3的UI观察并反映出来。
- 开放且可扩展:导航库避免像个黑盒子,内部组件和状态难以访问。Nav3的设计理念是开放且可扩展的,它提供构建模块和实用的默认设置。如果需要自定义导航行为,可以深入到更底层,创建自己的组件和自定义项。
- 自由构建模块:Nav3并没有将所有行为都嵌入库中,而是提供了更小的组件,可以将它们组合起来创建更复杂的功能。还提供了一本“配方手册”,展示了如何组合组件来解决常见的导航难题。
Nav3主要特点
- 动画:内置过渡动画用于目标位置的切换,包括预测返回。它还拥有灵活的API,支持自定义动画行为,允许在应用级别和单个屏幕级别覆盖动画。
- 自适应布局:灵活的布局API(名为“场景”)允许在同一布局中渲染多个目标页面(例如,在大屏幕设备上使用列表详情布局)。这使得在单窗格布局和多窗格布局之间切换变得容易。
- 状态作用域:允许将状态作用域限定在返回栈上的目标位置,包括通过专用的Jetpack生命周期库提供可选的ViewModel支持。
- 模块化:API设计允许将导航代码拆分到多个模块中。这不仅缩短了构建时间,还实现了功能模块之间职责的清晰分离。
示例代码
data object Home
data class Product(val id: String)
val backStack = remember { mutableStateListOf<Any>(Home) }
NavDisplay(
backStack = backStack,
onBack = { backStack.removeLastOrNull() },
entryProvider = { route ->
when (route) {
is Home -> NavEntry(route) {
Column {
Text("Welcome to Nav3")
Button(onClick = {
backStack.add(Product("123"))
}) {
Text("Click to navigate")
}
}
}
is Product -> NavEntry(route) {
Text("Product ${route.id} ")
}
else -> NavEntry(Unit) { Text("Unknown route: $route") }
}
}
)