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

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

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

  • نتایج رویدادمحور: برای رویدادهای گذرا و یک‌باره (مثل نمایش نوار اسنک تأیید یا راه‌اندازی اثر جانبی) بااستفاده از ResultEffect.
  • نتایج مبتنی بر وضعیت: برای مشاهده آخرین نتیجه به‌عنوان «نگارش» 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 ادغام‌شده: نتایج میانی را حذف می‌کند و فقط آخرین نتیجه را به‌عنوان «نوشتن 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 ازطریق توالی چرخه حیات زیر اجرا می‌شود:

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

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

دستور پخت

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