Jetpack Navigation を Navigation Compose に移行する

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

このページでは、ビューベースの大規模な UI の Jetpack Compose への移行の一環として、フラグメント ベースの Jetpack Navigation から Navigation Compose に移行する方法について説明します。

移行の前提条件

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

Compose のみのアプリで Navigation Compose を使用することは、前提条件ではありません。コンポーザブル コンテンツをホストするためにフラグメントを保持している限り、フラグメント ベースの Navigation コンポーネント引き続き使用できます。

移行手順

Google が推奨する移行戦略を採用している場合でも、別のアプローチを採用している場合でも、すべてのナビゲーション デスティネーションが画面コンポーザブルになり、フラグメントがコンポーザブル コンテナとしてのみ機能するようになります。この段階で、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. NavController は、参照する必要があるすべてのコンポーザブルがアクセスできる場所(通常は App コンポーザブル内)にセットアップします。このアプローチは状態ホイスティングの原則に従い、コンポーザブル画面間の移動とバックスタックのメンテナンスのための信頼できる情報源として NavController を使用できます。

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

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

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

  5. 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(/* ... */)
            }
            // ...
        }
    }

  6. Compose UI の設計のガイダンス(特に ViewModel とナビゲーション イベントをコンポーザブルに渡す方法)に従った場合、次のステップでは、各画面コンポーザブルに ViewModel を提供する方法を変更します。多くの場合、hiltViewModel を介して Hilt インジェクションとその統合ポイントを Compose と Navigation で使用できます。

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

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

    1. 以前に Safe Args プラグインを使用してナビゲーションのルートとアクションを生成していた場合は、ルート(デスティネーションごとに一意のコンポーザブルへの文字列パス)に置き換えます。
    2. データを渡すときに Safe Args を置き換える方法については、引数を使用して移動するをご覧ください。
    3. Navigation Compose での型安全性については、以下の Safe Args セクションをご覧ください。

      @Composable
      fun SampleNavHost(
          navController: NavHostController
      ) {
          NavHost(navController = navController, startDestination = "first") {
              composable("first") {
                  FirstScreen(
                      onButtonClick = {
                          // findNavController().navigate(firstScreenToSecondScreenAction)
                          navController.navigate("second_screen_route")
                      }
                  )
              }
              composable("second") {
                  SecondScreen(
                      onIconClick = {
                          // findNavController().navigate(secondScreenToThirdScreenAction)
                          navController.navigate("third_screen_route")
                      }
                  )
              }
              // ...
          }
      }

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

Navigation Compose に関連する詳細な手順について、セットアップ ドキュメントで確認できます。

一般的なユースケース

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

移行の一般的なユースケースは次のとおりです。

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

Safe Args

Jetpack Navigation とは異なり、Navigation Compose では、コード生成に Safe Args プラグインを使用することはサポートされていません。代わりに、実行時に型セーフになるようにコードを構造化することで、Navigation Compose で型安全性を実現できます。

ナビゲーション時に複雑なデータを取得する

Navigation Compose は文字列ルートベースであり、Jetpack Navigation とは異なり、カスタムの Parcelable と Serializable を引数として渡すことはできません

操作時には複雑なデータ オブジェクトを受け渡さないことを強くおすすめします。代わりに、ナビゲーション アクションを実行するときに、一意の識別子やその他の形式の 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 サンプルのリファクタリングと移行について記載したブログ投稿。