Ab Navigation 3 1.2.0 können Sie mit der ResultEventBus API Ergebnisse von Zielen 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 eines Nebeneffekts) wird
ResultEffectverwendet. - Statusbasierte Ergebnisse: Hier können Sie das letzte Ergebnis von Compose
StatemitconflateAsStatebeobachten.
Ergebnis-Eventbus einrichten
Wenn Sie ResultEventBus für Ihre zusammensetzbaren Ziele verfügbar machen möchten, fügen Sie rememberResultEventBusNavEntryDecorator der Liste der Dekoratoren hinzu, die an Ihre NavDisplay übergeben werden. So wird für die Inhalte jedes Ziels eine LocalResultEventBus-Komposition lokal bereitgestellt.
NavDisplay( /* ... */ entryDecorators = listOf( rememberSaveableStateHolderNavEntryDecorator(), rememberResultEventBusNavEntryDecorator() ) )
Ergebnisschlüssel
ResultEventBus identifiziert und leitet jedes Ergebnis mithilfe eines Schlüssels weiter. Absender und Empfänger gleichen Ergebnisse mit demselben Schlüssel ab.
Sie haben zwei Möglichkeiten, Ergebnisschlüssel anzugeben:
- Explizite Schlüssel: Sie können einen expliziten Schlüssel angeben, z. B.
resultKey = "pickup_address". Verwenden Sie explizite Schlüssel, wenn Sie gängige Typen (z. B.String,Booleanoder Primitiven) zurückgeben oder wenn mehrere Ziele unterschiedliche Instanzen desselben Datentyps zurückgeben. - Von 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 von Typ abgeleitete Schlüssel für unterschiedliche, domänenspezifische Datentypen.
Ergebnisse aus einem Ziel zurückgeben
Damit Bildschirm-Composables wiederverwendbar und testbar bleiben, sollten Sie nicht direkt in der Benutzeroberfläche des Bildschirms auf LocalResultEventBus zugreifen. Stattdessen sollten Sie Callback-Lambdas über Ihren Bildschirm verfügbar machen. Verarbeiten Sie in Ihrem entryProvider den Callback, indem Sie das Ergebnis mit LocalResultEventBus.current senden und zurücknavigieren.
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 typabgeleiteten 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
Für Zielvorhaben können Ergebnisse entweder mit ereignisbasierten Effekten oder mit zustandsbasierten beobachtbaren Variablen verwendet werden.
| API | Verhalten | Empfohlene Anwendungsfälle |
|---|---|---|
ResultEffect |
In der Warteschlange: Alle für den Schlüssel ausgegebenen Ergebnisse werden in der richtigen Reihenfolge verarbeitet. | Einmalige Ereignisse und Nebeneffekte (z. B. Anzeigen einer Snackbar oder Weiterleitung zu ViewModel). |
conflateAsState |
Zusammengeführt: Zwischenergebnisse werden verworfen und nur das letzte Ergebnis wird als Compose State beibehalten. |
Leichte UI-Statusmodifizierer wie aktive Filter-Tags oder Auswahlüberschreibungen. |
Einmalige Ereignisse mit ResultEffect verarbeiten
Verwenden Sie ResultEffect, wenn Sie einmalige Ereignisse wie das Auslösen von Analysen, das Anzeigen einer Snackbar oder das Weiterleiten eines Ergebnisses an ein ViewModel verarbeiten.
ResultEffect verwaltet eine Warteschlange für eingehende Ergebnisse. Wenn für einen bestimmten Schlüssel mehrere Ergebnisse 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 Effekt-Body 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 typabgeleiteten 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 ) }
Beim Navigieren zwischen Zielen wird ResultEffect in der folgenden Lebenszyklussequenz ausgeführt:
- Absender gibt aus: Das Ziel des Absenders sendet ein Ergebnis mit
resultBus.sendResult(resultKey = "pickup_address", address)und entfernt den Backstack. - Empfänger ruft Komposition auf: Das Empfängerziel wird zum aktiven Bildschirm und
ResultEffectbeginnt, auf Ergebnisse zu warten. - Empfänger verarbeitet Ergebnisse:
ResultEffectempfängt und führt den Effektkörper für jedes Ergebnis aus, das für diesen Schlüssel gesendet wird. 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 Backstack entfernt wird, verlässt
ResultEffectdie Komposition und beendet das Abrufen von Ergebnissen.
Letzte 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 Compose automatisch neu komponieren soll, wenn das Ergebnis aktualisiert wird, 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 Ergebnisse auch mit einem typabgeleiteten 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.
Sie können ein ResultEventBus explizit erstellen und hochladen, wenn Sie Folgendes benötigen:
- Übergeben Sie die
ResultEventBus-Instanz direkt an nicht zusammensetzbare Komponenten oder Dependency Injection-Diagramme. - Ergebnisse aus dem App-Scaffolding der obersten Ebene (z. B. App-Leiste oder Navigationsleiste) außerhalb der Zielhierarchie senden oder beobachten.
Um ResultEventBus zu verschieben, 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 verwendet, entfernen Sie es mit removeResult aus dem Eventbus. So wird verhindert, dass der Bus vergangene Ereignisse noch einmal an neue Beobachter sendet, 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 für verschiedene Strategien zum Übergeben von Ergebnissen finden Sie in den folgenden Rezepten:
- Rezept für ereignisbasierte Ergebnisse
- Zustandsbasiertes Ergebnisrezept
- Rezept für serialisierbares Ergebnis