Theme και ThemeData — οι κεντρικοί μηχανισμοί θεματοποίησης στο Flutter, που επιτρέπουν τον καθολικό ορισμό χρωμάτων, γραμματοσειρών, αποστάσεων και στυλ εξαρτημάτων για ολόκληρη την εφαρμογή. Το ThemeData είναι ένα αντικείμενο διαμόρφωσης που μεταβιβάζεται στο MaterialApp μέσω της παραμέτρου theme. Σύμφωνα με την επίσημη τεκμηρίωση του Flutter (2026), η σωστή διαμόρφωση του ThemeData μειώνει την ποσότητα του επαναλαμβανόμενου κώδικα στυλ κατά 70–80% και εξασφαλίζει ομοιόμορφη εμφάνιση των οθονών. Το ενσωματωμένο σύστημα κληρονομικότητας επιτρέπει την παράκαμψη του θέματος σε επίπεδο μεμονωμένων widget μέσω του Theme.of(context).
Κύρια σημεία
ThemeData — κλάση που περιέχει την πλήρη περιγραφή του οπτικού στυλ μιας εφαρμογής Material. Μεταβιβάζεται στο MaterialApp μέσω της παραμέτρου theme ή darkTheme και κληρονομείται από όλα τα θυγατρικά widget. Κάθε widget Material Design χρησιμοποιεί το ThemeData για να λάβει τα στυλ του: χρώματα κουμπιών, διαστάσεις AppBar, στυλ κειμένου, αποστάσεις καρτών και εικονίδια.
Στη βάση του ThemeData βρίσκεται το ColorScheme, το οποίο ορίζει τα χρώματα primary, secondary, surface, error και άλλα. Από αυτά τα χρώματα υπολογίζονται αυτόματα οι καταστάσεις (hover, focus, pressed) για όλα τα εξαρτήματα Material. Για παράδειγμα, το ElevatedButton χρησιμοποιεί το χρώμα primary για το φόντο και το onPrimary για το κείμενο — χωρίς πρόσθετη διαμόρφωση.
Το widget Theme — είναι ένα InheritedWidget που διαδίδει το θέμα προς τα κάτω στο δέντρο widget. Οποιοδήποτε widget μπορεί να λάβει το τρέχον θέμα μέσω του Theme.of(context), καθώς και να δημιουργήσει ένα νέο θέμα βάσει του υπάρχοντος με παρακάμψεις μέσω του Theme.copyWith ή του ThemeData.copyWith.
Συμπέρασμα: το ThemeData — η ενιαία πηγή στυλ της εφαρμογής. Ξεκινήστε την ανάπτυξη ορίζοντας το ThemeData — αυτό θέτει την οπτική γλώσσα ολόκληρου του έργου και αποτρέπει τα διάσπαρτα στυλ σε διαφορετικές οθόνες.
ColorScheme — το κεντρικό στοιχείο θεματοποίησης στο Material 3. Αντικαθιστά τα απαρχαιωμένα primarySwatch και primaryColor, παρέχοντας ένα πλήρες σύνολο χρωμάτων για όλες τις καταστάσεις και τα εξαρτήματα.
Το ColorScheme περιέχει 12 βασικά χρώματα: primary, onPrimary, secondary, onSecondary, surface, onSurface, error, onError, background, onBackground, outline και shadow. Για κάθε χρώμα υπολογίζονται αυτόματα οι παραλλαγές light και dark, καθώς και τα χρώματα για τις καταστάσεις hover, focus, pressed και dragged. Όταν ορίζεται μόνο το primary, το framework παράγει αυτόματα τα υπόλοιπα χρώματα μέσω του ColorScheme.fromSeed.
Η μέθοδος ColorScheme.fromSeed (Material 3) δέχεται ένα βασικό χρώμα seedColor και παράγει μια πλήρη αρμονική παλέτα βάσει του αλγορίθμου Material Design 3. Αυτό εξαλείφει τη μη αυτόματη επιλογή συμπληρωματικών και ανάλογων χρωμάτων. Αρκεί να μεταβιβάσετε ένα κύριο χρώμα επωνυμίας, και το ColorScheme.fromSeed θα δημιουργήσει μια συνεκτική παλέτα για ολόκληρο το σύστημα Material.
Συμπέρασμα: σε νέα έργα χρησιμοποιήστε το ColorScheme.fromSeed για τη δημιουργία παλέτας. Για συμβατότητα με Material 2 — primarySwatch. Το ColorScheme είναι το υποχρεωτικό ελάχιστο για το Material 3.
TextTheme — μέρος του ThemeData που ορίζει τα στυλ κειμένου για όλα τα επίπεδα επικεφαλίδων, υπότιτλων, σώματος και ετικετών. Το Material Design ορίζει 15 στυλ κειμένου: headlineLarge, headlineMedium, headlineSmall, titleLarge, titleMedium και άλλα.
Η τυπογραφία ορίζεται είτε μέσω του GoogleFonts για αυτόματη φόρτωση γραμματοσειρών, είτε μέσω του ενσωματωμένου Theme.of(context).textTheme με προσαρμοσμένα TextStyle. Το GoogleFonts παρέχει εκατοντάδες γραμματοσειρές μέσω του πακέτου google_fonts, το οποίο τις αποθηκεύει στην τοπική κρυφή μνήμη μετά την πρώτη φόρτωση. Εναλλακτικά — ενσωμάτωση γραμματοσειρών στα assets του έργου μέσω του pubspec.yaml.
Για προσαρμοστική τυπογραφία, το TextTheme υποστηρίζει το MediaQuery.textScaleFactor — όταν αυξάνεται η γραμματοσειρά συστήματος, όλα τα στυλ κειμένου κλιμακώνονται αναλογικά. Επιπλέον, μπορούν να οριστούν διαφορετικά μεγέθη γραμματοσειράς για διαφορετικούς παράγοντες μορφής μέσω του isTest ανάλογα με το MediaQuery.size.width.
Συμπέρασμα: ορίστε το TextTheme στο ThemeData και χρησιμοποιήστε το Theme.of(context).textTheme για όλα τα στοιχεία κειμένου. Αυτό εξασφαλίζει ομοιομορφία γραμματοσειρών και απλοποιεί τις καθολικές αλλαγές στην τυπογραφία.
Theme — InheritedWidget που διαδίδει το θέμα από τον γονέα στα παιδιά. Κάθε θυγατρικό widget μπορεί τόσο να διαβάσει το τρέχον θέμα μέσω του Theme.of(context) όσο και να δημιουργήσει το δικό του αντίγραφο με αλλαγές μέσω του ThemeData.copyWith.
Μηχανισμός κληρονομικότητας: εάν ένα widget είναι τυλιγμένο σε ένα Theme με νέα δεδομένα, όλοι οι απόγονοί του βλέπουν το παρακαμφθέν θέμα, ενώ τα widget εκτός αυτού του περιτυλίγματος βλέπουν το αρχικό. Αυτό επιτρέπει, για παράδειγμα, την απομόνωση στυλ για μια συγκεκριμένη οθόνη: η επικεφαλίδα χρησιμοποιεί ένα προσαρμοσμένο θέμα με τροποποιημένο AppBarTheme, ενώ η υπόλοιπη εφαρμογή χρησιμοποιεί το τυπικό.
Η ένθεση του Theme μπορεί να είναι απεριόριστη. Το Flutter χρησιμοποιεί ιεραρχική αναζήτηση: κατά την κλήση του Theme.of(context), το framework ανεβαίνει στο δέντρο μέχρι το πρώτο Theme και επιστρέφει το θέμα του. Εάν δεν βρεθεί κανένα Theme — ρίχνεται εξαίρεση. Γι' αυτό το MaterialApp πάντα τυλίγει την εφαρμογή σε ένα Theme.
Συμπέρασμα: η κληρονομικότητα του Theme — ένας ευέλικτος μηχανισμός για την απομόνωση στυλ. Χρησιμοποιήστε την παράκαμψη θέματος για μεμονωμένες οθόνες χωρίς να αντιγράφετε τη γενική διαμόρφωση.
Dark theme — η ενσωματωμένη δυνατότητα του Flutter για εναλλαγή μεταξύ φωτεινού και σκοτεινού θέματος. Το ThemeData αποθηκεύεται στις παραμέτρους theme (φωτεινό) και darkTheme (σκοτεινό) του MaterialApp. Η παράμετρος themeMode διαχειρίζεται τη λειτουργία: ThemeMode.light (πάντα φωτεινό), ThemeMode.dark (πάντα σκοτεινό) ή ThemeMode.system (ακολουθεί τις ρυθμίσεις συστήματος).
Κατά την υλοποίηση του σκοτεινού θέματος δεν χρειάζεται να δημιουργήσετε όλα τα στυλ από την αρχή. Αρκεί να μεταβιβάσετε το ColorScheme.fromSeed(seedColor, brightness: Brightness.dark) στο darkTheme. Το framework διορθώνει αυτόματα τα χρώματα για σκοτεινό φόντο: το surface γίνεται σκούρο γκρι, το κείμενο — φωτεινό. Τα υπόλοιπα υποθέματα (AppBarTheme, CardTheme, BottomNavigationBarTheme) κληρονομούνται από τη φωτεινή έκδοση, εκτός εάν έχουν ρητά παρακαμφθεί.
Για ομαλή μετάβαση μεταξύ θεμάτων, το Flutter παρέχει κινούμενα γραφικά μέσω του AnimatedTheme. Κατά την αλλαγή του themeMode, τα χρώματα και τα στυλ κινούνται, δημιουργώντας μια οπτικά ευχάριστη εναλλαγή. Το AnimatedTheme χρησιμοποιεί Duration και Curve για τη ρύθμιση της ταχύτητας κινούμενων γραφικών.
Συμπέρασμα: το dark theme υλοποιείται μέσω του darkTheme και του ColorScheme.fromSeed με brightness: Brightness.dark. Χρησιμοποιήστε το ThemeMode.system για να ακολουθείτε τις ρυθμίσεις συστήματος του χρήστη.
Παράδειγμα 1 — καθολική διαμόρφωση θέματος μέσω του MaterialApp με χρήση του ColorScheme.fromSeed.
final ThemeData appTheme = ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.indigo,
brightness: Brightness.light,
),
appBarTheme: const AppBarTheme(
centerTitle: true,
elevation: 0,
),
cardTheme: const CardTheme(
elevation: 2,
margin: EdgeInsets.all(8),
),
);
return MaterialApp(
theme: appTheme,
themeMode: ThemeMode.system,
home: const MyHomePage(),
);
Σε αυτό το παράδειγμα, δημιουργείται ThemeData με Material 3, χρωματικό σχήμα βάσει indigo και προσαρμοσμένα στυλ AppBar και Card. Το useMaterial3 ενεργοποιεί το νέο σύστημα θεμάτων Material 3, και το ColorScheme.fromSeed παράγει την πλήρη παλέτα.
Παράδειγμα 2 — πρόσβαση στο θέμα και παράκαμψη για ένα μέρος της εφαρμογής.
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Column(
children: [
Text(
'Τυπική επικεφαλίδα',
style: theme.textTheme.headlineMedium,
),
Theme(
data: theme.copyWith(
colorScheme: theme.colorScheme.copyWith(
primary: Colors.orange,
),
),
child: const SpecialSection(),
),
],
);
}
Το δεύτερο παράδειγμα δείχνει τη χρήση του Theme.of(context) για ανάγνωση στυλ και του Theme() με copyWith για παράκαμψη του θέματος σε ένα μέρος του δέντρου χωρίς αλλαγή της καθολικής διαμόρφωσης. Το SpecialSection μέσα στο Theme θα λάβει πορτοκαλί χρώμα primary, ενώ η υπόλοιπη εφαρμογή διατηρεί το αρχικό indigo.
Συμπέρασμα: το καθολικό θέμα ορίζεται στο MaterialApp, πρόσβαση μέσω του Theme.of(context), παράκαμψη μέσω του Theme με copyWith. Για Material 3 χρησιμοποιήστε το ColorScheme.fromSeed.
Λάθος 1: χρήση primarySwatch αντί ColorScheme στο Material 3. Το primarySwatch είναι απαρχαιωμένο και δεν υποστηρίζεται στο Material 3. Κατά τη χρήση του useMaterial3: true με primarySwatch, το Flutter εμφανίζει προειδοποίηση. Λύση: μεταβείτε στο ColorScheme.fromSeed για τη δημιουργία παλέτας.
Λάθος 2: άκαμπτος ορισμός χρωμάτων σε widget αντί χρήσης Theme.of(context). Οι προγραμματιστές γράφουν Colors.blue απευθείας στο Container, αντί για το χρώμα primary ή secondary από το θέμα. Κατά την αλλαγή θέματος, αυτά τα χρώματα παραμένουν αμετάβλητα, παραβιάζοντας το ενιαίο στυλ. Λύση: χρησιμοποιείτε πάντα το Theme.of(context).colorScheme.primary ή άλλα χρώματα από το θέμα.
Λάθος 3: απουσία darkTheme. Εάν η εφαρμογή δεν ορίζει darkTheme, κατά την ενεργοποίηση του σκοτεινού θέματος στη συσκευή, το Flutter προσπαθεί να προσαρμόσει αυτόματα το φωτεινό θέμα, το οποίο συχνά δίνει χαμηλής ποιότητας αποτέλεσμα. Λύση: μεταβιβάζετε πάντα darkTheme στο MaterialApp, ακόμη κι αν το σκοτεινό θέμα δεν είναι προτεραιότητα — αυτό είναι σημαντικό UX για τους χρήστες.
Λάθος 4: δημιουργία νέου θέματος σε κάθε build. Εάν το ThemeData δημιουργείται μέσα στη μέθοδο build, κάθε ανακατασκευή του widget δημιουργεί ένα νέο αντικείμενο θέματος, σπάζοντας τη βελτιστοποίηση του InheritedWidget. Λύση: μεταφέρετε το ThemeData σε ένα στατικό πεδίο ή const κατασκευαστή εκτός του build.
Συμπέρασμα: η σωστή θεματοποίηση απαιτεί μια ενοποιημένη κεντρική προσέγγιση. Μεταφέρετε το ThemeData σε σταθερές, χρησιμοποιήστε χρωματικά σχήματα από το θέμα και ορίζετε πάντα και τα δύο θέματα — φωτεινό και σκοτεινό.
Συχνές Ερωτήσεις
ThemeData — είναι τα δεδομένα διαμόρφωσης (χρώματα, γραμματοσειρές, στυλ). Theme — InheritedWidget που μεταβιβάζει το ThemeData προς τα κάτω στο δέντρο widget. Το Theme.of(context) επιστρέφει το ThemeData που το πλησιέστερο Theme διαδίδει στο τρέχον περιβάλλον.
Διαχειριστείτε το themeMode στο MaterialApp μέσω της κατάστασης εφαρμογής. Αλλάξτε την κατάσταση από ThemeMode.light σε ThemeMode.dark, και το Flutter θα ανακατασκευάσει ολόκληρο το δέντρο με το νέο θέμα. Για ομαλά κινούμενα γραφικά χρησιμοποιήστε το AnimatedTheme.
ColorScheme.fromSeed — μέθοδος Material 3 που δέχεται ένα βασικό χρώμα (seedColor) και παράγει μια πλήρη αρμονική παλέτα: primary, secondary, tertiary, surface, error και τις on-παραλλαγές τους. Επιτρέπει τη λήψη ενός συνεκτικού χρωματικού σχήματος από ένα χρώμα επωνυμίας.
Μέσω του TextTheme μέσα στο ThemeData: καθορίστε TextStyle με fontFamily για κάθε επίπεδο. Για Google Fonts χρησιμοποιήστε το πακέτο google_fonts, μεταβιβάζοντας το fontFamily απευθείας στο TextTheme. Για τοπικές γραμματοσειρές — συνδέστε τις μέσω του pubspec.yaml στην ενότητα fonts.
Ναι. Τυλίξτε την οθόνη σε ένα Theme με παρακαμφθέντα δεδομένα μέσω copyWith. Όλα τα θυγατρικά widget μέσα σε αυτό το Theme θα λάβουν τα τροποποιημένα στυλ, ενώ η υπόλοιπη εφαρμογή θα διατηρήσει το αρχικό θέμα. Αυτό δεν δημιουργεί πρόσθετο φόρτο.
Περίληψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης