فهم الأساسيات وتطبيقها

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

إضافة بيانات حالة التنقّل

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

زر إجراء لوحة المفاتيح الافتراضية (رمز علامة صح) محاط بدائرة حمراء
الشكل 1. رسم بياني يوضّح كيفية تغيُّر سجلّ الرجوع مع أحداث تنقّل المستخدم

إنشاء حزمة الخلفية

في Navigation 3، لا يحتوي سجلّ الرجوع فعليًا على محتوى. بدلاً من ذلك، يحتوي على إشارات إلى المحتوى، تُعرف باسم المفاتيح. يمكن أن تكون المفاتيح من أي نوع، ولكنها عادةً ما تكون فئات بيانات بسيطة وقابلة للتسلسل. يوفّر استخدام المراجع بدلاً من المحتوى المزايا التالية:

  • يسهل التنقّل من خلال إضافة مفاتيح إلى حزمة الخلفية.
  • وطالما أنّ المفاتيح قابلة للتسلسل، يمكن حفظ سجلّ الرجوع في مساحة تخزين ثابتة، ما يتيح له البقاء على قيد الحياة عند حدوث تغييرات في الإعدادات أو إيقاف العملية. وهذا أمر مهم لأنّ المستخدمين يتوقّعون مغادرة تطبيقك والعودة إليه لاحقًا ومتابعة المحتوى نفسه من حيث توقّفوا. يمكنك الاطّلاع على حفظ سجلّ الرجوع لمزيد من المعلومات.

من المفاهيم الأساسية في واجهة برمجة التطبيقات Navigation 3 أنّك تملك سجلّ الرجوع. المكتبة:

  • من المتوقّع أن يكون سجلّ الرجوع عبارة عن List<T> مستند إلى حالة اللقطة، حيث يمثّل T نوع سجلّ الرجوع keys. يمكنك استخدام Any أو تقديم مفاتيح خاصة بك ذات أنواع أكثر صرامة. عندما ترى المصطلحَين "دفع" أو "سحب"، يكون التنفيذ الأساسي هو إضافة عناصر إلى نهاية القائمة أو إزالتها منها.
  • يراقب سجلّ الرجوع ويعكس حالته في واجهة المستخدم باستخدام NavDisplay.

يوضّح المثال التالي كيفية إنشاء مفاتيح وحزمة سابقة، وتعديل الحزمة السابقة استجابةً لأحداث تنقّل المستخدم:

// Define keys that will identify content
data object ProductList
data class ProductDetail(val id: String)

@Composable
fun MyApp() {

    // Create a back stack, specifying the key the app should start with
    val backStack = remember { mutableStateListOf<Any>(ProductList) }

    // Supply your back stack to a NavDisplay so it can reflect changes in the UI
    // ...more on this below...

    // Push a key onto the back stack (navigate forward), the navigation library will reflect the change in state
    backStack.add(ProductDetail(id = "ABC"))

    // Pop a key off the back stack (navigate back), the navigation library will reflect the change in state
    backStack.removeLastOrNull()
}

حلّ المفاتيح للمحتوى

يتم تصميم المحتوى في Navigation 3 باستخدام NavEntry، وهو فئة تحتوي على دالة قابلة للإنشاء. يمثّل وجهة، أي جزءًا واحدًا من المحتوى يمكن للمستخدم الانتقال إليه والرجوع منه.

يمكن أن يحتوي NavEntry أيضًا على بيانات وصفية، أي معلومات عن المحتوى. يمكن أن تقرأ عناصر الحاويات، مثل NavDisplay، هذه البيانات الوصفية لمساعدتها في تحديد طريقة عرض محتوى NavEntry. على سبيل المثال، يمكن استخدام بيانات وصفية لتجاوز الرسوم المتحركة التلقائية لعنصر NavEntry معيّن. ‫NavEntry metadata هي خريطة لمفاتيح String إلى قيم Any، ما يوفّر تخزينًا متعدد الاستخدامات للبيانات.

لتحويل key إلى NavEntry، أنشئ Entry Provider. هذه دالة تقبل key وتعرض NavEntry لهذا key. يتم عادةً تعريفها كمعلَمة lambda عند إنشاء NavDisplay.

هناك طريقتان لإنشاء Entry Provider، إما عن طريق إنشاء دالة lambda مباشرةً، أو باستخدام لغة النطاق الخاص entryProvider.

إنشاء دالة Entry Provider مباشرةً

يمكنك عادةً إنشاء دالة Entry Provider باستخدام عبارة when، مع فرع لكل مفتاح من مفاتيحك.

entryProvider = { key ->
    when (key) {
        is ProductList -> NavEntry(key) { Text("Product List") }
        is ProductDetail -> NavEntry(
            key,
            metadata = mapOf("extraDataKey" to "extraDataValue")
        ) { Text("Product ${key.id} ") }

        else -> {
            NavEntry(Unit) { Text(text = "Invalid Key: $it") }
        }
    }
}

استخدام لغة DSL الخاصة بـ entryProvider

يمكن أن تبسّط entryProvider DSL دالة lambda من خلال تجنُّب الحاجة إلى الاختبار مقابل كل نوع من أنواع المفاتيح، وإنشاء NavEntry لكل نوع. استخدِم دالة الإنشاء entryProvider لهذا الغرض. ويتضمّن أيضًا السلوك التلقائي للتراجع (عرض خطأ) في حال عدم العثور على المفتاح.

entryProvider = entryProvider {
    entry<ProductList> { Text("Product List") }
    entry<ProductDetail>(
        metadata = mapOf("extraDataKey" to "extraDataValue")
    ) { key -> Text("Product ${key.id} ") }
}

يُرجى ملاحظة ما يلي من المقتطف:

  • تُستخدَم السمة entry لتحديد NavEntry بالنوع المحدّد والمحتوى القابل للإنشاء
  • تقبل entry المَعلمة metadata لضبط NavEntry.metadata

عرض سجلّ الرجوع

يمثّل سجلّ الرجوع حالة التنقّل في تطبيقك. وعندما يتغيّر سجلّ الرجوع، يجب أن تعكس واجهة مستخدم التطبيق حالة سجلّ الرجوع الجديدة. في Navigation 3، تراقب NavDisplay حزمة الخلفية وتعدّل واجهة المستخدم وفقًا لذلك. أنشئها باستخدام المَعلمات التالية:

  • سجلّ الرجوع: يجب أن يكون من النوع SnapshotStateList<T>، حيث T هو نوع مفاتيح سجلّ الرجوع. وهي List قابلة للملاحظة، لذا تؤدي إلى إعادة إنشاء NavDisplay عند تغييرها.
  • entryProvider لتحويل المفاتيح في سجلّ الرجوع إلى NavEntry عناصر
  • يمكنك اختياريًا تقديم دالة lambda إلى المَعلمة onBack. يتم استدعاء هذا الإجراء عندما ينفّذ المستخدم حدث الرجوع.

يوضّح المثال التالي كيفية إنشاء NavDisplay.

data object Home
data class Product(val id: String)

@Composable
fun NavExample() {

    val backStack = remember { mutableStateListOf<Any>(Home) }

    NavDisplay(
        backStack = backStack,
        onBack = { backStack.removeLastOrNull() },
        entryProvider = { key ->
            when (key) {
                is Home -> NavEntry(key) {
                    ContentGreen("Welcome to Nav3") {
                        Button(onClick = {
                            backStack.add(Product("123"))
                        }) {
                            Text("Click to navigate")
                        }
                    }
                }

                is Product -> NavEntry(key) {
                    ContentBlue("Product ${key.id} ")
                }

                else -> NavEntry(Unit) { Text("Unknown route") }
            }
        }
    )
}

تعرض NavDisplay تلقائيًا NavEntry في أعلى حزمة الخلفية في تصميم ذي لوحة واحدة. يعرض التسجيل التالي هذا التطبيق قيد التشغيل:

السلوك التلقائي لـ `NavDisplay` مع وجهتَين
الشكل 2. NavDisplay السلوك التلقائي مع وجهتَين

خلاصة ما سبق ذكره

يوضّح الرسم البياني التالي كيفية تدفّق البيانات بين العناصر المختلفة في Navigation 3:

تصور لكيفية تدفّق البيانات بين العناصر المختلفة في Navigation 3
الشكل 3. مخطّط يوضّح كيفية انتقال البيانات عبر عناصر مختلفة في Navigation 3
  1. أحداث التنقّل تبدأ التغييرات تتم إضافة المفاتيح أو إزالتها من سجلّ الرجوع استجابةً لتفاعلات المستخدم.

  2. تغيير حالة سجلّ الرجوع يؤدي إلى استرداد المحتوى. تراقب NavDisplay (عنصر قابل للإنشاء يعرض الأنشطة السابقة) الأنشطة السابقة. في الإعدادات التلقائية، يعرض التطبيق إدخال سجلّ الرجوع في أعلى الشاشة بتنسيق لوحة واحدة. عندما يتغير المفتاح العلوي في سجلّ الرجوع، يستخدم NavDisplay هذا المفتاح لطلب المحتوى المقابل من مقدّم بيانات التطبيق.

  3. يقدّم موفّر المحتوى المحتوى. موفّر الإدخال هو دالة تحوّل المفتاح إلى NavEntry. عند تلقّي مفتاح من NavDisplay، يقدّم موفّر البيانات NavEntry المرتبط، والذي يتضمّن المفتاح والمحتوى.

  4. يتم عرض المحتوى. يتلقّى NavDisplay NavEntry ويعرض المحتوى.