عرض النتائج

بدءًا من الإصدار 1.2.0 من Navigation 3، يمكنك عرض نتائج من وجهات باستخدام واجهة برمجة التطبيقات ResultEventBus.

توفّر 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 مباشرةً داخل واجهة مستخدم الشاشة. بدلاً من ذلك، يمكنك عرض دوال lambda الخاصة بعمليات الرجوع من الشاشة. في 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()
        }
    )
}

تلقّي النتائج

يمكن أن تستخدم الوجهات النتائج باستخدام المؤثرات المستندة إلى الأحداث أو العناصر القابلة للمراقبة المستندة إلى الحالة.

واجهة برمجة التطبيقات السلوك حالات الاستخدام المقترَحة
ResultEffect في قائمة الانتظار: تعالج هذه الحالة جميع النتائج التي تمّ إصدارها للمفتاح بالترتيب. الأحداث التي تحدث لمرة واحدة والآثار الجانبية (مثل عرض شريط إعلام منبثق أو إعادة التوجيه إلى ViewModel).
conflateAsState الدمج: يتم إسقاط النتائج الوسيطة والاحتفاظ بأحدث نتيجة فقط كـ Compose State. أدوات تعديل حالة واجهة المستخدم البسيطة (مثل علامات الفلاتر النشطة أو عمليات إلغاء التحديد).

التعامل مع الأحداث لمرة واحدة باستخدام ResultEffect

استخدِم ResultEffect عند التعامل مع الأحداث التي تحدث لمرة واحدة، مثل تشغيل "إحصاءات Google" أو عرض شريط إعلام منبثق أو إعادة توجيه نتيجة إلى 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>()). وللحصول على تفاصيل حول مطابقة المفاتيح، اطّلِع على مفاتيح النتائج.

وصفات طعام

للاطّلاع على أمثلة كاملة قابلة للتنفيذ على الرموز البرمجية توضّح استراتيجيات مختلفة لتمرير النتائج، يمكنك الرجوع إلى الوصفات التالية: