[{"data":1,"prerenderedAt":85},["ShallowReactive",2],{"tutorial-liste-in-flutter-con-listviewbuilder-e-listtile-la-guida-per-iniziare":3,"comments-tutorial-liste-in-flutter-con-listviewbuilder-e-listtile-la-guida-per-iniziare":84},{"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},74,"Liste in Flutter con ListView.builder e ListTile: la guida per iniziare","liste-in-flutter-con-listviewbuilder-e-listtile-la-guida-per-iniziare","Impara a costruire liste scorrevoli in Flutter partendo da zero: dai dati di esempio a ListView.builder, ListTile, separatori, tocco sugli elementi e gli errori più comuni dei principianti.","Quasi ogni app mostra una lista: contatti, prodotti, messaggi, attività da fare. In Flutter la lista è un widget come tutti gli altri, e imparare a usarlo bene è uno dei primi passi fondamentali.\n\nIn questo tutorial partiamo da una semplice lista di dati in memoria e costruiamo passo dopo passo una schermata scorrevole usando **`ListView.builder`** (la variante efficiente, che costruisce solo gli elementi visibili) e **`ListTile`** (il widget pronto all'uso per le righe di una lista).\n\nAlla fine saprai:\n\n- la differenza tra `ListView` \"statica\" e `ListView.builder`;\n- come comporre una riga con `ListTile` (icona, titolo, sottotitolo, azione);\n- come aggiungere separatori con `ListView.separated`;\n- come reagire al tocco su un elemento;\n- come evitare i classici errori (`ListView` dentro `Column`, altezza illimitata, overflow).\n\n> Serve solo un progetto Flutter funzionante (`flutter create liste_demo`) e un editor. Nessun pacchetto esterno.","https:\u002F\u002Fflutter.it\u002Fstorage\u002Ftutorials\u002F15bdd287-f69d-4695-ba30-6f8670bbfc27.jpg","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1521579498714-ff08ba4836ab?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w5NzA2NTJ8MHwxfHJhbmRvbXx8fHx8fHx8fDE3ODc2MzIyNzl8&ixlib=rb-4.1.0&q=80&w=1080",{"name":12,"author_url":13,"photo_url":14},"Thought Catalog","https:\u002F\u002Funsplash.com\u002F@thoughtcatalog","https:\u002F\u002Funsplash.com\u002Fphotos\u002Fperson-using-phone-YSZS_nDU8js","https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=lTIIOAv9Wb4",{"youtube_id":17,"duration_seconds":18,"published_at":19},"lTIIOAv9Wb4",122,"2026-08-25T05:22:42+00:00","beginner",35,"3.x","published","2026-08-25T04:31:19+00:00","ListView.builder e ListTile in Flutter: guida base","Guida per principianti alle liste in Flutter: ListView.builder, ListTile, separatori, onTap e gli errori più comuni da evitare. Esempi di codice pronti.",{"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},493,"Preparare il progetto e i dati di esempio","Crea un nuovo progetto (`flutter create liste_demo`) e svuota il contenuto di `lib\u002Fmain.dart`.\n\nPrima della UI serve qualcosa da mostrare: definiamo una piccola classe `Contatto` e una lista in memoria. Lavorare con un modello tipizzato (invece che con `Map` generiche) rende il codice più leggibile e ti fa scoprire subito gli errori grazie alla **null safety**.\n\nNota l'uso di `final` per i campi e del costruttore con parametri `required`: sono buone abitudini che ti eviteranno molti problemi più avanti.","import 'package:flutter\u002Fmaterial.dart';\n\nvoid main() => runApp(const MyApp());\n\nclass Contatto {\n  final String nome;\n  final String ruolo;\n  final String iniziali;\n\n  const Contatto({\n    required this.nome,\n    required this.ruolo,\n    required this.iniziali,\n  });\n}\n\nconst List\u003CContatto> contatti = [\n  Contatto(nome: 'Giulia Rossi', ruolo: 'Product Manager', iniziali: 'GR'),\n  Contatto(nome: 'Marco Bianchi', ruolo: 'Flutter Developer', iniziali: 'MB'),\n  Contatto(nome: 'Sara Conti', ruolo: 'UI Designer', iniziali: 'SC'),\n  Contatto(nome: 'Luca Ferrari', ruolo: 'Backend Developer', iniziali: 'LF'),\n  Contatto(nome: 'Elena Moretti', ruolo: 'QA Engineer', iniziali: 'EM'),\n];\n\nclass MyApp extends StatelessWidget {\n  const MyApp({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return MaterialApp(\n      title: 'Liste Demo',\n      theme: ThemeData(\n        colorSchemeSeed: Colors.indigo,\n        useMaterial3: true,\n      ),\n      home: const ContattiPage(),\n    );\n  }\n}\n\nclass ContattiPage extends StatelessWidget {\n  const ContattiPage({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(title: const Text('Contatti')),\n      body: const Center(child: Text('Qui arriverà la lista')),\n    );\n  }\n}","dart","L'app si avvia e mostra una AppBar con il titolo \"Contatti\" e il testo segnaposto al centro. Nessun errore in console.",null,{"id":43,"position":44,"title":45,"body":46,"code_snippet":47,"code_language":39,"expected_result":48,"demo_url":41,"video_url":41},494,2,"La ListView \"statica\": comoda ma limitata","Il modo più semplice di creare una lista è il costruttore `ListView(children: [...])`. Riceve una lista di widget già pronti e li rende scorrevoli.\n\nÈ perfetto quando gli elementi sono **pochi e conosciuti a priori** (per esempio le voci di una schermata Impostazioni), ma ha un difetto importante: **costruisce tutti i figli subito**, anche quelli fuori dallo schermo. Con 1.000 elementi (o con una lista che arriva dal server) l'app diventerebbe lenta e sprecherebbe memoria.\n\nSostituisci il `body` dello `Scaffold` con il codice qui sotto per vedere il risultato, poi passa allo step successivo per la versione corretta.","\u002F\u002F Dentro ContattiPage.build\nreturn Scaffold(\n  appBar: AppBar(title: const Text('Contatti')),\n  body: ListView(\n    children: [\n      for (final c in contatti)\n        ListTile(\n          title: Text(c.nome),\n          subtitle: Text(c.ruolo),\n        ),\n    ],\n  ),\n);","Vedi i 5 contatti in un elenco scorrevole. Funziona, ma tutti gli elementi vengono costruiti insieme: va bene solo per liste corte.",{"id":50,"position":51,"title":52,"body":53,"code_snippet":54,"code_language":39,"expected_result":55,"demo_url":41,"video_url":41},495,3,"Passare a ListView.builder","`ListView.builder` è la versione **lazy**: costruisce gli elementi solo quando stanno per entrare nell'area visibile e li ricicla durante lo scroll.\n\nRichiede due parametri chiave:\n\n- **`itemCount`**: quanti elementi ha la lista (se lo ometti, la lista è considerata infinita);\n- **`itemBuilder`**: una funzione `(BuildContext context, int index)` che restituisce il widget per l'elemento in posizione `index`.\n\nDentro `itemBuilder` recuperi l'oggetto con `contatti[index]` e lo trasformi in una riga. È esattamente il pattern che userai per i dati che arrivano da un'API o da un database.","class ContattiPage extends StatelessWidget {\n  const ContattiPage({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(title: const Text('Contatti')),\n      body: ListView.builder(\n        itemCount: contatti.length,\n        itemBuilder: (context, index) {\n          final contatto = contatti[index];\n          return ListTile(\n            title: Text(contatto.nome),\n            subtitle: Text(contatto.ruolo),\n          );\n        },\n      ),\n    );\n  }\n}","La lista appare identica a prima, ma ora è efficiente: prova a cambiare i dati con `List.generate(1000, ...)` e lo scroll resterà fluido.",{"id":57,"position":58,"title":59,"body":60,"code_snippet":61,"code_language":39,"expected_result":62,"demo_url":41,"video_url":41},496,4,"Rendere le righe complete con ListTile","`ListTile` è il widget Material pensato per le righe di lista. Le sue proprietà più usate:\n\n- **`leading`**: widget a sinistra (icona, avatar, immagine);\n- **`title`**: il testo principale;\n- **`subtitle`**: il testo secondario;\n- **`trailing`**: widget a destra (freccia, switch, badge);\n- **`onTap`** \u002F **`onLongPress`**: le interazioni;\n- **`selected`**, **`dense`**, **`contentPadding`**: personalizzazioni rapide.\n\nAggiungiamo un `CircleAvatar` con le iniziali e una freccia a destra. Nota `overflow: TextOverflow.ellipsis` sul titolo: è la difesa più semplice contro i nomi lunghi che sforerebbero lo spazio disponibile.","itemBuilder: (context, index) {\n  final contatto = contatti[index];\n  return ListTile(\n    leading: CircleAvatar(\n      backgroundColor: Theme.of(context).colorScheme.primaryContainer,\n      child: Text(contatto.iniziali),\n    ),\n    title: Text(\n      contatto.nome,\n      maxLines: 1,\n      overflow: TextOverflow.ellipsis,\n    ),\n    subtitle: Text(contatto.ruolo),\n    trailing: const Icon(Icons.chevron_right),\n    onTap: () {\n      ScaffoldMessenger.of(context).showSnackBar(\n        SnackBar(content: Text('Hai toccato ${contatto.nome}')),\n      );\n    },\n  );\n},","Ogni riga mostra avatar con iniziali, nome, ruolo e freccia. Toccando una riga compare in basso una SnackBar con il nome del contatto.",{"id":64,"position":65,"title":66,"body":67,"code_snippet":68,"code_language":39,"expected_result":69,"demo_url":41,"video_url":41},497,5,"Separatori e header con ListView.separated","Per inserire una linea (o qualsiasi widget) **tra** un elemento e l'altro, non aggiungere un `Divider` dentro l'`itemBuilder`: useresti un separatore anche dopo l'ultimo elemento. La soluzione corretta è `ListView.separated`, che aggiunge `separatorBuilder` al set di parametri già visti.\n\nAggiungiamo anche un po' di respiro con `padding` e mostriamo come gestire il caso della **lista vuota**: un controllo con `if (contatti.isEmpty)` evita di presentare una schermata bianca all'utente, uno degli scivoloni più frequenti nelle prime app.","@override\nWidget build(BuildContext context) {\n  return Scaffold(\n    appBar: AppBar(title: const Text('Contatti')),\n    body: contatti.isEmpty\n        ? const Center(child: Text('Nessun contatto disponibile'))\n        : ListView.separated(\n            padding: const EdgeInsets.symmetric(vertical: 8),\n            itemCount: contatti.length,\n            separatorBuilder: (context, index) => const Divider(\n              height: 1,\n              indent: 72,\n            ),\n            itemBuilder: (context, index) {\n              final contatto = contatti[index];\n              return ListTile(\n                leading: CircleAvatar(child: Text(contatto.iniziali)),\n                title: Text(contatto.nome,\n                    maxLines: 1, overflow: TextOverflow.ellipsis),\n                subtitle: Text(contatto.ruolo),\n                trailing: const Icon(Icons.chevron_right),\n                onTap: () {},\n              );\n            },\n          ),\n  );\n}","Tra una riga e l'altra compare una sottile linea allineata al testo (grazie a `indent: 72`), senza linea finale dopo l'ultimo elemento.",{"id":71,"position":72,"title":73,"body":74,"code_snippet":75,"code_language":39,"expected_result":76,"demo_url":41,"video_url":41},498,6,"Evitare i due errori classici: ListView dentro Column e altezza illimitata","Appena provi a mettere un titolo sopra la lista dentro una `Column`, incontri il famigerato errore:\n\n> *RenderBox was not laid out* oppure *Vertical viewport was given unbounded height*.\n\nIl motivo è che la `Column` dà ai figli altezza \"illimitata\", mentre la `ListView` vuole sapere quanto spazio occupare. Hai tre soluzioni:\n\n1. **`Expanded`** (consigliata): la lista prende tutto lo spazio rimanente e resta scorrevole.\n2. **`shrinkWrap: true` + `physics: NeverScrollableScrollPhysics()`**: la lista si dimensiona sul contenuto e lo scroll lo gestisce il genitore (`SingleChildScrollView`). Usalo solo con **poche righe**: perde il vantaggio del lazy loading.\n3. **`CustomScrollView` con `SliverList`**: la soluzione più flessibile per layout complessi.\n\nAltro errore frequente: mettere una `Row` con testi lunghi dentro un `ListTile` senza `Expanded`, ottenendo le classiche strisce gialle e nere di *overflow*. Avvolgi il testo in `Expanded` e usa `overflow: TextOverflow.ellipsis`.\n\nUltimo consiglio: quando la lista è **modificabile** (elementi aggiunti, rimossi o riordinati), assegna una `key` stabile agli elementi, ad esempio `key: ValueKey(contatto.id)`, così Flutter riconosce le righe e non mescola gli stati.","@override\nWidget build(BuildContext context) {\n  return Scaffold(\n    appBar: AppBar(title: const Text('Contatti')),\n    body: Column(\n      crossAxisAlignment: CrossAxisAlignment.start,\n      children: [\n        const Padding(\n          padding: EdgeInsets.all(16),\n          child: Text(\n            'Team di progetto',\n            style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),\n          ),\n        ),\n        \u002F\u002F ✅ Expanded risolve l'altezza illimitata\n        Expanded(\n          child: ListView.builder(\n            itemCount: contatti.length,\n            itemBuilder: (context, index) {\n              final contatto = contatti[index];\n              return ListTile(\n                key: ValueKey(contatto.nome),\n                leading: CircleAvatar(child: Text(contatto.iniziali)),\n                title: Row(\n                  children: [\n                    \u002F\u002F ✅ Expanded evita l'overflow orizzontale\n                    Expanded(\n                      child: Text(\n                        contatto.nome,\n                        maxLines: 1,\n                        overflow: TextOverflow.ellipsis,\n                      ),\n                    ),\n                    const SizedBox(width: 8),\n                    const Icon(Icons.star_border, size: 18),\n                  ],\n                ),\n                subtitle: Text(contatto.ruolo),\n                onTap: () {},\n              );\n            },\n          ),\n        ),\n      ],\n    ),\n  );\n}","La schermata mostra un titolo fisso in alto e sotto la lista scorrevole, senza errori di layout né strisce gialle e nere di overflow.",{"id":78,"position":79,"title":80,"body":81,"code_snippet":82,"code_language":39,"expected_result":83,"demo_url":41,"video_url":41},499,7,"Ricapitolare e proseguire","Hai costruito una lista completa partendo da zero. I punti da ricordare:\n\n- usa **`ListView(children: [...])`** solo per pochi elementi fissi;\n- usa **`ListView.builder`** con `itemCount` + `itemBuilder` per liste lunghe o dinamiche;\n- **`ListView.separated`** aggiunge separatori nel modo corretto;\n- **`ListTile`** copre il 90% dei casi con `leading`, `title`, `subtitle`, `trailing` e `onTap`;\n- dentro una `Column` avvolgi sempre la lista in **`Expanded`**;\n- gestisci lo **stato vuoto** e usa `TextOverflow.ellipsis` per i testi lunghi.\n\n**Esercizi per fissare i concetti:**\n\n1. Genera 500 contatti con `List.generate` e verifica che lo scroll resti fluido.\n2. Aggiungi un `Switch` come `trailing` per marcare i preferiti (ti servirà uno `StatefulWidget` e `setState`).\n3. Apri una schermata di dettaglio al tocco con `Navigator.push`.\n4. Prova `ListView.builder(scrollDirection: Axis.horizontal)` per una lista orizzontale di card.","\u002F\u002F Esercizio 1: lista lunga generata al volo\nfinal contattiLunghi = List.generate(\n  500,\n  (i) => Contatto(\n    nome: 'Contatto n. $i',\n    ruolo: i.isEven ? 'Sviluppatore' : 'Designer',\n    iniziali: 'C$i',\n  ),\n);","Con 500 elementi la lista scorre senza rallentamenti: è la conferma che `ListView.builder` costruisce solo le righe visibili.",[],1789120599995]