Theme e ThemeData — i meccanismi centrali di tematizzazione in Flutter che consentono di definire colori, caratteri, margini e stili dei componenti a livello globale per l'intera applicazione. ThemeData è un oggetto di configurazione passato a MaterialApp tramite il parametro theme. Secondo la documentazione ufficiale di Flutter (2026), una corretta configurazione di ThemeData riduce il codice di stile duplicato del 70–80% e garantisce un aspetto visivo uniforme su tutti gli schermi. Il sistema di ereditarietà integrato consente di sovrascrivere il tema a livello di singoli widget tramite Theme.of(context).
Punti chiave
ThemeData è una classe che contiene la descrizione completa dello stile visivo di un'applicazione Material. Viene passata a MaterialApp tramite il parametro theme o darkTheme ed è ereditata da tutti i widget figli. Ogni widget Material Design utilizza ThemeData per ottenere i propri stili: colori dei pulsanti, dimensioni dell'AppBar, stili del testo, margini delle card e icone.
Al centro di ThemeData si trova ColorScheme, che definisce i colori primary, secondary, surface, error e altri. Da questi colori vengono automaticamente calcolati gli stati (hover, focus, pressed) per tutti i componenti Material. Ad esempio, ElevatedButton utilizza il colore primary per lo sfondo e onPrimary per il testo — senza configurazione aggiuntiva.
Il widget Theme è un InheritedWidget che propaga il tema verso il basso nell'albero dei widget. Qualsiasi widget può accedere al tema corrente tramite Theme.of(context) e può anche creare un nuovo tema basato su quello esistente con valori sovrascritti tramite Theme.copyWith o ThemeData.copyWith.
In sintesi: ThemeData è l'unica fonte di stili dell'applicazione. Inizia lo sviluppo definendo ThemeData — questo imposta il linguaggio visivo dell'intero progetto e previene stili sparsi su schermi diversi.
ColorScheme è l'elemento centrale della tematizzazione in Material 3. Sostituisce i deprecati primarySwatch e primaryColor, fornendo un set completo di colori per tutti gli stati e componenti.
ColorScheme contiene 12 colori principali: primary, onPrimary, secondary, onSecondary, surface, onSurface, error, onError, background, onBackground, outline e shadow. Per ogni colore vengono automaticamente calcolate le varianti chiara e scura, nonché i colori per gli stati hover, focus, pressed e dragged. Quando viene specificato solo primary, il framework genera automaticamente i colori rimanenti tramite ColorScheme.fromSeed.
Il metodo ColorScheme.fromSeed (Material 3) prende un colore di base seedColor e genera una tavolozza armoniosa completa basata sull'algoritmo di Material Design 3. Ciò elimina la necessità di selezionare manualmente colori complementari e analoghi. Basta passare un colore primario del marchio, e ColorScheme.fromSeed crea una tavolozza coerente per l'intero sistema Material.
In sintesi: nei nuovi progetti, utilizza ColorScheme.fromSeed per generare la tavolozza. Per la compatibilità con Material 2 — usa primarySwatch. ColorScheme è il minimo richiesto per Material 3.
TextTheme è una parte di ThemeData che definisce gli stili del testo per tutti i livelli di intestazioni, sottotitoli, corpo ed etichette. Material Design definisce 15 stili di testo: headlineLarge, headlineMedium, headlineSmall, titleLarge, titleMedium e altri.
La tipografia viene impostata tramite GoogleFonts per il caricamento automatico dei caratteri, oppure tramite il Theme.of(context).textTheme integrato con oggetti TextStyle personalizzati. GoogleFonts fornisce centinaia di caratteri tramite il pacchetto google_fonts, che li memorizza nella cache locale dopo il primo caricamento. L'alternativa è incorporare i caratteri negli asset del progetto tramite pubspec.yaml.
Per una tipografia adattiva, TextTheme supporta MediaQuery.textScaleFactor — quando la dimensione del carattere di sistema aumenta, tutti gli stili del testo vengono ridimensionati proporzionalmente. Inoltre, è possibile impostare diverse dimensioni dei caratteri per diversi fattori di forma tramite isTest in base a MediaQuery.size.width.
In sintesi: definisci TextTheme in ThemeData e utilizza Theme.of(context).textTheme per tutti gli elementi di testo. Ciò garantisce la coerenza dei caratteri e semplifica le modifiche globali alla tipografia.
Theme è un InheritedWidget che propaga il tema dai genitori ai figli. Qualsiasi widget figlio può sia leggere il tema corrente tramite Theme.of(context) sia creare la propria copia con modifiche tramite ThemeData.copyWith.
Meccanismo di ereditarietà: se un widget è avvolto in un Theme con nuovi dati, tutti i suoi discendenti vedono il tema sovrascritto, mentre i widget al di fuori di questo involucro vedono l'originale. Ciò consente, ad esempio, di isolare gli stili per uno schermo specifico: un'intestazione utilizza un tema personalizzato con un AppBarTheme modificato, mentre il resto dell'applicazione utilizza quello standard.
L'annidamento di Theme può essere illimitato. Flutter utilizza una ricerca gerarchica: quando si chiama Theme.of(context), il framework risale l'albero fino al primo Theme e restituisce il suo tema. Se non viene trovato alcun Theme — viene lanciata un'eccezione. Pertanto, MaterialApp avvolge sempre l'applicazione in un Theme.
In sintesi: l'ereditarietà di Theme è un meccanismo flessibile per isolare gli stili. Utilizza la sovrascrittura del tema per schermi individuali senza duplicare la configurazione globale.
Tema scuro è la capacità integrata di Flutter di passare tra tema chiaro e scuro. ThemeData viene memorizzato nei parametri theme (chiaro) e darkTheme (scuro) di MaterialApp. Il parametro themeMode controlla la modalità: ThemeMode.light (sempre chiaro), ThemeMode.dark (sempre scuro) o ThemeMode.system (segue le impostazioni di sistema).
Nell'implementare un tema scuro, non è necessario ricreare tutti gli stili da zero. Basta passare ColorScheme.fromSeed(seedColor, brightness: Brightness.dark) a darkTheme. Il framework regolerà automaticamente i colori per uno sfondo scuro: surface diventa grigio scuro, il testo diventa chiaro. Altri sottotemi (AppBarTheme, CardTheme, BottomNavigationBarTheme) vengono ereditati dalla versione chiara a meno che non vengano esplicitamente sovrascritti.
Per transizioni fluide tra i temi, Flutter fornisce animazione tramite AnimatedTheme. Quando themeMode cambia, i colori e gli stili si animano, creando una transizione visivamente piacevole. AnimatedTheme utilizza Duration e Curve per configurare la velocità dell'animazione.
In sintesi: il tema scuro viene implementato tramite darkTheme e ColorScheme.fromSeed con brightness: Brightness.dark. Utilizza ThemeMode.system per seguire le impostazioni di sistema dell'utente.
Esempio 1 — configurazione globale del tema tramite MaterialApp usando 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(),
);
Questo esempio crea un ThemeData con Material 3, una combinazione di colori basata sull'indaco e stili personalizzati di AppBar e Card. useMaterial3 abilita il nuovo sistema di tematizzazione Material 3, e ColorScheme.fromSeed genera la tavolozza completa.
Esempio 2 — accesso al tema e sovrascrittura per una parte dell'applicazione.
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Column(
children: [
Text(
'Standard headline',
style: theme.textTheme.headlineMedium,
),
Theme(
data: theme.copyWith(
colorScheme: theme.colorScheme.copyWith(
primary: Colors.orange,
),
),
child: const SpecialSection(),
),
],
);
}
Il secondo esempio dimostra l'uso di Theme.of(context) per leggere gli stili e Theme() con copyWith per sovrascrivere il tema in una parte dell'albero senza modificare la configurazione globale. SpecialSection all'interno di Theme riceverà il colore primary arancione, mentre il resto dell'applicazione mantiene l'indaco originale.
In sintesi: il tema globale viene impostato in MaterialApp, accessibile tramite Theme.of(context) e sovrascritto tramite Theme con copyWith. Per Material 3, utilizza ColorScheme.fromSeed.
Errore 1: usare primarySwatch invece di ColorScheme in Material 3. primarySwatch è deprecato e non supportato in Material 3. Quando si usa useMaterial3: true con primarySwatch, Flutter emette un avviso. Soluzione: passare a ColorScheme.fromSeed per la generazione della tavolozza.
Errore 2: codificare direttamente i colori nei widget invece di usare Theme.of(context). Gli sviluppatori scrivono Colors.blue direttamente in un Container invece del colore primary o secondary del tema. Quando il tema cambia, questi colori rimangono invariati, rompendo la coerenza dello stile. Soluzione: usa sempre Theme.of(context).colorScheme.primary o altri colori del tema.
Errore 3: mancanza di darkTheme. Se l'applicazione non definisce darkTheme, quando il tema scuro viene attivato sul dispositivo, Flutter tenta di adattare automaticamente il tema chiaro, spesso con un risultato di bassa qualità. Soluzione: passa sempre darkTheme a MaterialApp, anche se il tema scuro non è una priorità — questa è un'importante UX per gli utenti.
Errore 4: creare un nuovo tema ad ogni build. Se ThemeData viene creato all'interno del metodo build, ogni ricostruzione del widget crea un nuovo oggetto tema, rompendo l'ottimizzazione di InheritedWidget. Soluzione: sposta ThemeData in un campo static o costruttore const al di fuori di build.
In sintesi: una corretta tematizzazione richiede un approccio centralizzato. Sposta ThemeData in costanti, usa gli schemi di colore dal tema e definisci sempre entrambi i temi — chiaro e scuro.
Domande frequenti
ThemeData sono i dati di configurazione (colori, caratteri, stili). Theme è un InheritedWidget che trasmette ThemeData verso il basso nell'albero dei widget. Theme.of(context) restituisce il ThemeData che il Theme più vicino propaga nel contesto corrente.
Controlla themeMode in MaterialApp tramite lo stato dell'applicazione. Cambia lo stato da ThemeMode.light a ThemeMode.dark, e Flutter ricostruirà l'intero albero con il nuovo tema. Per un'animazione fluida, usa AnimatedTheme.
ColorScheme.fromSeed è un metodo di Material 3 che prende un colore di base (seedColor) e genera una tavolozza armoniosa completa: primary, secondary, tertiary, surface, error e le loro varianti on. Permette di ottenere una combinazione di colori coerente da un singolo colore del marchio.
Tramite TextTheme all'interno di ThemeData: specifica TextStyle con fontFamily per ogni livello. Per Google Fonts, usa il pacchetto google_fonts, passando fontFamily direttamente a TextTheme. Per i caratteri locali — includili tramite pubspec.yaml nella sezione fonts.
Sì. Avvolgi lo schermo in un Theme con dati sovrascritti tramite copyWith. Tutti i widget figli all'interno di questo Theme riceveranno gli stili modificati, mentre il resto dell'applicazione mantiene il tema originale. Ciò non crea overhead aggiuntivo.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche