Widget — είναι η κεντρική έννοια του πλαισίου Flutter, που περιγράφει τη διαμόρφωση ενός στοιχείου διεπαφής χρήστη. Κάθε οπτικό στοιχείο, από ένα κουμπί μέχρι μια σύνθετη κινούμενη εικόνα, είναι ένα Widget. Σε αντίθεση με άλλα πλαίσια, όπου το UI περιγράφεται με ξεχωριστά αρχεία XML ή σχεδιάζεται προστακτικά, το Flutter χτίζει τη διεπαφή μέσω σύνθεσης Widget — συνδυασμού μικρών αδιαίρετων στοιχείων σε ένα ιεραρχικό δέντρο. Σύμφωνα με το Flutter Documentation (2025), η βιβλιοθήκη Flutter SDK περιλαμβάνει πάνω από 260 ενσωματωμένα Widget, που καλύπτουν Material Design, Cupertino και προσαρμοσμένα στυλ.
Κύρια Σημεία
Widget στο Flutter είναι μια αμετάβλητη (immutable) περιγραφή ενός μέρους της διεπαφής χρήστη. Κάθε Widget περιέχει ιδιότητες διαμόρφωσης: μέγεθος, χρώμα, θέση, κείμενο, χειριστές συμβάντων και θυγατρικά Widget. Τα ίδια τα Widget δεν αποδίδονται άμεσα — είναι συνταγές (blueprints) βάσει των οποίων το Flutter Engine δημιουργεί ένα RenderObject — ένα πραγματικό γραφικό αντικείμενο στην οθόνη.
Η φιλοσοφία του Flutter λέει: “Everything is a Widget”. Αυτό σημαίνει ότι όχι μόνο τα ορατά στοιχεία (Text, Image, Button) είναι Widget, αλλά και τα δομικά μπλοκ (Padding, Center, Column, Stack), τα μπλοκ συμπεριφοράς (GestureDetector, AnimatedBuilder), ακόμα και η ίδια η εφαρμογή (MaterialApp, CupertinoApp). Αυτή η προσέγγιση εξασφαλίζει ομοιομορφία: οποιοδήποτε στοιχείο οθόνης μπορεί να συνδυαστεί με οποιοδήποτε άλλο μέσω απλής ένθεσης.
Σύμφωνα με δεδομένα από το Google I/O 2024 — Flutter Widgets Deep Dive, μια μέση εφαρμογή στο Flutter περιέχει μεταξύ 200 και 1500 Widget σε μια δεδομένη στιγμή. Παρά τον αριθμό αυτό, το Flutter διατηρεί 60 FPS ακόμα και σε οικονομικές συσκευές χάρη στις βελτιστοποιήσεις σε επίπεδο C++ Skia/Impeller engine. Τα Widget είναι ελαφριά αντικείμενα (40–80 bytes το καθένα), επομένως η δημιουργία τους δεν αποτελεί σημείο συμφόρησης απόδοσης.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(
body: Center(
child: Text('Γεια σου, Flutter!'),
),
),
);
}
}
Για να κατανοήσετε πώς λειτουργεί το Widget, πρέπει να γνωρίσετε την αρχιτεκτονική του Flutter που αποτελείται από τρία διασυνδεδεμένα δέντρα. Το πρώτο — Widget tree — περιγράφει τη διαμόρφωση του UI. Είναι ένα ελαφρύ δέντρο που μπορεί να ξαναχτιστεί πλήρως σε κάθε καρέ (ο garbage collector αφαιρεί παλιά Widget και δημιουργεί νέα). Τα Widget είναι αμετάβλητα (immutable): αν άλλαξε το χρώμα του κειμένου — δημιουργείται ένα νέο Text Widget με το νέο χρώμα, το παλιό απορρίπτεται.
Το δεύτερο δέντρο — Element tree — είναι ο συνδετικός κρίκος μεταξύ Widget και RenderObject. Το Element περιέχει μια αναφορά στο Widget (διαμόρφωση) και στο RenderObject (απόδοση). Όταν το Widget αλλάζει, το Flutter συγκρίνει το νέο Widget με το παλιό Element και αποφασίζει: να ενημερώσει το υπάρχον RenderObject (αν το Widget είναι ίδιου τύπου) ή να δημιουργήσει ένα νέο (αν ο τύπος του Widget άλλαξε). Αυτή η διαδικασία ονομάζεται Reconciliation και είναι ανάλογη του React Virtual DOM.
Το τρίτο δέντρο — RenderObject tree — είναι υπεύθυνο για την πραγματική απόδοση στην οθόνη. Το RenderObject περιέχει συγκεκριμένες διαστάσεις, θέσεις και μεθόδους paint. Το Flutter Engine (C++ Skia ή Impeller) διασχίζει το RenderObject tree και αποδίδει κάθε κόμβο. Το RenderObject tree είναι το βαρύτερο δέντρο, γι' αυτό το Flutter ελαχιστοποιεί τις αλλαγές του επαναχρησιμοποιώντας το RenderObject κατά την αλλαγή Widget ίδιου τύπου.
| Δέντρο | Σκοπός | Immutable; | Κύκλος ζωής |
|---|---|---|---|
| Widget | Διαμόρφωση UI (συνταγή) | Ναι | Αναδημιουργείται σε κάθε build |
| Element | Σύνδεση Widget ↔ RenderObject | Όχι | Υπάρχει όσο το widget είναι στο δέντρο |
| RenderObject | Απόδοση και layout | Όχι | Βαρύ, επαναχρησιμοποιείται όταν είναι δυνατό |
Το Flutter χωρίζει τα Widget σε δύο θεμελιώδεις τύπους: StatelessWidget και StatefulWidget. Το StatelessWidget είναι ένα widget που δεν περιέχει μεταβλητή κατάσταση. Η εμφάνιση του StatelessWidget καθορίζεται πλήρως από τον κατασκευαστή του και δεν μπορεί να αλλάξει μετά την απόδοση. Παραδείγματα: Text, Icon, Divider, Padding. Όλες οι ιδιότητες του StatelessWidget δηλώνονται ως final στον κατασκευαστή και είναι μόνο για ανάγνωση.
StatefulWidget είναι ένα widget με μεταβλητή κατάσταση. Αποτελείται από δύο κλάσεις: το ίδιο το Widget (αμετάβλητη διαμόρφωση, όπως το StatelessWidget) και το State (μεταβλητή κατάσταση). Ο διαχωρισμός του Widget από το State είναι μια βασική αρχιτεκτονική απόφαση του Flutter. Το Widget αναδημιουργείται σε κάθε build, αλλά το αντικείμενο State παραμένει ζωντανό καθ' όλη τη διάρκεια ζωής του widget στο δέντρο, διατηρώντας την κατάστασή του.
Όταν καλείται η setState(), το Flutter σημαδεύει το State ως “βρώμικο” και στο επόμενο καρέ καλεί τη μέθοδο build() για την ανακατασκευή του υποδέντρου. Σημαντικό: η setState() δεν αναδημιουργεί το ίδιο το Widget — απλώς ενεργοποιεί την κλήση build() στο υπάρχον State. Αυτό σημαίνει ότι το StatefulWidget μπορεί να ενημερώσει το UI χωρίς να χάσει την κατάσταση των θυγατρικών Widget, εφόσον τα κλειδιά (Key) των θυγατρικών στοιχείων παραμένουν σταθερά.
// StatelessWidget — η εμφάνιση δεν αλλάζει ποτέ
class GreetingWidget extends StatelessWidget {
const GreetingWidget({super.key, required this.name});
final String name;
@override
Widget build(BuildContext context) {
return Text('Γεια σου, $name');
}
}
// StatefulWidget — μετρητής με μεταβλητή κατάσταση
class CounterWidget extends StatefulWidget {
const CounterWidget({super.key});
@override
State<CounterWidget> createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
int _count = 0;
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Μέτρηση: $_count'),
ElevatedButton(
onPressed: () => setState(() => _count++),
child: const Text('Αύξηση'),
),
],
);
}
}
Όλα τα Widget στο Flutter μπορούν να χωριστούν σε τρεις κύριες κατηγορίες ανάλογα με τον λειτουργικό σκοπό. Layout Widget — είναι υπεύθυνα για τη διάταξη των θυγατρικών στοιχείων στην οθόνη. Τα Row και Column τοποθετούν τα παιδιά σε μια γραμμή, το Stack τοποθετεί το ένα πάνω στο άλλο, τα Expanded και Flexible κατανέμουν τον διαθέσιμο χώρο. Τα Layout Widget δεν έχουν δική τους οπτική αναπαράσταση — διαχειρίζονται τη θέση και το μέγεθος των θυγατρικών widget.
Painting Widget — είναι υπεύθυνα για την οπτική εμφάνιση. Το Container συνδυάζει διακοσμήσεις (χρώμα, gradient, σκιά, περίγραμμα) με ιδιότητες layout. Το Padding προσθέτει διάστιχο, το DecoratedBox σχεδιάζει φόντο, το Transform εφαρμόζει μετασχηματισμούς (περιστροφή, κλιμάκωση). Τα Painting Widget είναι τα δομικά στοιχεία του οπτικού στυλ και συχνά χρησιμοποιούνται μαζί με layout Widget για την επίτευξη της επιθυμητής εμφάνισης.
Interactive Widget — επεξεργάζονται είσοδο χρήστη. Το GestureDetector ανιχνεύει χειρονομίες (tap, swipe, pinch), το InkWell προσθέτει εφέ κυματισμού Material, το TextField δέχεται είσοδο κειμένου, τα Slider και Switch παρέχουν τυπικά στοιχεία ελέγχου. Τα Interactive Widget ανυψώνουν συμβάντα μέσω συναρτήσεων callback που μεταβιβάζονται στον κατασκευαστή ή διαχειρίζονται μέσω παρόχων κατάστασης.
| Κατηγορία | Παραδείγματα Widget | Σκοπός |
|---|---|---|
| Layout | Row, Column, Stack, Expanded, Flexible, Align | Διάταξη και μέγεθος θυγατρικών στοιχείων |
| Painting | Container, Padding, DecoratedBox, RotatedBox | Χρώμα, φόντο, περιγράμματα, σκιές, μετασχηματισμοί |
| Interactive | GestureDetector, InkWell, TextField, Slider | Επεξεργασία αφής, εισόδου, χειρονομιών |
| Platform | MaterialApp, CupertinoApp, Theme, MediaQuery | Ενσωμάτωση με πλατφόρμα, θέματα, προσαρμογή |
| Async | FutureBuilder, StreamBuilder, ValueListenableBuilder | Αντιδραστική ενημέρωση από ασύγχρονα δεδομένα |
Σύμφωνα με δεδομένα από το Flutter Widget of the Week (Google, 2025), η κοινότητα Flutter χρησιμοποιεί ενεργά τον συνδυασμό layout + painting + interactive Widget για την κατασκευή σχεδόν οποιασδήποτε διεπαφής. Για παράδειγμα, ένα κουμπί: InkWell (interactive) + Container (painting) + Text (static) + Padding (layout). Αυτή η αρθρωτότητα επιτρέπει την επαναχρησιμοποίηση τυπικών μπλοκ σε διαφορετικά περιβάλλοντα χωρίς αντιγραφή κώδικα.
Σύνθεση Widget είναι η διαδικασία κατασκευής UI μέσω ένθεσης Widget το ένα μέσα στο άλλο. Σε αντίθεση με την κλασική κληρονομικότητα (extends), όπου η θυγατρική κλάση κληρονομεί τη συμπεριφορά του γονέα, το Flutter χρησιμοποιεί συνάθροιση: κάθε Widget περιέχει άλλα Widget μέσω της παραμέτρου child (για ένα) ή children (για πολλά). Αυτή η προσέγγιση παρέχει μεγαλύτερη ευελιξία και δυνατότητα επαναχρησιμοποίησης.
BuildContext είναι η δεύτερη πιο σημαντική έννοια μετά το Widget. Το BuildContext είναι ένας περιγραφέας της θέσης του Widget στο δέντρο στοιχείων. Μέσω του BuildContext, το Widget μπορεί να έχει πρόσβαση σε πρόγονα Widget (Theme.of(context), MediaQuery.of(context), Navigator.of(context)). Το BuildContext μεταβιβάζεται στη μέθοδο build() και χρησιμοποιείται για αλληλεπίδραση με γονικά και θυγατρικά στοιχεία. Κάθε Widget έχει ακριβώς ένα BuildContext που προσδιορίζει μοναδικά τη θέση του στο δέντρο.
Σύμφωνα με το Flutter Architectural Overview (Google, 2025), το BuildContext είναι η βάση για το InheritedWidget — έναν μηχανισμό που επιτρέπει τη μεταβίβαση δεδομένων προς τα κάτω στο δέντρο χωρίς ρητή μεταβίβαση μέσω κατασκευαστών. Τα Theme, MediaQuery, Navigator και Provider χρησιμοποιούν το InheritedWidget κάτω από το καπό. Οποιοδήποτε βαθιά ένθετο Widget μπορεί να έχει πρόσβαση σε δεδομένα προγόνου μέσω του BuildContext.dependOnInheritedWidgetOfExactType, καθιστώντας το BuildContext το κλειδί για την αντιδραστική αρχιτεκτονική του Flutter.
// Σύνθεση Widget μέσω ένθεσης
Scaffold(
appBar: AppBar(title: const Text('Η Εφαρμογή Μου')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('Καλώς ορίσατε στο Flutter',
style: TextStyle(fontSize: 24)),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () { /* nav */ },
child: const Text('Ξεκινήστε'),
),
],
),
),
)
// Πρόσβαση σε θέματα μέσω BuildContext
Text(
'Στυλιζαρισμένο Κείμενο',
style: Theme.of(context).textTheme.headlineMedium,
)
Το πρώτο και πιο συνηθισμένο λάθος — χρήση StatefulWidget εκεί που αρκεί το StatelessWidget. Πολλοί αρχάριοι προγραμματιστές Flutter δημιουργούν StatefulWidget για όλα τα widget, ακόμα κι αν η κατάσταση αποθηκεύεται σε εξωτερικό πάροχο (Provider, Riverpod, BLoC). Αυτό είναι περιττό και υποβαθμίζει την απόδοση. Κανόνας: χρησιμοποιήστε StatelessWidget αν η κατάσταση διαχειρίζεται εξωτερικά ή αν το widget δεν έχει δική του μεταβλητή κατάσταση.
Το δεύτερο λάθος — δημιουργία Widget μέσα στη μέθοδο build χωρίς const κατασκευαστή. Κάθε Widget που δημιουργείται χωρίς const επανακατανέμεται σε κάθε build. Αν μέσα στο build() δημιουργούνται Widget με const κατασκευαστή, το Flutter μπορεί να επαναχρησιμοποιήσει το ίδιο στιγμιότυπο, μειώνοντας το φορτίο στον garbage collector. Προσθέτετε const όπου είναι δυνατό — ειδικά για Text, Icon, SizedBox, Padding και άλλα StatelessWidget.
Το τρίτο λάθος — λανθασμένη εργασία με κλειδιά (Key). Το Flutter χρησιμοποιεί το Key για την αναγνώριση Widget κατά την ανακατασκευή του δέντρου. Αν μια λίστα Widget ανακατασκευάζεται χωρίς Key, το Flutter μπορεί να μπερδέψει τη σειρά των στοιχείων, οδηγώντας σε λανθασμένη κινούμενη εικόνα ή απώλεια κατάστασης. Πάντα να προσθέτετε Key (π.χ. ValueKey ή ObjectKey) για στοιχεία σε λίστες, ειδικά όταν χρησιμοποιείτε ListView.builder με δυναμικά δεδομένα.
Συχνές Ερωτήσεις
StatelessWidget — widget χωρίς μεταβλητή κατάσταση, η εμφάνισή του καθορίζεται πλήρως από τον κατασκευαστή. StatefulWidget — widget με μεταβλητή κατάσταση, η οποία αποθηκεύεται σε ξεχωριστό αντικείμενο State και μπορεί να ενημερωθεί μέσω setState() χωρίς αναδημιουργία του ίδιου του widget. Χρησιμοποιήστε StatelessWidget όπου είναι δυνατό, StatefulWidget — όταν απαιτείται τοπική κατάσταση.
Widget immutable — είναι μια αρχιτεκτονική απόφαση του Flutter για απόδοση. Αν τα Widget ήταν mutable, το Flutter δεν θα μπορούσε να συγκρίνει με ασφάλεια την παλιά και νέα διαμόρφωση σε κάθε build. Η αμεταβλητότητα επιτρέπει στο Flutter να προσδιορίζει γρήγορα αν το Widget έχει αλλάξει (μέσω του τελεστή ==) και να επαναχρησιμοποιεί το υπάρχον RenderObject, ελαχιστοποιώντας τις δαπανηρές λειτουργίες απόδοσης.
BuildContext είναι ένας περιγραφέας της θέσης του Widget στο δέντρο στοιχείων. Μέσω αυτού, το Widget έχει πρόσβαση σε πρόγονα Widget (Theme, MediaQuery, Navigator) και InheritedWidget. Το BuildContext χρησιμοποιείται επίσης για πλοήγηση (Navigator.of(context)), εμφάνιση SnackBar και αλληλεπίδραση με Provider. Κάθε Widget λαμβάνει BuildContext μέσω της μεθόδου build() και το μεταβιβάζει στους διαδόχους του.
Χρησιμοποιήστε Row για οριζόντια διάταξη στοιχείων, Column — για κάθετη, Stack — για επικάλυψη στοιχείων. Τα Row και Column λειτουργούν με βάση την αρχή flexbox: τα children καταλαμβάνουν χώρο σύμφωνα με mainAxisSize, mainAxisAlignment και crossAxisAlignment. Το Stack χρησιμοποιεί positioned-children για ακριβή τοποθέτηση σε σχέση με τις άκρες ή το κέντρο.
Το Flutter επιτυγχάνει υψηλή απόδοση μέσω τριών μηχανισμών: (1) Widget are cheap — ελαφριά immutable αντικείμενα (40–80 bytes), η δημιουργία τους δεν επιβαρύνει τον GC. (2) RenderObject reuse — κατά την αλλαγή Widget ίδιου τύπου, το RenderObject επαναχρησιμοποιείται, αποφεύγοντας τη δαπανηρή αναδημιουργία. (3) Skia/Impeller engine — απόδοση σε επίπεδο C++ με ελαχιστοποίηση κλήσεων σχεδίασης μέσω repaint boundaries.
Περίληψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης