Flutter — Cheat Sheet

Tag: flutter, dart, mobile, cross-platform, widgets, state-management

Livello: intermedio

TL;DR: Framework UI dichiarativo cross-platform basato su Dart. Tutto è un Widget, rendering gestito via canvas Skia/Impeller e separazione tra Widget, Element e RenderObject.

Concetti chiave

Tutto è un Widget: classi immutabili che descrivono la configurazione della UI.

Albero triplo: Widget (configurazione), Element (istanza e ciclo di vita), RenderObject (layout e pittura).

StatelessWidget: widget immutabile senza stato interno, ricostruito solo al variare dei parametri in ingresso.

StatefulWidget: mantiene lo stato persistente tramite una classe State separata tra vari rebuild.

Motore di rendering grafico dedicato (Impeller o Skia), evitando i widget nativi della piattaforma.

Linguaggio Dart: compilazione AOT per produzione ad alte prestazioni e JIT per Hot Reload rapido in sviluppo.

Hot Reload: inietta il codice sorgente aggiornato nella Dart VM mantenendo lo stato applicativo intatto.

Hot Restart: ricompila il codice e azzera completamente lo stato dell'app riavviando l'entrypoint.

Gestione dello stato: approcci integrati (InheritedWidget) o architetturali (Bloc/Cubit, Riverpod, Provider).

Platform Channels: comunicazione asincrona binaria basata su messaggi per invocare API native iOS/Android.

Domande tipiche e risposte pronte

Q: Qual è la differenza tra StatelessWidget e StatefulWidget?R: StatelessWidget descrive un'interfaccia immutabile basata esclusivamente sui parametri ricevuti. StatefulWidget associa un'istanza di State separata che sopravvive ai cicli di build e può mutare nel tempo tramite setState.

Q: Come funziona il meccanismo di rendering e perché Flutter non usa le view native?R: Flutter controlla ogni singolo pixel disegnando direttamente su canvas tramite il proprio engine C++ (Impeller/Skia). Questo elimina il bridge a runtime verso le view native garantendo 60/120 fps costanti e parità grafica totale.

Q: Quando si usa FutureBuilder rispetto a StreamBuilder?R: FutureBuilder gestisce operazioni asincrone a valore singolo che si risolvono una sola volta (come chiamate HTTP). StreamBuilder si mette in ascolto di flussi continui di dati nel tempo (come WebSocket o eventi di sensori).

Q: Cosa succede sotto il cofano quando chiami setState?R: setState marca l'Element associato come dirty e pianifica una nuova esecuzione del metodo build per il frame successivo nel widget tree locale.

Insidie e errori comuni

Invocare computazioni pesanti o chiamate asincrone direttamente dentro il metodo build.

Mancato dispose di Controller, StreamSubscription o FocusNode nei metodi dispose() di uno State.

Chiamare setState su un widget State dopo che è stato rimosso dall'albero (eccezione setState called after dispose).

Usare costruttori non const per widget statici, causando rebuild inutili dell'intero sotto-albero.

Errori di layout unbounded constraints (ad esempio inserire una ListView non shrink-wrapped dentro una Column).

Eseguire operazioni CPU-intensive sul main isolate di Dart bloccando il frame rate (usare compute o worker isolate).

Snippet e comandi

# Verifica configurazione ambiente e dipendenze
flutter doctor -v

# Esecuzione in debug o release su dispositivo target
flutter run -d chrome
flutter run --release

# Analisi statica del codice e formattazione
flutter analyze
dart format .

# Generazione di codice automatico (es. Freezed, JSON Serializable)
dart run build_runner build --delete-conflicting-outputs

Dart

// Esempio StatefulWidget con gestione corretta del ciclo di vita
class CounterView extends StatefulWidget {
  const CounterView({super.key});

  @override
  State<CounterView> createState() => _CounterViewState();
}

class _CounterViewState extends State<CounterView> {
  late final TextEditingController _controller;
  int _counter = 0;

  @override
  void initState() {
    super.initState();
    _controller = TextEditingController();
  }

  void _increment() {
    setState(() {
      _counter++;
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(child: Text('Count: $_counter')),
      floatingActionButton: FloatingActionButton(
        onPressed: _increment,
        child: const Icon(Icons.add),
      ),
    );
  }
}

Da ripassare

[ ] Pattern Bloc / Cubit per separazione tra logica di business e interfaccia grafica

[ ] Riverpod e gestione della dependency injection compile-time

[ ] Architettura del motore di rendering: PipelineOwner, RenderBox e LayoutConstraints

[ ] Comunicazione nativa avanzata tramite FFI (Foreign Function Interface) e MethodChannel

[ ] Profilazione delle performance con Flutter DevTools (CPU profiler, raster thread, memory leaks)

Jarvis Interviews
Jarvis