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 dan satu kali (seperti menampilkan
snackbar konfirmasi atau memicu efek samping) menggunakan
ResultEffect. - Hasil berbasis status: Untuk mengamati hasil terbaru sebagai Compose
StatemenggunakanconflateAsState.
Menyiapkan bus peristiwa hasil
Untuk membuat ResultEventBus tersedia bagi tujuan composable Anda, tambahkan
rememberResultEventBusNavEntryDecorator ke daftar
decorator yang diteruskan ke NavDisplay Anda. Hal ini menyediakan 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 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 jenis data yang sama yang berbeda. - Kunci yang diturunkan dari jenis: Jika Anda tidak menentukan kunci eksplisit,
ResultEventBusakan otomatis membuat kunci menggunakan representasitoStringdariKClassjenis hasil (sepertiContact::class.toString()). Gunakan kunci yang diturunkan dari jenis untuk jenis data khusus domain yang berbeda.
Menampilkan hasil dari tujuan
Agar composable layar dapat digunakan kembali dan diuji, jangan akses
LocalResultEventBus secara langsung di dalam UI layar Anda. Sebagai gantinya, ekspos lambda callback dari layar Anda. Di entryProvider, tangani callback dengan
mengirim hasil menggunakan
LocalResultEventBus.current dan kembali.
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 dapat diamati berbasis status.
| API | Perilaku | Kasus penggunaan yang direkomendasikan |
|---|---|---|
ResultEffect |
Dalam antrean: Memproses semua hasil yang dikeluarkan untuk kunci secara berurutan. | Peristiwa dan efek samping satu kali (seperti menampilkan snackbar atau meneruskan ke ViewModel). |
conflateAsState |
Conflated: Menghapus hasil sementara dan hanya mempertahankan hasil terbaru sebagai Compose State. |
Pengubah status UI ringan (seperti tag filter aktif atau penggantian pilihan). |
Menangani acara satu kali dengan ResultEffect
Gunakan ResultEffect saat menangani peristiwa satu kali seperti
memicu analisis, menampilkan snackbar, atau meneruskan hasil ke
ViewModel.
ResultEffect mempertahankan antrean untuk hasil yang masuk. Jika beberapa hasil dikirim untuk kunci tertentu, ResultEffect akan memproses setiap hasil dalam urutan pengirimannya. Selain itu, ResultEffect berjalan dalam cakupan coroutine, yang memungkinkan Anda
memanggil fungsi penangguhan secara 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 yang berasal dari 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 melakukan navigasi antar-tujuan, ResultEffect dieksekusi melalui
urutan siklus proses berikut:
- Pengirim memancarkan: Tujuan pengirim mengirimkan hasil menggunakan
resultBus.sendResult(resultKey = "pickup_address", address)dan memunculkan back stack. - 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 dalam urutan pengirimannya. - Penerima keluar dari komposisi: Saat tujuan penerima dikeluarkan
dari entri data sebelumnya,
ResultEffectkeluar 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 vulgar:
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 yang berasal dari 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 ) }
Kerekan ResultEventBus
Secara default, rememberResultEventBusNavEntryDecorator membuat dan mengingat
ResultEventBus-nya sendiri secara internal menggunakan
rememberResultEventBus.
Anda dapat membuat dan mengangkat ResultEventBus secara eksplisit saat Anda perlu:
- Teruskan instance
ResultEventBuslangsung ke komponen non-composable atau grafik injeksi dependensi. - Mengirim atau mengamati hasil dari scaffolding aplikasi tingkat teratas (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 pengamat baru saat tujuan memasuki kembali 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 menurut kunci eksplisit (resultBus.removeResult(resultKey)) atau
menurut kunci yang berasal dari jenis (resultBus.removeResult<T>()). Untuk mengetahui detail tentang pencocokan
kunci, lihat Kunci hasil.
Resep
Untuk contoh kode yang dapat dijalankan lengkap yang menunjukkan berbagai strategi penerusan hasil, lihat resep berikut:
- Resep hasil berbasis peristiwa
- Resep hasil berbasis negara bagian
- Resep hasil yang dapat diserialisasi