迁移到 Navigation 3

Navigation 3 代表着 Jetpack Compose 处理导航状态方式的根本转变,与 Navigation 2 相比,它具有显著的架构优势。

了解将 Wear Compose 应用从 Navigation 2 迁移到 Navigation 3 所需的架构变更和步骤。

Navigation 3 的主要优势

  • 直接返回堆栈控制NavBackStack 从根本上来说只是 一个可变 NavKey 对象列表,表示用户访问过的屏幕的历史记录。您可以像控制任何 Kotlin MutableListaddremoveLastclear)一样控制它。您可以直接操纵列表来执行导航操作,例如添加键以向前移动或移除键以向后移动。
  • 以 Compose 为先的设计:返回堆栈被建模为标准可观测 状态。修改导航历史记录的行为与更新任何其他 Compose 状态的行为完全相同,会自动触发重组以显示当前屏幕。
  • 默认类型安全:完全消除了基于字符串的路线。 导航使用可序列化数据对象和数据类。
  • 分离的演示文稿(场景策略):界面转换层 (NavDisplaySwipeDismissableSceneStrategy)与状态跟踪(NavBackStack)完全 分离,从而可以更轻松地集成内置的 Wear OS 导航转换。

迁移步骤

1. 更新依赖项

移除旧的 androidx.wear.compose:compose-navigation 依赖项,并引入新的拆分 Navigation 3 依赖项以及 Kotlin 序列化支持。

移除

implementation("androidx.wear.compose:compose-navigation:...")

添加

implementation("androidx.navigation3:navigation3-runtime:...") // State logic
implementation("androidx.navigation3:navigation3-ui:...")      // Display logic
implementation("androidx.wear.compose:compose-navigation3:...") // Wear gestures
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:...") // Requires compiler plugin

2. 更新目的地以实现 NavKey

在 Navigation 2 中,您可能使用了字符串或通用对象进行路由。在 Navigation 3 中,您 必须 实现 NavKey 标记接口,并 使用 @Serializable 注解每个屏幕对象。

为什么需要这样做?为了保证返回堆栈可以在进程终止后保存和 恢复,底层 navigation3-runtime 依赖于 kotlinx-serialization 来序列化状态。

之前(Navigation 2 - 通用类型安全路线)

sealed class Nav2Screen {
    data object Landing : Nav2Screen()
    data object List : Nav2Screen()
}

之后(Navigation 3 - NavKey + 可序列化)

@Serializable
sealed interface MigrationScreen : NavKey {
    @Serializable
    data object Landing : MigrationScreen

    @Serializable
    data object List : MigrationScreen
}

3. 替换路由逻辑(NavControllerNavBackStack

NavController 替换为通过 rememberNavBackStack 初始化的 NavBackStack。您还需要专门为 Wear OS 实例化 SwipeDismissableSceneStrategy

之前(Navigation 2)

val navController = rememberSwipeDismissableNavController()

之后(Navigation 3)

val backStack = rememberNavBackStack(MigrationScreen.Landing as NavKey)
val strategy = rememberSwipeDismissableSceneStrategy<NavKey>()

4. 将 NavHost 替换为 NavDisplayentryProvider DSL

NavHost 容器及其内部 composable("route") { ... } 构建器 DSL 将被 NavDisplayentryProvider { entry<Key> { ... } } DSL 替换。

之前(Navigation 2)

SwipeDismissableNavHost(navController = navController, startDestination = "menu") {
    composable("menu") {
        GreetingScreen(
            onShowList = { navController.navigate("list") }
        )
    }
    composable("list") {
        ListScreen()
    }
}

之后(Navigation 3)

NavDisplay(
    backStack = backStack,
    sceneStrategies = listOf(strategy),
    entryProvider = entryProvider {
        entry<MigrationScreen.Landing> {
            GreetingScreen(
                onShowList = { backStack.add(MigrationScreen.List) }
            )
        }
        entry<MigrationScreen.List> {
            ListScreen()
        }
    }
)