نتایج را برگردانید

از نسخه Navigation 3 1.2.0 به بعد، می‌توانید با استفاده از API ResultEventBus ، نتایج را از مقصدها برگردانید.

ResultEventBus دو مدل ارتباطی ارائه می‌دهد:

  • نتایج مبتنی بر رویداد : برای رویدادهای گذرا و یک‌باره (مانند نمایش یک اسنک‌بار تأیید یا ایجاد یک عارضه جانبی) با استفاده از ResultEffect .
  • نتایج مبتنی بر وضعیت : برای مشاهده آخرین نتیجه به عنوان Compose State با استفاده از conflateAsState .

تنظیم گذرگاه رویداد نتیجه

برای اینکه ResultEventBus برای مقاصد ترکیب‌پذیر شما در دسترس باشد، rememberResultEventBusNavEntryDecorator را به لیست دکوراتورهای ارسالی به NavDisplay خود اضافه کنید. این کار محتوای هر مقصد را با یک ترکیب LocalResultEventBus محلی فراهم می‌کند.

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

کلیدهای نتیجه

ResultEventBus هر نتیجه را با استفاده از یک کلید شناسایی و مسیریابی می‌کند. فرستنده‌ها و گیرنده‌ها با استفاده از همان کلید، نتایج را مطابقت می‌دهند.

شما می‌توانید کلیدهای نتیجه را به دو روش مشخص کنید:

  • کلیدهای صریح : می‌توانید یک کلید صریح (مانند resultKey = "pickup_address" ) مشخص کنید. هنگام بازگرداندن انواع رایج (مانند String ، Boolean یا primitives) یا زمانی که چندین مقصد نمونه‌های مختلفی از یک نوع داده را برمی‌گردانند، از کلیدهای صریح استفاده کنید.
  • کلیدهای مشتق‌شده از نوع : وقتی کلید صریحی مشخص نمی‌کنید، ResultEventBus به‌طور خودکار با استفاده از نمایش toString از KClass نوع نتیجه (مانند Contact::class.toString() ) یک کلید تولید می‌کند. از کلیدهای مشتق‌شده از نوع برای انواع داده متمایز و مختص دامنه استفاده کنید.

نتایج را از یک مقصد برگردانید

برای اینکه کامپوننت‌های صفحه نمایش قابل استفاده مجدد و تست باشند، مستقیماً درون رابط کاربری صفحه نمایش خود به LocalResultEventBus دسترسی پیدا نکنید. در عوض، لامبداهای callback را از صفحه نمایش خود در معرض نمایش قرار دهید. در entryProvider خود، با ارسال نتیجه با استفاده از LocalResultEventBus.current و پیمایش به عقب، callback را مدیریت کنید.

شما می‌توانید نتایج را با استفاده از یک کلید نتیجه صریح ارسال کنید:

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

دریافت نتایج

مقصدها می‌توانند نتایج را با استفاده از اثرات مبتنی بر رویداد یا مشاهدات مبتنی بر حالت مصرف کنند.

رابط برنامه‌نویسی کاربردی رفتار موارد استفاده توصیه شده
ResultEffect صف‌بندی‌شده (Queued ): تمام نتایج منتشر شده برای کلید را به ترتیب پردازش می‌کند. رویدادهای یک‌باره و عوارض جانبی (مانند نمایش یک snackbar یا ارسال به ViewModel ).
conflateAsState Conlated : نتایج میانی را حذف می‌کند و فقط آخرین نتیجه را به عنوان Compose State نگه می‌دارد. اصلاح‌کننده‌های سبک وضعیت رابط کاربری (مانند تگ‌های فیلتر فعال یا لغو انتخاب).

مدیریت رویدادهای یک‌باره با ResultEffect

هنگام مدیریت رویدادهای یک‌باره مانند راه‌اندازی تجزیه و تحلیل، نمایش یک snackbar یا ارسال نتیجه به ViewModel ، ResultEffect استفاده کنید.

ResultEffect یک صف برای نتایج ورودی نگه می‌دارد. اگر چندین نتیجه برای یک کلید مشخص ارسال شود، ResultEffect هر نتیجه را به ترتیبی که ارسال شده است پردازش می‌کند. علاوه بر این، ResultEffect در یک محدوده‌ی کوروتین اجرا می‌شود که به شما امکان می‌دهد توابع معلق‌کننده را مستقیماً درون بدنه‌ی effect فراخوانی کنید.

می‌توانید به نتایج مرتبط با یک کلید نتیجه‌ی صریح گوش دهید:

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 از طریق توالی چرخه عمر زیر اجرا می‌شود:

  1. فرستنده ارسال می‌کند : مقصد فرستنده با استفاده از resultBus.sendResult(resultKey = "pickup_address", address) نتیجه‌ای را ارسال می‌کند و پشته پشتی را باز می‌کند.
  2. گیرنده وارد ترکیب می‌شود : مقصد گیرنده به صفحه فعال تبدیل می‌شود و ResultEffect شروع به گوش دادن به نتایج می‌کند.
  3. گیرنده نتایج را پردازش می‌کند : ResultEffect بدنه‌ی اثر خود را برای هر نتیجه‌ی ارسالی برای آن کلید دریافت و اجرا می‌کند و تمام انتشارها را به ترتیبی که ارسال شده‌اند پردازش می‌کند.
  4. گیرنده از ترکیب خارج می‌شود : وقتی مقصد گیرنده از پشته پشتی حذف می‌شود، ResultEffect از ترکیب خارج می‌شود و گوش دادن به نتایج را متوقف می‌کند.

مشاهده آخرین نتایج به صورت state با استفاده از 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 با استفاده از rememberResultEventBus ResultEventBus مخصوص به خود را به صورت داخلی ایجاد و به خاطر می‌سپارد.

شما می‌توانید به طور صریح یک 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>() ) پاک کنید. برای جزئیات بیشتر در مورد تطبیق کلید، به Result keys مراجعه کنید.

دستور پخت‌ها

برای مثال‌های کامل کد قابل اجرا که استراتژی‌های مختلف انتقال نتیجه را نشان می‌دهند، به دستور العمل‌های زیر مراجعه کنید: