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:

  • Etkinlik tabanlı 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 olarak en son sonucu gözlemlemek için.

Sonuç etkinlik veri yolunu ayarlama

ResultEventBus öğesini composable hedeflerinizde kullanılabilir hale getirmek için rememberResultEventBusNavEntryDecorator öğesini NavDisplay öğenize iletilen dekoratörler listesine ekleyin. Bu sayede her hedefin içeriği, LocalResultEventBus bileşen yereliyle sağlanır.

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 öğeler) 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üretilen anahtarlar: Açık bir anahtar belirtmediğinizde ResultEventBus, sonuç türünün KClass (ör. Contact::class.toString()) toString gösterimini kullanarak otomatik olarak bir anahtar oluşturur. Farklı, alana özgü veri türleri için türden türetilen anahtarları kullanın.

Bir varış noktasından sonuçları döndürme

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

Açık bir sonuç anahtarı kullanarak sonuç 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()
        }
    )
}

Türe göre türetilmiş bir anahtar kullanarak da sonuç 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, etkinliğe dayalı efektleri veya duruma dayalı gözlemlenebilirleri kullanarak sonuçları kullanabilir.

API Davranış Önerilen kullanım alanları
ResultEffect Sıraya alındı: Anahtar için yayınlanan tüm sonuçları sırayla işler. Tek seferlik etkinlikler ve yan etkiler (ör. snackbar gösterme veya ViewModel öğesine yönlendirme)
conflateAsState Conflated: Ara sonuçları siler ve yalnızca en son sonucu Compose State olarak saklar. Hafif 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

Analizleri tetikleme, snackbar gösterme veya sonucu bir ViewModel öğesine iletme gibi tek seferlik etkinlikleri işlerken ResultEffect kullanın.

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, askıya alma işlevlerini doğrudan efekt gövdesinde çağırmanıza olanak tanıyan bir eş yordam kapsamında çalışır.

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

Türe göre türetilmiş bir anahtar kullanarak da 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ıyla yürütülür:

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

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

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

Açık bir sonuç anahtarıyla ilişkili sonuçları gözlemleyebilirsiniz:

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 türetilmiş 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
    )
}

Hoist ResultEventBus

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

Aşağıdaki durumlarda açıkça bir ResultEventBus oluşturup yukarı taşıyabilirsiniz:

  • ResultEventBus örneğini doğrudan, composable olmayan bileşenlere veya bağımlılık ekleme grafiklerine iletin.
  • Hedef hiyerarşisinin dışındaki üst düzey uygulama iskeletinden (ör. uygulama çubuğu veya gezinme çekmecesi) sonuçları gönderin veya gözlemleyin.

ResultEventBus öğesini yukarı taşımak 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 bir sonuç kullandığında removeResult kullanarak sonucu etkinlik veri yolundan temizleyin. Bu sayede, hedefler yeniden oluşturmaya girdiğinde veri yolu, geçmiş etkinlikleri yeni gözlemcilere yeniden göndermez:

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şleştirme hakkında ayrıntılı bilgi için Sonuç anahtarları bölümüne bakın.

Yemek tarifleri

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