Начиная с версии Navigation 3 1.2.0 , вы можете получать результаты из целевых страниц, используя API ResultEventBus .
ResultEventBus предоставляет две модели связи:
- Результаты, основанные на событиях : для кратковременных, разовых событий (например, отображения всплывающего окна подтверждения или запуска побочного эффекта) используется
ResultEffect. - Результаты, основанные на состоянии : Для просмотра последнего результата в качестве
Stateкомпозиции с помощьюconflateAsState.
Настройте шину событий результатов.
Чтобы сделать ResultEventBus доступным для ваших компонуемых целевых объектов, добавьте rememberResultEventBusNavEntryDecorator в список декораторов, передаваемых в ваш NavDisplay . Это обеспечит содержимому каждого целевого объекта локальную композицию LocalResultEventBus .
NavDisplay( /* ... */ entryDecorators = listOf( rememberSaveableStateHolderNavEntryDecorator(), rememberResultEventBusNavEntryDecorator() ) )
Ключи результатов
ResultEventBus идентифицирует и направляет каждый результат, используя ключ. Отправители и получатели сопоставляют результаты, используя один и тот же ключ.
Ключи результатов можно указать двумя способами:
- Явные ключи : Вы можете указать явный ключ (например,
resultKey = "pickup_address"). Используйте явные ключи при возврате распространенных типов данных (таких какString,Booleanили примитивные типы) или когда несколько пунктов назначения возвращают разные экземпляры одного и того же типа данных. - Ключи, определяемые типом : Если вы не указываете явный ключ,
ResultEventBusавтоматически генерирует ключ, используя представление типа результата вKClasstoString(например,Contact::class.toString()). Используйте ключи, определяемые типом, для различных типов данных, специфичных для предметной области.
Возврат результатов из пункта назначения
Чтобы обеспечить повторное использование и возможность тестирования компонентов экрана, не обращайтесь к LocalResultEventBus напрямую внутри пользовательского интерфейса экрана. Вместо этого предоставляйте лямбда-функции обратного вызова из самого экрана. В вашем entryProvider обработайте обратный вызов, отправив результат с помощью LocalResultEventBus.current и вернувшись назад.
Вы можете отправлять результаты, используя явно указанный ключ результата :
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() } ) }
Вы также можете отправлять результаты, используя ключ, определяемый типом данных:
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() } ) }
Получите результаты
Получатели данных могут обрабатывать результаты, используя либо эффекты, основанные на событиях, либо наблюдаемые объекты, основанные на состоянии.
| API | Поведение | Рекомендуемые варианты использования |
|---|---|---|
ResultEffect | В очереди : Обрабатывает все результаты, полученные для данного ключа, в порядке очереди. | Одноразовые события и побочные эффекты (например, отображение всплывающего уведомления или переадресация на ViewModel ). |
conflateAsState | Conflated : Отбрасывает промежуточные результаты и сохраняет только последний результат в качестве State композиции. | Легковесные модификаторы состояния пользовательского интерфейса (например, активные теги фильтров или переопределение выбора). |
Обработка разовых событий с помощью ResultEffect
Используйте ResultEffect при обработке разовых событий, таких как запуск аналитики, отображение всплывающего уведомления или передача результата в ViewModel .
ResultEffect поддерживает очередь для входящих результатов. Если для заданного ключа отправляется несколько результатов, ResultEffect обрабатывает каждый результат в порядке его отправки. Кроме того, ResultEffect работает в сопрограммной области видимости, что позволяет вызывать приостанавливаемые функции непосредственно внутри тела эффекта.
Вы можете отслеживать результаты, связанные с явно заданным ключом результата :
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") } ) }
Также можно отслеживать результаты, используя ключ, полученный из типа данных:
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 ) }
При переходе между целевыми объектами ResultEffect выполняет следующие действия в рамках жизненного цикла:
- Отправитель генерирует : получатель-отправитель отправляет результат с помощью
resultBus.sendResult(resultKey = "pickup_address", address)и извлекает результат из стека возврата. - Приёмник переходит в режим композиции : адрес приёмника становится активным экраном, и
ResultEffectначинает прослушивать результаты. - Приёмник обрабатывает результаты :
ResultEffectполучает и выполняет тело своего эффекта для каждого результата, отправленного для этого ключа, обрабатывая все излучения в порядке их отправки. - Приемник покидает композицию : Когда целевой объект приемника удаляется из стека возврата,
ResultEffectпокидает композицию и перестает прослушивать результаты.
Отслеживайте последние результаты как состояние с помощью conflateAsState
Если вам нужно только последнее значение результата для непосредственного изменения или фильтрации локального состояния пользовательского интерфейса, и вы хотите, чтобы Compose автоматически пересобирал результаты при каждом обновлении, вызовите conflateAsState для ResultEventBus .
Вы можете просмотреть результаты, связанные с явно заданным ключом результата :
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") } ) }
Результаты также можно просмотреть, используя ключ, полученный из типа данных:
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
По умолчанию rememberResultEventBusNavEntryDecorator создает и запоминает собственную ResultEventBus внутри себя, используя rememberResultEventBus .
При необходимости вы можете явно создать и поднять объект ResultEventBus :
- Передавайте экземпляр
ResultEventBusнепосредственно в некомпозируемые компоненты или графы внедрения зависимостей. - Отправляйте или отслеживайте результаты из элементов верхнего уровня приложения (таких как панель приложения или выдвижная панель навигации), находящихся за пределами иерархии назначения.
Чтобы поднять ResultEventBus , создайте его с помощью rememberResultEventBus и передайте его в 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 ) )
Управление и четкие результаты
Когда получатель обрабатывает одноразовый результат, удалите его из шины событий с помощью removeResult . Это предотвратит повторную доставку прошлых событий новым наблюдателям при повторном включении получателей в композицию:
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") } }
Результаты можно очистить по явному ключу ( resultBus.removeResult(resultKey) ) или по ключу, определяемому типом ( resultBus.removeResult<T>() ). Подробную информацию о сопоставлении ключей см. в разделе «Ключи результатов» .
Рецепты
Полные примеры работающего кода, демонстрирующие различные стратегии передачи результатов, см. в следующих примерах:
- Рецепт получения результатов на основе событий
- Рецепт получения результатов на основе состояния
- Последовательно исполняемый рецепт результата