遷移至 Navigation 3

Navigation 3 在 Jetpack Compose 處理導覽狀態的方式上,有根本性的轉變,相較於 Navigation 2,在架構上具有顯著優勢。

瞭解將 Wear Compose 應用程式從 Navigation 2 遷移至 Navigation 3 時,需要進行的架構變更和步驟。

Navigation 3 的主要優點

  • 直接控制返回堆疊NavBackStack 本質上只是 NavKey 物件的可變動清單,代表使用者造訪的畫面記錄。您可以像控制任何 Kotlin MutableList (addremoveLastclear) 一樣控制它。您可以直接操控清單來執行導覽動作,例如新增鍵來前進,或移除鍵來返回。
  • 以 Compose 為優先的設計:返回堆疊會以標準可觀察狀態建模。修改導覽記錄的行為與更新任何其他 Compose 狀態完全相同,會自動觸發重新組合,以顯示目前的畫面。
  • 預設為型別安全:完全淘汰以字串為基礎的路徑。 導覽功能會使用可序列化的資料物件和資料類別。
  • 分離式簡報 (場景策略):UI 轉場效果層 (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 - Generic Type-Safe Routes):

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

之後 (Navigation 3 - NavKey + Serializable):

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

    @Serializable
    data object List : MigrationScreen
}

3. 取代轉送邏輯 (NavControllerNavBackStack)

NavController 替換為透過 rememberNavBackStack 初始化的 NavBackStack。您也需要針對 Wear OS 具現化 SwipeDismissableSceneStrategy

變更前 (導覽 2):

val navController = rememberSwipeDismissableNavController()

變更後 (導覽 3):

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

4. 將 NavHost 替換為 NavDisplayentryProvider DSL

NavHost 容器及其內部 composable("route") { ... } 建構工具 DSL 會由 NavDisplayentryProvider { entry<Key> { ... } } DSL 取代。

變更前 (導覽 2):

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

變更後 (導覽 3):

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