NavController — Jetpack Composeにおける本質、メソッド、ナビゲーション管理

著者: IT Sectr 公開日: 2026-06-29 読了時間: 7 分

NavControllerはNavigation Composeライブラリの中心的なコンポーネントであり、Androidアプリケーションのナビゲーションスタックとback stackの状態を管理します。NavControllerを通じて、画面間の遷移、前のページへの復帰、ルート間のデータ転送が実行されます。Android Developers (2025)によると、NavControllerは複数の画面を持つComposeアプリケーションに必須の要素です。コントローラはrememberNavController()で作成され、NavHostに渡され、コンポジションの任意の場所からnavigate()を呼び出すことができます。組み込みのSavedStateHandleサポートにより、再構成時にViewModelの状態が自動的に保存されます。

重要なポイント

  • NavController — Composeの中央ナビゲーションコントローラ。back stackと画面間の遷移を管理
  • navigate() — スタック管理のためのNavOptionsサポート付きでルートに遷移する主要メソッド
  • popBackStack() — 指定ルートまでのオプションのクリーンアップを伴う前の画面への復帰
  • SavedStateHandle — ナビゲーション中に画面状態を保持するためのViewModelとの統合
  • currentBackStackEntryAsState() — UI同期のための現在のルートの監視

Jetpack ComposeのNavControllerとは?

NavControllerはNavigation Composeライブラリのクラスで、Composeアプリケーション用のナビゲーションコントローラを実装します。NavControllerはNavBackStackEntryスタックを管理し、各エントリにはルート、引数、画面状態が含まれます。コントローラは基本的なナビゲーション操作(遷移、復帰、置換、クリーンアップ)をサポートします。

Viewシステム(FragmentManagerやIntentを使用)とは異なり、NavControllerはComposeコンテキストでのみ動作します。Back stackはFragmentスタックではなくNavDestinationグラフとして保存されます。これによりFragmentの作成と破棄のオーバーヘッドがなくなり、テストが簡素化されます。NavControllerはTestNavHostControllerを介してモック化できます。

NavControllerはNavHostと密接に関連しています。NavHostはグラフから現在の画面をレンダリングするコンテナです。NavHostがないと、NavControllerはcomposable関数を表示できませんが、スタックを管理する機能は保持します。典型的なアーキテクチャでは、NavControllerはActivityまたはメインコンポーザブルのレベルで作成され、パラメータを介してコンポジションツリーの下位に渡されます。

Googleによると、NavControllerはいくつかのメジャーリリースを経てきました。バージョン2.8.0ではType-Safe Navigationが追加され、バージョン2.9.0ではpredictive back gesture(Android 14+)のサポートが追加されました。コントローラはMaterial3 ScaffoldおよびBottomNavigationと互換性があります。マルチモジュールプロジェクトでは、NavControllerはDI(Hilt/Koin)またはコンストラクタパラメータを介して渡されます。

NavControllerはcomposable関数rememberNavController()で作成されます。この関数は、現在のcomposableのライフサイクルにバインドされたNavHostController(NavControllerのサブクラス)のインスタンスを返します。コンポジションを離れると、コントローラはクリアされます。再構成時にコントローラを保持するには、rememberSaveableまたはViewModelを使用します。

kotlin
@Composable
fun MyApp() {
    val navController = rememberNavController()
    NavHost(
        navController = navController,
        startDestination = "main"
    ) {
        composable("main") { MainScreen(navController) }
        composable("details") { DetailsScreen(navController) }
    }
}

NavControllerの設定には次のものがあります:NavHostController(メイン)、TestNavHostController(テスト)、ScopedNavController(ネストされたグラフ用の子)。BottomNavigationの場合、NavControllerはアプリ全体で単一である必要があります。各タブで新しいコントローラを作成するとスタックが失われます。ネストされた画面にコントローラを渡すには、可読性を維持するためにCompositionLocalProviderではなく関数パラメータを使用します。

ナビゲーションテストには、compose-test-ruleとともにTestNavHostControllerを使用します。このコントローラでは、初期ルートを設定し、navigate()が期待される遷移をトリガーしたことを確認できます。NavControllerのテストにエミュレータは不要で、Compose TestのSemanticsマッチャーで動作します。

navigate(route: String)メソッドはNavControllerの主要なナビゲーションメカニズムです。ルート文字列、オプションのNavOptions、Navigator.Extrasを受け入れます。NavOptionsは遷移動作を制御します:launchSingleTop(スタック内のルートを複製しない)、popUpTo(指定ルートまでスタックをクリア)、restoreState(以前の状態を復元)。

NavOptionsはビルダー構文(NavOptionsBuilder)で設定します。主なパラメータ:popUpTo(ルート+inclusive/saveState)、launchSingleTop(Boolean、true — 複製を作成しない)、restoreState(復帰時に状態を復元)。popUpToがないと、navigate()のたびにスタックにエントリが追加され、back stackが蓄積されてBackボタンの動作が不正になります。

kotlin
navController.navigate("profile/42") {
    popUpTo("main") { saveState = true }
    launchSingleTop = true
    restoreState = true
}

Navigator.Extrasを使用すると、ルートの一部ではない追加データ(アニメーション用の共有要素、Intentフラグ、Pac-Manバンドル)を渡せます。Extrasはめったに使用されず、主にAccompanist AnimationやカスタムNavigatorとの統合に使用されます。ほとんどのシナリオでは、ルート文字列とNavOptionsで十分です。

popBackStack:復帰とスタッククリーンアップの管理

popBackStack()は前の画面に戻るためのメソッドです。引数なしではスタックの一番上のエントリを削除し、成功した場合はtrueを返します。スタックが空の場合、メソッドはfalseを返し、Activityが閉じます(super.onBackPressed()と同様)。

オーバーロード版のpopBackStack(route: String, inclusive: Boolean)は、指定されたルートまでのすべてのエントリを削除します。inclusive = trueの場合、指定されたルート自体も削除されます。メソッドはBooleanを返します。エントリが見つかって削除された場合はtrueです。inclusive版は、認証後や注文完了後の「ルート画面に戻る」シナリオに便利です。

メソッド説明
popBackStack()1つ前の画面に戻るnavController.popBackStack()
popBackStack(route, false)routeまでクリア(routeは残る)popBackStack(“home”, false)
popBackStack(route, true)routeまでを含めてクリアpopBackStack(“home”, true)
navigate(route) { popUpTo(route) { inclusive = true } }完全クリアして遷移navigate(“login”) { popUpTo(0) { inclusive = true } }

システムのBackボタン(ハードウェアバックボタン)を処理するには、ComposeのBackHandlerを使用します。BackHandlerはenabledとonBack(押下時に呼び出されるコールバック)を受け入れます。Android 14+では、NavControllerバージョン2.9.0から統合されたPredictiveBackGestureが使用されます。Predictive backは復帰のプレビューアニメーションを追加します。

SavedStateHandle:画面状態の保持

SavedStateHandleは、ナビゲーションおよび再構成中にViewModelの状態を保持するためのメカニズムです。NavControllerは各NavBackStackEntryにSavedStateHandleを自動的に提供します。SavedStateHandleを介して、ViewModelは画面状態を保存し、復帰時に復元します(restoreState = true)。

Navigation Composeでは、SavedStateHandleはViewModelと一緒に使用されます。ViewModelはbackStackEntryから渡されるSavedStateHandleを介して初期化されます。別の画面に遷移して戻る場合(restoreStateあり)、ViewModelは新しく作成されるのではなく、保存された状態を受け取ります。これはデータ入力、フィルター、スクロールを伴う画面にとって重要です。

kotlin
class ProfileViewModel(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {
    val userId: String = savedStateHandle.get<String>("userId") ?: ""
    var searchQuery by savedStateHandle.getStateFlow("search", "")
        .collectAsState()
}

SavedStateHandleはプリミティブ型、String、Bundle、Parcelableをサポートします。複雑なオブジェクトの場合は、IDのみを保存し、完全なデータはリポジトリからロードします。SavedStateHandleの制限は約1MBで、これを超えるとTransactionTooLargeExceptionが発生します。大量のデータの場合は、handleに保存する代わりにRoomまたはDataStoreを使用します。

重要:SavedStateHandleはNavOptionsでrestoreState = trueが使用されている場合にのみ状態を保持します。restoreStateが指定されていない場合、復帰時にViewModelはデフォルト値で新しく作成されます。restoreStateを使用したBottomNavigationの切り替えでは、NavControllerは各タブの状態を保持し、再選択時に復元します。

currentBackStackEntryAsStateによる現在のルートの監視

currentBackStackEntryAsState()はState<NavBackStackEntry?>を返す関数で、現在のルートが変更されるたびに更新されます。これはナビゲーションとUIを同期する主要なメカニズムです。BottomNavigationはアクティブな項目を強調表示し、Toolbarはタイトルを更新し、Drawerは遷移時に閉じます。

この関数はsnapshotFlowとcollectAsStateを介して動作します。back stackが変更されると、Composeはサブスクライブされた要素を再コンポーズします。重要:currentBackStackEntryAsState()は遷移アニメーションが完了した後にのみ更新されます。即時更新が必要な場合は、currentDestinationを使用します。これはnavigate()と同期的に変更されますが、状態はサポートしません。

kotlin
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route

Text(
    text = when (currentRoute) {
        "home" -> "Home"
        "profile" -> "Profile"
        else -> ""
    }
)

現在のルートの引数にアクセスするには、navBackStackEntry?.argumentsを使用します。これはBottomNavigationで便利です。selectedItemはcurrentRouteに基づいて計算されます。ナビゲーションデバッグには、各遷移をログに記録するNavController.addOnDestinationChangedListener()を使用します。本番環境では、多数のcomposable内でのサブスクリプションを避け、ViewModelに単一のソースを作成してStateをUIに渡します。

よくある質問

1つのActivityで複数のNavControllerを作成できますか?

技術的には可能ですが、推奨されません。単一のNavControllerが一貫したback stackを保証し、デバッグを簡素化します。複数のコントローラは、個別のナビゲーションを持つネストされたグラフ(独自のスタックを持つmodal bottom sheetなど)にのみ正当化されます。

ViewModelを介してNavControllerを渡すには?

コンストラクタまたはDIを介してNavControllerをViewModelに渡します。ただし、NavController自体ではなくコールバック関数(onNavigate、onBack)のみを渡す方がテストが簡素化されるため推奨されます。イベントには、ViewModelでChannel<NavEvent>を使用し、UIで収集します。

非同期操作後にnavigateが機能しないのはなぜですか?

問題はライフサイクルにあります。NavControllerがまだ初期化されていない場合(NavHostが構築されていない)、navigate()は無視されます。データロード後にナビゲーションを呼び出すには、任意のライフサイクルを持つコルーチン内ではなく、LaunchedEffectを使用します。

back stack全体をクリアして新しい画面に遷移するには?

navController.navigate(“target”) { popUpTo(0) { inclusive = true } }を呼び出します。popUpTo(0)パラメータはスタックを完全にクリアし、inclusive = trueは開始エントリも削除します。launchSingleTop = trueフラグはルートの重複を防ぎます。

NavHostControllerとNavControllerの違いは?

NavHostControllerはNavControllerのサブクラスで、NavHost用の追加メソッド(setOnBackStackChangedListenerなど)があります。NavControllerはベースクラスで、NavHostの外部でプログラムによるスタック管理に使用できます。ほとんどの場合、NavHostControllerが使用されます。

まとめ

  • NavController — Navigation Composeの中心的なコンポーネント。ルートスタックと画面間の遷移を管理
  • navigate() — NavOptionsを介してpopUpTo、launchSingleTop、restoreStateの設定で遷移を実行
  • popBackStack() — 復帰を管理:単一ステップまたはinclusive付きの指定ルートまでの一括クリア
  • SavedStateHandle — ナビゲーション中の画面状態の自動保存のためにViewModelと統合
  • currentBackStackEntryAsState() — UI同期のための現在のルートのリアクティブ監視を提供
  • BackHandler — システムのBackボタンを処理。PredictiveBackGestureはNavController 2.9.0以降でサポート
  • テストには、compose-test-ruleとSemanticsマッチャーを使用したTestNavHostControllerを使用

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください