Jetpack Navigation を Navigation Compose に移行する

Navigation Compose API を使用すると、Jetpack Navigation のコンポーネント、インフラストラクチャ、機能を活用しながら、Compose アプリのコンポーザブル間を移動できます。

このページでは、Jetpack Compose へのビューベースの UI の移行の一環として、Fragment ベースの Jetpack Navigation から Navigation Compose に移行する方法について説明します。

移行の前提条件

すべてのフラグメントを対応する画面コンポーザブルに置き換えたら、Navigation Compose に移行できます。画面コンポーザブルには、Compose と View のコンテンツを組み合わせて含めることができますが、Navigation Compose への移行を有効にするには、すべてのナビゲーション デスティネーションをコンポーザブルにする必要があります。それまでは、相互運用ビューと Compose のコードベースで フラグメント ベースの Navigation コンポーネントを引き続き使用する必要があります。詳細については、ナビゲーションの相互運用性に関するドキュメントをご覧ください。

Compose のみのアプリで Navigation Compose を使用する必要はありません。コンポーズ可能なコンテンツをホストするためのフラグメントを保持している限り、フラグメント ベースの Navigation コンポーネント引き続き使用できます。

移行手順

推奨される移行戦略に沿って移行する場合でも、別の方法で移行する場合でも、すべてのナビゲーション デスティネーションが画面コンポーザブルになり、フラグメントがコンポーザブル コンテナとしてのみ機能するようになります。この段階で、Navigation Compose に移行できます。

アプリがすでに UDF 設計パターンアーキテクチャのガイドに準拠している場合、Jetpack Compose と Navigation Compose に移行しても、UI レイヤを除くアプリの他のレイヤの大幅なリファクタリングは必要ありません。

Navigation Compose に移行する手順は次のとおりです。

  1. Navigation Compose の依存関係をアプリに追加します。
  2. App-level コンポーザブルを作成し、Compose エントリ ポイントとして Activity に追加して、ビュー レイアウトの設定を置き換えます。

    class SampleActivity : ComponentActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            // setContentView<ActivitySampleBinding>(this, R.layout.activity_sample)
            setContent {
                SampleApp(/* ... */)
            }
        }
    }

  3. ナビゲーションの各デスティネーションのタイプを作成します。データを必要としないデスティネーションには data object を使用し、データを必要とするデスティネーションには data class または class を使用します。

    @Serializable data object First
    @Serializable data class Second(val id: String)
    @Serializable data object Third
    

  4. NavController は、これを参照する必要があるすべてのコンポーザブルがアクセスできる場所に設定します(通常は App コンポーザブル内)。このアプローチは状態ホイスティングの原則に従っており、コンポーザブル画面間を移動してバックスタックを維持する際の信頼できる情報源として NavController を使用できます。

    @Composable
    fun SampleApp() {
        val navController = rememberNavController()
        // ...
    }

  5. App コンポーザブル内にアプリの NavHost を作成し、navController を渡します。

    @Composable
    fun SampleApp() {
        val navController = rememberNavController()
    
        SampleNavHost(navController = navController)
    }
    
    @Composable
    fun SampleNavHost(
        navController: NavHostController
    ) {
        NavHost(navController = navController, startDestination = First) {
            // ...
        }
    }

  6. composable デスティネーションを追加してナビゲーション グラフを作成します。各画面がすでに Compose に移行されている場合は、このステップでは、これらの画面コンポーザブルをフラグメントから composable の宛先に抽出するのみです。

    class FirstFragment : Fragment() {
    
        override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View {
            return ComposeView(requireContext()).apply {
                setContent {
                    // FirstScreen(...) EXTRACT FROM HERE
                }
            }
        }
    }
    
    @Composable
    fun SampleNavHost(
        navController: NavHostController
    ) {
        NavHost(navController = navController, startDestination = First) {
            composable<First> {
                FirstScreen(/* ... */) // EXTRACT TO HERE
            }
            composable<Second> {
                SecondScreen(/* ... */)
            }
            // ...
        }
    }

  7. Compose UI のアーキテクチャのガイダンスに沿って、特に ViewModel とナビゲーション イベントをコンポーザブルに渡す方法を学んだら、次のステップとして、各画面のコンポーザブルに ViewModel を提供する方法を変更します。多くの場合、hiltViewModel を介して Hilt インジェクションと、Compose と Navigation との統合ポイントを使用できます。

    @Composable
    fun FirstScreen(
        // viewModel: FirstViewModel = viewModel(),
        viewModel: FirstViewModel = hiltViewModel(),
        onButtonClick: () -> Unit = {},
    ) {
        // ...
    }

  8. すべての findNavController() ナビゲーション呼び出しを navController 呼び出しに置き換え、navController 全体を渡すのではなく、ナビゲーション イベントとして各コンポーザブル スクリーンに渡します。このアプローチは、コンポーズ可能な関数から呼び出し元にイベントを公開するベスト プラクティスに従い、navController を信頼できる唯一の情報源として保持します。

    データをデスティネーションに渡すには、そのデスティネーションに定義されたルートクラスのインスタンスを作成します。その後、デスティネーションのバックスタック エントリから直接取得するか、SavedStateHandle.toRoute() を使用して ViewModel から取得できます。

    @Composable
    fun SampleNavHost(
        navController: NavHostController
    ) {
        NavHost(navController = navController, startDestination = First) {
            composable<First> {
                FirstScreen(
                    onButtonClick = {
                        // findNavController().navigate(firstScreenToSecondScreenAction)
                        navController.navigate(Second(id = "ABC"))
                    }
                )
            }
            composable<Second> { backStackEntry ->
                val secondRoute = backStackEntry.toRoute<Second>()
                SecondScreen(
                    id = secondRoute.id,
                    onIconClick = {
                        // findNavController().navigate(secondScreenToThirdScreenAction)
                        navController.navigate(Third)
                    }
                )
            }
            // ...
        }
    }

  9. すべての Fragment、関連する XML レイアウト、不要なナビゲーションなどのリソース、古い Fragment と Jetpack Navigation の依存関係を削除します。

Navigation Compose に関する詳細な手順については、設定のドキュメントをご覧ください。

一般的なユースケース

どの Navigation コンポーネントを使用する場合でも、同じナビゲーションの原則が適用されます

移行の一般的なユースケースには、次のようなものがあります。

これらのユースケースの詳細については、Compose によるナビゲーションをご覧ください。

移動時の複雑なデータの取得

移動時には複雑なデータ オブジェクトを渡さないことを強くおすすめします。代わりに、ナビゲーション アクションの実行時に引数として必要最低限の情報(一意の識別子やその他の形式の ID など)を渡します。複雑なオブジェクトは、データレイヤなどの信頼できる単一の情報源にデータとして保存する必要があります。詳細については、ナビゲーション時の複雑なデータの取得をご覧ください。

フラグメントが複雑なオブジェクトを引数として渡している場合は、まずコードをリファクタリングして、これらのオブジェクトをデータレイヤに保存および取得できるようにすることを検討してください。例については、Now in Android リポジトリをご覧ください。

制限事項

このセクションでは、Navigation Compose の現在の制限事項について説明します。

Navigation Compose への段階的な移行

現時点では、コードでフラグメントをデスティネーションとして使用しながら Navigation Compose を使用することはできません。Navigation Compose の使用を開始するには、すべてのデスティネーションをコンポーザブルにする必要があります。この機能リクエストは、Issue Tracker で追跡できます。

遷移アニメーション

Navigation 2.7.0-alpha01 以降、以前は AnimatedNavHost でサポートされていたカスタム遷移の設定が、NavHost で直接サポートされるようになりました。詳しくは、リリースノートをご覧ください。

詳細

Navigation Compose への移行の詳細については、次のリソースをご覧ください。

  • Navigation Compose Codelab: ハンズオン Codelab で Navigation Compose の基本を学びます。
  • Now in Android リポジトリ: Kotlin と Jetpack Compose のみで構築された、完全に機能する Android アプリ。Android の設計と開発のベスト プラクティスに準拠しており、Navigation Compose が含まれています。
  • Sunflower を Jetpack Compose に移行する: Sunflower サンプルアプリをビューから Compose に移行する過程を記述したブログ投稿です。Navigation Compose への移行も含まれています。
  • あらゆる画面に対応した Jetnews: Jetpack Compose と Navigation Compose を使用してすべての画面をサポートするように Jetnews サンプルをリファクタリングして移行する方法について説明するブログ投稿。