mutableStateOf تابعی در Jetpack Compose است که یک ظرف حالت قابل مشاهده متغیر ایجاد میکند. وقتی مقدار داخل این ظرف تغییر میکند، Compose به طور خودکار بازترکیب تمام اجزایی که این حالت را میخوانند آغاز میکند. بدون mutableStateOf، UI نمیتوانست هنگام تغییرات دادهها به صورت واکنشی بهروزرسانی شود. به گفته Google Android Developers، 2026، mutableStateOf بلوک ساختمانی اصلی برای حالت محلی در 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 انجام میشود که حالتهای میانی را در تغییرات آبشاری حذف میکند.
Parameter policy — دومین آرگومان mutableStateOf، که رفتار هنگام مقایسه را تعیین میکند. structuralEquality() equals() را بررسی میکند — این رفتار پیشفرض است. referentialEquality() === (برابری ارجاعی) را بررسی میکند. neverEqual() هر انتساب را تغییر تلقی میکند. انتخاب policy بر این تأثیر میگذارد که آیا بازترکیب هنگام انتساب مقدار یکسان آغاز میشود یا خیر.
سادهترین راه برای اعلان حالت قابل مشاهده استفاده از mutableStateOf با remember است. بدون remember در هر بازترکیب یک State جدید ایجاد میشد و تمام تغییرات قبلی از دست میرفت. remember تضمین میکند که همان MutableState در یک سری بازترکیب باقی میماند تا زمانی که تابع Composable در ترکیب باقی بماند.
@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.value | count.value = n |
| با تفویض | var count by mutableStateOf(0) | count | count = n |
مکانیزم ویژگیهای تفویض شده 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 Delegation شکر نحوی است که کد را سادهتر میکند اما مکانیک را تغییر نمیدهد. کامپایلر Kotlin var x by mutableStateOf(0) را به getter/setter تبدیل میکند که mutableStateOf.getValue() و mutableStateOf.setValue() را فراخوانی میکنند. در بایت کد تولید شده، تفاوتی بین val و var با by وجود ندارد — هر دو از طریق همان ظرف MutableState کار میکنند.
// تفویض سفارشی برای State Compose
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 مکانیزم Compose Runtime است که سازگاری خواندن State را در تغییرات همزمان تضمین میکند. وقتی تابع Composable mutableStateOf را میخواند، snapshot مقدار فعلی را ثبت میکند. اگر در طول ترکیب تغییر دیگری در همان State بنویسد، snapshot نوشتن را میبیند اما اجازه خواندن دادههای ناسازگار را نمیدهد — خواندن همیشه مقداری را برمیگرداند که در لحظه شروع snapshot بهروز است.
هنگام فراخوانی setter mutableStateOf.value = newValue، Compose Runtime بلافاصله بازترکیب را آغاز نمیکند. در عوض، تغییر در snapshot فعلی ثبت میشود. وقتی snapshot اعمال میشود (در مرز فریم)، Compose از لیست Stateهای تغییر یافته عبور میکند و اجزای خواننده را به عنوان Invalid علامتگذاری میکند. فقط در فریم بعدی بازترکیب آغاز میشود. این تضمین میکند که UI در تغییرات آبشاری دهها بار دوباره ترسیم نشود.
Snapshotهای سراسری و محلی: به طور پیشفرض mutableStateOf در snapshot سراسری کار میکند که به طور خودکار اعمال میشود. میتوان یک snapshot محلی از طریق Snapshot.takeSnapshot() برای خواندن ایزوله بدون عوارض جانبی ایجاد کرد. این در داخل Modifier استفاده میشود، جایی که نیاز به خواندن State است اما نیازی به اشتراک در تغییرات نیست. این رویکرد عملکرد را بهینه میکند و از بازترکیبهای غیرمنتظره جلوگیری میکند.
یک سناریوی واقعی را در نظر بگیرید — فرم ورود با سه فیلد: ایمیل، رمز عبور و وضعیت بارگذاری. هر سه فیلد از mutableStateOf استفاده میکنند، اما با policy و تودرتویی متفاوت. ایمیل از تفویض استفاده میکند، رمز عبور — دسترسی مستقیم.
data class LoginState(
val email: String = "",
val password: String = "",
val isLoading: Boolean = false,
val error: String? = null
)
@Composable
fun LoginForm(onLogin: (String, String) -> Unit) {
// State واحد برای فرم، 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 و policy referentialEquality استفاده شده است. این بدان معناست که بازترکیب فقط هنگام انتساب یک نمونه جدید LoginState از طریق copy() آغاز میشود. isValid بر اساس formState محاسبه میشود و فقط هنگام تغییر آن دوباره محاسبه میشود. این رویکرد کنترل دقیقی بر بازترکیبها میدهد: هر فیلد فرم فقط با ایجاد یک کپی جدید تغییر میکند.
سوالات متداول
mutableStateOf یک ظرف مخصوص Compose است که در داخل snapshotها کار میکند. StateFlow از kotlinx.coroutines.flow است که به Compose وابسته نیست. mutableStateOf به طور خودکار بازترکیب را آغاز میکند، StateFlow نیاز به collectAsState() دارد. برای حالت UI داخل Composable، mutableStateOf ترجیح داده میشود.
بله، mutableStateOf را میتوان خارج از تابع Composable فراخوانی کرد، اما ردیابی نخواهد شد. برای کارکرد واکنشپذیری در UI باید State را داخل Composable بخوانید. بسیاری از ViewModelها از MutableStateField (پوششی بر روی mutableStateOf) برای ارسال حالت به UI از طریق StateFlow استفاده میکنند.
Snapshot سیستم سازگاری را تضمین میکند: هر بازترکیب حالت سازگاری را در لحظه شروع snapshot میبیند. تغییرات از رشتههای مختلف به صورت اتمی در مرز فریم اعمال میشوند که race condition را هنگام خواندن در یک ترکیب حذف میکند.
یک مقدار جدید انتساب دهید: count.value = 0 (یا count = 0 در صورت تفویض). اگر نیاز به بازآفرینی کامل State است — از remember با کلید استفاده کنید: remember(key) { mutableStateOf(initial) } — با تغییر کلید State دوباره ایجاد میشود.
Composer از snapshotها استفاده میکند که تغییرات را گروهبندی میکنند: حتی با صد انتساب در یک فریم، بازترکیب فقط یک بار انجام میشود. برای بهروزرسانیهای بسیار مکرر (انیمیشنها) از Animatable یا animate*AsState استفاده کنید — آنها برای بهروزرسانی فریم بهینه شدهاند.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید