Widget è il concetto centrale del framework Flutter, che descrive la configurazione di un elemento dell'interfaccia utente. Ogni componente visivo, da un pulsante a un'animazione complessa, è un Widget. A differenza di altri framework dove l'UI è descritta da file XML separati o disegnata in modo imperativo, Flutter costruisce l'interfaccia attraverso la composizione di Widget — combinando piccoli elementi indivisibili in un albero gerarchico. Secondo la Documentazione Flutter (2025), la libreria Flutter SDK include oltre 260 Widget integrati che coprono Material Design, Cupertino e stili personalizzati.
Punti Chiave
Un Widget in Flutter è una descrizione immutabile (immutable) di una parte dell'interfaccia utente. Ogni Widget contiene proprietà di configurazione: dimensione, colore, posizione, testo, gestori di eventi e Widget figli. I Widget non vengono renderizzati direttamente — sono progetti (blueprint) sulla base dei quali il Flutter Engine crea un RenderObject, l'oggetto grafico reale sullo schermo.
La filosofia di Flutter dichiara: “Everything is a Widget”. Ciò significa che non solo gli elementi visibili (Text, Image, Button) sono Widget, ma anche i blocchi strutturali (Padding, Center, Column, Stack), i blocchi comportamentali (GestureDetector, AnimatedBuilder) e persino l'applicazione stessa (MaterialApp, CupertinoApp). Questo approccio garantisce uniformità: qualsiasi elemento dello schermo può essere combinato con qualsiasi altro attraverso il semplice annidamento.
Secondo Google I/O 2024 — Flutter Widgets Deep Dive, l'applicazione media su Flutter contiene tra 200 e 1500 Widget in un dato momento. Nonostante questa quantità, Flutter mantiene 60 FPS anche su dispositivi economici grazie alle ottimizzazioni a livello del motore C++ Skia/Impeller. I Widget sono oggetti leggeri (40–80 byte ciascuno), quindi la loro creazione non è un collo di bottiglia delle prestazioni.
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('Ciao, Flutter!'),
),
),
);
}
}
Per capire come funziona Widget, è necessario comprendere l'architettura di Flutter, composta da tre alberi interconnessi. Il primo — Widget tree — descrive la configurazione dell'UI. È un albero leggero che può essere completamente ricostruito a ogni frame (il garbage collector rimuove i vecchi Widget e ne crea di nuovi). I Widget sono immutabili: se il colore del testo cambia, viene creato un nuovo Widget Text con il nuovo colore, quello vecchio viene scartato.
Il secondo albero — Element tree — è il collegamento tra Widget e RenderObject. Element contiene un riferimento al Widget (configurazione) e al RenderObject (rendering). Quando un Widget cambia, Flutter confronta il nuovo Widget con il vecchio Element e decide: aggiornare il RenderObject esistente (se il Widget è dello stesso tipo) o crearne uno nuovo (se il tipo di Widget è cambiato). Questo processo si chiama Reconciliation ed è analogo al Virtual DOM di React.
Il terzo albero — RenderObject tree — è responsabile del rendering effettivo sullo schermo. RenderObject contiene dimensioni, posizioni e metodi di pittura specifici. Il Flutter Engine (C++ Skia o Impeller) attraversa il RenderObject tree e renderizza ogni nodo. Il RenderObject tree è l'albero più pesante, quindi Flutter minimizza le sue modifiche riutilizzando i RenderObject quando si passa a Widget dello stesso tipo.
| Albero | Scopo | Immutable? | Ciclo di Vita |
|---|---|---|---|
| Widget | Configurazione UI (progetto) | Sì | Ricreato a ogni build |
| Element | Collegamento Widget ↔ RenderObject | No | Esiste finché il widget è nell'albero |
| RenderObject | Rendering e layout | No | Pesante, riutilizzato quando possibile |
Flutter divide i Widget in due tipi fondamentali: StatelessWidget e StatefulWidget. StatelessWidget è un widget che non contiene stato mutabile. L'aspetto di un StatelessWidget è completamente determinato dal suo costruttore e non può cambiare dopo il rendering. Esempi: Text, Icon, Divider, Padding. Tutte le proprietà di un StatelessWidget sono dichiarate come final nel costruttore e sono di sola lettura.
Un StatefulWidget è un widget con stato mutabile. È composto da due classi: il Widget stesso (configurazione immutabile, come StatelessWidget) e State (stato mutabile). Separare Widget da State è una decisione architetturale chiave in Flutter. Il Widget viene ricreato a ogni build, ma l'oggetto State continua a vivere per tutto il ciclo di vita del widget nell'albero, preservando il suo stato.
Quando viene chiamato setState(), Flutter segna lo State come “sporco” e nel frame successivo chiama il metodo build() per ricostruire il sottoalbero. Importante: setState() non ricrea il Widget stesso — attiva solo la chiamata build() sullo State esistente. Ciò significa che StatefulWidget può aggiornare l'UI senza perdere lo stato dei Widget figli, finché le chiavi (Key) degli elementi figli rimangono stabili.
// StatelessWidget — l'aspetto non cambia mai
class GreetingWidget extends StatelessWidget {
const GreetingWidget({super.key, required this.name});
final String name;
@override
Widget build(BuildContext context) {
return Text('Ciao, $name');
}
}
// StatefulWidget — contatore con stato mutabile
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('Conteggio: $_count'),
ElevatedButton(
onPressed: () => setState(() => _count++),
child: const Text('Incrementa'),
),
],
);
}
}
Tutti i Widget in Flutter possono essere suddivisi in tre categorie principali in base allo scopo funzionale. Layout Widget — responsabili del posizionamento degli elementi figli sullo schermo. Row e Column dispongono i figli in linea, Stack sovrappone uno sull'altro, Expanded e Flexible distribuiscono lo spazio disponibile. I Layout Widget non hanno una propria rappresentazione visiva — gestiscono la posizione e la dimensione dei widget figli.
Painting Widget — responsabili dello stile visivo. Container combina decorazioni (colore, gradiente, ombra, bordo) con proprietà di layout. Padding aggiunge spaziatura, DecoratedBox disegna uno sfondo, Transform applica trasformazioni (rotazione, scala). I Painting Widget sono i mattoni dello stile visivo e sono spesso usati insieme ai Layout Widget per ottenere l'aspetto desiderato.
Interactive Widget — gestiscono l'input dell'utente. GestureDetector rileva i gesti (tap, swipe, pinch), InkWell aggiunge l'effetto ripple di Material, TextField accetta input di testo, Slider e Switch forniscono elementi di controllo standard. Gli Interactive Widget sollevano eventi attraverso funzioni callback passate al costruttore o gestite tramite provider di stato.
| Categoria | Esempi di Widget | Scopo |
|---|---|---|
| Layout | Row, Column, Stack, Expanded, Flexible, Align | Posizionamento e dimensione degli elementi figli |
| Painting | Container, Padding, DecoratedBox, RotatedBox | Colore, sfondo, bordi, ombre, trasformazioni |
| Interactive | GestureDetector, InkWell, TextField, Slider | Gestione di tocchi, input, gesti |
| Platform | MaterialApp, CupertinoApp, Theme, MediaQuery | Integrazione piattaforma, temi, adattamento |
| Async | FutureBuilder, StreamBuilder, ValueListenableBuilder | Aggiornamento reattivo da dati asincroni |
Secondo Flutter Widget of the Week (Google, 2025), la comunità Flutter utilizza attivamente la combinazione di layout + painting + interactive Widget per costruire praticamente qualsiasi interfaccia. Ad esempio, un pulsante: InkWell (interactive) + Container (painting) + Text (static) + Padding (layout). Questa modularità consente di riutilizzare blocchi standard in contesti diversi senza duplicazione di codice.
La composizione di Widget è il processo di costruzione dell'UI annidando alcuni Widget dentro altri. A differenza dell'ereditarietà classica (extends), dove una classe figlia eredita il comportamento del genitore, Flutter utilizza l'aggregazione: ogni Widget contiene altri Widget attraverso il parametro child (per uno) o children (per molti). Questo approccio offre maggiore flessibilità e riutilizzabilità.
BuildContext è il secondo concetto più importante dopo Widget. BuildContext è un descrittore della posizione di un Widget nell'albero degli elementi. Attraverso BuildContext, un Widget può accedere ai widget antenati (Theme.of(context), MediaQuery.of(context), Navigator.of(context)). BuildContext viene passato al metodo build() ed è utilizzato per interagire con elementi genitori e figli. Ogni Widget ha esattamente un BuildContext, che identifica univocamente la sua posizione nell'albero.
Secondo la Panoramica Architetturale di Flutter (Google, 2025), BuildContext è il fondamento di InheritedWidget — un meccanismo che consente di passare dati verso il basso nell'albero senza passaggio esplicito attraverso costruttori. Theme, MediaQuery, Navigator e Provider utilizzano InheritedWidget internamente. Qualsiasi Widget profondamente annidato può accedere ai dati di un antenato tramite BuildContext.dependOnInheritedWidgetOfExactType, rendendo BuildContext la chiave dell'architettura reattiva di Flutter.
// Composizione di widget tramite annidamento
Scaffold(
appBar: AppBar(title: const Text('La mia App')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('Benvenuto in Flutter',
style: TextStyle(fontSize: 24)),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () { /* nav */ },
child: const Text('Inizia'),
),
],
),
),
)
// Accedere ai temi tramite BuildContext
Text(
'Testo Stilizzato',
style: Theme.of(context).textTheme.headlineMedium,
)
Il primo errore e il più comune è usare StatefulWidget dove StatelessWidget è sufficiente. Molti sviluppatori Flutter principianti creano StatefulWidget per tutti i widget, anche quando lo stato è memorizzato in un provider esterno (Provider, Riverpod, BLoC). Ciò è eccessivo e danneggia le prestazioni. Regola: usa StatelessWidget se lo stato è gestito esternamente o se il widget non ha un proprio stato mutabile.
Il secondo errore è creare Widget all'interno del metodo build senza un costruttore const. Ogni Widget creato senza const viene riallocato a ogni build. Se crei Widget con costruttore const dentro build(), Flutter può riutilizzare la stessa istanza, riducendo il carico del garbage collector. Aggiungi const ovunque possibile — specialmente per Text, Icon, SizedBox, Padding e altri widget stateless.
Il terzo errore è lavorare in modo errato con le chiavi (Key). Flutter usa Key per identificare i Widget durante la ricostruzione dell'albero. Se un elenco di Widget viene ricostruito senza Key, Flutter può confondere l'ordine degli elementi, portando ad animazioni errate o perdita di stato. Aggiungi sempre una Key (ad esempio ValueKey o ObjectKey) per gli elementi negli elenchi, specialmente quando usi ListView.builder con dati dinamici.
Domande Frequenti
StatelessWidget è un widget senza stato mutabile; il suo aspetto è completamente determinato dal costruttore. StatefulWidget è un widget con stato mutabile, che è memorizzato in un oggetto State separato e può essere aggiornato tramite setState() senza ricreare il widget stesso. Usa StatelessWidget dove possibile, StatefulWidget — quando è necessario uno stato locale.
L'immutabilità dei Widget è una decisione architetturale di Flutter per le prestazioni. Se i Widget fossero mutabili, Flutter non potrebbe confrontare in modo sicuro le configurazioni vecchie e nuove a ogni build. L'immutabilità consente a Flutter di determinare rapidamente se un Widget è cambiato (tramite l'operatore ==) e riutilizzare il RenderObject esistente, minimizzando le costose operazioni di rendering.
BuildContext è un descrittore della posizione di un Widget nell'albero degli elementi. Attraverso di esso, un Widget ottiene accesso ai widget antenati (Theme, MediaQuery, Navigator) e a InheritedWidget. BuildContext è anche usato per la navigazione (Navigator.of(context)), mostrare SnackBar e interagire con Provider. Ogni Widget riceve BuildContext attraverso il metodo build() e lo passa ai discendenti.
Usa Row per la disposizione orizzontale degli elementi, Column per la disposizione verticale, Stack per sovrapporre elementi l'uno sull'altro. Row e Column funzionano secondo il principio flexbox: i figli occupano spazio in base a mainAxisSize, mainAxisAlignment e crossAxisAlignment. Stack utilizza figli posizionati per un posizionamento preciso rispetto ai bordi o al centro.
Flutter raggiunge alte prestazioni attraverso tre meccanismi: (1) I Widget sono leggeri — oggetti immutabili leggeri (40–80 byte), la loro creazione non carica il GC. (2) Riutilizzo di RenderObject — quando si passa a un Widget dello stesso tipo, il RenderObject viene riutilizzato, evitando costose ricreazioni. (3) Motore Skia/Impeller — rendering a livello C++ con chiamate di disegno minimizzate attraverso i repaint boundaries.
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