mutableStateOf: إنشاء حالة قابلة للملاحظة والتفاعلية في Compose

المؤلف: IT Sectr نُشر: 2026-06-28 وقت القراءة: 7 دق

mutableStateOf هي دالة في Jetpack Compose تنشئ حاوية حالة قابلة للتغيير والملاحظة. عندما تتغير القيمة داخل هذه الحاوية، يقوم Compose تلقائياً بتشغيل إعادة التركيب لجميع المكونات التي تقرأ هذه الحالة. بدون mutableStateOf، لم تكن واجهة المستخدم قادرة على التحديث بشكل تفاعلي عند تغير البيانات. وفقاً لـ Google Android Developers, 2026، mutableStateOf هو اللبنة الأساسية للحالة المحلية في Compose.

النقاط الرئيسية

  • mutableStateOf تنشئ حاوية MutableState يتتبعها Compose Runtime
  • إعادة التركيب تُشغل تلقائياً عند تغيير value لكائن State هذا
  • التفويض عبر var يسمح باستخدام mutableStateOf دون الوصول إلى .value
  • المفاتيح في remember(mutableStateOf) ليست ضرورية — State نفسه يخطر Compose بالتغييرات
  • نظام Snapshot يضمن اتساق القراءة في البيئات متعددة الخيوط

ما هو mutableStateOf في Jetpack Compose

mutableStateOf هي دالة من حزمة compose.runtime تنشئ كائن MutableState<T> يخزّن قيمة ويمكنه إخطار Compose Runtime بالتغييرات. التوقيع: fun <T> mutableStateOf(value: T, policy: SnapshotMutationPolicy<T> = structuralEquality()): MutableState<T>. تحدد المعلمة policy متى يعتبر التغيير مهماً — عند المساواة الهيكلية، المساواة المرجعية، أو أبداً.

MutableState هي واجهة ذات خاصية واحدة value: getter للقراءة و setter للكتابة. عند استدعاء setter، يسجل Compose Runtime التغيير في snapshot ويضع علامة على جميع دوال Composable التي تقرأ متغير State هذا بحاجة إلى إعادة التركيب. تحدث هذه العملية بشكل متزامن ضمن دورة snapshot واحدة، مما يلغي الحالات الوسيطة أثناء التغييرات المتتالية.

المعلمة policy — الوسيطة الثانية لـ mutableStateOf، تحدد سلوك المقارنة. structuralEquality() تتحقق من equals() — هذا هو السلوك الافتراضي. referentialEquality() تتحقق من === (المساواة المرجعية). neverEqual() تعتبر كل تعيين تغييراً. يؤثر اختيار policy على ما إذا كانت إعادة التركيب ستُشغل عند تعيين نفس القيمة.

بناء الجملة وطرق تعريف mutableStateOf

أبسط طريقة لتعريف حالة قابلة للملاحظة هي استخدام mutableStateOf مع remember. بدون remember، كل إعادة تركيب كانت ستنشئ State جديداً، وجميع التغييرات السابقة كانت ستضيع. يضمن remember أن نفس MutableState يستمر عبر سلسلة من إعادة التركيب طالما بقيت دالة Composable في التركيب.

kotlin
@Composable
fun Counter() {
    // بدون تفويض: قراءة/كتابة عبر .value
    val count = remember { mutableStateOf(0) }
    Button(onClick = { count.value++ }) {
        Text("Count: ${count.value}")
    }
}

@Composable
fun CounterDelegated() {
    // مع التفويض: var + by = Property Delegation
    var count by remember { mutableStateOf(0) }
    Button(onClick = { count++ }) {
        Text("Count: $count")
    }
}

الفرق بين النهجين هو نحوي. Property Delegation (by) يستخدم اتفاقية Kotlin: يُنشئ المترجم استدعاءات getValue() و setValue() للقراءة والكتابة. هذا مكافئ للوصول المباشر إلى count.value لكن يبدو كعمل مع متغير عادي. كلا النهجين متطابقان وظيفياً: Compose يتتبع القراءة في getter والكتابة في setter بغض النظر عن الشكل.

الشكلالكودالقراءةالكتابة
بدون تفويضval count = mutableStateOf(0)count.valuecount.value = n
مع تفويضvar count by mutableStateOf(0)countcount = n

الخصائص المفوضة و var

آلية الخصائص المفوضة في Kotlin ليست ميزة خاصة بـ Compose بل قدرة مدمجة في اللغة. يمكن لأي فئة تنفيذ عاملي getValue(thisRef, property) و setValue(thisRef, property, value)، وبعد ذلك يمكن استخدام مثيلها مع الكلمة المفتاحية by. يعمل MutableState تماماً هكذا: getValue يُرجع القيمة الحالية، و setValue يعين قيمة جديدة.

تمييز مهم: val مقابل var. يمكن تعيين mutableStateOf لكل من val و var. مع val (val count = mutableStateOf(0))، كائن MutableState نفسه غير قابل للتغيير، لكن خاصيته value يمكن تغييرها. مع var (var count by mutableStateOf(0))، يخلق التفويض وهم العمل مع قيمة بدائية، لكن setter في الواقع يستدعي setValue على MutableState. الاختيار بين val و var هو اختيار بين الوصول الصريح والضمني إلى .value.

تفويض State هو سكر نحوي يبسط الكود لكنه لا يغير الآلية. يترجم مترجم Kotlin var x by mutableStateOf(0) إلى getter/setter يستدعيان mutableStateOf.getValue() و mutableStateOf.setValue(). في bytecode المولد، لا يوجد فرق بين val و var مع by — كلاهما يعمل عبر نفس حاوية MutableState.

kotlin
    // مفوض مخصص لـ Compose State
class ValidatedState<T>(initialValue: T) {
    private val state = mutableStateOf(initialValue)

    operator fun getValue(thisRef: Any?, property: KProperty<*>) = state.value

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        if (value != state.value) {
            state.value = value
        }
    }
}

@Composable
fun Test() {
    var text by remember { ValidatedState("") }
}

نظام Snapshot: كيف يعمل mutableStateOf داخلياً

Snapshot هي آلية من Compose Runtime تضمن اتساق قراءة State أثناء التغييرات المتوازية. عندما تقرأ دالة Composable mutableStateOf، يسجل snapshot القيمة الحالية. إذا كتب تغيير آخر في نفس State أثناء التركيب، يرى snapshot الكتابة لكنه لا يسمح بقراءة بيانات غير متسقة — القراءة دائماً تُرجع القيمة الصالحة في بداية snapshot.

عند استدعاء setter mutableStateOf.value = newValue، لا يقوم Compose Runtime بتشغيل إعادة التركيب فوراً. بدلاً من ذلك، يسجل التغيير في snapshot الحالي. عندما يُطبق snapshot (عند حدود الإطار)، يجتاز Compose قائمة States المتغيرة ويضع علامة على المكونات القارئة كـ Invalid. فقط في الإطار التالي تبدأ إعادة التركيب. هذا يضمن عدم إعادة رسم واجهة المستخدم عشرات المرات أثناء التغييرات المتتالية.

Snapshots عامة ومحلية: افتراضياً، يعمل mutableStateOf في snapshot العام الذي يُطبق تلقائياً. يمكنك إنشاء snapshot محلي عبر Snapshot.takeSnapshot() للقراءة المعزولة دون آثار جانبية. يُستخدم هذا داخل Modifier عندما تحتاج لقراءة State دون الاشتراك في التغييرات. هذا النهج يحسن الأداء ويمنع إعادة التركيب غير المتوقعة.

أمثلة على استخدام mutableStateOf

لنفكر في سيناريو حقيقي — نموذج تسجيل دخول بثلاثة حقول: البريد الإلكتروني، كلمة المرور، وحالة التحميل. جميع الحقول الثلاثة تستخدم mutableStateOf، لكن بسياسات policy مختلفة ومستويات تداخل مختلفة. email يستخدم التفويض، password يستخدم الوصول المباشر.

kotlin
data class LoginState(
    val email: String = "",
    val password: String = "",
    val isLoading: Boolean = false,
    val error: String? = null
)

@Composable
fun LoginForm(onLogin: (String, String) -> Unit) {
    // حالة واحدة للنموذج، policy = referentialEquality
    var formState by remember {
        mutableStateOf(LoginState(), SnapshotMutationPolicy.referentialEquality())
    }

    val isValid = remember(formState) {
        formState.email.contains("@") && formState.password.length() >= 6
    }

    Column(modifier = Modifier.padding(16.dp)) {
        OutlinedTextField(
            value = formState.email,
            onValueChange = { formState = formState.copy(email = it) },
            label = { Text("البريد الإلكتروني") }
        )
        OutlinedTextField(
            value = formState.password,
            onValueChange = { formState = formState.copy(password = it) },
            label = { Text("كلمة المرور") },
            visualTransformation = PasswordVisualTransformation()
        )
        Button(
            onClick = { onLogin(formState.email, formState.password) },
            enabled = isValid
        ) {
            Text("تسجيل الدخول")
        }
    }
}

في هذا المثال، يُستخدم mutableStateOf مع فئة بيانات مخصصة LoginState وسياسة referentialEquality. هذا يعني أن إعادة التركيب ستُشغل فقط عند تعيين مثيل جديد من LoginState عبر copy(). isValid يُحسب بناءً على formState ويُعاد حسابه فقط عندما يتغير. هذا النهج يوفر تحكماً واضحاً في إعادة التركيب: كل حقل في النموذج يتغير فقط من خلال إنشاء نسخة جديدة.

الأسئلة الشائعة

ما الفرق بين mutableStateOf و StateFlow؟

mutableStateOf هي حاوية خاصة بـ Compose تعمل داخل snapshots. StateFlow من kotlinx.coroutines.flow وهي غير مرتبطة بـ Compose. mutableStateOf تُشغل إعادة التركيب تلقائياً، بينما StateFlow تتطلب collectAsState(). لحالة واجهة المستخدم داخل Composable، يُفضل mutableStateOf.

هل يمكن استخدام mutableStateOf خارج دوال @Composable؟

نعم، يمكن استدعاء mutableStateOf خارج دوال Composable، لكنه لن يتم تتبعه. للتفاعلية في واجهة المستخدم، يجب قراءة State داخل Composable. العديد من ViewModel تستخدم MutableStateField (غلاف حول mutableStateOf) لتمرير الحالة إلى واجهة المستخدم عبر StateFlow.

ماذا يحدث عندما يتغير State في وقت واحد من خيطين؟

نظام Snapshot يضمن الاتساق: كل إعادة تركيب ترى حالة متسقة في بداية snapshot. يتم تطبيق التغييرات من خيوط مختلفة بشكل ذري عند حدود الإطار، مما يلغي حالات السباق أثناء القراءات ضمن تركيب واحد.

كيف إعادة تعيين mutableStateOf إلى قيمته الأولية؟

قم بتعيين قيمة جديدة: count.value = 0 (أو count = 0 مع التفويض). إذا كنت بحاجة إلى إعادة إنشاء State بالكامل، استخدم remember مع مفتاح: remember(key) { mutableStateOf(initial) } — عندما يتغير المفتاح، سيتم إنشاء State من جديد.

هل يؤثر mutableStateOf على الأداء مع التغييرات المتكررة؟

يستخدم Composer snapshots التي تجمع التغييرات: حتى مع مئات التعيينات في إطار واحد، يتم تنفيذ إعادة التركيب مرة واحدة فقط. للتحديثات المتكررة جداً (الرسوم المتحركة)، استخدم Animatable أو animate*AsState — فهي محسنة للتحديثات إطاراً بإطار.

الخلاصة

  • mutableStateOf تنشئ حاوية MutableState قابلة للملاحظة يتتبعها Compose Runtime
  • التفويض عبر by يبسط الكود لكنه لا يغير آلية عمل State
  • نظام Snapshot يضمن اتساق القراءة ويمنع إعادة التركيب غير الضرورية
  • Policy تحدد متى يعتبر التغيير مهماً — structuralEquality, referentialEquality, neverEqual
  • remember إلزامي للحفاظ على State بين إعادة التركيب داخل Composable
  • توصية: استخدم mutableStateOf مع التفويض لحالة واجهة المستخدم و referentialEquality لفئات البيانات
  • تجنب: إنشاء mutableStateOf بدون remember — كل تعيين سينشئ كائناً جديداً

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا