Navigation 3 は、Jetpack Compose でナビゲーションの状態を処理する方法の根本的な変化を表しており、Navigation 2 よりもアーキテクチャ上の利点が大幅に向上しています。
Wear Compose アプリを Navigation 2 から Navigation 3 に移行するために必要なアーキテクチャの変更と手順について説明します。
Navigation 3 の主な利点
- 直接的なバックスタック制御:
NavBackStackは、ユーザーがアクセスした画面の履歴を表すNavKeyオブジェクトの可変リストにすぎません。Kotlin のMutableListと同様に制御できます(add、removeLast、clear)。リストを直接操作して、キーを追加して進む、キーを削除して戻るなどのナビゲーション アクションを実行します。 - Compose ファースト設計: バックスタックは標準のオブザーバブルな 状態としてモデル化されます。ナビゲーション履歴を変更すると、他の Compose の状態を更新する場合とまったく同じように動作し、自動的に再コンポーズがトリガーされて現在の画面が表示されます。
- デフォルトで型安全: 文字列ベースのルートは完全に排除されます。 ナビゲーションでは、シリアル化可能なデータ オブジェクトとデータクラスが使用されます。
- プレゼンテーションの分離(シーン戦略): UI 遷移レイヤ
(
NavDisplayとSwipeDismissableSceneStrategy)は状態トラッキング(NavBackStack)から完全に 分離されているため、組み込みの Wear OS ナビゲーション遷移を簡単に 統合できます。
移行手順
1. 依存関係を更新する
古い androidx.wear.compose:compose-navigation 依存関係を削除し、Kotlin シリアル化のサポートとともに、分割された新しい Navigation 3 の依存関係を導入します。
以下を削除します。
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 では、実装し、すべての画面オブジェクトに @Serializable アノテーションを付ける必要があります。NavKey
なぜこれが必要なのですか?プロセスが終了してもバックスタックを保存して
復元できるようにするため、基盤となる 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. ルーティング ロジックを置き換える(NavController から NavBackStack)
NavController を NavBackStack initialized via
rememberNavBackStack に置き換えます。また、Wear OS 用に
SwipeDismissableSceneStrategyをインスタンス化する必要があります。
前(Navigation 2):
val navController = rememberSwipeDismissableNavController()
後(Navigation 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 に置き換えられます。
前(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() } } )