بدءًا من الإصدار 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 من خلال تسلسل مراحل النشاط التالي:
- إرسال المُرسِل: يرسل وجهة المُرسِل نتيجة باستخدام
resultBus.sendResult(resultKey = "pickup_address", address)ويحذف الأنشطة السابقة. - إدخال المستلِم للتركيبة: تصبح وجهة المستلِم هي الشاشة النشطة، ويبدأ
ResultEffectفي الاستماع إلى النتائج. - المستلِم يعالج النتائج: يتلقّى
ResultEffectوينفّذ نص التأثير لكل نتيجة تم إرسالها لهذا المفتاح، ويعالج جميع عمليات الإرسال بالترتيب الذي تم إرسالها به. - مغادرة المستلِم للتركيب: عند إزالة وجهة المستلِم من حزمة الخلف، تغادر
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>()). وللحصول على تفاصيل حول مطابقة المفاتيح، اطّلِع على مفاتيح النتائج.
وصفات طعام
للاطّلاع على أمثلة كاملة قابلة للتنفيذ على الرموز البرمجية توضّح استراتيجيات مختلفة لتمرير النتائج، يمكنك الرجوع إلى الوصفات التالية:
- إعداد مسبق لنتائج مستندة إلى الأحداث
- وصفة النتائج المستندة إلى الحالة
- إعداد مسبق لنتائج قابلة للنشر على نحو متسلسِل