نقل بيانات التنقّل في Jetpack إلى التنقّل Compose

تتيح لك واجهة برمجة التطبيقات Navigation Compose التنقّل بين العناصر القابلة للإنشاء في تطبيق Compose، مع الاستفادة من مكوِّن Jetpack Navigation والبنية الأساسية والميزات.

توضّح هذه الصفحة كيفية نقل البيانات من Jetpack Navigation المستند إلى Fragment إلى Navigation Compose، وذلك كجزء من عملية نقل أكبر لواجهة المستخدم المستندة إلى View إلى Jetpack Compose.

المتطلبات الأساسية لعملية نقل البيانات

يمكنك الانتقال إلى Navigation Compose بعد أن تتمكّن من استبدال جميع Fragments بعناصر واجهة المستخدم القابلة للإنشاء للشاشة المقابلة. يمكن أن تحتوي العناصر القابلة للإنشاء على الشاشة على مزيج من محتوى Compose وView، ولكن يجب أن تكون جميع وجهات التنقّل عناصر قابلة للإنشاء لتفعيل عملية نقل البيانات إلى Navigation Compose. وحتى ذلك الحين، عليك مواصلة استخدام مكوّن التنقّل المستند إلى Fragment في قاعدة الرموز البرمجية المتوافقة مع View وCompose. يمكنك الاطّلاع على مستندات التوافق التشغيلي الخاصة بالتنقّل لمزيد من المعلومات.

لا يُشترط استخدام Navigation Compose في تطبيق يعتمد على Compose فقط. يمكنك مواصلة استخدام مكوّن التنقّل المستند إلى Fragment، طالما أنّك تحتفظ بـ Fragments لاستضافة المحتوى القابل للإنشاء.

خطوات نقل البيانات

سواء كنت تتّبع استراتيجية النقل المقترَحة أو تتّخذ نهجًا آخر، ستصل إلى مرحلة تكون فيها جميع وجهات التنقّل عبارة عن عناصر واجهة مستخدم قابلة للإنشاء، وتعمل الفئات Fragment كحاويات فقط للعناصر القابلة للإنشاء. في هذه المرحلة، يمكنك الانتقال إلى Navigation Compose.

إذا كان تطبيقك يتّبع حاليًا نمط تصميم لوظائف المستخدم المحدّدة ودليلنا حول البنية، لن يتطلّب نقل التطبيق إلى Jetpack Compose وNavigation Compose إجراء عمليات إعادة تصميم كبيرة في طبقات تطبيقك الأخرى، باستثناء طبقة واجهة المستخدم.

للانتقال إلى Navigation Compose، اتّبِع الخطوات التالية:

  1. أضِف Navigation Compose dependency إلى تطبيقك.
  2. أنشئ عنصر App-level قابل للإنشاء وأضِفه إلى Activity كنقطة دخول إلى Compose، مع استبدال عملية إعداد تنسيق View:

    class SampleActivity : ComponentActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            // setContentView<ActivitySampleBinding>(this, R.layout.activity_sample)
            setContent {
                SampleApp(/* ... */)
            }
        }
    }

  3. أنشئ أنواعًا لكل وجهة تنقّل. استخدِم data object للمواقع التي لا تتطلّب أي بيانات، وdata class أو class للمواقع التي تتطلّب بيانات.

    @Serializable data object First
    @Serializable data class Second(val id: String)
    @Serializable data object Third
    

  4. اضبط NavController في مكان يمكن لجميع العناصر القابلة للإنشاء التي تحتاج إلى الرجوع إليه الوصول إليه (يكون ذلك عادةً داخل العنصر القابل للإنشاء App). يتّبع هذا الأسلوب مبادئ نقل الحالة للأعلى ويتيح لك استخدام NavController كمصدر موثوق للتنقّل بين الشاشات القابلة للإنشاء والحفاظ على سجلّ الرجوع:

    @Composable
    fun SampleApp() {
        val navController = rememberNavController()
        // ...
    }

  5. أنشئ NavHost تطبيقك داخل العنصر القابل للإنشاء App ومرِّر navController:

    @Composable
    fun SampleApp() {
        val navController = rememberNavController()
    
        SampleNavHost(navController = navController)
    }
    
    @Composable
    fun SampleNavHost(
        navController: NavHostController
    ) {
        NavHost(navController = navController, startDestination = First) {
            // ...
        }
    }

  6. أضِف وجهات composable لإنشاء الرسم البياني للتنقّل. إذا سبق أن تم نقل كل شاشة إلى Compose، ستتضمّن هذه الخطوة استخراج عناصر Compose الخاصة بالشاشة من "التقسيمات" إلى composable الوجهات فقط:

    class FirstFragment : Fragment() {
    
        override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View {
            return ComposeView(requireContext()).apply {
                setContent {
                    // FirstScreen(...) EXTRACT FROM HERE
                }
            }
        }
    }
    
    @Composable
    fun SampleNavHost(
        navController: NavHostController
    ) {
        NavHost(navController = navController, startDestination = First) {
            composable<First> {
                FirstScreen(/* ... */) // EXTRACT TO HERE
            }
            composable<Second> {
                SecondScreen(/* ... */)
            }
            // ...
        }
    }

  7. إذا اتّبعت الإرشادات الواردة في تصميم واجهة المستخدم المستندة إلى Compose، خاصةً كيفية تمرير ViewModels وأحداث التنقّل إلى عناصر قابلة للإنشاء، تكون الخطوة التالية هي تغيير طريقة توفير ViewModel لكل عنصر قابل للإنشاء خاص بالشاشة. يمكنك غالبًا استخدام ميزة "إدخال التبعية" في Hilt ونقطة الدمج مع Compose وNavigation من خلال hiltViewModel:

    @Composable
    fun FirstScreen(
        // viewModel: FirstViewModel = viewModel(),
        viewModel: FirstViewModel = hiltViewModel(),
        onButtonClick: () -> Unit = {},
    ) {
        // ...
    }

  8. استبدِل جميع طلبات التنقّل findNavController() بطلبات navController وامررها كأحداث تنقّل إلى كل شاشة قابلة للإنشاء، بدلاً من تمرير navController بأكملها. يتّبع هذا الأسلوب أفضل الممارسات لعرض الأحداث من الدوال القابلة للإنشاء على المتصلين، ويحافظ على navController كمصدر وحيد للحقيقة.

    يمكن تمرير البيانات إلى وجهة من خلال إنشاء مثيل لفئة المسار المحدّدة لهذه الوجهة. ويمكن بعد ذلك الحصول على هذا المعرّف إما مباشرةً من إدخال سجلّ التصفّح الخلفي في الوجهة أو من ViewModel باستخدام SavedStateHandle.toRoute().

    @Composable
    fun SampleNavHost(
        navController: NavHostController
    ) {
        NavHost(navController = navController, startDestination = First) {
            composable<First> {
                FirstScreen(
                    onButtonClick = {
                        // findNavController().navigate(firstScreenToSecondScreenAction)
                        navController.navigate(Second(id = "ABC"))
                    }
                )
            }
            composable<Second> { backStackEntry ->
                val secondRoute = backStackEntry.toRoute<Second>()
                SecondScreen(
                    id = secondRoute.id,
                    onIconClick = {
                        // findNavController().navigate(secondScreenToThirdScreenAction)
                        navController.navigate(Third)
                    }
                )
            }
            // ...
        }
    }

  9. أزِل جميع الفئات Fragment وتنسيقات XML ذات الصلة وعناصر التنقّل غير الضرورية والموارد الأخرى، بالإضافة إلى الفئات Fragment القديمة وتبعيات Jetpack Navigation.

يمكنك العثور على الخطوات نفسها مع المزيد من التفاصيل المتعلّقة بمكتبة Navigation Compose في مستندات الإعداد.

حالات الاستخدام الشائعة

بغض النظر عن مكوّن التنقّل الذي تستخدمه، تنطبق مبادئ التنقّل نفسها.

تشمل حالات الاستخدام الشائعة عند نقل البيانات ما يلي:

لمزيد من المعلومات التفصيلية حول حالات الاستخدام هذه، يمكنك الاطّلاع على التنقّل باستخدام Compose.

استرداد بيانات معقّدة أثناء التنقّل

ننصح بشدة بعدم تمرير عناصر بيانات معقّدة عند التنقّل. بدلاً من ذلك، مرِّر الحد الأدنى من المعلومات الضرورية، مثل معرّف فريد أو أي شكل آخر من أشكال التعريف، كمعلَمات عند تنفيذ إجراءات التنقّل. يجب تخزين العناصر المعقّدة كبيانات في مصدر واحد للحقيقة، مثل طبقة البيانات. لمزيد من المعلومات، يُرجى الاطّلاع على استرداد البيانات المعقّدة عند التنقّل.

إذا كانت الفئات Fragments تمرّر عناصر معقّدة كمعلَمات، ننصحك بإعادة تصميم الرمز البرمجي أولاً بطريقة تتيح تخزين هذه العناصر واسترجاعها من طبقة البيانات. يمكنك الاطّلاع على مستودع Now in Android للحصول على أمثلة.

القيود

يوضّح هذا القسم القيود الحالية على Navigation Compose.

النقل التدريجي إلى Navigation Compose

في الوقت الحالي، لا يمكنك استخدام Navigation Compose مع الاستمرار في استخدام "التقسيمات" كوجهات في الرمز البرمجي. لبدء استخدام Navigation Compose، يجب أن تكون جميع وجهاتك قابلة للإنشاء. يمكنك تتبُّع طلب الميزة هذا على Issue Tracker.

الصور المتحركة للانتقال

بدءًا من Navigation 2.7.0-alpha01، أصبح بإمكانك ضبط انتقالات مخصّصة مباشرةً في NavHost، بعد أن كان ذلك متاحًا سابقًا من خلال AnimatedNavHost. يمكنك الاطّلاع على ملاحظات الإصدار للحصول على مزيد من المعلومات.

مزيد من المعلومات

لمزيد من المعلومات حول الانتقال إلى Navigation Compose، يُرجى الاطّلاع على المراجع التالية: