Sonuçları döndür

Navigation 3 1.2.0 sürümünden itibaren, ResultEventBus API'yi kullanarak hedeflerden sonuç döndürebilirsiniz.

ResultEventBus iki iletişim modeli sunar:

  • Etkinliğe dayalı sonuçlar: ResultEffect kullanarak geçici, tek seferlik etkinlikler (ör. onay snackbar'ı gösterme veya yan etki tetikleme) için.
  • Duruma dayalı sonuçlar: conflateAsState kullanarak Compose State ile ilgili en son sonucu gözlemlemek için.

Sonuç etkinlik veri yolunu ayarlama

ResultEventBus özelliğini birleştirilebilir hedeflerinizde kullanmak için rememberResultEventBusNavEntryDecorator öğesini NavDisplay öğenize iletilen dekoratörler listesine ekleyin. Bu, her hedefteki içeriğe LocalResultEventBus yerel kompozisyon sağlar.

NavDisplay(
    /* ... */
    entryDecorators = listOf(
        rememberSaveableStateHolderNavEntryDecorator(),
        rememberResultEventBusNavEntryDecorator()
    )
)

Sonuç anahtarları

ResultEventBus, her sonucu bir anahtar kullanarak tanımlar ve yönlendirir. Gönderenler ve alıcılar, aynı anahtarı kullanarak sonuçları eşleştirir.

Sonuç anahtarlarını iki şekilde belirtebilirsiniz:

  • Açık anahtarlar: Açık bir anahtar (ör. resultKey = "pickup_address") belirtebilirsiniz. Ortak türleri (ör. String, Boolean veya temel türler) döndürürken ya da birden fazla hedef aynı veri türünün farklı örneklerini döndürürken açık anahtarları kullanın.
  • Türden türetilmiş anahtarlar: Açık bir anahtar belirtmediğinizde ResultEventBus, sonuç türünün toString KClass gösterimini (ör. Contact::class.toString()) kullanarak otomatik olarak bir anahtar oluşturur. Türden türetilmiş anahtarları farklı, alana özgü veri türleri için kullanın.

Bir hedeften sonuç döndürme

Ekran composable'larının yeniden kullanılabilir ve test edilebilir olması için LocalResultEventBus öğesine doğrudan ekran kullanıcı arayüzünüzden erişmeyin. Bunun yerine, ekranınızdan geri çağırma lambda'larını kullanıma sunun. entryProvider içinde, LocalResultEventBus.current kullanarak sonucu gönderip geri giderek geri aramayı işleyin.

Sonuçları açık bir sonuç anahtarı kullanarak gönderebilirsiniz:

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()
        }
    )
}

Sonuçları türden türetilmiş bir anahtar kullanarak da gönderebilirsiniz:

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()
        }
    )
}

Sonuçları alma

Hedefler, sonuçları etkinliğe dayalı efektler veya duruma dayalı gözlemlenebilir öğeler kullanarak tüketebilir.

API Davranış Önerilen kullanım alanları
ResultEffect Sıraya alınmış: Anahtar için verilen tüm sonuçları sırayla işler. Tek seferlik etkinlikler ve yan etkiler (ör. snackbar görüntüleme veya ViewModel adresine yönlendirme).
conflateAsState Birleştirilmiş: Ara sonuçları bırakır ve yalnızca en son sonucu Oluştur State olarak tutar. Basit kullanıcı arayüzü durumu değiştiricileri (ör. etkin filtre etiketleri veya seçim geçersiz kılmaları).

ResultEffect ile tek seferlik etkinlikleri yönetme

ResultEffect, Analytics'i tetikleme, snackbar görüntüleme veya sonucu ViewModel'ye yönlendirme gibi tek seferlik etkinlikleri işlerken kullanılır.

ResultEffect, gelen sonuçlar için bir sıra tutar. Belirli bir anahtar için birden fazla sonuç gönderilirse ResultEffect her sonucu gönderildiği sırayla işler. Ayrıca, ResultEffect bir coroutine kapsamında çalışır. Bu sayede, askıya alma işlevlerini doğrudan efekt gövdesinin içinde çağırabilirsiniz.

Uygunsuz bir sonuç anahtarıyla ilişkili sonuçları dinleyebilirsiniz:

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") }
    )
}

Ayrıca, türden türetilmiş bir anahtar kullanarak sonuçları dinleyebilirsiniz:

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
    )
}

Hedefler arasında gezinirken ResultEffect, aşağıdaki yaşam döngüsü sırası üzerinden yürütülür:

  1. Gönderen yayınlıyor: Gönderen hedef, resultBus.sendResult(resultKey = "pickup_address", address) kullanarak bir sonuç gönderir ve geri yığını açar.
  2. Alıcı, oluşturma işlemine girer: Alıcı hedefi etkin ekran haline gelir ve ResultEffect sonuçları dinlemeye başlar.
  3. Alıcı sonuçları işler: ResultEffect, bu anahtar için gönderilen her sonuç için kendi efekt gövdesini alır ve yürütür. Tüm emisyonları gönderildikleri sırayla işler.
  4. Alıcı, oluşturma işleminden ayrılıyor: Alıcı hedefi eski yığından çıkarıldığında ResultEffect, oluşturma işleminden ayrılır ve sonuçları dinlemeyi durdurur.

conflateAsState ile en son sonuçları durum olarak gözlemleyin

Yalnızca yerel kullanıcı arayüzü durumunu doğrudan değiştirmek veya filtrelemek için en son sonuç değerine ihtiyacınız olduğunda ve sonuç güncellendiğinde Compose'un otomatik olarak yeniden oluşturulmasını istediğinizde ResultEventBus üzerinde conflateAsState işlevini çağırın.

Aşağıdaki sonuçları inceleyebilirsiniz:

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") }
    )
}

Sonuçları türe göre oluşturulmuş bir anahtar kullanarak da gözlemleyebilirsiniz:

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
    )
}

Kaldıraç ResultEventBus

Varsayılan olarak rememberResultEventBusNavEntryDecorator, rememberResultEventBus kullanarak dahili olarak kendi ResultEventBus değerini oluşturur ve hatırlar.

Aşağıdaki durumlarda açıkça ResultEventBus oluşturup yükseltebilirsiniz:

  • ResultEventBus örneğini doğrudan birleştirilemeyen bileşenlere veya bağımlılık ekleme grafiklerine aktarın.
  • Hedef hiyerarşisinin dışında üst düzey uygulama iskeletinden (ör. uygulama çubuğu veya gezinme çekmecesi) sonuç gönderme veya sonuçları gözlemleme.

ResultEventBus öğesini yükseltmek için rememberResultEventBus kullanarak oluşturun ve rememberResultEventBusNavEntryDecorator(resultEventBus) öğesine iletin:

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
    )
)

Sonuçları yönetme ve temizleme

Bir hedef tek seferlik sonucu kullandığında removeResult kullanarak etkinlik veri yolundan temizleyin. Bu, hedefler kompozisyona yeniden girdiğinde otobüsün geçmiş etkinlikleri yeni gözlemcilere yeniden iletmesini engeller:

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")
    }
}

Sonuçları açık anahtara (resultBus.removeResult(resultKey)) veya türe göre türetilmiş anahtara (resultBus.removeResult<T>()) göre temizleyebilirsiniz. Anahtar eşleme hakkında ayrıntılı bilgi için Sonuç anahtarları başlıklı makaleyi inceleyin.

Yemek tarifleri

Farklı sonuç iletme stratejilerini gösteren, çalıştırılabilir kod örneklerinin tamamı için aşağıdaki tariflere bakın: