State — το κεντρικό αντικείμενο διαχείρισης δεδομένων στο Flutter, συνδεδεμένο με το StatefulWidget και υπεύθυνο για την αποθήκευση μεταβαλλόμενων πληροφοριών και τη δημιουργία του περιβάλλοντος διεπαφής. Σύμφωνα με την επίσημη τεκμηρίωση του Flutter (Flutter.dev, 2026), το State υπάρχει καθ' όλη τη διάρκεια ζωής του widget και επιβιώνει από τις ανακατασκευές του, διασφαλίζοντας τη συνέπεια των δεδομένων μεταξύ των ενημερώσεων του UI. Σε αντίθεση με το ίδιο το widget, το State μπορεί να αλλάζει τα πεδία του και να προκαλεί ανακατασκευή μέσω της κλήσης setState.
Κύρια σημεία
State — είναι ένα αντικείμενο στην αρχιτεκτονική Flutter που αποθηκεύει μεταβαλλόμενα δεδομένα του StatefulWidget και καθορίζει πώς αυτά τα δεδομένα εμφανίζονται στη διεπαφή. Κάθε StatefulWidget κατά την ενσωμάτωση στο δέντρο δημιουργεί ακριβώς ένα αντικείμενο State μέσω της μεθόδου createState. Το State υπάρχει ανεξάρτητα από το widget: εάν ο γονέας ανακατασκευάσει το StatefulWidget με νέες παραμέτρους, το State παραμένει το ίδιο και λαμβάνει το ενημερωμένο widget μέσω της ιδιότητας widget.
Σύμφωνα με το Flutter Architectural Overview (Google, 2026), ο διαχωρισμός Widget και State είναι μια συνειδητή αρχιτεκτονική απόφαση που επιτρέπει στο framework να επαναχρησιμοποιεί στοιχεία του δέντρου. Το widget (ελαφριά περιγραφή) μπορεί να δημιουργηθεί και να καταστραφεί πολλές φορές, αλλά το State (βαρύ αντικείμενο με δεδομένα) παραμένει στη μνήμη όσο το στοιχείο βρίσκεται στο δέντρο. Αυτό αποτρέπει την απώλεια δεδομένων κατά τις συχνές ανακατασκευές των γονικών widget.
Το State υλοποιεί τη διεπαφή StatefulWidget μέσω generics: class _MyState extends State<MyWidget>. Το generic συνδέει το State με συγκεκριμένο τύπο StatefulWidget, διασφαλίζοντας type-safe πρόσβαση στα πεδία του μέσω της ιδιότητας widget.
Το αντικείμενο State αποθηκεύεται στο StatefulElement — το ενδιάμεσο επίπεδο μεταξύ Widget και RenderObject. Το StatefulElement δημιουργεί το State μέσω createState, διατηρεί αναφορά σε αυτό και μεταβιβάζει το State ως κάτοχο. Το Element καταστρέφεται μόνο όταν το widget αφαιρεθεί από το δέντρο — μέχρι τότε το State ζει στη μνήμη.
Ο κύκλος ζωής του State είναι ντετερμινιστικός και αποτελείται από μια αυστηρή ακολουθία κλήσεων. Η κατανόηση αυτής της ακολουθίας είναι το θεμέλιο για τη σωστή διαχείριση πόρων και την πρόληψη διαρροών μνήμης.
Το initState καλείται πρώτο κατά τη δημιουργία του State. Σε αυτή τη μέθοδο αρχικοποιούνται οι ελεγκτές, οι συνδρομές σε ροές δεδομένων, τα χρονόμετρα και οι αρχικές τιμές πεδίων. Είναι υποχρεωτική η κλήση super.initState() στην πρώτη γραμμή. Στο στάδιο initState το δέντρο widget δεν έχει ακόμα πλήρως τοποθετηθεί, επομένως μέθοδοι όπως MediaQuery.of(context) μπορεί να λειτουργήσουν εσφαλμένα.
Το didChangeDependencies καλείται μετά το initState και σε κάθε αλλαγή εξαρτήσεων InheritedWidget. Εδώ, και όχι στο initState, πρέπει να καλείτε MediaQuery.of(context) ή Theme.of(context), καθώς σε αυτό το σημείο το δέντρο έχει ήδη τοποθετηθεί. Αυτή η μέθοδος καλείται επίσης εάν το widget μεταφερθεί σε άλλο πλαίσιο όπου το InheritedWidget παρέχει διαφορετικές τιμές.
Το build είναι η κύρια μέθοδος του State που επιστρέφει το δέντρο widget. Καλείται μετά το initState, μετά το didChangeDependencies και μετά από κάθε setState. Η μέθοδος build δεν πρέπει να έχει παρενέργειες — περιγράφει μόνο τη διεπαφή βάσει των τρεχουσών τιμών των πεδίων του State.
Το didUpdateWidget καλείται όταν ο γονέας ανακατασκευάζει το StatefulWidget με νέες παραμέτρους. Το State έχει πρόσβαση στο παλιό widget μέσω oldWidget και μπορεί να το συγκρίνει με το νέο. Εάν οι παράμετροι άλλαξαν, μπορείτε να ενημερώσετε την κατάσταση, να φορτώσετε νέα δεδομένα ή να επανεκκινήσετε ένα animation.
Το dispose είναι η τελική μέθοδος στην οποία απελευθερώνονται όλοι οι πόροι: ελεγκτές, συνδρομές, χρονόμετρα. Μετά το dispose, το State χαρακτηρίζεται ως νεκρό: το mounted επιστρέφει false, η κλήση setState προκαλεί εξαίρεση. Είναι υποχρεωτική η κλήση super.dispose() στην τελευταία γραμμή της μεθόδου.
| Μέθοδος | Πότε καλείται | Υποχρεωτικό super |
|---|---|---|
| initState | Κατά τη δημιουργία State | Ναι, στην πρώτη γραμμή |
| didChangeDependencies | Μετά το initState και σε αλλαγή InheritedWidget | Ναι |
| build | Μετά από initState, didChangeDependencies, setState | Όχι |
| didUpdateWidget | Με νέο widget από τον γονέα | Ναι |
| setState | Με κλήση από τον προγραμματιστή | Όχι |
| dispose | Κατά την αφαίρεση από το δέντρο | Ναι, στην τελευταία γραμμή |
Ο μηχανισμός λειτουργίας του State βασίζεται σε τρεις βασικές αρχές: συσχέτιση με το Element, αντιδραστικότητα μέσω setState και πρόσβαση στον γονέα μέσω της ιδιότητας widget. Όταν το Flutter χτίζει το δέντρο στοιχείων και συναντήσει StatefulElement, καλεί το createState του συνδεδεμένου widget. Το δημιουργημένο State αποθηκεύεται στο στοιχείο και υπάρχει μέχρι το στοιχείο να αφαιρεθεί.
Κατά την κλήση setState, το State επισημαίνεται ως «βρώμικο» (dirty) και προγραμματίζει ανακατασκευή για το επόμενο καρέ. Σημαντικό: το setState δεν καλεί το build άμεσα — απλώς καταγράφει την ανάγκη ανακατασκευής. Το Flutter συλλέγει όλα τα dirty-στοιχεία του τρέχοντος καρέ και τα ανακατασκευάζει μαζικά, βελτιστοποιώντας την απόδοση. Μετά την κλήση build, το State επιστρέφει σε κατάσταση «καθαρό» (clean).
Η ιδιότητα widget επιτρέπει στο State να διαβάζει παραμέτρους που μεταβιβάστηκαν στον κατασκευαστή του StatefulWidget. Επειδή το StatefulWidget είναι αμετάβλητο (όπως το StatelessWidget), τα πεδία του δεν αλλάζουν — όταν αλλάζουν οι παράμετροι, ο γονέας δημιουργεί νέο widget και το State το λαμβάνει μέσω didUpdateWidget. Αυτό εγγυάται ότι το State λειτουργεί πάντα με τα τρέχοντα δεδομένα του γονέα.
Βασικό παράδειγμα State με πεδίο που αλλάζει μέσω χρονομέτρου. Παρουσιάζει initState, setState και dispose:
class _TimerWidgetState extends State<TimerWidget> {
int _seconds = 0;
Timer? _timer;
@override
void initState() {
super.initState();
_timer = Timer.periodic(
const Duration(seconds: 1),
(_) => setState(() => _seconds++),
);
}
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Text('$_seconds δευτερόλεπτα πέρασαν');
}
}
Παράδειγμα χρήσης της ιδιότητας widget για πρόσβαση σε παραμέτρους του γονέα και αντίδραση στις αλλαγές τους μέσω didUpdateWidget:
class _GreetingState extends State<GreetingWidget> {
String _displayName = '';
@override
void initState() {
super.initState();
_displayName = _formatName(widget.name);
}
@override
void didUpdateWidget(GreetingWidget oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.name != oldWidget.name) {
setState(() {
_displayName = _formatName(widget.name);
});
}
}
String _formatName(String name) => name.trim().isEmpty ? 'Guest' : name;
@override
Widget build(BuildContext context) {
return Text('Γεια σου, $_displayName!');
}
}
Στο δεύτερο παράδειγμα, το State παρακολουθεί την αλλαγή της παραμέτρου εισόδου name και αναμορφώνει την εμφάνιση μόνο σε πραγματική αλλαγή. Χωρίς τον έλεγχο widget.name != oldWidget.name, η μέθοδος θα καλούνταν σε κάθε ανακατασκευή του γονέα, ακόμα κι αν το όνομα δεν άλλαξε — περιττή εργασία για το framework.
Το State και το StatefulWidget είναι δύο διαφορετικές κλάσεις στην αρχιτεκτονική Flutter, που εκτελούν διαφορετικούς ρόλους. Το StatefulWidget είναι ένα ελαφρύ αμετάβλητο περιτύλιγμα που περιγράφει τη διαμόρφωση του widget και δημιουργεί το State. Το State είναι ένα βαρύ αντικείμενο που αποθηκεύει μεταβαλλόμενα δεδομένα, διαχειρίζεται συνδρομές και χτίζει το UI. Αυτός ο διαχωρισμός επιτρέπει στο Flutter να καταστρέφει και να δημιουργεί widget χωρίς απώλεια κατάστασης.
Όλα τα πεδία του StatefulWidget πρέπει να είναι final και να ορίζονται στον κατασκευαστή — δεν αλλάζουν μετά τη δημιουργία. Το State, αντίθετα, μπορεί να αλλάζει τα πεδία του ανά πάσα στιγμή, αλλά όλες οι αλλαγές πρέπει να προηγούνται από κλήση setState, ώστε το Flutter να ενημερωθεί για την ανάγκη ανακατασκευής. Αυτή είναι η βασική διαφορά: StatefulWidget — «τι να δείξω», State — «πώς να το δείξω και ποια δεδομένα να χρησιμοποιήσω».
Σύμφωνα με την ανάλυση πηγαίου κώδικα Flutter (Flutter SDK, 2026), το StatefulWidget περιέχει μόνο ένα υποχρεωτικό πεδίο — createState, ενώ το State έχει πρόσβαση στο BuildContext, μπορεί να εγγραφεί σε ροές, να διαχειρίζεται animations και ελεγκτές. Συνιστάται να διατηρείτε το StatefulWidget όσο το δυνατόν πιο απλό, μεταφέροντας όλη τη λογική στο State.
Ο διαχωρισμός Widget και State είναι μια αρχιτεκτονική απόφαση που διασφαλίζει την αμεταβλητότητα της διαμόρφωσης. Εάν το StatefulWidget αποθήκευε το ίδιο την κατάσταση, σε κάθε ανακατασκευή του γονέα η κατάσταση θα χανόταν. Μεταφέροντας την κατάσταση σε ξεχωριστό αντικείμενο, το Flutter εγγυάται ότι τα δεδομένα επιβιώνουν από ανακατασκευές, ενώ τα widget παραμένουν ελαφριά και συγκρίσιμα.
Το αντικείμενο State είναι απομονωμένο — δεν έχει άμεση πρόσβαση στο State άλλων widget. Για ανταλλαγή δεδομένων μεταξύ widget χρησιμοποιούνται InheritedWidget ή εξωτερικά εργαλεία διαχείρισης κατάστασης: Provider, Riverpod, Bloc, Redux. Κάθε προσέγγιση λύνει το πρόβλημα με διαφορετικό τρόπο: το InheritedWidget λειτουργεί μέσω του δέντρου widget, το Provider μέσω DI-container, το Bloc μέσω ροών συμβάντων.
Η επιλογή εργαλείου εξαρτάται από την κλίμακα του έργου. Για μια μικρή εφαρμογή, αρκούν τα InheritedWidget και το τοπικό State. Για μεσαίο και μεγάλο έργο, συνιστώνται Riverpod ή Bloc — παρέχουν ελεγξιμότητα, προβλεψιμότητα και διαχωρισμό λογικής από UI. Το State χρησιμοποιείται τότε μόνο για τοπικά δεδομένα του widget (focus, scroll, animation).
Σύμφωνα με το Flutter Community Survey 2025 (Flutter Foundation, Δεκέμβριος 2025), το Riverpod είναι η πιο δημοφιλής λύση διαχείρισης κατάστασης σε νέα έργα (38%), ακολουθούμενο από Bloc (31%) και Provider (22%). Και τα τρία εργαλεία είναι συμβατά με το State και δεν απαιτούν εγκατάλειψη του τυπικού κύκλου ζωής.
Πρώτο λάθος — να ξεχάσετε να ελέγξετε το mounted πριν από setState σε ασύγχρονο callback. Όταν το widget έχει αφαιρεθεί από το δέντρο (π.χ. ο χρήστης έφυγε από την οθόνη), αλλά η ασύγχρονη λειτουργία (HTTP-αίτημα) εκτελείται ακόμα, μετά την ολοκλήρωσή της το State είναι ήδη νεκρό. Η κλήση setState σε νεκρό State προκαλεί εξαίρεση. Ο έλεγχος if (mounted) setState(...) λύνει το πρόβλημα.
Δεύτερο λάθος — αρχικοποίηση εξαρτήσεων InheritedWidget στο initState αντί στο didChangeDependencies. Στο initState το πλαίσιο δεν έχει ακόμα τοποθετηθεί, επομένως MediaQuery.of(context) θα προκαλέσει εξαίρεση. Όλες οι εξαρτήσεις από InheritedWidget πρέπει να ρυθμίζονται στο didChangeDependencies ή στο build.
Τρίτο λάθος — μεταβολή πεδίων χωρίς κλήση setState. Εάν ο προγραμματιστής αλλάζει πεδίο του State χωρίς setState, το Flutter δεν θα ενημερωθεί για την αλλαγή και το UI δεν θα ενημερωθεί. Για παράδειγμα: _list.add(item) χωρίς επόμενο setState((){}) θα αλλάξει τη λίστα, αλλά η οθόνη θα παραμείνει ίδια.
Μοτίβο ασφαλείας για ασύγχρονες λειτουργίες σε State:
Future<void> _fetchData() async {
final data = await ApiService.fetch();
if (mounted) {
setState(() => _data = data);
}
}
Ο έλεγχος mounted διασφαλίζει ότι το setState καλείται μόνο για ζωντανό State, αποτρέποντας την εξαίρεση «setState called after dispose».
Συχνές ερωτήσεις
StatefulWidget — αμετάβλητη διαμόρφωση widget, ενώ State — μεταβαλλόμενο αντικείμενο που αποθηκεύει δεδομένα και διαχειρίζεται τον κύκλο ζωής. Το Widget μπορεί να αναδημιουργηθεί, το State — όχι. Το StatefulWidget δημιουργεί το State μέσω createState.
Ακριβώς ένα. Η μέθοδος createState καλείται μία φορά κατά την πρώτη ενσωμάτωση του StatefulWidget στο δέντρο. Ακόμα κι αν ο γονέας ανακατασκευάζεται πολλές φορές, το αντικείμενο State παραμένει το ίδιο, εφόσον δεν αλλάξει ο τύπος ή το Key του widget.
mounted — boolean σημαία που δείχνει αν το State βρίσκεται στο δέντρο widget. Μετά την κλήση dispose, το mounted γίνεται false. Χρησιμοποιείται για έλεγχο πριν από setState σε ασύγχρονα callbacks, ώστε να αποφευχθεί εξαίρεση.
Όχι. Το State είναι πάντα συνδεδεμένο με συγκεκριμένο StatefulWidget μέσω generic: State<T extends StatefulWidget>. Δεν είναι αρχιτεκτονικά δυνατό να δημιουργηθεί State απευθείας, χωρίς συσχέτιση με widget.
Θα προκληθεί εξαίρεση «setState called after dispose». Μετά την κλήση dispose, το State θεωρείται νεκρό και οποιαδήποτε προσπάθεια ανακατασκευής UI μέσω setState απαγορεύεται. Λύση — ελέγχετε mounted πριν από κάθε setState.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης