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

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

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

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

راه‌اندازی گذرگاه رویداد نتیجه

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

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

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

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

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

  • کلیدهای صریح: می‌توانید کلید صریحی (مثل resultKey = "pickup_address") مشخص کنید. هنگام برگرداندن انواع رایج (مثل String، Boolean، یا انواع اولیه)، یا زمانی که چندین مقصد نمونه‌های متفاوتی از یک نوع داده را برمی‌گردانند، از کلیدهای صریح استفاده کنید.
  • کلیدهای مشتق‌شده از نوع: وقتی کلید صریحی را مشخص نمی‌کنید، ResultEventBus به‌طور خودکار کلیدی را بااستفاده ازtoString نمایش KClass نوع نتیجه (مانند 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: نتایج میانی را حذف می‌کند و فقط آخرین نتیجه را به‌عنوان Compose State نگه می‌دارد. اصلاح‌کننده‌های سبک وضعیت میانای کاربر (مانند برچسب‌های فیلتر فعال یا لغو انتخاب).

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

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

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

  1. فرستنده منتشر می‌کند: مقصد فرستنده نتیجه‌ای را بااستفاده از resultBus.sendResult(resultKey = "pickup_address", address) ارسال می‌کند و پشته قبلی‌ها را بالا می‌آورد.
  2. گیرنده وارد ترکیب‌بندی می‌شود: مقصد گیرنده به صفحه فعال تبدیل می‌شود و ResultEffect شروع به گوش دادن برای نتایج می‌کند.
  3. گیرنده نتایج را پردازش می‌کند: ResultEffect بدنه تأثیر خود را برای هر نتیجه ارسال‌شده برای آن کلید دریافت و اجرا می‌کند و همه انتشارها را به‌ترتیبی که ارسال شده‌اند پردازش می‌کند.
  4. گیرنده ترکیب‌بندی را ترک می‌کند: وقتی مقصد گیرنده از پشته قبلی‌ها خارج می‌شود، 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>()) پاک کنید. برای جزئیات مربوط به مطابقت کلید، به کلیدهای نتیجه مراجعه کنید.

دستور پخت

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