Hot reload e Widget Inspector: debuggare la UI Flutter con i DevTools

Foto di Will Ulmer su Unsplash

GuidePrincipiante30 min Flutter 3.x

Hot reload e Widget Inspector: debuggare la UI Flutter con i DevTools

Uno dei motivi per cui Flutter è così amato è la velocità del ciclo di sviluppo: modifichi il codice, salvi e in meno di un secondo vedi il risultato sul dispositivo. Questa magia si chiama hot reload.

Ma hot reload da solo non basta: quando un layout non si comporta come previsto (un Container che non si colora, un testo che va in overflow, uno spazio che non capisci da dove arrivi) hai bisogno di uno strumento che ti mostri l'albero dei widget e le loro proprietà reali a runtime. È il compito del Widget Inspector, incluso nei Flutter DevTools.

In questo tutorial per principianti vedremo:

  • la differenza tra hot reload, hot restart e full restart;
  • quando hot reload non funziona e perché;
  • come aprire i DevTools da VS Code, Android Studio e da terminale;
  • come usare il Widget Inspector, il Select Widget Mode, il Layout Explorer e le guide di debug (debugPaintSizeEnabled, Slow Animations, Highlight Repaints).

Alla fine avrai un metodo pratico per capire cosa sta realmente disegnando la tua app, invece di procedere per tentativi.

  1. 1

    Preparare un progetto di prova con un layout "sbagliato"

    Creiamo un progetto nuovo e sostituiamo il contenuto di lib/main.dart con una schermata semplice, che useremo come cavia per gli esperimenti.

    flutter create devtools_demo
    cd devtools_demo
    flutter run
    

    Il codice qui sotto mostra una card con avatar, titolo e descrizione. Il testo del sottotitolo è volutamente lungo: più avanti lo useremo per generare (e risolvere) un errore di overflow.

    Avvia l'app con flutter run da terminale, oppure con il tasto Run/Debug del tuo IDE: è importante avviarla in modalità debug, perché hot reload e DevTools non funzionano in release.

    import 'package:flutter/material.dart';
    
    void main() => runApp(const MyApp());
    
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'DevTools Demo',
          theme: ThemeData(colorSchemeSeed: Colors.indigo, useMaterial3: true),
          home: const HomePage(),
        );
      }
    }
    
    class HomePage extends StatefulWidget {
      const HomePage({super.key});
    
      @override
      State<HomePage> createState() => _HomePageState();
    }
    
    class _HomePageState extends State<HomePage> {
      int _contatore = 0;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: const Text('DevTools Demo')),
          body: Center(
            child: Card(
              margin: const EdgeInsets.all(16),
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: Row(
                  children: [
                    const CircleAvatar(radius: 28, child: Icon(Icons.person)),
                    const SizedBox(width: 16),
                    Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      mainAxisSize: MainAxisSize.min,
                      children: [
                        const Text('Mario Rossi',
                            style: TextStyle(fontSize: 20)),
                        Text('Hai premuto il bottone $_contatore volte'),
                      ],
                    ),
                  ],
                ),
              ),
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () => setState(() => _contatore++),
            child: const Icon(Icons.add),
          ),
        );
      }
    }

    Risultato atteso

    L'app parte in modalità debug e mostra una card centrata con avatar, nome e contatore; il FAB incrementa il numero.

  2. 2

    Usare hot reload e capire la differenza con hot restart

    Con l'app in esecuzione, premi il FAB 3 volte: il contatore arriva a 3. Ora modifica il colore del tema e salva.

    • In VS Code / Android Studio basta salvare il file (Ctrl+S / Cmd+S): l'hot reload parte da solo.
    • Da terminale, nella console dove gira flutter run, premi il tasto r.

    Noterai due cose fondamentali:

    1. La UI si aggiorna in meno di un secondo.
    2. Il contatore resta a 3: lo stato dell'app è conservato.

    È questa la differenza chiave:

    Comando Tasto Cosa fa Stato
    Hot reload r Inietta il nuovo codice e ricostruisce i widget Conservato
    Hot restart R Ricarica l'app da zero mantenendo il processo Azzerato
    Full restart stop + run Ricompila tutto (nativo incluso) Azzerato

    Quando hot reload NON basta

    Devi usare hot restart (o riavviare del tutto) quando:

    • cambi il codice di main() o gli inizializzatori di variabili static/globali;
    • modifichi lo stato iniziale in initState() e vuoi rivederlo eseguire;
    • cambi il tipo di un widget (da StatelessWidget a StatefulWidget);
    • aggiungi o modifichi enum, gerarchie di classi o generici in modo strutturale.

    Serve invece uno stop e run completo quando:

    • aggiungi un pacchetto con codice nativo in pubspec.yaml;
    • modifichi asset dichiarati nel pubspec.yaml (a volte basta hot restart);
    • tocchi file nativi in android/ o ios/.

    Prova tu: cambia il valore iniziale in int _contatore = 100;, salva (hot reload) e osserva che non cambia nulla. Premi ora R (hot restart) e vedrai 100.

    // Prova 1 -> hot reload: la UI cambia colore, il contatore resta invariato
    theme: ThemeData(colorSchemeSeed: Colors.deepOrange, useMaterial3: true),
    
    // Prova 2 -> hot reload NON ha effetto, serve hot restart (tasto R)
    int _contatore = 100;

    Risultato atteso

    Con hot reload cambiano i colori senza perdere il contatore; con hot restart l'app riparte dal nuovo valore iniziale.

  3. 3

    Aprire i DevTools e il Widget Inspector

    I Flutter DevTools sono una suite di strumenti web (Inspector, Performance, Memory, Network, Logging) inclusa nell'SDK: non devi installare nulla.

    Da VS Code

    1. Avvia l'app in debug.
    2. Ctrl+Shift+P / Cmd+Shift+P"Flutter: Open DevTools" → scegli Open in Browser o Widget Inspector.

    Da Android Studio / IntelliJ

    1. Avvia l'app in debug.
    2. Nella barra della finestra Flutter Inspector (a destra) o dal pulsante Open DevTools nella toolbar di esecuzione.

    Da terminale

    Quando lanci flutter run, la console stampa un URL simile a:

    A Dart VM Service on sdk gphone64 is available at: http://127.0.0.1:52341/AbCdEf=/
    The Flutter DevTools debugger and profiler is available at:
    http://127.0.0.1:9101?uri=http://127.0.0.1:52341/AbCdEf=/
    

    Apri il secondo link nel browser. In alternativa premi v nella console di flutter run.

    Una volta dentro, seleziona la scheda Flutter Inspector: a sinistra vedrai l'albero dei widget della tua app, a destra i dettagli del widget selezionato (constraints, dimensioni, proprietà di rendering).

    # Avvia l'app e apri i DevTools dal terminale
    flutter run
    
    # Tasti utili nella console di flutter run:
    #   r  -> hot reload
    #   R  -> hot restart
    #   v  -> apre i DevTools nel browser
    #   p  -> mostra/nasconde le guide di debug del layout
    #   o  -> alterna piattaforma Android/iOS
    #   q  -> esce

    Risultato atteso

    I DevTools si aprono nel browser (o nell'IDE) e la scheda Flutter Inspector mostra l'albero dei widget dell'app in esecuzione.

  4. 4

    Individuare un widget con il Select Widget Mode

    Il Select Widget Mode è la funzione più utile per chi inizia: ti permette di toccare un elemento sullo schermo dell'app e vedere immediatamente quale widget lo disegna nell'albero.

    Come si usa:

    1. Nei DevTools, clicca l'icona Select Widget Mode (il cursore con il riquadro) in alto a sinistra nell'Inspector.
    2. Tocca l'avatar della card sul dispositivo/emulatore.
    3. Nell'albero verrà evidenziato il CircleAvatar, e nel pannello di destra vedrai le sue proprietà e la catena di genitori (RowPaddingCardCenter → ...).

    Suggerimenti pratici:

    • Il pulsante "Show Implementation Widgets" (o la spunta relativa) mostra anche i widget interni del framework: tienilo spento all'inizio per non perderti.
    • Selezionando un widget nell'albero, con il pulsante di navigazione al sorgente l'IDE salta direttamente alla riga di codice che lo crea.
    • La sezione Details Tree / Render Object ti mostra size e constraints reali: è oro puro per capire perché un widget è più grande o più piccolo del previsto.

    Prova a selezionare il Text('Mario Rossi') e leggi le sue dimensioni effettive: capirai subito quanto spazio occupa davvero.

    Risultato atteso

    Toccando un elemento dell'app, il widget corrispondente viene evidenziato nell'albero dei DevTools con tutte le sue proprietà.

  5. 5

    Risolvere un overflow con il Layout Explorer

    Ora provochiamo l'errore più comune per chi inizia: la famosa striscia gialla e nera "RIGHT OVERFLOWED BY X PIXELS".

    Sostituisci il testo del sottotitolo con una frase lunga e salva (hot reload):

    Text('Sviluppatore Flutter con una passione smisurata per i layout complessi'),
    

    La Column dentro la Row cerca di occupare la larghezza del testo, ma non c'è spazio: overflow.

    Diagnosticare con il Layout Explorer

    1. Con il Select Widget Mode, seleziona la Row.
    2. Apri la scheda Layout Explorer (accanto ai dettagli).
    3. Vedrai una rappresentazione grafica di Row/Column con: larghezze dei figli, flex, mainAxisAlignment e crossAxisAlignment modificabili al volo.
    4. Il figlio che sfora è evidenziato in giallo/rosso.

    La soluzione

    Basta avvolgere la Column in un Expanded: le dice "prendi tutto lo spazio orizzontale rimasto, non di più". Aggiungiamo anche maxLines e overflow: TextOverflow.ellipsis per un troncamento elegante.

    Salva (hot reload) e l'errore scompare istantaneamente.

    Nota: nel Layout Explorer puoi cambiare mainAxisAlignment con un menu a tendina e vedere l'effetto in tempo reale sull'app. È il modo più veloce per imparare come funzionano Row e Column.

    Row(
      children: [
        const CircleAvatar(radius: 28, child: Icon(Icons.person)),
        const SizedBox(width: 16),
        Expanded( // <-- risolve l'overflow
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            mainAxisSize: MainAxisSize.min,
            children: [
              const Text('Mario Rossi', style: TextStyle(fontSize: 20)),
              Text(
                'Sviluppatore Flutter con una passione smisurata per i layout complessi',
                maxLines: 2,
                overflow: TextOverflow.ellipsis,
              ),
            ],
          ),
        ),
      ],
    )

    Risultato atteso

    La striscia gialla e nera di overflow sparisce e il testo lungo viene troncato con i puntini di sospensione su due righe.

  6. 6

    Attivare le guide visive di debug: paint, animazioni e repaint

    L'Inspector offre alcuni interruttori nella barra in alto che disegnano informazioni direttamente sopra l'app. Sono la versione visuale di variabili di debug che puoi anche impostare da codice.

    Show Guidelines / Debug Paint (o tasto p nella console): disegna i bordi di ogni box, le frecce del padding e gli allineamenti. Perfetto per capire "da dove arriva questo spazio".

    Show Baselines: mostra le baseline tipografiche, utile per allineare testi di dimensioni diverse.

    Slow Animations: rallenta le animazioni di 5 volte per verificarne fluidità e curve.

    Highlight Repaints: colora i bordi dei layer che vengono ridisegnati; se un'area lampeggia in continuazione hai un problema di performance.

    Highlight Oversized Images: evidenzia le immagini caricate a una risoluzione molto maggiore di quella mostrata (spreco di memoria).

    Puoi attivare le stesse opzioni da codice, ma ricorda di rimuoverle prima del rilascio: sono strumenti di sviluppo.

    Metti tutto insieme con questo flusso di lavoro:

    1. Scrivi il layout → salva → hot reload.
    2. Qualcosa non torna? → Select Widget Mode sul widget incriminato.
    3. Problema di spazi/dimensioni? → Layout Explorer + Debug Paint.
    4. Lo stato è sporco o l'app parte male? → hot restart (R).
    5. Hai aggiunto un pacchetto nativo? → stop e full restart.
    import 'package:flutter/rendering.dart';
    
    void main() {
      // Attiva le guide di layout solo in sviluppo.
      // In alternativa usa gli interruttori dei DevTools (consigliato).
      debugPaintSizeEnabled = true;      // bordi e padding di ogni box
      // debugPaintBaselinesEnabled = true; // baseline del testo
      // debugRepaintRainbowEnabled = true; // colora i layer ridisegnati
    
      runApp(const MyApp());
    }

    Risultato atteso

    L'app mostra bordi, frecce di padding e guide sopra ogni widget: puoi individuare visivamente margini e dimensioni inattesi.

CondividiXLinkedInFacebookWhatsApp

Commenti (0)

Ancora nessun commento. Inizia tu!