Navigation は、ユーザーがアプリ内を移動する方法を説明します。ユーザーは通常、タップまたはクリックによって UI 要素を操作し、アプリはそれに応じて新しいコンテンツを表示します。ユーザーが前のコンテンツに戻りたい場合は、[戻る] 操作を使用するか、[戻る] ボタンをタップします。
ナビゲーションの状態をモデル化する
この動作をモデル化する便利な方法は、コンテンツのスタックを使用することです。ユーザーが新しいコンテンツに移動すると、そのコンテンツはスタックの一番上にプッシュされます。 そのコンテンツから戻ると、スタックからポップされ、前のコンテンツが表示されます。 ナビゲーションの用語では、このスタックは通常、ユーザーが戻ることができるコンテンツを表すため、バックスタック と呼ばれます。
バックスタックを作成する
Navigation 3 では、バックスタックに実際のコンテンツは含まれません。代わりに、 **キー** と呼ばれるコンテンツへの参照が含まれます。キーは任意の型にできますが、通常はシリアル化可能なシンプルなデータクラスです。コンテンツではなく参照を使用することには、次のようなメリットがあります。
- キーをバックスタックにプッシュするだけで簡単に移動できます。
- キーがシリアル化可能であれば、バックスタックを永続ストレージに保存できるため、構成の変更やプロセスの終了後も維持できます。ユーザーは、アプリを離れて後で戻ってきたときに、同じコンテンツが表示された状態で中断したところから再開できることを期待するため、これは重要です。詳しくは、バックスタックを保存するをご覧ください。
Navigation 3 API の主なコンセプトは、デベロッパーがバックスタックを所有することです。ライブラリは次のことを想定しています。
- バックスタックは、スナップショット状態の
List<T>であり、Tはバックスタックのkeysの型です。Anyを使用することも、独自のより厳密な型付きキーを指定することもできます。「プッシュ」または「ポップ」という用語は、リストの末尾からアイテムを追加または削除することを意味します。 - バックスタックを監視し、その状態を UI に反映します
NavDisplay。
次の例は、キーとバックスタックを作成し、ユーザー ナビゲーション イベントに応じてバックスタックを変更する方法を示しています。
// Define keys that will identify content data object ProductList data class ProductDetail(val id: String) @Composable fun MyApp() { // Create a back stack, specifying the key the app should start with val backStack = remember { mutableStateListOf<Any>(ProductList) } // Supply your back stack to a NavDisplay so it can reflect changes in the UI // ...more on this below... // Push a key onto the back stack (navigate forward), the navigation library will reflect the change in state backStack.add(ProductDetail(id = "ABC")) // Pop a key off the back stack (navigate back), the navigation library will reflect the change in state backStack.removeLastOrNull() }
キーをコンテンツに解決する
コンテンツは、コンポーズ可能な関数を含むクラスである NavEntry を使用して Navigation 3 でモデル化されます。これは、ユーザーが移動できる単一のコンテンツであるデスティネーションを表します。
前方および後方
NavEntry には、コンテンツに関する情報であるメタデータを含めることもできます。このメタデータは、NavDisplay
などのコンテナ オブジェクトによって読み取られ、NavEntry のコンテンツの表示方法を決定するのに役立ちます。たとえば、メタデータを使用して、特定の
NavEntry のデフォルトのアニメーションをオーバーライドできます。NavEntry metadata は、String
キーから Any 値へのマッピングであり、汎用性の高いデータ ストレージを提供します。
key を NavEntry に変換するには、エントリ プロバイダを作成します。これは、key を受け取り、その key
の NavEntry を返す関数です。通常、NavDisplay を作成するときにラムダ パラメータとして定義されます。
エントリ プロバイダを作成する方法は 2 つあります。ラムダ
関数を直接作成する方法と、entryProvider DSL を使用する方法です。
エントリ プロバイダ関数を直接作成する
通常、エントリ プロバイダ関数は、各キーのブランチを含む when ステートメントを使用して作成します。
entryProvider = { key -> when (key) { is ProductList -> NavEntry(key) { Text("Product List") } is ProductDetail -> NavEntry( key, metadata = mapOf("extraDataKey" to "extraDataValue") ) { Text("Product ${key.id} ") } else -> { NavEntry(Unit) { Text(text = "Invalid Key: $it") } } } }
entryProvider DSL を使用する
entryProvider DSL を使用すると、各キータイプに対してテストを行い、それぞれに NavEntry
を構築する必要がないため、ラムダ関数を簡素化できます。これには、entryProvider
ビルダー関数を使用します。また、キーが見つからない場合のデフォルトのフォールバック動作(エラーをスローする)も含まれています。
entryProvider = entryProvider { entry<ProductList> { Text("Product List") } entry<ProductDetail>( metadata = mapOf("extraDataKey" to "extraDataValue") ) { key -> Text("Product ${key.id} ") } }
スニペットから次の点に注意してください。
entryは、指定された型とコンポーズ可能なコンテンツを持つNavEntryを定義するために使用されます。entryは、NavEntry.metadataを設定するためのmetadataパラメータを受け取ります。
バックスタックを表示する
バックスタックは、アプリのナビゲーション状態を表します。バックスタックが変更されるたびに、アプリの UI
に新しいバックスタックの状態が反映されます。Navigation 3 では、NavDisplay
がバックスタックを監視し、それに応じて UI を更新します。次のパラメータを使用して構築します。
- バックスタック - これは
SnapshotStateList<T>型である必要があります。Tは バックスタックのキーの型です。これは監視可能なListであるため、変更されるとNavDisplayの再コンポーズがトリガーされます。 - バックスタック内のキーを
NavEntryオブジェクトに変換するentryProvider。 - 必要に応じて、
onBackパラメータにラムダを指定します。これは、ユーザーが戻るイベントをトリガーしたときに呼び出されます。
次の例は、NavDisplay を作成する方法を示しています。
data object Home data class Product(val id: String) @Composable fun NavExample() { val backStack = remember { mutableStateListOf<Any>(Home) } NavDisplay( backStack = backStack, onBack = { backStack.removeLastOrNull() }, entryProvider = { key -> when (key) { is Home -> NavEntry(key) { ContentGreen("Welcome to Nav3") { Button(onClick = { backStack.add(Product("123")) }) { Text("Click to navigate") } } } is Product -> NavEntry(key) { ContentBlue("Product ${key.id} ") } else -> NavEntry(Unit) { Text("Unknown route") } } } ) }
デフォルトでは、NavDisplay は、バックスタックの一番上の NavEntry をシングルペイン
レイアウトで表示します。次の録画は、このアプリの実行を示しています。
NavDisplay 2 つの
デスティネーションでのデフォルトの動作。デスティネーションのライフサイクル
NavDisplay は、カスタム LifecycleOwners を使用して、
NavEntry のライフサイクル状態を、シーンレベルの制約とエントリレベル
の制約の両方に基づいて制限します。
Compose のライフサイクルについて詳しくは、Jetpack Compose のライフサイクルをご覧ください。
シーンレベルのライフサイクル制約
NavDisplay は、アクティブな Scene のライフサイクルを管理します。シーンレベルの上限は次のように決定されます。
オーバーレイ以外のシーンの場合:
RESUMED: シーンの切り替えが完了し、その上にアクティブなオーバーレイ シーンが表示されていない場合にのみ許可されます。STARTED: 前後への移動時やオーバーレイで覆われている場合など、シーンの切り替え中はSTARTEDに制限されます。
RESUMED: 一番上の現在アクティブなオーバーレイ シーンでのみ許可されます。STARTED: 新しいオーバーレイで覆われている基盤となるオーバーレイ シーンの場合は、STARTEDに制限されます。
エントリレベルのライフサイクル状態
ライブラリは、バックスタック内の存在に基づいて、個々の NavEntry の最大ライフサイクル状態を管理します。
RESUMED: エントリが現在のバックスタックに存在する場合、そのライフサイクルはRESUMEDまで進むことができます(シーンレベルの上限が適用されます)。CREATED: エントリがバックスタックに存在しなくなった場合(ポップされたが、アニメーションで消えるときに画面にレンダリングされている場合など)、ライブラリはそのライフサイクルをCREATEDに厳密に制限します。この制限により、バックグラウンドまたは終了中のエントリは、終了トランジションが完了するまで、フローの収集やRESUMED状態またはSTARTED状態にバインドされたコルーチンの起動など、アクティブな作業の実行を停止します。
組み合わせ方
たとえば、NavEntry の最終的なライフサイクル状態は次のように解決されます。
| シナリオ | シーンレベルの上限 | エントリレベルの上限 | 有効な上限 |
|---|---|---|---|
| アクティブなエントリ、画面が安定している (切り替えやオーバーレイがない) | RESUMED |
RESUMED |
RESUMED |
| アクティブなエントリ、切り替え中 ( または に移動中) | STARTED |
RESUMED |
STARTED |
| アクティブなエントリ、オーバーレイで覆われている (ダイアログが開いているなど) | STARTED |
RESUMED |
STARTED |
| ポップされたエントリ、アニメーションで消える | STARTED または RESUMED |
CREATED |
CREATED |
すべてを組み合わせる
次の図は、Navigation 3 のさまざまなオブジェクト間でデータがどのように流れるかを示しています。
ナビゲーション イベントによって変更が開始されます 。ユーザーの操作に応じて、キーがバックスタックに追加または削除されます。
バックスタックの状態が変化すると、コンテンツの取得がトリガーされます 。
NavDisplay(バックスタックをレンダリングするコンポーズ可能)は、バックスタックを監視します。デフォルト構成では、一番上のバックスタック エントリをシングルペイン レイアウトで表示します。バックスタックの一番上のキーが変更されると、NavDisplayはこのキーを使用して、エントリ プロバイダから対応するコンテンツをリクエストします。エントリ プロバイダがコンテンツを提供します 。エントリ プロバイダは、キーを
NavEntryに解決する関数です。NavDisplayからキーを受け取ると、エントリ プロバイダは、キーとコンテンツの両方を含む関連付けられたNavEntryを提供します。コンテンツが表示されます 。
NavDisplayはNavEntryを受け取り、コンテンツを表示します。