Jetpack Navigation を Navigation Compose に移行する

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

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

移行の前提条件

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

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

移行手順

Google が推奨する移行戦略を採用しているか、別のアプローチを採用しているかにかかわらず、フラグメントはコンポーズ可能なコンテナとしてのみ機能し、すべてのナビゲーション デスティネーションが画面コンポーザブルとなるポイントに到達します。このステージで、Navigation Compose に移行できます。

アプリがすでに UDF 設計パターンと Google のアーキテクチャ ガイドに従っている場合、Jetpack Compose と Navigation Compose に移行する際に、UI レイヤ以外のアプリの他のレイヤに大きなリファクタリングを行う必要はありません。

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

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

    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 を提供する方法を変更することです。多くの場合、Hilt インジェクション、およびその統合ポイントを Compose および Navigation で hiltViewModel を介して使用できます。

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

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

    1. 以前に Safe Args プラグインを使用してナビゲーションの経路とアクションを生成していた場合は、各コンポーザブルへの一意の文字列パスである route に置き換えてください。
    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. すべての Fragment、関連する 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 など、必要最小限の情報を渡してください。複雑なオブジェクトは、データレイヤーなどの信頼できる唯一の情報源にデータとして保存する必要があります。詳しくは、操作時に複雑なデータを取得するをご覧ください。

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

制限事項

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

Navigation Compose への増分移行

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

遷移アニメーション

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

さらに詳しく

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

  • Navigation Compose Codelab: 実践的な Codelab で Navigation Compose の基本を学習します。
  • Now in Android リポジトリ: Android の設計と開発のベスト プラクティスに従い、Navigation Compose を含む Kotlin と Jetpack Compose で完全に構築された、完全に機能する Android アプリ。
  • Sunflower から Jetpack Compose への移行: Sunflower サンプルアプリのビューから Compose への移行の過程を記したブログ投稿。Navigation Compose への移行も含まれています。
  • すべての画面に対応する Jetnews: Jetpack Compose と Navigation Compose ですべての画面をサポートする Jetnews サンプルのリファクタリングと移行について記載したブログ投稿。