remember هي دالة في Jetpack Compose تتيح لك الحفاظ على قيمة بين عمليات إعادة التركيب، مما يمنع إعادة تعيين الحالة عند كل إعادة تشغيل لدالة Composable. بدون remember، جميع المتغيرات المعلنة داخل الدالة ستعاد تعيينها عند كل تحديث لواجهة المستخدم، مما يجعل الحالة غير مستقرة وعديمة الفائدة. وفقًا لـ Google Android Developers, 2026، الاستخدام الصحيح لـ remember هو أساس إدارة الحالة الصحيحة في واجهة المستخدم التصريحية.
الخلاصة
remember هي دالة مدمجة في Jetpack Compose من حزمة androidx.compose.runtime تنشئ كائنًا يحتفظ بقيمته بين الاستدعاءات المتتالية لدالة Composable. تقنيًا، remember تعمل مع الفتحات — خلايا ذاكرة خاصة مخصصة لكل موضع استدعاء دالة في شجرة واجهة المستخدم. طالما بقيت الدالة في التركيب (لم تتم إزالتها عبر شرط if)، سيعيد remember القيمة المحفوظة عند كل إعادة تركيب.
صياغة remember بسيطة: داخل الأقواس المتعرجة يتم تحديد كتلة حساب القيمة الأولية. يتم تنفيذ الكتلة مرة واحدة فقط — أثناء التركيب الأول للدالة. جميع عمليات إعادة التركيب اللاحقة تعيد القيمة المحفوظة بالفعل دون إعادة تنفيذ الكتلة. ومع ذلك، إذا تم تحديد معامل مفتاح، يتم إعادة تنفيذ الكتلة عند تغير المفتاح، مما يسمح بإعادة حساب القيمة بناءً على بيانات الإدخال.
remember ليس سحرًا في Kotlin — إنها دالة بسيطة بمعامل مفتاح ولامدا حسابية. يستخدم تنفيذها الداخلي Composer للقراءة والكتابة في الفتحات. يقوم مترجم Kotlin، عند رؤية استدعاء remember، بتوليد كود يصل إلى CompositionContext الحالي ويدير الفتحات. هذا يعني أن remember يمكن استدعاؤه فقط داخل دالة Composable أو دالة أخرى في سياق التركيب.
في Jetpack Compose، هناك ثلاثة أنواع رئيسية من remember، لكل منها غرضه الخاص. remember الأساسي يحتفظ بالقيمة فقط في ذاكرة العملية الحالية — عند تدوير الشاشة (تغيير الإعدادات)، يعيد Compose إنشاء التركيب ويتم إعادة تعيين جميع قيم remember. للحفاظ على البيانات أثناء تغييرات الإعدادات وإيقاف العملية يتم استخدام rememberSaveable.
rememberSaveable يسلسل القيمة في Bundle عبر آلية SavedStateHandle أو Parcelable. هذا يسمح بالبقاء على قيد الحياة عند تدوير الشاشة، إعداد "عدم الاحتفاظ بالأنشطة" وحتى تصغير التطبيق المؤقت. ومع ذلك، يفرض rememberSaveable قيودًا على نوع البيانات التي يمكن تخزينها: يجب أن تكون بدائية أو Parcelable أو Serializable أو تدعم محول Saver.
derivedStateOf ليست آلية حفظ بل تحسين. ينشئ State يتم حساب قيمته بناءً على كائنات State أخرى. يستجيب derivedStateOf للتغييرات في حالات المصدر ولكنه يعيد حساب القيمة فقط عندما يكون هناك مشتركون. إذا كانت إعادة التركيب الحالية لا تقرأ derivedStateOf، لا يتم إجراء الحساب، مما يوفر الموارد عند التحديثات المتكررة ولكن غير الضرورية.
@Composable
fun RememberVariants() {
// 1. Remember أساسي: يستمر حتى مغادرة التركيب
val createdAt = remember { System.currentTimeMillis() }
// 2. rememberSaveable: يبقى على قيد الحياة عند تدوير الشاشة
var username by rememberSaveable { mutableStateOf("") }
// 3. derivedStateOf: يُحسب فقط عند الحاجة
val isButtonVisible = remember {
derivedStateOf { username.length() > 3 }
}
Text("تم الإنشاء: $createdAt")
TextField(value = username, onValueChange = { username = it })
if (isButtonVisible.value) {
Text("سيتم إظهار الزر")
}
}
الاختيار بين remember و rememberSaveable يعتمد على مدى أهمية الحفاظ على البيانات أثناء تغييرات الإعدادات. للحالات المؤقتة التي ليس من المهم فقدانها عند التدوير (الرسوم المتحركة، موضع التمرير الحالي، حالة التركيز) — remember العادي كافٍ. للبيانات الحيوية (نص في النموذج، العناصر المحددة، مربعات الاختيار) — استخدم rememberSaveable.
الأداء: rememberSaveable أبطأ من remember العادي لأنه يتطلب تسلسلاً في Bundle. استخدم rememberSaveable فقط للبيانات التي تحتاج حقًا للبقاء على قيد الحياة عند إعادة إنشاء Activity. لكل شيء آخر — remember بسيط. الاستخدام المفرط لـ rememberSaveable يؤدي إلى بطء عند تدوير الشاشة والتبديل بين التطبيقات.
إذا كنت تعمل مع فئات لا تدعم Parcelable أو Serializable، استخدم Saver — محول يحدد كيفية حفظ واستعادة الكائن. يتم وصف Saver بزوج من اللامدا: save (يحول الكائن إلى نوع قابل للتخزين) و restore (يستعيد الكائن من البيانات المحفوظة). يوجد Saver قياسي مطبق بالفعل لـ mutableStateListOf و mutableStateMapOf.
| الخاصية | remember | rememberSaveable |
|---|---|---|
| الحفظ أثناء إعادة التركيب | نعم | نعم |
| الحفظ عند التدوير | لا | نعم |
| الحفظ عند إيقاف العملية | لا | نعم |
| متطلبات النوع | أي | Parcelable، Serializable، Saver |
| الأداء | عالي | متوسط |
لنفكر في سيناريو نموذجي — شاشة تحرير الملف الشخصي حيث يتم استخدام remember لعدة أغراض: تخزين حالة حقول النموذج، حساب القيم المشتقة، وتخزين العمليات الحسابية المكلفة في الذاكرة المؤقتة.
data class ProfileUiState(
val name: String = "",
val bio: String = "",
val isSaving: Boolean = false
)
@Composable
fun ProfileEditor() {
var state by rememberSaveable { mutableStateOf(ProfileUiState()) }
val isValid = remember {
derivedStateOf { state.name.isNotBlank() && state.bio.length() <= 500 }
}
val bioWarning = remember(state.bio) {
if (state.bio.length() > 400) {
"${500 - state.bio.length} chars left"
} else null
}
Column(modifier = Modifier.padding(16.dp)) {
OutlinedTextField(
value = state.name,
onValueChange = { state = state.copy(name = it) },
label = { Text("الاسم") }
)
OutlinedTextField(
value = state.bio,
onValueChange = { state = state.copy(bio = it) },
label = { Text("حول") }
)
bioWarning?.let { Text(it, color = MaterialTheme.colorScheme.error) }
Button(onClick = { /* save */ },
enabled = isValid.value) {
Text("حفظ")
}
}
}
في المثال، يتم حفظ الحالة عبر rememberSaveable — النص لن يفقد عند تدوير الشاشة. isValid يتم حسابه عبر derivedStateOf، مما يمنع الحسابات غير الضرورية أثناء عمليات إعادة التركيب. bioWarning يستخدم remember مع المفتاح bio — هذه عملية حسابية مكلفة (اختيارية، للتوضيح) يتم إعادة حسابها فقط عندما يتغير bio، وليس عند كل إعادة تركيب.
الحالات المشتقة هي قيم يتم حسابها من كائنات State أخرى. بدلاً من حسابها عند كل إعادة تركيب وإهدار وحدة المعالجة المركزية على نتائج متطابقة، remember مع derivedStateOf يحسب القيمة فقط عندما تتغير المصادر. هذا مفيد بشكل خاص للتصفية والفرز وتجميع البيانات.
remember مع المفاتيح (remember(key) { calculation }) هي آلية تحسين أخرى. إذا لم يتغير المفتاح منذ آخر إعادة تركيب، لا يتم تنفيذ كتلة الحساب ويتم إرجاع القيمة المخزنة مؤقتًا. هذا مناسب لتخزين الكائنات التي يكون إنشاؤها مكلفًا: تنسيق التواريخ، تحليل JSON، إنشاء مجموعات كبيرة غير قابلة للتغيير.
@Composable
fun SearchResults(allItems: List<Item>, query: String) {
// derivedStateOf: إعادة حساب الفلتر فقط عند تغير المدخلات
val filtered = remember {
derivedStateOf {
allItems.filter { it.title.contains(query, true) }
}
}
// remember بمفتاح: إحصائيات منسقة يُعاد حسابها فقط عند تغير الاستعلام
val statsText = remember(query) {
"Results for query \"$query\": ${filtered.value.size}"
}
Text(statsText)
LazyColumn {
items(filtered.value, key = { it.id }) { item ->
Text(item.title)
}
}
}
filtered هو derivedStateOf يعيد الحساب تلقائيًا عندما يتغير allItems أو query. statsText يستخدم remember(query) — تنسيق السلسلة المكلف يتم فقط عندما يتغير استعلام البحث. الجمع بين derivedStateOf و remember مع المفاتيح يعطي أقصى أداء: يتم حساب الحالة المشتقة فقط عند الحاجة، ويتم تخزين الكائنات المعقدة مؤقتًا حتى يتغير المفتاح.
الأسئلة الشائعة
لا، remember هي دالة من حزمة compose.runtime تتطلب CompositionContext. يمكن استدعاؤها فقط داخل دالة @Composable أو داخل دالة أخرى تم استدعاؤها من Composable. لتخزين البيانات خارج التركيب، استخدم ViewModel.
بدون مفتاح، remember ينفذ كتلة الحساب مرة واحدة فقط — أثناء التركيب الأول. جميع عمليات إعادة التركيب اللاحقة تعيد القيمة المحفوظة. إذا كنت بحاجة إلى إعادة حساب القيمة عند تغير البيانات، تأكد من تحديدها كمفتاح: remember(data) { compute(data) }.
لا توجد طريقة مباشرة لإعادة تعيين remember. الطريقة الوحيدة هي إزالة الدالة من التركيب (على سبيل المثال، إخفاؤها بشرط if) ثم إظهارها مرة أخرى. عند إعادة الدخول، يتم تنفيذ كتلة remember مرة أخرى، مما ينشئ قيمة أولية جديدة.
remember يخزن الحالة في فتحة دالة Composable ويعيش طالما أن الدالة في التركيب. ViewModel يعيش طالما يعيش دورة حياة الشاشة. ViewModel يتم حفظه عند التدوير ويستخدم للمنطق التجاري. remember مخصص لحالة UI المحلية التي لا تحتاج إلى خارج دالة واحدة.
نعم، remember يعمل بشكل صحيح في دوال @Preview Composable لأن Preview ينشئ CompositionContext كامل. ومع ذلك، قد لا يعمل rememberSaveable بشكل صحيح في Preview لأن SavedStateHandle قد يكون غائبًا في بيئة المعاينة.
الملخص
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.