[{"data":1,"prerenderedAt":86},["ShallowReactive",2],{"tutorial-gli-errori-tipici-di-chi-inizia-con-flutter-overflow-setstate-fuori-posto-e-null-safety":3,"comments-tutorial-gli-errori-tipici-di-chi-inizia-con-flutter-overflow-setstate-fuori-posto-e-null-safety":85},{"id":4,"title":5,"slug":6,"excerpt":7,"intro":8,"cover_image":9,"cover_remote_url":10,"cover_credit":11,"video_url":15,"video":16,"difficulty":20,"estimated_minutes":21,"flutter_version":22,"status":23,"published_at":24,"meta_title":25,"meta_description":26,"category":27,"author":31,"steps":33},80,"Gli errori tipici di chi inizia con Flutter: overflow, setState fuori posto e null safety","gli-errori-tipici-di-chi-inizia-con-flutter-overflow-setstate-fuori-posto-e-null-safety","Una guida pratica ai tre errori che bloccano quasi tutti i principianti Flutter: RenderFlex overflow, setState chiamato nel posto sbagliato e problemi di null safety. Con cause, messaggi di errore e soluzioni testate.","Quando si inizia con Flutter, ci si scontra quasi sempre con gli stessi tre ostacoli: la fascia gialla e nera dell'**overflow**, l'errore `setState() called after dispose()` (o peggio, `setState() during build`) e i temuti errori di **null safety** come `LateInitializationError` o `Null check operator used on a null value`.\n\nLa buona notizia è che questi errori non sono bug del framework: sono messaggi che Flutter ti manda per dirti che stai violando una regola precisa. Una volta capita la regola, l'errore diventa banale da risolvere.\n\nIn questo tutorial ricreeremo **volontariamente** ciascun errore in un piccolo progetto di prova, leggeremo insieme il messaggio nella console e applicheremo la correzione corretta (non il classico \"workaround\" che nasconde il problema).\n\n> Livello: principiante. Serve solo un progetto Flutter funzionante e l'editor con il terminale aperto.","https:\u002F\u002Fflutter.it\u002Fstorage\u002Ftutorials\u002F54c0ea34-75bd-474d-b5a2-c43b5d57a3e7.jpg","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1604591259403-81d6c9cf87d7?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w5NzA2NTJ8MHwxfHJhbmRvbXx8fHx8fHx8fDE3ODgxNTA2OTh8&ixlib=rb-4.1.0&q=80&w=1080",{"name":12,"author_url":13,"photo_url":14},"Artur Shamsutdinov","https:\u002F\u002Funsplash.com\u002F@roketpik","https:\u002F\u002Funsplash.com\u002Fphotos\u002Ftext-7MJpHQdGXGA","https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=8k1_Pb90kC4",{"youtube_id":17,"duration_seconds":18,"published_at":19},"8k1_Pb90kC4",140,"2026-08-31T11:15:33+00:00","beginner",35,"3.x","published","2026-08-31T04:31:39+00:00","Errori tipici Flutter: overflow, setState e null safety","Guida per principianti agli errori più comuni in Flutter: RenderFlex overflow, setState fuori posto e null safety. Cause, messaggi e soluzioni pratiche.",{"id":28,"name":29,"slug":30},1,"Guide","guide",{"id":28,"name":32},"Flutter Bot",[34,42,49,56,63,70,77],{"id":35,"position":28,"title":36,"body":37,"code_snippet":38,"code_language":39,"expected_result":40,"demo_url":41,"video_url":41},533,"Preparare un progetto di prova e imparare a leggere gli errori","Creiamo un progetto pulito dove riprodurre gli errori in sicurezza.\n\n```bash\nflutter create errori_flutter\ncd errori_flutter\nflutter run\n```\n\nPrima di scrivere codice, tieni a mente **dove guardare** quando qualcosa va storto:\n\n- **Sullo schermo**: il riquadro rosso (\"Red screen of death\") in debug mostra il messaggio dell'eccezione di build.\n- **Nella console** (terminale o pannello *Debug Console*): il blocco che inizia con `══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞══` contiene la causa vera.\n- La riga più utile è quasi sempre quella che cita **un file del tuo progetto** (es. `package:errori_flutter\u002Fmain.dart:42`), non i file interni di Flutter.\n\nSostituisci il contenuto di `lib\u002Fmain.dart` con lo scheletro qui sotto: nei prossimi passi cambieremo solo il corpo di `HomePage`.","import 'package:flutter\u002Fmaterial.dart';\n\nvoid main() => runApp(const MyApp());\n\nclass MyApp extends StatelessWidget {\n  const MyApp({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return MaterialApp(\n      title: 'Errori Flutter',\n      theme: ThemeData(colorSchemeSeed: Colors.indigo, useMaterial3: true),\n      home: const HomePage(),\n    );\n  }\n}\n\nclass HomePage extends StatelessWidget {\n  const HomePage({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(title: const Text('Laboratorio errori')),\n      body: const Center(child: Text('Pronti a sbagliare!')),\n    );\n  }\n}","dart","L'app si avvia mostrando una AppBar e il testo centrato \"Pronti a sbagliare!\". Console senza errori.",null,{"id":43,"position":44,"title":45,"body":46,"code_snippet":47,"code_language":39,"expected_result":48,"demo_url":41,"video_url":41},534,2,"Errore 1: RenderFlex overflowed — la fascia gialla e nera","L'overflow avviene quando i figli di una `Row` o `Column` chiedono **più spazio di quello disponibile**. Flutter non ridimensiona automaticamente: te lo segnala.\n\nProva questo `body` e osserva la fascia gialla\u002Fnera a destra:\n\n```dart\nbody: Row(\n  children: [\n    Container(width: 200, height: 100, color: Colors.red),\n    Container(width: 200, height: 100, color: Colors.green),\n    Container(width: 200, height: 100, color: Colors.blue),\n  ],\n),\n```\n\nIn console leggerai: `A RenderFlex overflowed by 137 pixels on the right.`\n\n**Le tre soluzioni corrette**, da scegliere in base all'intenzione:\n\n1. `Expanded` \u002F `Flexible`: il figlio occupa lo spazio residuo. Usalo quando i widget devono *dividersi* lo spazio.\n2. `SingleChildScrollView` (o `ListView`): il contenuto deve poter **scorrere**, tipico quando appare la tastiera in un form.\n3. Per il testo lungo: avvolgilo in `Expanded` e usa `maxLines` + `overflow: TextOverflow.ellipsis`.\n\n⚠️ **Errore da non fare**: mettere un `Container` con altezza infinita o usare `Expanded` dentro un widget che non è `Row`\u002F`Column`\u002F`Flex` → otterresti `Incorrect use of ParentDataWidget`.","\u002F\u002F VERSIONE CORRETTA\nbody: Column(\n  children: [\n    \u002F\u002F 1. I tre blocchi si dividono la larghezza disponibile\n    Row(\n      children: [\n        Expanded(child: Container(height: 100, color: Colors.red)),\n        Expanded(child: Container(height: 100, color: Colors.green)),\n        Expanded(child: Container(height: 100, color: Colors.blue)),\n      ],\n    ),\n    const SizedBox(height: 16),\n    \u002F\u002F 2. Testo lungo dentro una Row: Expanded + ellipsis\n    Padding(\n      padding: const EdgeInsets.symmetric(horizontal: 16),\n      child: Row(\n        children: [\n          const Icon(Icons.info_outline),\n          const SizedBox(width: 8),\n          Expanded(\n            child: Text(\n              'Questo è un testo molto lungo che altrimenti causerebbe un overflow orizzontale nella Row.',\n              maxLines: 2,\n              overflow: TextOverflow.ellipsis,\n            ),\n          ),\n        ],\n      ),\n    ),\n  ],\n),","La fascia gialla e nera sparisce: i tre riquadri si adattano alla larghezza e il testo lungo viene troncato con i puntini.",{"id":50,"position":51,"title":52,"body":53,"code_snippet":54,"code_language":39,"expected_result":55,"demo_url":41,"video_url":41},535,3,"Errore 2: overflow verticale e ListView dentro Column","Il secondo overflow più frequente è **verticale**, e nasce da due situazioni.\n\n**a) Contenuto più alto dello schermo** (tipico quando si apre la tastiera): la soluzione è rendere scrollabile la colonna con `SingleChildScrollView`.\n\n**b) `ListView` dentro una `Column`**: qui il messaggio è diverso, `Vertical viewport was given unbounded height`. La `Column` dice al figlio \"prendi l'altezza che vuoi\", ma la `ListView` risponde \"dimmi tu quanta ne ho\". Nessuno decide → errore.\n\nLe soluzioni:\n\n- Avvolgi la `ListView` in **`Expanded`** se deve occupare lo spazio rimanente e scorrere (soluzione preferita).\n- Usa `shrinkWrap: true` + `physics: NeverScrollableScrollPhysics()` **solo** se la lista è corta e deve scorrere insieme al resto della pagina: `shrinkWrap` costruisce tutti gli elementi, quindi è costoso su liste lunghe.\n\nProva prima la versione sbagliata (togliendo `Expanded`) per vedere l'errore, poi applica il codice corretto.","class HomePage extends StatelessWidget {\n  const HomePage({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    final items = List.generate(50, (i) => 'Elemento ${i + 1}');\n\n    return Scaffold(\n      appBar: AppBar(title: const Text('Overflow verticale')),\n      body: Column(\n        crossAxisAlignment: CrossAxisAlignment.stretch,\n        children: [\n          const Padding(\n            padding: EdgeInsets.all(16),\n            child: Text('Intestazione fissa',\n                style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),\n          ),\n          \u002F\u002F Expanded dà alla ListView un'altezza definita: niente unbounded height\n          Expanded(\n            child: ListView.builder(\n              itemCount: items.length,\n              itemBuilder: (context, index) => ListTile(\n                leading: const Icon(Icons.chevron_right),\n                title: Text(items[index]),\n              ),\n            ),\n          ),\n        ],\n      ),\n    );\n  }\n}","L'intestazione resta fissa in alto e la lista di 50 elementi scorre correttamente, senza errori in console.",{"id":57,"position":58,"title":59,"body":60,"code_snippet":61,"code_language":39,"expected_result":62,"demo_url":41,"video_url":41},536,4,"Errore 3: setState fuori posto (durante build, dopo dispose, in initState)","`setState()` significa: \"i dati sono cambiati, ricostruisci questo widget\". Va chiamato **solo** in risposta a un evento (tap, timer, risposta di rete) e **solo** finché il widget è vivo.\n\nI tre casi patologici:\n\n1. **`setState() called during build`** — hai chiamato `setState` dentro `build()` (es. `onPressed: _incrementa()` invece di `onPressed: _incrementa`, oppure aggiornando lo stato mentre costruisci la UI). Si crea un loop infinito. *Soluzione*: passa il riferimento alla funzione, oppure usa `WidgetsBinding.instance.addPostFrameCallback` se devi davvero aggiornare dopo il primo frame.\n2. **`setState() called after dispose()`** — un `Future` o un `Timer` termina quando l'utente ha già lasciato la schermata. *Soluzione*: controlla `if (!mounted) return;` prima di chiamare `setState`, e cancella timer\u002Fsubscription in `dispose()`.\n3. **`setState` con operazioni pesanti dentro** — mettici solo l'assegnazione delle variabili, il lavoro (chiamate HTTP, calcoli) va fatto **fuori**, prima.\n\n⚠️ Ricorda anche: modificare una variabile senza `setState` non aggiorna la UI; e `setState` in uno `StatelessWidget` non esiste (serve `StatefulWidget`).","class ContatoreDemo extends StatefulWidget {\n  const ContatoreDemo({super.key});\n\n  @override\n  State\u003CContatoreDemo> createState() => _ContatoreDemoState();\n}\n\nclass _ContatoreDemoState extends State\u003CContatoreDemo> {\n  int _contatore = 0;\n  bool _caricamento = false;\n\n  Future\u003Cvoid> _caricaDati() async {\n    setState(() => _caricamento = true);\n\n    \u002F\u002F Lavoro pesante FUORI da setState\n    await Future.delayed(const Duration(seconds: 2));\n    final risultato = _contatore + 10;\n\n    \u002F\u002F Il widget potrebbe essere stato smontato durante l'attesa\n    if (!mounted) return;\n\n    setState(() {\n      _contatore = risultato;\n      _caricamento = false;\n    });\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(title: const Text('setState corretto')),\n      body: Center(\n        child: Column(\n          mainAxisAlignment: MainAxisAlignment.center,\n          children: [\n            Text('Valore: $_contatore', style: const TextStyle(fontSize: 24)),\n            const SizedBox(height: 16),\n            _caricamento\n                ? const CircularProgressIndicator()\n                \u002F\u002F NOTA: _caricaDati SENZA parentesi -> passiamo la funzione,\n                \u002F\u002F non il suo risultato. Con le parentesi partirebbe durante build.\n                : ElevatedButton(\n                    onPressed: _caricaDati,\n                    child: const Text('Carica'),\n                  ),\n          ],\n        ),\n      ),\n    );\n  }\n}","Premendo \"Carica\" appare lo spinner per 2 secondi, poi il valore aumenta di 10. Uscendo dalla schermata durante il caricamento non compare più l'errore \"setState() called after dispose()\".",{"id":64,"position":65,"title":66,"body":67,"code_snippet":68,"code_language":39,"expected_result":69,"demo_url":41,"video_url":41},537,5,"Errore 4: null safety — `?`, `!`, `late` e i loro tranelli","In Dart una variabile **non può essere null** a meno che il tipo non finisca con `?`. Gli errori più comuni per chi inizia:\n\n- **`The argument type 'String?' can't be assigned to the parameter type 'String'`**: stai passando un valore potenzialmente nullo dove ne serve uno sicuro. Risolvi con un valore di default (`??`) o con un controllo esplicito.\n- **`Null check operator used on a null value`**: hai usato `!` (\"fidati, non è null\") su qualcosa che era null. Il `!` non è una correzione: è una promessa che stai facendo al compilatore.\n- **`LateInitializationError: Field 'x' has not been initialized`**: hai dichiarato `late` una variabile e l'hai letta prima di assegnarla. `late` è utile in `initState`, non come scorciatoia per zittire il compilatore.\n\nRegole pratiche:\n\n1. Usa `?.` per accedere in sicurezza e `??` per un valore di riserva.\n2. Usa il **type promotion**: `if (valore != null) { ... }` su una variabile locale rende il tipo non nullabile all'interno del blocco.\n3. Per i campi di classe nullabili, copiali prima in una variabile locale (`final v = campo;`) per abilitare la promozione.\n4. Riserva `!` ai casi in cui hai davvero una garanzia (es. `snapshot.data!` dentro `if (snapshot.hasData)`).","class Utente {\n  final String nome;\n  final String? email; \u002F\u002F può mancare\n\n  Utente({required this.nome, this.email});\n}\n\nString descriviUtente(Utente? utente) {\n  \u002F\u002F 1. Accesso sicuro + valore di default\n  final nome = utente?.nome ?? 'Ospite';\n\n  \u002F\u002F 2. Type promotion su variabile locale\n  final email = utente?.email;\n  if (email != null && email.isNotEmpty) {\n    return '$nome \u003C$email>'; \u002F\u002F qui email è String, non String?\n  }\n\n  return '$nome (email non disponibile)';\n}\n\n\u002F\u002F Uso corretto di late: assegnato in initState, letto solo dopo\nclass _EsempioLateState extends State\u003CEsempioLate> {\n  late final TextEditingController _controller;\n\n  @override\n  void initState() {\n    super.initState();\n    _controller = TextEditingController(text: 'valore iniziale');\n  }\n\n  @override\n  void dispose() {\n    _controller.dispose(); \u002F\u002F sempre! altrimenti memory leak\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return TextField(controller: _controller);\n  }\n}","`descriviUtente(null)` restituisce \"Ospite (email non disponibile)\" senza lanciare eccezioni, e il TextField funziona senza LateInitializationError.",{"id":71,"position":72,"title":73,"body":74,"code_snippet":75,"code_language":39,"expected_result":76,"demo_url":41,"video_url":41},538,6,"Errore 5: usare il BuildContext dopo un await","Molto comune quando si mescolano `async` e navigazione. Dopo un `await`, il widget potrebbe non essere più nell'albero: usare quel `context` per `Navigator`, `ScaffoldMessenger` o `showDialog` provoca eccezioni o warning dell'analyzer (`use_build_context_synchronously`).\n\nLa soluzione moderna (Flutter 3.7+) è il controllo **`context.mounted`**; in alternativa, in uno `State`, il classico `if (!mounted) return;`.\n\nUn'altra buona pratica: **catturare** gli oggetti che dipendono dal context *prima* dell'await, ad esempio `final messenger = ScaffoldMessenger.of(context);`.","Future\u003Cvoid> _salvaEChiudi(BuildContext context) async {\n  \u002F\u002F Catturiamo prima dell'await ciò che dipende dal context\n  final messenger = ScaffoldMessenger.of(context);\n  final navigator = Navigator.of(context);\n\n  await Future.delayed(const Duration(seconds: 1)); \u002F\u002F simula il salvataggio\n\n  \u002F\u002F Controllo esplicito prima di usare di nuovo il context\n  if (!context.mounted) return;\n\n  messenger.showSnackBar(\n    const SnackBar(content: Text('Salvataggio completato')),\n  );\n  navigator.pop();\n}","Nessun warning dell'analyzer e nessuna eccezione se l'utente chiude la schermata durante l'operazione asincrona.",{"id":78,"position":79,"title":80,"body":81,"code_snippet":82,"code_language":83,"expected_result":84,"demo_url":41,"video_url":41},539,7,"Checklist finale e strumenti per non ricadere negli stessi errori","Prima di chiedere aiuto su un errore, passa questa checklist:\n\n**Overflow**\n- [ ] Il figlio problematico è dentro `Row`\u002F`Column`? → prova `Expanded` o `Flexible`.\n- [ ] Il contenuto può superare lo schermo (tastiera, testi lunghi, tablet)? → `SingleChildScrollView`.\n- [ ] `ListView` dentro `Column`? → `Expanded` (o `shrinkWrap: true` solo per liste corte).\n- [ ] Testo lungo in una `Row`? → `Expanded` + `overflow: TextOverflow.ellipsis`.\n\n**setState**\n- [ ] Lo chiamo solo da callback\u002Feventi, mai dentro `build`.\n- [ ] `onPressed: _funzione` (senza parentesi).\n- [ ] `if (!mounted) return;` dopo ogni `await`.\n- [ ] `dispose()` cancella controller, timer e subscription.\n\n**Null safety**\n- [ ] Ho evitato `!` a meno di avere una garanzia reale.\n- [ ] Uso `??` e `?.` per i valori opzionali.\n- [ ] `late` solo quando l'inizializzazione avviene sicuramente prima dell'uso.\n\n**Strumenti da attivare subito**\n\n- `flutter analyze` prima di ogni commit.\n- Il pacchetto `flutter_lints` (già incluso nei progetti nuovi) tramite `analysis_options.yaml`.\n- **Flutter DevTools → Widget Inspector**, in particolare il pulsante *Debug Paint*, che disegna i bordi dei layout e rende evidente chi sta sforando.\n\nAggiungi queste regole al tuo `analysis_options.yaml` per farti avvisare in anticipo.","# analysis_options.yaml\ninclude: package:flutter_lints\u002Fflutter.yaml\n\nlinter:\n  rules:\n    - use_build_context_synchronously\n    - prefer_const_constructors\n    - avoid_print\n    - cancel_subscriptions\n    - close_sinks\n\nanalyzer:\n  errors:\n    use_build_context_synchronously: error","yaml","Eseguendo `flutter analyze` l'editor segnala in anticipo l'uso scorretto del BuildContext e le altre cattive pratiche, prima ancora di lanciare l'app.",[],1789205524447]