Compose为什么推出Navigation3来代替Navigation2

173 阅读3分钟

最初的Jetpack Navigation库(有时也称为Nav2,因为它已经是主版本2)最初于2018年发布,早于Compose。虽然它很好地实现了最初的目标,但它在处理现代Compose模式时存在一些局限性。

一个关键的限制是,返回栈的状态只能间接观察。这意味着可能存在两个不同的数据源,从而导致应用程序状态不一致。此外,Nav2的NavHost被设计成只能显示单个目标页面——即返回栈顶部的目标页面——并填充可用空间。现在各种折叠屏等大屏设备越来越多,这使得实现能够同时显示多个内容窗格的自适应布局变得困难,例如在大屏幕上显示列表详情布局。

img007_1.png

Nav3的设计理念

Nav3 的设计理念旨在提供更大的灵活性和开发者控制权:

  • 自由掌控返回栈:开发者拥有并控制返回栈。它是一个简单的列表,由Compose状态提供支持。具体来说,Nav3要求返回栈是SnapshotStateList类型,其中T可以是任何类型。可以通过添加或删除项(T)来进行导航,状态更改会被Nav3的UI观察并反映出来。
  • 开放且可扩展:导航库避免像个黑盒子,内部组件和状态难以访问。Nav3的设计理念是开放且可扩展的,它提供构建模块和实用的默认设置。如果需要自定义导航行为,可以深入到更底层,创建自己的组件和自定义项。
  • 自由构建模块:Nav3并没有将所有行为都嵌入库中,而是提供了更小的组件,可以将它们组合起来创建更复杂的功能。还提供了一本“配方手册”,展示了如何组合组件来解决常见的导航难题。

img007_2.png

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") }
    }
  }
)