StatefulWidget — ένα widget Flutter με μεταβλητή κατάσταση, που επιτρέπει στο UI να αντιδρά σε ενέργειες χρήστη, ασύγχρονα συμβάντα και ροές δεδομένων. Σύμφωνα με την επίσημη τεκμηρίωση Flutter (Flutter.dev, 2026), το StatefulWidget χρησιμοποιείται για όλα τα διαδραστικά στοιχεία της εφαρμογής: φόρμες εισαγωγής, κινητικά, πεδία επιλογής, διακόπτες και οθόνες που φορτώνουν δεδομένα από το δίκτυο. Σε αντίθεση με το StatelessWidget, δημιουργεί ένα ξεχωριστό αντικείμενο State που διατηρείται σε όλο τον κύκλο ζωής και μπορεί να ανακατασκευαστεί χωρίς να επαναδημιουργηθεί το ίδιο το widget.
Κύρια σημεία
StatefulWidget — είναι μια κλάση Flutter που μπορεί να αλλάξει την κατάστασή της ως απάντηση σε ενέργειες χρήστη, συστημικά συμβάντα ή ασύγχρονες λειτουργίες. Σε αντίθεση με το StatelessWidget, το StatefulWidget δεν εμφανίζεται άμεσα — δημιουργεί ένα αντικείμενο State που είναι υπεύθυνο για την απόδοση. Η διαίρεση σε δύο κλάσεις (Widget και State) επιτρέπει στο Flutter να ανακατασκευάζει το UI χωρίς να επαναδημιουργεί το ίδιο το widget, πράγμα που παρέχει σημαντικό πλεονέκτημα απόδοσης σε συχνές ενημερώσεις.
Η αρχιτεκτονική του StatefulWidget ακολουθεί το μοτίβο «διαχωρισμός μεταβλητού και αμετάβλητου»: το ίδιο το widget παραμένει αμετάβλητο (όπως το StatelessWidget), και όλη η μεταβλητή κατάσταση αποθηκεύεται σε ένα ξεχωριστό αντικείμενο State. Αυτό επιτρέπει στο Flutter να επαναχρησιμοποιεί τα widgets συγκρίνοντάς τα ανά τύπο και Key, και ταυτόχρονα να διατηρεί την τρέχουσα κατάσταση μεταξύ ανακατασκευών.
Σύμφωνα με τη Google (Flutter Architectural Overview, 2026), το StatefulWidget είναι βέλτιστο για σενάρια όπου η κατάσταση αλλάζει περισσότερες από μία φορά κατά τη διάρκεια ζωής του widget: πεδία κειμένου, κινητικά, χρονομετρητές, ροές δεδομένων, ασύγχρονες φορτώσεις. Για εφάπαξ αρχικοποίηση, το StatelessWidget είναι αρκετό.
StatefulWidget είναι υποχρεωτικό όταν το widget πρέπει να αντιδρά σε εξωτερικά συμβάντα: πάτημα κουμπιού, ολοκλήρωση HTTP αιτήματος, ενημέρωση δεδομένων από βάση δεδομένων, συνδρομή σε WebSocket. Είναι επίσης απαραίτητο για widgets με κινητικά, πεδία κειμένου με controllers και στοιχεία που διαχειρίζονται το focus. Αν το widget μόνο εμφανίζει δεδομένα και δεν δημιουργεί συμβάντα — χρησιμοποιήστε StatelessWidget.
Το StatefulWidget αποτελείται από δύο κλάσεις: αυτό καθαυτό το StatefulWidget (ελαφρύ, αμετάβλητο) και το State (βαρύ, μεταβλητό). Το πλαίσιο δημιουργεί το State μέσω της μεθόδου createState(), που καλείται μία φορά κατά την ενσωμάτωση στο δέντρο. Το State λαμβάνει αναφορά στο widget μέσω της ιδιότητας widget και μπορεί να έχει πρόσβαση στα πεδία του σε οποιοδήποτε στιγμή του κύκλου ζωής.
Ο κύκλος ζωής του StatefulWidget αποτελείται από έξι κύρια στάδια, κάθε ένα από τα οποία παρέχει μια μέθοδο που μπορεί να παρακαμφθεί για την εκτέλεση συγκεκριμένων εργασιών. Η κατανόηση αυτών των σταδίων είναι κρίσιμης σημασίας για τη σωστή εργασία με πόρους και την αποφυγή διαρροών μνήμης.
createState — η πρώτη μέθοδος του κύκλου ζωής, που καλείται κατά την ενσωμάτωση του StatefulWidget στο δέντρο. Πρέπει να επιστρέφει μια νέα περίπτωση State που σχετίζεται με αυτό το widget. Αυτή η μέθοδος καλείται ακριβώς μία φορά κατά τη διάρκεια ζωής του στοιχείου. Είναι σημαντικό να μην εκτελούνται βαριές λειτουργίες εδώ — το createState πρέπει να είναι όσο το δυνατόν ελαφρύτερο.
initState — καλείται αμέσως μετά τη δημιουργία του State, πριν από την πρώτη κατασκευή UI. Εδώ εκτελούνται: αρχικοποίηση ελεγκτών (TextEditingController, AnimationController), εγγραφή σε ροές δεδομένων (StreamSubscription), ρύθμιση χρονομετρητών και αρχική αρχικοποίηση πεδίων. Σύμφωνα με τα έγγραφα Flutter (Flutter.dev, 2026), στο initState δεν μπορείτε να καλέσετε το BuildContext.of() — το δέντρο δεν είναι ακόμη πλήρως τοποθετημένο.
didChangeDependencies — καλείται μετά το initState και κάθε φορά που αλλάζουν οι εξαρτήσεις InheritedWidget. Αυτό είναι το κατάλληλο σημείο για να καλέσετε MediaQuery.of(context) ή να εγγραφείτε στο Theme — τιμές που μπορούν να αλλάξουν κατά τη λειτουργία της εφαρμογής. Αν το widget χρησιμοποιεί InheritedWidget, η λογική αρχικοποίησης πρέπει να είναι εδώ, όχι στο initState.
build — η κύρια μέθοδος που επιστρέφει το δέντρο widgets. Καλείται μετά το initState, μετά το didChangeDependencies και μετά από κάθε setState. didUpdateWidget καλείται όταν ο γονέας ανακατασκευάζει και μεταβιβάζει το StatefulWidget με νέες παραμέτρους. Εδώ μπορείτε να συγκρίνετε τα παλιά και νέα πεδία του widget και, αν χρειαστεί, να ενημερώσετε την κατάσταση.
dispose — το τελικό στάδιο του κύκλου ζωής. Εδώ απελευθερώνονται όλοι οι πόροι: ακύρωση εγγραφών από ροές, διαγραφή ελεγκτών, ακύρωση χρονομετρητών. Η μη κλήση του dispose οδηγεί σε διαρροές μνήμης. Μετά το dispose, το State θεωρείται νεκρό — η κλήση του setState μέσα σε αυτό πετάει εξαίρεση.
Ο μηχανισμός λειτουργίας του StatefulWidget βασίζεται στη συντονισμένη εργασία τριών οντοτήτων: Widget (ελαφρύς περιγραφή), Element (ενδιάμεσο στρώμα) και State (αποθήκη δεδομένων). Όταν το Flutter συναντά ένα StatefulWidget στην περιγραφή, δημιουργεί ένα StatefulElement που καλεί το createState και αποθηκεύει την αναφορά στο αντικείμενο State. Κατά την ανακατασκευή του γονέα, το Flutter συγκρίνει το νέο widget με το τρέχον Element — αν ο τύπος και το Key συμπίπτουν, το Element ενημερώνεται, ενώ το State παραμένει το ίδιο.
Η κατάσταση αλλάζει μόνο μέσω της κλήσης setState, που ειδοποιεί το πλαίσιο για την ανάγκη ανακατασκευής. Είναι σημαντικό να κατανοήσετε: το setState δεν αλλάζει αυτόματα την κατάσταση — απλώς σημαίνει το widget ως «βρώμικο». Ο προγραμματιστής ενημερώνει ο ίδιος τα πεδία του State στο callback που μεταβιβάζεται στο setState. Μετά τον τερματισμό του callback, το Flutter καλεί το build και ενημερώνει το UI.
Σύμφωνα με την ομάδα Dart/Flutter (Dart Language Specification, 2026), αυτός ο διαχωρισμός εγγυάται ότι όλες οι αλλαγές κατάστασης συμβαίνουν σύγχρονα πριν από την κλήση του build, αποκλείοντας την κατάσταση όπου το UI εμφανίζει μερικώς ενημερωμένα δεδομένα. Αυτός είναι ο βασικός μηχανισμός συνέπειας διεπαφής στο Flutter.
Ας δούμε ένα απλό StatefulWidget — έναν μετρητή κλικ κουμπιού. Δείχνει το βασικό μοτίβο: δημιουργία State, αρχικοποίηση πεδίου στο initState, αλλαγή μέσω setState:
class CounterScreen extends StatefulWidget {
const CounterScreen({super.key});
@override
State<CounterScreen> createState() => _CounterScreenState();
}
class _CounterScreenState extends State<CounterScreen> {
int _count = 0;
void _increment() {
setState(() {
_count++;
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Αριθμός: $_count'),
ElevatedButton(
onPressed: _increment,
child: const Text('Αύξηση'),
),
],
);
}
}
Παράδειγμα με ασύγχρονη φόρτωση δεδομένων και διαχείριση κύκλου ζωής. Το StatefulWidget φορτώνει δεδομένα από το δίκτυο και εμφανίζει την κατάσταση φόρτωσης:
class UserProfilePage extends StatefulWidget {
final String userId;
const UserProfilePage({super.key, required this.userId});
@override
State<UserProfilePage> createState() => _UserProfilePageState();
}
class _UserProfilePageState extends State<UserProfilePage> {
UserModel? _user;
bool _isLoading = true;
@override
void initState() {
super.initState();
_loadUser();
}
Future<void> _loadUser() async {
final user = await UserService.fetchUser(widget.userId);
setState(() {
_user = user;
_isLoading = false;
});
}
@override
Widget build(BuildContext context) {
if (_isLoading) return const CircularProgressIndicator();
return Text('Γεια, ${_user!.name}');
}
}
Στο δεύτερο παράδειγμα είναι σημαντικό να σημειωθεί: το initState ξεκινά μια ασύγχρονη λειτουργία, αλλά η ίδια η μέθοδος δεν είναι ασύγχρονη. Η ασυγχρονία υλοποιείται μέσω async/await μέσα στην ξεχωριστή μέθοδο _loadUser, η οποία ενημερώνει την κατάσταση μέσω setState μετά την ολοκλήρωση του αιτήματος. Αυτή η προσέγγιση εγγυάται ότι το widget θα εμφανίσει σωστά τον δείκτη φόρτωσης πριν λάβει τα δεδομένα.
Η επιλογή μεταξύ StatefulWidget και StatelessWidget δεν είναι μόνο ζήτημα ύπαρξης κατάστασης. Το StatefulWidget παρέχει έναν πλήρη κύκλο ζωής με τις μεθόδους initState, didChangeDependencies, didUpdateWidget και dispose, που είναι απαραίτητο για την εργασία με ελεγκτές, κινητικά και ροές. Το StatelessWidget, από την άλλη πλευρά, δεν έχει αυτές τις μεθόδους και είναι πάντα ελαφρύτερο για το πλαίσιο.
Σύσταση της ομάδας Flutter (Flutter docs, 2026) — ελαχιστοποιήστε τον αριθμό των StatefulWidget στην εφαρμογή, ανυψώνοντας την κατάσταση προς τα πάνω στο δέντρο (State Hoisting) ή χρησιμοποιώντας λύσεις διαχείρισης κατάστασης (Riverpod, Bloc, Provider). Κάθε StatefulWidget δημιουργεί ένα αντικείμενο State που ζει μέχρι την αφαίρεση του στοιχείου — όσο περισσότερα τέτοια widgets, τόσο μεγαλύτερο το φορτίο μνήμης.
| Κριτήριο | StatefulWidget | StatelessWidget |
|---|---|---|
| Κατάσταση | Μεταβλητή | Αμετάβλητη |
| Κύκλος ζωής | 6 στάδια | Μόνο build |
| Αντικείμενο State | Δημιουργείται ξεχωριστά | Δεν απαιτείται |
| setState | Διαθέσιμο | Μη διαθέσιμο |
| Εγγραφές | initState/dispose | Δεν υποστηρίζονται |
| const κατασκευαστής | Περιορισμένος | Πλήρως υποστηριζόμενος |
| Κατανάλωση μνήμης | Περισσότερη | Μικρότερη |
StatefulWidget απαιτεί περισσότερους πόρους από το StatelessWidget λόγω της ανάγκης δημιουργίας και συντήρησης του αντικειμένου State. Ωστόσο, η σωστή χρήση του StatefulWidget δεν οδηγεί σε προβλήματα απόδοσης αν τηρούνται μερικοί κανόνες. Πρώτον, αποφεύγετε τη βαθιά έμπνευση του StatefulWidget — κάθε επίπεδο προσθέτει επιβάρυνση για τη διάσχιση του δέντρου. Δεύτερον, χωρίστε το σύνθετο StatefulWidget σε πολλά απλά widgets, κάθε ένα υπεύθυνο για το δικό του μέρος της κατάστασης.
Σύμφωνα με την έρευνα Flutter Performance (Flutter.dev, Φεβρουάριος 2026), η συχνότερη αιτία πτώσης FPS είναι η κλήση setState στο γονικό widget που ανακατασκευάζει όλους τους απογόνους, συμπεριλαμβανομένων και των StatelessWidget που δεν έχουν αλλάξει την εμφάνισή τους. Η λύση — μεταφέρετε το μεταβλητό μέρος του UI σε ένα ξεχωριστό StatefulWidget, έτσι ώστε το setState να ανακατασκευάζει μόνο το ελάχιστο απαιτούμενων widgets.
Η χρήση const μέσα στο State είναι μια ακόμη σημαντική τεχνική. Αν τα θυγατρικά widgets είναι δηλωμένα ως const, το Flutter δεν θα τα ανακατασκευάσει κατά την κλήση setState στον γονέα. Αυτό μειώνει το φορτίο του πλαισίου και μειώνει τον χρόνο απόδοσης κάθε καρέ.
Κάθε κλήση setState εκκινά μια πλήρη ανακατασκευή του widget. Αν η κατάσταση αλλάζει με υψηλή συχνότητα (π.χ. κινητική ή ροή δεδομένων), σκεφτείτε τη χρήση AnimatedBuilder, ValueListenableBuilder ή StreamBuilder αντί για χειροκίνητη κλήση setState. Αυτά τα widgets βελτιστοποιούν την ανακατασκευή, ενημερώνοντας μόνο εκείνο το μέρος του UI που πραγματικά άλλαξε.
Το πρώτο συνηθισμένο λάθος με το StatefulWidget — η κλήση setState μετά από dispose. Όταν το widget αφαιρεθεί από το δέντρο, το State θεωρείται νεκρό και κάθε κλήση setState πετάει την εξαίρεση «setState called after dispose». Το πιο συχνά αυτό συμβαίνει όταν μια ασύγχρονη λειτουργία ολοκληρώνεται μετά την αφαίρεση του widget. Η λύση — ελέγξτε τη σημαία mounted πριν την κλήση setState ή ακυρώστε τις ασύγχρονες λειτουργίες στο dispose.
Το δεύτερο λάθος — η εκτέλεση βαρέων υπολογισμών στη μέθοδο build. Δεδομένου ότι το build καλείται σε κάθε setState και σε κάθε ανακατασκευή του γονέα, όλοι οι υπολογισμοί πρέπει να είναι όσο το δυνατόν ελαφρύτεροι. Αν πρέπει να εκτελέσετε μια λειτουργία έντασης πόρων — μεταφέρετε την σε ένα ξεχωριστό Isolate ή αποθηκεύστε το αποτέλεσμα σε ένα πεδίο State.
Το τρίτο λάθος — η έλλειψη κλήσης super.initState() και super.dispose(). Υπερβαίνοντας αυτές τις μεθόδους, ο προγραμματιστής είναι υποχρεωμένος να καλέσει τη γονική υλοποίηση. Αν αυτό δεν γίνει, το πλαίσιο δεν μπορεί να διαχειριστεί σωστά την κατάσταση του Element, πράγμα που οδηγεί σε δύσκολα εντοπίσιμα σφάλματα.
mounted πριν το setState σε ασύγχρονα callbackssuper.initState() και super.dispose()Συχνές Ερωτήσεις
StatefulWidget μπορεί να αλλάξει την κατάστασή του μέσω setState, έχει κύκλο ζωής (initState, dispose) και δημιουργεί ξεχωριστό αντικείμενο State. Το StatelessWidget δεν μπορεί να αλλάξει κατάσταση και δεν έχει μεθόδους κύκλου ζωής — απλώς εμφανίζει τα δεδομένα που του δίνονται.
createState καλείται ακριβώς μία φορά για κάθε περίπτωση StatefulElement. Ακόμα και αν ο γονέας ανακατασκευάζεται πολλές φορές, όσο ο τύπος και το Key του widget δεν αλλάζουν, το createState δεν καλείται — χρησιμοποιείται το υπάρχον αντικείμενο State.
Οι πόροι δεν θα απελευθερωθούν: οι ελεγκτές θα συνεχίσουν να λειτουργούν στο παρασκήνιο, οι εγγραφές ροών θα παραμείνουν ενεργές, οι χρονομετρητές δεν θα ακυρωθούν. Αυτό οδηγεί σε διαρροές μνήμης και μπορεί να προκαλέσει κλήση setState μετά από dispose, που πετάει εξαίρεση.
Ναι, ο κατασκευαστής του StatefulWidget μπορεί να είναι const. Ωστόσο, αυτό δεν προσφέρει το ίδιο όφελος όπως για το StatelessWidget — το αντικείμενο State θα δημιουργηθεί ούτως ή άλλως κατά την πρώτη ενσωμάτωση. Το const επηρεάζει μόνο το ίδιο το widget (ελαφρύ περιτύλιγμα), όχι το State.
didUpdateWidget καλείται όταν ο γονέας μεταβιβάζει το StatefulWidget με νέες παραμέτρους. Αυτό είναι απαραίτητο για να συγχρονιστεί η κατάσταση με νέα δεδομένα — για παράδειγμα, αν το userId άλλαξε στις παραμέτρους, πρέπει να φορτωθεί το προφίλ του νέου χρήστη.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης