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(). Στον παραγόμενο bytecode, δεν υπάρχει διαφορά μεταξύ val και var με by — και τα δύο λειτουργούν μέσω του ίδιου δοχείου MutableState.
// Προσαρμοσμένη ανάθεση για 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 είναι ένας μηχανισμός του 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, αλλά δεν χρειάζεται εγγραφή στις αλλαγές. Αυτή η προσέγγιση βελτιστοποιεί την απόδοση και αποτρέπει απροσδόκητες ανασυνθέσεις.
Ας εξετάσουμε ένα πραγματικό σενάριο — μια φόρμα σύνδεσης με τρία πεδία: email, κωδικός πρόσβασης και κατάσταση φόρτωσης. Και τα τρία πεδία χρησιμοποιούν mutableStateOf, αλλά με διαφορετικό policy και διαφορετική ένθεση. Το email χρησιμοποιεί ανάθεση, ο κωδικός πρόσβασης — άμεση πρόσβαση.
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("Email") }
)
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. Οι αλλαγές από διαφορετικά νήματα εφαρμόζονται ατομικά στο όριο του καρέ, εξαλείφοντας τη συνθήκη ανταγωνισμού κατά την ανάγνωση εντός μιας σύνθεσης.
Αναθέστε μια νέα τιμή: count.value = 0 (ή count = 0 με ανάθεση). Αν χρειάζεται πλήρης αναδημιουργία του State — χρησιμοποιήστε remember με κλειδί: remember(key) { mutableStateOf(initial) } — με την αλλαγή του κλειδιού, το State θα δημιουργηθεί ξανά.
Ο Composer χρησιμοποιεί snapshot που ομαδοποιούν τις αλλαγές: ακόμα και με εκατό αναθέσεις σε ένα καρέ, η ανασύνθεση εκτελείται μόνο μία φορά. Για πολύ συχνές ενημερώσεις (κινήσεις), χρησιμοποιήστε Animatable ή animate*AsState — είναι βελτιστοποιημένα για ενημερώσεις καρέ.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης