Navigation 3 在 Jetpack Compose 處理導覽狀態的方式上,有根本性的轉變,相較於 Navigation 2,在架構上具有顯著優勢。
瞭解將 Wear Compose 應用程式從 Navigation 2 遷移至 Navigation 3 時,需要進行的架構變更和步驟。
Navigation 3 的主要優點
- 直接控制返回堆疊:
NavBackStack本質上只是NavKey物件的可變動清單,代表使用者造訪的畫面記錄。您可以像控制任何 KotlinMutableList(add、removeLast、clear) 一樣控制它。您可以直接操控清單來執行導覽動作,例如新增鍵來前進,或移除鍵來返回。 - 以 Compose 為優先的設計:返回堆疊會以標準可觀察狀態建模。修改導覽記錄的行為與更新任何其他 Compose 狀態完全相同,會自動觸發重新組合,以顯示目前的畫面。
- 預設為型別安全:完全淘汰以字串為基礎的路徑。 導覽功能會使用可序列化的資料物件和資料類別。
- 分離式簡報 (場景策略):UI 轉場效果層 (
NavDisplay和SwipeDismissableSceneStrategy) 完全與狀態追蹤 (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. 取代轉送邏輯 (NavController 至 NavBackStack)
將 NavController 替換為透過 rememberNavBackStack 初始化的 NavBackStack。您也需要針對 Wear OS 具現化 SwipeDismissableSceneStrategy。
變更前 (導覽 2):
val navController = rememberSwipeDismissableNavController()
變更後 (導覽 3):
val backStack = rememberNavBackStack(MigrationScreen.Landing as NavKey) val strategy = rememberSwipeDismissableSceneStrategy<NavKey>()
4. 將 NavHost 替換為 NavDisplay 和 entryProvider DSL
NavHost 容器及其內部 composable("route") { ... } 建構工具 DSL 會由 NavDisplay 和 entryProvider {
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() } } )