Cos'è Flutter e come fa a disegnare la stessa app ovunque
Cos'è Flutter: il motore che disegna ogni pixel senza componenti nativi, i widget, la gestione dello stato, il web come punto debole e gli errori comuni.
Hai aperto la stessa app Flutter su un iPhone e su un Android e ti sei accorto che è identica al pixel: stessi pulsanti, stesse ombre, stesse animazioni. Comodo, finché un utente iOS ti scrive che "sembra un'app Android" e non capisci da dove venga la sensazione. In questo articolo trovi la risposta, che è anche la chiave per capire tutto il resto di Flutter: non usa i componenti del sistema, li disegna da sé.
Cos'è Flutter
Flutter è un framework open source di Google per costruire interfacce grafiche da un'unica base di codice scritta in Dart, che invece di usare i componenti nativi di iOS e Android disegna ogni elemento a schermo con il proprio motore di rendering, come farebbe un videogioco.
Se devi ancora decidere se il cross-platform fa per te, parti da come scegliere la tecnologia per un'app mobile. Se sei già al bivio tra Flutter e React Native, il confronto rapido è in React Native o Flutter. Qui invece entriamo nel meccanismo.
Il linguaggio è Dart, e non lo rispiego: la doppia compilazione JIT/AOT che rende possibile l'hot reload e il modello a isolate sono in cos'è Dart. Ti basta sapere che in produzione il codice diventa codice macchina nativo, senza un interprete in mezzo.
Il punto centrale: Flutter disegna, non chiede
Quasi tutti i framework di interfaccia funzionano per delega. Tu dici "voglio un pulsante", il framework chiede al sistema operativo un pulsante, e il sistema disegna il suo. React Native funziona così: a schermo finiscono i veri componenti di iOS e Android.
Flutter fa l'opposto. Il sistema operativo gli concede una superficie vuota — sostanzialmente una tela — e Flutter ci disegna sopra tutto: testo, bordi, ombre, il cursore lampeggiante di un campo di testo, l'effetto di rimbalzo dello scorrimento. Il motore di rendering (oggi si chiama Impeller, in passato era basato su Skia) parla direttamente con la GPU.
Il pulsante "stile iOS" che vedi in un'app Flutter non è un pulsante iOS. È un'imitazione, scritta in Dart dal team di Flutter, che assomiglia molto a quello vero.
Da questa singola scelta discendono quasi tutti i pro e i contro del framework.
Cosa guadagni
- Aspetto identico ovunque. Non esistono differenze di resa tra versioni di Android, produttori di telefoni o sistemi operativi. Quello che vedi sul simulatore è quello che vede l'utente.
- Controllo totale della grafica. Animazioni complesse, forme personalizzate, transizioni elaborate: tutto è codice tuo, nessun componente di sistema che si rifiuta di fare quello che vuoi.
- Prestazioni prevedibili. Non c'è un passaggio continuo di messaggi tra il tuo codice e i componenti nativi: il disegno avviene tutto dentro il motore.
- Portabilità. Se l'unica cosa che ti serve da una piattaforma è una superficie su cui disegnare, portare Flutter su desktop o nel browser diventa possibile. Ci torniamo.
Cosa paghi
- L'app non sembra nativa in automatico. Di default Flutter usa il suo design Material ovunque. Se vuoi che su iOS abbia l'aspetto di un'app iOS devi usare i widget Cupertino, o gestire le due varianti a mano. Ed è qui che nasce la sensazione "sembra un'app Android".
- Ogni cambio grafico del sistema va reimplementato. Quando Apple o Google rinnovano il linguaggio visivo dei loro controlli, un'app nativa lo eredita gratis alla prima ricompilazione. Un'app Flutter no: qualcuno deve riscrivere quei widget in Dart, e finché non succede la tua app resta con l'aspetto precedente.
- I dettagli di piattaforma sono imitazioni. Selezione del testo, menu contestuali, comportamento della tastiera, accessibilità: Flutter li riproduce con cura, ma l'utente più attento a volte percepisce che qualcosa è leggermente diverso.
- Integrare componenti nativi costa. Una mappa, un player video, una WebView sono componenti del sistema. Flutter li inserisce nella sua tela tramite le cosiddette platform view, che funzionano ma hanno un costo in prestazioni e complessità.
Non è una scelta giusta o sbagliata: è un compromesso consapevole. Se la tua app ha un'identità grafica forte e personalizzata, il "contro" quasi sparisce. Se deve sembrare indistinguibile da un'app di sistema, diventa lavoro in più.
Tutto è un widget
In Flutter l'interfaccia si costruisce componendo widget, e la parola va presa alla lettera. Un pulsante è un widget, ma lo sono anche il margine attorno al pulsante (Padding), il suo allineamento (Center), la colonna che lo contiene (Column), il tema grafico dell'app (Theme).
Non esiste un file di stile separato né un sistema di layout a parte: anche il layout è fatto di widget annidati.
class SchedaProdotto extends StatelessWidget {
const SchedaProdotto({super.key, required this.nome, required this.prezzo});
final String nome;
final String prezzo;
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(nome, style: Theme.of(context).textTheme.titleMedium),
const SizedBox(height: 8),
Text(prezzo),
],
),
),
);
}
}
L'idea è la composizione: invece di ereditare da un componente enorme e configurarlo con decine di parametri, metti insieme pezzi piccoli che fanno una cosa sola. È potente e coerente, e dopo qualche giorno diventa naturale.
Il rovescio è che il codice cresce in profondità molto in fretta. Quella parentesi di chiusura a sette livelli di indentazione, se non stai attento, diventa la norma.
Stateless e Stateful: dove vive lo stato
I widget sono descrizioni immutabili dell'interfaccia. Quando qualcosa cambia, Flutter non modifica il widget esistente: ne costruisce uno nuovo chiamando di nuovo build, e confronta il risultato con quello precedente per aggiornare solo ciò che serve.
Da qui le due famiglie di base:
- StatelessWidget: dipende solo dai parametri che riceve. Come la scheda prodotto sopra.
- StatefulWidget: ha uno stato interno che può cambiare nel tempo. Quando chiami
setState, Flutter ricostruisce quel widget.
class Contatore extends StatefulWidget {
const Contatore({super.key});
@override
State<Contatore> createState() => _ContatoreState();
}
class _ContatoreState extends State<Contatore> {
int _valore = 0;
@override
Widget build(BuildContext context) {
return TextButton(
onPressed: () => setState(() => _valore++),
child: Text('Premuto $_valore volte'),
);
}
}
Per un contatore basta. Il problema arriva quando lo stesso dato — l'utente autenticato, il carrello, le preferenze — serve a dieci schermate diverse. Passarlo di widget in widget attraverso i costruttori diventa rapidamente ingestibile.
È il motivo per cui nell'ecosistema Flutter esiste un intero filone di librerie di gestione dello stato. Le più diffuse sono Provider, Riverpod e Bloc, con filosofie diverse: più leggere e vicine al framework le prime due, più strutturata e basata su eventi la terza. Non esiste una scelta ufficiale unica, e questo per chi inizia è una fonte di confusione reale: ogni tutorial ne usa una diversa. Il consiglio pratico è sceglierne una all'inizio del progetto e restarci.
Oltre il mobile: desktop e web
Siccome Flutter ha bisogno solo di una superficie su cui disegnare, gira anche su Windows, macOS, Linux e nel browser.
Sul desktop funziona bene per applicazioni interne, strumenti e prodotti che hanno già una versione mobile in Flutter. Vale lo stesso discorso dell'aspetto: non sembrerà un'app Windows o macOS nativa senza lavoro esplicito. Se il desktop è il tuo obiettivo principale, confronta le alternative in come creare un'app desktop.
Sul web è il punto più debole, e va detto chiaramente. Flutter nel browser non genera una pagina HTML come faresti a mano: disegna su un canvas, con un motore scaricato insieme all'app. Le conseguenze:
| Aspetto | Cosa succede con Flutter web |
|---|---|
| Peso iniziale | Alto: prima di vedere qualcosa, il browser scarica il motore di rendering |
| SEO | Debole: il contenuto non è HTML semantico che un motore di ricerca legge facilmente |
| Accessibilità | Flutter costruisce un albero semantico parallelo, ma è più fragile di un normale HTML |
| Comportamenti del browser | Selezione del testo, tasto destro, traduzione automatica: spesso diversi da quelli attesi |
| Dove funziona | App web dietro login, dashboard, strumenti interni, versioni web di un'app mobile |
Tradotto: Flutter web ha senso per applicazioni, non per siti.
Errori comuni
Annidare widget all'infinito in un unico build. Un metodo build da trecento righe è illeggibile e difficile da ottimizzare. Estrai sottowidget piccoli e con un nome: è la composizione che il framework ti chiede di fare.
Usare setState per tutto. Va benissimo per lo stato locale di un widget. Usarlo per dati condivisi tra schermate porta a passaggi di parametri contorti e a ricostruzioni troppo ampie. Quando lo stesso dato serve in più punti, è il momento di una libreria di gestione dello stato.
Ignorare il costo delle ricostruzioni. build può essere chiamato molte volte al secondo, per esempio durante un'animazione. Mettere lì dentro calcoli pesanti, chiamate di rete o creazione di oggetti costosi rallenta tutto. Usa i costruttori const dove possibile e tieni build leggero.
Aspettarsi l'aspetto nativo gratis. Se il cliente vuole un'app che su iPhone sembri un'app iOS, va progettato da subito, non scoperto alla fine.
Usarlo per un sito web. Un sito vetrina, un blog, un e-commerce che deve posizionarsi su Google: Flutter è lo strumento sbagliato. Per quello esistono framework web come Next.js.
In sintesi
Flutter disegna ogni pixel con il proprio motore invece di usare i componenti del sistema operativo. È la scelta da cui discende tutto: aspetto identico ovunque, controllo grafico totale e portabilità verso desktop e web.
Il prezzo di quella scelta è l'aspetto nativo, che non arriva in automatico: su iOS l'app può sembrare "diversa", e ogni rinnovamento grafico del sistema va reimplementato dal team di Flutter prima che tu possa averlo.
Il modello a widget è coerente ma chiede disciplina: composizione di pezzi piccoli, build leggeri, e una libreria di gestione dello stato scelta presto quando l'app cresce oltre qualche schermata.
Ha senso sceglierlo per app mobile con un'identità grafica forte, per team che vogliono una base di codice unica anche verso il desktop, e per applicazioni web dietro login. Non ha senso per siti pubblici che vivono di SEO, né quando l'app deve sembrare indistinguibile da un'app di sistema.
Se vuoi costruire le basi per lavorare su progetti come questi, trovi i percorsi nei corsi. Se invece hai un'app da realizzare e vuoi capire se Flutter è la strada giusta, è uno dei servizi di cui mi occupo.
Contenuto redatto con l'assistenza di strumenti di intelligenza artificiale e rivisto dalla redazione di Codegrind, che ne è responsabile. Hai trovato un errore? Scrivici.