Ab Navigation 3 1.2.0 können Sie Ergebnisse von Zielen mit der ResultEventBus API zurückgeben.
ResultEventBus bietet zwei Kommunikationsmodelle:
- Ereignisbasierte Ergebnisse: Für vorübergehende, einmalige Ereignisse (z. B. das Anzeigen einer Bestätigungs-Snackbar oder das Auslösen einer Nebenwirkung) mit
ResultEffect. - Zustandsbasierte Ergebnisse: Zum Beobachten des neuesten Ergebnisses als Compose-
StatemitconflateAsState.
Ergebnis-EventBus einrichten
Wenn Sie ResultEventBus für Ihre composable Ziele verfügbar machen möchten, fügen Sie den rememberResultEventBusNavEntryDecorator der Liste der Decorators hinzu, die an NavDisplay übergeben werden. Dadurch wird der Inhalt jedes Ziels mit einem LocalResultEventBus-CompositionLocal versehen.
NavDisplay( /* ... */ entryDecorators = listOf( rememberSaveableStateHolderNavEntryDecorator(), rememberResultEventBusNavEntryDecorator() ) )
Ergebnisschlüssel
ResultEventBus identifiziert und leitet jedes Ergebnis mithilfe eines Schlüssels weiter. Sender und Empfänger gleichen die Ergebnisse mit demselben Schlüssel ab.
Sie haben zwei Möglichkeiten, Ergebnisschlüssel anzugeben:
- Explizite Schlüssel: Sie können einen expliziten Schlüssel (z. B.
resultKey = "pickup_address") angeben. Verwenden Sie explizite Schlüssel, wenn Sie gängige Typen (z. B.String,Booleanoder Primitive) zurückgeben oder wenn mehrere Ziele unterschiedliche Instanzen desselben Datentyps zurückgeben. - Vom Typ abgeleitete Schlüssel: Wenn Sie keinen expliziten Schlüssel angeben, generiert
ResultEventBusautomatisch einen Schlüssel mit dertoString-Darstellung desKClassdes Ergebnistyps (z. B.Contact::class.toString()). Verwenden Sie vom Typ abgeleitete Schlüssel für eindeutige, domainspezifische Datentypen.
Ergebnisse von einem Ziel zurückgeben
Damit zusammensetzbare Funktionen für Bildschirme wiederverwendbar und testbar bleiben, sollten Sie nicht direkt in der Benutzeroberfläche des Bildschirms auf LocalResultEventBus zugreifen. Stattdessen sollten Sie Callback-Lambdas von Ihrem Bildschirm verfügbar machen. Verarbeite in deiner entryProvider den Callback, indem du das Ergebnis mit LocalResultEventBus.current sendest und zurücknavigierst.
Sie können Ergebnisse mit einem expliziten Ergebnisschlüssel senden:
import androidx.compose.runtime.Composable import androidx.navigation3.runtime.result.LocalResultEventBus entry<AddressPickerRoute> { val resultBus = LocalResultEventBus.current AddressPickerScreen( onAddressSelected = { selectedAddress: Address -> resultBus.sendResult( resultKey = "pickup_address", result = selectedAddress ) navigator.goBack() } ) }
Sie können Ergebnisse auch mit einem vom Typ abgeleiteten Schlüssel senden:
import androidx.compose.runtime.Composable import androidx.navigation3.runtime.result.LocalResultEventBus entry<ContactPickerRoute> { val resultBus = LocalResultEventBus.current ContactPickerScreen( onContactSelected = { selectedContact: Contact -> resultBus.sendResult(result = selectedContact) navigator.goBack() } ) }
Ergebnisse erhalten
Ziele können Ergebnisse entweder über ereignisbasierte Effekte oder zustandsbasierte Observables nutzen.
| API | Verhalten | Empfohlene Anwendungsfälle |
|---|---|---|
ResultEffect |
In der Warteschlange: Alle für den Schlüssel ausgegebenen Ergebnisse werden der Reihe nach verarbeitet. | Einmalige Ereignisse und Nebenwirkungen (z. B. Anzeigen einer Snackbar oder Weiterleiten an ein ViewModel). |
conflateAsState |
Zusammengeführt: Zwischenergebnisse werden verworfen und nur das neueste Ergebnis wird als Compose-State beibehalten. |
Leichte UI-Zustandsmodifikatoren (z. B. aktive Filter-Tags oder Auswahlüberschreibungen). |
Einmalige Ereignisse mit ResultEffect verarbeiten
Verwende ResultEffect, wenn du einmalige Ereignisse verarbeitest, z. B. das Auslösen von Analysen, das Anzeigen einer Snackbar oder das Weiterleiten eines Ergebnisses an ein ViewModel.
ResultEffect verwaltet eine Warteschlange für eingehende Ergebnisse. Wenn mehrere Ergebnisse für einen bestimmten Schlüssel gesendet werden, verarbeitet ResultEffect jedes Ergebnis in der Reihenfolge, in der es gesendet wurde. Außerdem wird ResultEffect in einem Koroutinenbereich ausgeführt, sodass Sie suspend-Funktionen direkt im Effekttext aufrufen können.
Sie können auf Ergebnisse warten, die mit einem expliziten Ergebnisschlüssel verknüpft sind:
import androidx.compose.runtime.Composable import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation3.runtime.result.ResultEffect @Composable fun RideSummaryScreen( onOpenAddressPicker: (key: String) -> Unit, viewModel: RideSummaryViewModel = viewModel() ) { ResultEffect<Address>(resultKey = "pickup_address") { address -> viewModel.onPickupAddressSelected(address) } ResultEffect<Address>(resultKey = "destination_address") { address -> viewModel.onDestinationAddressSelected(address) } RideSummaryContent( pickupAddress = viewModel.pickupAddress, destinationAddress = viewModel.destinationAddress, onPickPickup = { onOpenAddressPicker("pickup_address") }, onPickDestination = { onOpenAddressPicker("destination_address") } ) }
Sie können auch mit einem vom Typ abgeleiteten Schlüssel auf Ergebnisse warten:
import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation3.runtime.result.ResultEffect @Composable fun ComposeMessageScreen( onPickContact: () -> Unit, snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }, viewModel: ComposeMessageViewModel = viewModel() ) { ResultEffect<Contact> { contact -> // Suspending calls are supported directly in the effect body snackbarHostState.showSnackbar("Selected ${contact.name}") viewModel.onRecipientSelected(contact) } ComposeMessageContent( recipient = viewModel.recipient, onPickContact = onPickContact ) }
Wenn Sie zwischen Zielen wechseln, durchläuft ResultEffect die folgende Lebenszyklussequenz:
- Sender gibt aus: Das Senderziel sendet ein Ergebnis mit
resultBus.sendResult(resultKey = "pickup_address", address)und entfernt das Element aus dem Back Stack. - Empfänger wechselt in den Kompositionsmodus: Das Empfängerziel wird zum aktiven Bildschirm und
ResultEffectwartet auf Ergebnisse. - Empfänger verarbeitet Ergebnisse:
ResultEffectempfängt und führt den Effekttext für jedes Ergebnis aus, das für diesen Schlüssel gesendet wurde. Dabei werden alle Emissionen in der Reihenfolge verarbeitet, in der sie gesendet wurden. - Empfänger verlässt die Komposition: Wenn das Empfängerziel aus dem Back Stack entfernt wird, verlässt
ResultEffectdie Komposition und wartet nicht mehr auf Ergebnisse.
Aktuelle Ergebnisse als Status mit conflateAsState beobachten
Wenn Sie nur den neuesten Ergebniswert benötigen, um den lokalen UI-Status direkt zu ändern oder zu filtern, und möchten, dass Compose bei jeder Aktualisierung des Ergebnisses automatisch eine Neuzusammensetzung vornimmt, rufen Sie conflateAsState für ResultEventBus auf.
Sie können Ergebnisse beobachten, die mit einem expliziten Ergebnisschlüssel verknüpft sind:
import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.graphics.Color import androidx.navigation3.runtime.result.LocalResultEventBus @Composable fun ThemePreviewScreen( onOpenColorPicker: (key: String) -> Unit ) { val resultBus = LocalResultEventBus.current val primaryColor by resultBus.conflateAsState<Color>( resultKey = "primary_color", defaultValue = MaterialTheme.colorScheme.primary ) val accentColor by resultBus.conflateAsState<Color>( resultKey = "accent_color", defaultValue = MaterialTheme.colorScheme.tertiary ) ThemePreviewContent( primaryColor = primaryColor, accentColor = accentColor, onPickPrimary = { onOpenColorPicker("primary_color") }, onPickAccent = { onOpenColorPicker("accent_color") } ) }
Sie können die Ergebnisse auch mit einem vom Typ abgeleiteten Schlüssel beobachten:
import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.navigation3.runtime.result.LocalResultEventBus @Composable fun FilterableProductListScreen( initialFilter: ProductFilter = ProductFilter.All, onOpenFilterPicker: () -> Unit ) { val resultBus = LocalResultEventBus.current // Observe latest filter result as Compose State, starting with initialFilter val activeFilter by resultBus.conflateAsState<ProductFilter>( defaultValue = initialFilter ) ProductListContent( activeFilter = activeFilter, onOpenFilterPicker = onOpenFilterPicker ) }
Hoist ResultEventBus
Standardmäßig erstellt und speichert rememberResultEventBusNavEntryDecorator intern einen eigenen ResultEventBus mit rememberResultEventBus.
In folgenden Fällen können Sie einen ResultEventBus explizit erstellen und hochstufen:
- Übergeben Sie die
ResultEventBus-Instanz direkt an nicht kompositionsfähige Komponenten oder Diagramme zur Abhängigkeitsinjektion. - Ergebnisse von der App-Struktur auf oberster Ebene (z. B. App-Leiste oder Navigationsleiste) außerhalb der Zielhierarchie senden oder beobachten.
Wenn Sie ResultEventBus hochstufen möchten, erstellen Sie es mit rememberResultEventBus und übergeben Sie es an rememberResultEventBusNavEntryDecorator(resultEventBus):
import androidx.compose.runtime.Composable import androidx.navigation3.runtime.result.rememberResultEventBus import androidx.navigation3.runtime.result.rememberResultEventBusNavEntryDecorator import androidx.navigation3.ui.NavDisplay // Hoist the ResultEventBus at the top level val resultEventBus = rememberResultEventBus() // Pass the hoisted bus to the decorator val resultEventBusNavEntryDecorator = rememberResultEventBusNavEntryDecorator<NavKey>( resultEventBus = resultEventBus ) NavDisplay( /* ... */ entryDecorators = listOf( rememberSaveableStateHolderNavEntryDecorator(), resultEventBusNavEntryDecorator ) )
Ergebnisse verwalten und löschen
Wenn ein Ziel ein einmaliges Ergebnis verarbeitet, löschen Sie es mit removeResult aus dem Ereignisbus. Dadurch wird verhindert, dass der Bus vergangene Ereignisse noch einmal an neue Beobachter liefert, wenn Ziele wieder in die Komposition aufgenommen werden:
import androidx.compose.runtime.Composable import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation3.runtime.result.LocalResultEventBus import androidx.navigation3.runtime.result.ResultEffect @Composable fun NotificationSettingsScreen( viewModel: NotificationViewModel = viewModel() ) { val resultBus = LocalResultEventBus.current ResultEffect<ConfirmationResult>(resultKey = "confirm_permission") { confirmation -> viewModel.onPermissionConfirmed(confirmation) // Clear the result after consumption to prevent re-delivery resultBus.removeResult(resultKey = "confirm_permission") } }
Sie können Ergebnisse nach explizitem Schlüssel (resultBus.removeResult(resultKey)) oder nach typabgeleitetem Schlüssel (resultBus.removeResult<T>()) löschen. Weitere Informationen zum Schlüsselabgleich finden Sie unter Ergebnisschlüssel.
Rezepte
Vollständige ausführbare Codebeispiele, die verschiedene Strategien zur Ergebnisübergabe veranschaulichen, finden Sie in den folgenden Rezepten: