Mulai Navigation 3 1.2.0, Anda dapat menampilkan
hasil dari tujuan menggunakan API
ResultEventBus.
ResultEventBus menyediakan dua model komunikasi:
- Hasil berbasis peristiwa: Untuk peristiwa sementara yang terjadi satu kali (seperti menampilkan
snackbar konfirmasi atau memicu efek samping) menggunakan
ResultEffect. - Hasil berbasis status: Untuk mengamati hasil terbaru sebagai
StateCompose menggunakanconflateAsState.
Menyiapkan bus peristiwa hasil
Agar ResultEventBus tersedia untuk tujuan composable, tambahkan
rememberResultEventBusNavEntryDecorator ke daftar
dekorator yang diteruskan ke NavDisplay. Tindakan ini akan memberikan konten setiap
tujuan dengan lokal komposisi
LocalResultEventBus.
NavDisplay( /* ... */ entryDecorators = listOf( rememberSaveableStateHolderNavEntryDecorator(), rememberResultEventBusNavEntryDecorator() ) )
Kunci hasil
ResultEventBus mengidentifikasi dan merutekan setiap hasil menggunakan kunci. Pengirim dan penerima mencocokkan hasil dengan menggunakan kunci yang sama.
Anda dapat menentukan kunci hasil dengan dua cara:
- Kunci eksplisit: Anda dapat menentukan kunci eksplisit (seperti
resultKey = "pickup_address"). Gunakan kunci eksplisit saat menampilkan jenis umum (sepertiString,Boolean, atau primitif), atau saat beberapa tujuan menampilkan instance berbeda dari jenis data yang sama. - Kunci turunan jenis: Jika Anda tidak menentukan kunci eksplisit,
ResultEventBusakan otomatis membuat kunci menggunakan representasitoStringdariKClassjenis hasil (sepertiContact::class.toString()). Gunakan kunci turunan jenis untuk jenis data yang berbeda dan khusus domain.
Menampilkan hasil dari tujuan
Agar composable layar dapat digunakan kembali dan diuji, jangan mengakses
LocalResultEventBus secara langsung di dalam UI layar Anda. Sebagai gantinya, ekspos lambda callback dari layar Anda. Di entryProvider, tangani callback dengan
mengirimkan hasilnya menggunakan
LocalResultEventBus.current dan kembali ke halaman sebelumnya.
Anda dapat mengirim hasil menggunakan kunci hasil eksplisit:
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() } ) }
Anda juga dapat mengirim hasil menggunakan kunci turunan jenis:
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() } ) }
Menerima hasil
Tujuan dapat menggunakan hasil menggunakan efek berbasis peristiwa atau observable berbasis status.
| API | Perilaku | Kasus penggunaan yang direkomendasikan |
|---|---|---|
ResultEffect |
Queued: Memproses semua hasil yang dikeluarkan untuk kunci secara berurutan. | Peristiwa satu kali dan efek samping (seperti menampilkan snackbar atau meneruskan ke ViewModel). |
conflateAsState |
Conflated: Menghapus hasil perantara dan hanya mempertahankan hasil terbaru sebagai Compose State. |
Pengubah status UI ringan (seperti tag filter aktif atau penggantian pilihan). |
Menangani peristiwa satu kali dengan ResultEffect
Gunakan ResultEffect saat menangani peristiwa satu kali seperti
memicu analisis, menampilkan snackbar, atau meneruskan hasil ke
ViewModel.
ResultEffect mengelola antrean untuk hasil yang masuk. Jika beberapa hasil dikirim untuk kunci tertentu, ResultEffect akan memproses setiap hasil sesuai urutan pengirimannya. Selain itu, ResultEffect berjalan dalam cakupan coroutine, yang memungkinkan Anda
memanggil fungsi penangguhan langsung di dalam isi efek.
Anda dapat memproses hasil yang terkait dengan kunci hasil eksplisit:
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") } ) }
Anda juga dapat memproses hasil menggunakan kunci turunan jenis:
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 ) }
Saat menavigasi antartujuan, ResultEffect dijalankan melalui
urutan siklus proses berikut:
- Pengirim memunculkan: Tujuan pengirim mengirimkan hasil menggunakan
resultBus.sendResult(resultKey = "pickup_address", address)dan memunculkan data sebelumnya. - Penerima memasuki komposisi: Tujuan penerima menjadi
layar aktif, dan
ResultEffectmulai memproses hasil. - Penerima memproses hasil:
ResultEffectmenerima dan mengeksekusi isi efeknya untuk setiap hasil yang dikirim untuk kunci tersebut, memproses semua emisi sesuai urutan pengirimannya. - Penerima keluar dari komposisi: Saat tujuan penerima dikeluarkan
dari data sebelumnya,
ResultEffectakan keluar dari komposisi dan berhenti memproses hasil.
Mengamati hasil terbaru sebagai status dengan conflateAsState
Jika Anda hanya memerlukan nilai hasil terbaru untuk langsung mengubah atau memfilter status UI lokal dan ingin Compose otomatis merekomposisi setiap kali hasil diperbarui, panggil conflateAsState di ResultEventBus.
Anda dapat mengamati hasil yang terkait dengan kunci hasil eksplisit:
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") } ) }
Anda juga dapat mengamati hasil menggunakan kunci turunan jenis:
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
Secara default, rememberResultEventBusNavEntryDecorator membuat dan mengingat
ResultEventBus-nya sendiri secara internal menggunakan
rememberResultEventBus.
Anda dapat membuat dan mengangkat ResultEventBus secara eksplisit saat perlu:
- Teruskan instance
ResultEventBuslangsung ke komponen non-composable atau grafik injeksi dependensi. - Mengirim atau mengamati hasil dari scaffolding aplikasi tingkat atas (seperti panel aplikasi atau panel navigasi) di luar hierarki tujuan.
Untuk mengangkat ResultEventBus, buat menggunakan rememberResultEventBus dan teruskan
ke 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 ) )
Mengelola dan menghapus hasil
Saat tujuan menggunakan hasil satu kali, hapus hasil tersebut dari bus peristiwa menggunakan
removeResult. Hal ini mencegah bus mengirim ulang
peristiwa sebelumnya ke observer baru saat tujuan masuk kembali ke komposisi:
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") } }
Anda dapat menghapus hasil berdasarkan kunci eksplisit (resultBus.removeResult(resultKey)) atau
berdasarkan kunci turunan jenis (resultBus.removeResult<T>()). Untuk mengetahui detail tentang pencocokan
kunci, lihat Kunci hasil.
Resep
Untuk contoh kode lengkap yang dapat dijalankan yang menunjukkan berbagai strategi penerusan hasil, lihat resep berikut: