宛先にロジックまたはラッパーを適用する

NavEntryDecorator クラスを使用すると、追加情報を提供したり、同じロジックをデスティネーションに適用したりできます。このクラスは、バックスタック内の各 NavEntry をコンポーズ可能な関数でラップします。つまり、エントリのコンテンツを装飾します。

カスタム デコレータを作成する

デコレータを作成するには、NavEntryDecorator クラスを拡張し、次のメソッドをオーバーライドします。

  • decorate - バックスタック内の NavEntry ごとに呼び出されるコンポーザブル ラムダ。NavEntry をパラメータとして受け取ります。これにより、エントリの contentKey をキーとする状態オブジェクトを作成できます。CompositionLocalProvider を使用して、エントリのコンテンツに依存関係を提供できます。また、コンテンツをコンポーズ可能な関数で囲んだり、副作用をトリガーしたりすることもできます。このメソッド内では、常に entry.Content() を呼び出す必要があります。
  • onPop - NavEntry がバックスタックから削除され、コンポジションから離れたときに呼び出されるコールバック。削除されたエントリの contentKey を受け取ります。contentKey を使用して、そのエントリに関連付けられている状態を特定してクリーンアップします。

次の例では、NavEntryDecorator クラスを拡張してカスタム デコレータを作成しています。

// import androidx.navigation3.runtime.NavEntryDecorator
class CustomNavEntryDecorator<T : Any> : NavEntryDecorator<T>(
    decorate = { entry ->
        Log.d("CustomNavEntryDecorator", "entry with ${entry.contentKey} entered composition and was decorated")
        entry.Content()
    },
    onPop = { contentKey -> Log.d("CustomNavEntryDecorator", "entry with $contentKey was popped") }
)

デコレータが状態にアクセスする必要がある場合は、その状態を作成するコンポーズ可能な関数を作成し、それを使用してデコレータを構築します。実装例については、rememberSaveableStateHolderNavEntryDecorator のソースコードをご覧ください。これにより、状態(SaveableStateHolder)が作成され、デコレータの作成に使用されます。

バックスタックを装飾する

NavEntryDecorator を作成したら、次の 2 つの方法のいずれかでバックスタックのエントリを装飾します。

  • rememberDecoratedNavEntries を使用します。この関数は、複数のバックスタックがあり、それぞれに独自のデコレータ セットがある場合に便利です(詳細については、こちらのコードレシピをご覧ください)。この関数は、NavDisplay で使用できる NavEntry のデコレートされたリストを返します。
  • entryDecorators パラメータを使用して、デコレータを NavDisplay に直接指定します。NavDisplay は内部で rememberDecoratedNavEntries を呼び出し、装飾されたエントリを表示します。

デフォルトのデコレータを含める

Navigation 3 には、SaveableStateHolderNavEntryDecorator という名前のデフォルトのデコレータが含まれています。これにより、構成の変更やプロセスの終了後も NavEntry の状態を保持できます。NavEntry コンテンツを SaveableStateProvider でラップします。これにより、NavEntry コンテンツ内の rememberSaveable 呼び出しが正しく機能するようになります。

デコレータが SaveableStateProvider を提供していない限り、提供されたデコレータのリストの最初のデコレータとして SaveableStateHolderNavEntryDecorator を含める必要があります。rememberSaveableStateHolderNavEntryDecorator を使用して作成されます。

次に例を示します。

// import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator
NavDisplay(
    entryDecorators = listOf(
        rememberSaveableStateHolderNavEntryDecorator(),
        remember { CustomNavEntryDecorator() }
    ),
    // ...
)

ResultEventBusNavEntryDecorator を使用して結果を渡す

Navigation 3 1.2.0 以降では、LocalResultEventBus コンポジション ローカルを使用して、ResultEventBusNavEntryDecorator で各 NavEntry に ResultEventBus を提供できます。

デフォルトでは、rememberResultEventBusNavEntryDecorator は rememberResultEventBus を使用して、独自の ResultEventBus インスタンスを内部で作成して記憶します。デコレータ階層の外(最上位のアプリ スキャフォールディングなど)でイベントバスにアクセスする必要がある場合は、rememberResultEventBus を呼び出して rememberResultEventBusNavEntryDecorator(resultEventBus) に渡すことで、ResultEventBus をホイスティングできます。

val resultEventBus = rememberResultEventBus()

NavDisplay(
    /* ... */
    entryDecorators = listOf(
        rememberSaveableStateHolderNavEntryDecorator(),
        rememberResultEventBusNavEntryDecorator(resultEventBus = resultEventBus)
    )
)

結果の送信とモニタリングの詳細については、結果を返すをご覧ください。

デコレータを使用するタイミング

デコレータを使用すると、次のことができます。

  • バックスタック内の NavEntry ごとに依存関係を作成します。たとえば、ViewModelStoreNavEntryDecorator は NavEntry ごとに ViewModelStore を作成します。
  • デスティネーション間で結果を渡し、通信します。たとえば、ResultEventBusNavEntryDecorator は、バックスタック内のデスティネーションに ResultEventBus を提供します。
  • オブジェクトのスコープを複数の NavEntry に設定します。たとえば、複数のエントリ間で ViewModel を共有する場合です。
  • 複数の NavEntry に対して同じ操作を行います。たとえば、各エントリのロギング、デバッグ、トレース オペレーションを実行します。
  • 同じコンポーズ可能な関数で NavEntry をラップする。
  • NavEntry に関連付けられた状態をクリーンアップします。たとえば、エントリがバックスタックから削除されると、ViewModelStoreNavEntryDecorator は関連付けられた ViewModelStore をクリアします。

デコレータは、次の目的で使用しないでください。

  • 依存関係を 1 つの NavEntry に渡します。
  • バックスタックよりもスコープが広い依存関係を提供する。

どちらの場合も、代わりに NavEntry の作成時に依存関係を直接渡します。

その他のコード例については、NavEntryDecorator をご覧ください。