السياق في Jetpack Compose: دليل شامل مع أمثلة برمجية

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة: لماذا يعتبر Context في Jetpack Compose كابوسًا للمبتدئين؟

\n

إذا سبق لك البرمجة لنظام Android باستخدام XML، فأنت تعلم أن Context هو حرفيًا "المفتاح" لكل شيء: من الوصول إلى الموارد إلى تشغيل أنشطة جديدة. ولكن عندما تنتقل إلى Jetpack Compose، يتغير كل شيء. الدوال التركيبية (Composable functions) لا تملك وصولًا مباشرًا إلى Context، وغالبًا ما يصبح هذا حجر عثرة للمطورين، خاصة أولئك الذين بدأوا للتو في تعلم النهج التصريحي (declarative approach).

\n

سؤال "How to get Context in Jetpack Compose" على Stack Overflow حصد آلاف المشاهدات وعشرات الإجابات. هذا ليس من قبيل الصدفة. في هذه المقالة، سنستعرض جميع الطرق الممكنة للحصول على Context في Compose، من البسيطة إلى المتقدمة، مع أمثلة برمجية حقيقية بلغة Kotlin. ستتعرف على متى تستخدم LocalContext.current، وكيف تتجنب تسرب الذاكرة، وما هي الممارسات التي تعتبر "أفضل الممارسات" في عام 2025.

\n\n

1. الطريقة الأسهل: LocalContext.current

\n

إذا كنت بحاجة إلى الحصول على Context داخل دالة تركيبية، فإن الطريقة الأكثر مباشرة هي استخدام LocalContext.current. هذا هو composable-local الذي يوفر Context الحالي (عادةً ما يكون Activity أو ContextWrapper).

\n\n
@Composable\nfun MyScreen() {\n    val context = LocalContext.current\n    \n    Button(onClick = {\n        Toast.makeText(context, "مرحبًا من Compose!", Toast.LENGTH_SHORT).show()\n    }) {\n        Text("إظهار Toast")\n    }\n}
\n\n

هذا الكود يعمل، ولكن هناك فارق بسيط: LocalContext.current يمنحك Context مرتبطًا بـ CompositionLocal الحالي. في معظم الحالات، هذا هو ما تحتاجه. لكن تذكر، لا ينبغي تمرير هذا Context إلى ViewModel أو حفظه في كائنات طويلة العمر - فقد يؤدي ذلك إلى تسرب الذاكرة.

\n\n

متى تستخدم هذا؟

\n
    \n
  • للإجراءات لمرة واحدة في واجهة المستخدم: Toast، Snackbar، تشغيل Activity.
  • \n
  • للوصول إلى الموارد: context.getString(R.string.app_name).
  • \n
  • للعمل مع خدمات النظام: context.getSystemService().
  • \n
\n\n

2. Context في ViewModel: النهج الصحيح

\n

أحد الأخطاء الأكثر شيوعًا هو تمرير Context مباشرة إلى ViewModel. في Android Architecture Components، يعتبر هذا نمطًا مضادًا (antipattern)، لأن ViewModel قد يعمر أكثر من Activity، وسيصبح Context قديمًا. بدلاً من ذلك، استخدم AndroidViewModel.

\n\n
class MyViewModel(application: Application) : AndroidViewModel(application) {\n    \n    private val context = getApplication<Application>().applicationContext\n    \n    fun loadData() {\n        // نستخدم applicationContext وهو آمن\n        val prefs = context.getSharedPreferences("my_prefs", Context.MODE_PRIVATE)\n    }\n}
\n\n

في Compose، يمكنك الحصول على مثيل من ViewModel بالطريقة القياسية:

\n\n
@Composable\nfun MyScreen() {\n    val viewModel: MyViewModel = viewModel()\n    // Context موجود بالفعل داخل ViewModel، لا حاجة لتمريره من واجهة المستخدم\n}
\n\n

الميزة: applicationContext يعيش طالما أن العملية حية، ولا يعتمد على دورة حياة Activity. هذا آمن وموصى به من Google.

\n\n

3. Context في CompositionLocal: موفرو مخصصون

\n

أحيانًا تحتاج إلى تمرير Context محدد (على سبيل المثال، للاختبار أو لوحدة تحتوي على مكتبة). في مثل هذه الحالات، يمكنك إنشاء CompositionLocal خاص بك.

\n\n
val LocalCustomContext = staticCompositionLocalOf<Context> {\n    error("لم يتم توفير سياق مخصص")\n}\n\n@Composable\nfun CustomContextProvider(context: Context, content: @Composable () -> Unit) {\n    CompositionLocalProvider(LocalCustomContext provides context) {\n        content()\n    }\n}\n\n// الاستخدام:\n@Composable\nfun MyScreen() {\n    val customContext = LocalCus                

المدونات

توصيات الكتب