[{"data":1,"prerenderedAt":87},["ShallowReactive",2],{"tutorial-testo-immagini-icone-e-asset-in-flutter-configurare-il-pubspecyaml":3,"comments-tutorial-testo-immagini-icone-e-asset-in-flutter-configurare-il-pubspecyaml":86},{"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},73,"Testo, immagini, icone e asset in Flutter: configurare il pubspec.yaml","testo-immagini-icone-e-asset-in-flutter-configurare-il-pubspecyaml","Impara a mostrare testi con Text, caricare immagini locali e di rete, usare le icone Material e dichiarare correttamente asset e font nel pubspec.yaml di un progetto Flutter.","Ogni app ha bisogno di contenuti: parole, immagini, icone e magari un font personalizzato. In Flutter tutto questo passa da tre elementi fondamentali: i widget `Text`, `Image` e `Icon`, e il file di configurazione `pubspec.yaml`.\n\nIl `pubspec.yaml` è il \"documento d'identità\" del progetto: dichiara nome, dipendenze e — cosa che interessa a noi in questo tutorial — le risorse statiche (asset) che vogliamo includere nel bundle dell'app. Se dimentichi di dichiarare un'immagine lì dentro, Flutter non la troverà a runtime e ti mostrerà il classico errore `Unable to load asset`.\n\nIn questa guida costruiremo passo dopo passo una piccola schermata \"profilo prodotto\" che mette insieme:\n\n- testi formattati con `TextStyle` e `Theme.of(context).textTheme`;\n- un'immagine locale caricata dalla cartella `assets\u002F`;\n- un'immagine di rete con placeholder e gestione dell'errore;\n- icone Material e un font personalizzato dichiarato nel `pubspec.yaml`.\n\nNon serve alcuna conoscenza avanzata: basta avere Flutter installato e un progetto creato con `flutter create`.","https:\u002F\u002Fflutter.it\u002Fstorage\u002Ftutorials\u002F15e6887d-df14-42e4-b125-4bcc7c97b034.jpg","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1654892968823-ea564870a96f?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w5NzA2NTJ8MHwxfHJhbmRvbXx8fHx8fHx8fDE3ODc1NDU4ODl8&ixlib=rb-4.1.0&q=80&w=1080",{"name":12,"author_url":13,"photo_url":14},"2H Media","https:\u002F\u002Funsplash.com\u002F@2hmedia","https:\u002F\u002Funsplash.com\u002Fphotos\u002Ftext-letter-4blzwmR6q3o","https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=isaZvOPebfM",{"youtube_id":17,"duration_seconds":18,"published_at":19},"isaZvOPebfM",158,"2026-08-24T05:31:27+00:00","beginner",35,"3.x","published","2026-08-24T04:31:30+00:00","Asset in Flutter: testo, immagini e pubspec.yaml","Guida beginner a Text, Image, Icon e alla dichiarazione di asset e font nel pubspec.yaml di Flutter, con esempi pratici e errori comuni da evitare.",{"id":28,"name":29,"slug":30},1,"Guide","guide",{"id":28,"name":32},"Flutter Bot",[34,42,50,58,65,72,79],{"id":35,"position":28,"title":36,"body":37,"code_snippet":38,"code_language":39,"expected_result":40,"demo_url":41,"video_url":41},486,"Preparare il progetto e la cartella degli asset","Creiamo un nuovo progetto (o apriamone uno esistente) e organizziamo le risorse in cartelle dedicate. La convenzione più diffusa prevede una cartella `assets\u002F` nella root del progetto, con sottocartelle per tipologia.\n\n```\nflutter create asset_demo\ncd asset_demo\n```\n\nCrea questa struttura accanto a `lib\u002F` e `pubspec.yaml`:\n\n```\nasset_demo\u002F\n├── assets\u002F\n│   ├── images\u002F\n│   │   └── prodotto.png\n│   └── fonts\u002F\n│       ├── Poppins-Regular.ttf\n│       └── Poppins-Bold.ttf\n├── lib\u002F\n└── pubspec.yaml\n```\n\nPer l'immagine puoi usare un qualsiasi PNG\u002FJPG (rinominalo `prodotto.png`). Per il font puoi scaricare gratuitamente i file `.ttf` di Poppins da Google Fonts.\n\n> **Nota**: i nomi dei file asset devono essere in minuscolo, senza spazi né accenti. Su alcune piattaforme il filesystem è case-sensitive e `Prodotto.PNG` non verrebbe trovato.","# Comandi da terminale\nflutter create asset_demo\ncd asset_demo\nmkdir -p assets\u002Fimages assets\u002Ffonts","bash","Il progetto compila con `flutter run` e nella root esiste la cartella `assets\u002F` con le sottocartelle `images\u002F` e `fonts\u002F`.",null,{"id":43,"position":44,"title":45,"body":46,"code_snippet":47,"code_language":48,"expected_result":49,"demo_url":41,"video_url":41},487,2,"Dichiarare asset e font nel pubspec.yaml","Flutter include nel bundle finale **solo** i file dichiarati esplicitamente. Apriamo `pubspec.yaml` e cerchiamo la sezione `flutter:` (attenzione: quella in fondo al file, non `dependencies`).\n\nL'indentazione in YAML è fondamentale: si usano **spazi, mai tab**, e ogni livello vale due spazi.\n\nDue modi per dichiarare le immagini:\n\n1. **File per file**: `- assets\u002Fimages\u002Fprodotto.png`\n2. **Intera cartella**: `- assets\u002Fimages\u002F` (con la barra finale). Include tutti i file della cartella, ma **non** delle sottocartelle: queste vanno dichiarate a parte.\n\nPer i font si usa la chiave `fonts`, con `family` (il nome che userai nel codice) e la lista degli `asset` con il relativo `weight`.","name: asset_demo\ndescription: Demo su asset, testo e immagini\npublish_to: 'none'\nversion: 1.0.0+1\n\nenvironment:\n  sdk: '>=3.0.0 \u003C4.0.0'\n\ndependencies:\n  flutter:\n    sdk: flutter\n  cupertino_icons: ^1.0.6\n\ndev_dependencies:\n  flutter_test:\n    sdk: flutter\n  flutter_lints: ^3.0.0\n\nflutter:\n  uses-material-design: true\n\n  assets:\n    - assets\u002Fimages\u002F\n\n  fonts:\n    - family: Poppins\n      fonts:\n        - asset: assets\u002Ffonts\u002FPoppins-Regular.ttf\n          weight: 400\n        - asset: assets\u002Ffonts\u002FPoppins-Bold.ttf\n          weight: 700","yaml","Dopo il salvataggio, l'IDE esegue `flutter pub get` senza errori di parsing YAML. Se hai modificato il pubspec, ferma e riavvia l'app: l'hot reload non basta per i nuovi asset.",{"id":51,"position":52,"title":53,"body":54,"code_snippet":55,"code_language":56,"expected_result":57,"demo_url":41,"video_url":41},488,3,"Mostrare del testo con Text e TextStyle","Il widget `Text` è il modo base per scrivere sullo schermo. Accetta una stringa e, opzionalmente, uno `style` di tipo `TextStyle`.\n\nProprietà utili di `TextStyle`:\n\n- `fontSize`, `fontWeight`, `color`, `letterSpacing`;\n- `fontFamily`: qui useremo `'Poppins'`, esattamente come scritto in `family` nel pubspec;\n- `height`: moltiplicatore dell'interlinea.\n\nSu `Text` invece trovi:\n\n- `maxLines` e `overflow: TextOverflow.ellipsis` per troncare testi lunghi con i tre puntini;\n- `textAlign` per l'allineamento.\n\nUna buona pratica è però **non** scrivere stili a mano ovunque, ma partire dal tema: `Theme.of(context).textTheme.titleLarge` restituisce uno stile coerente con il resto dell'app, che puoi personalizzare con `copyWith()`.\n\nImpostando `fontFamily: 'Poppins'` in `ThemeData`, tutti i testi dell'app useranno il font personalizzato senza ripeterlo widget per widget.","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: 'Asset Demo',\n      theme: ThemeData(\n        colorSchemeSeed: Colors.teal,\n        fontFamily: 'Poppins', \u002F\u002F font dichiarato nel pubspec\n        useMaterial3: true,\n      ),\n      home: const ProdottoPage(),\n    );\n  }\n}\n\nclass ProdottoPage extends StatelessWidget {\n  const ProdottoPage({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    final textTheme = Theme.of(context).textTheme;\n\n    return Scaffold(\n      appBar: AppBar(title: const Text('Scheda prodotto')),\n      body: Padding(\n        padding: const EdgeInsets.all(16),\n        child: Column(\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: [\n            Text('Zaino da trekking 30L', style: textTheme.headlineSmall),\n            const SizedBox(height: 4),\n            Text(\n              'Leggero, impermeabile e con schienale traspirante. '\n              'Perfetto per escursioni in giornata.',\n              style: textTheme.bodyMedium?.copyWith(\n                color: Colors.grey.shade700,\n                height: 1.4,\n              ),\n              maxLines: 3,\n              overflow: TextOverflow.ellipsis,\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}","dart","L'app mostra un titolo e una descrizione su tre righe al massimo, resi con il font Poppins.",{"id":59,"position":60,"title":61,"body":62,"code_snippet":63,"code_language":56,"expected_result":64,"demo_url":41,"video_url":41},489,4,"Caricare un'immagine locale con Image.asset","Per mostrare un file dichiarato nel pubspec si usa `Image.asset('percorso\u002Frelativo\u002Fdalla\u002Froot')`. Il percorso è **identico** a quello scritto nel pubspec, quindi `assets\u002Fimages\u002Fprodotto.png`.\n\nParametri importanti:\n\n- `width` \u002F `height`: dimensioni in logical pixel;\n- `fit`: come l'immagine riempie lo spazio. `BoxFit.cover` riempie tutto ritagliando l'eccesso, `BoxFit.contain` mostra tutto lasciando eventuali spazi vuoti;\n- `errorBuilder`: widget alternativo se il caricamento fallisce.\n\nPer angoli arrotondati avvolgi l'immagine in `ClipRRect`. Se invece l'immagine serve come sfondo di un contenitore, usa `Container(decoration: BoxDecoration(image: DecorationImage(...)))`.\n\n### Immagini a risoluzione multipla\n\nFlutter supporta le varianti per densità di schermo: basta creare le sottocartelle `2.0x` e `3.0x` accanto al file base. Dichiari solo il percorso base e il framework sceglie la variante giusta.\n\n```\nassets\u002Fimages\u002Fprodotto.png\nassets\u002Fimages\u002F2.0x\u002Fprodotto.png\nassets\u002Fimages\u002F3.0x\u002Fprodotto.png\n```","ClipRRect(\n  borderRadius: BorderRadius.circular(16),\n  child: Image.asset(\n    'assets\u002Fimages\u002Fprodotto.png',\n    width: double.infinity,\n    height: 200,\n    fit: BoxFit.cover,\n    errorBuilder: (context, error, stackTrace) => Container(\n      height: 200,\n      color: Colors.grey.shade200,\n      alignment: Alignment.center,\n      child: const Icon(Icons.broken_image_outlined, size: 48),\n    ),\n  ),\n)","L'immagine locale appare con angoli arrotondati e altezza 200. Se il percorso è sbagliato compare l'icona di fallback invece di un crash.",{"id":66,"position":67,"title":68,"body":69,"code_snippet":70,"code_language":56,"expected_result":71,"demo_url":41,"video_url":41},490,5,"Immagini da internet con Image.network e stato di caricamento","Per le immagini remote si usa `Image.network(url)`. A differenza degli asset, il download è asincrono: conviene sempre gestire i due casi limite.\n\n- `loadingBuilder`: viene chiamato mentre l'immagine scarica; riceve `ImageChunkEvent` con i byte ricevuti e totali, utili per una progress bar reale.\n- `errorBuilder`: mostrato in caso di URL non valido o assenza di rete.\n\n> **Attenzione**: su Android, dalla versione 9 in poi, il traffico HTTP in chiaro è bloccato. Usa sempre URL `https`. Su macOS e Windows, per le app desktop, ricorda di abilitare il permesso di rete nelle entitlement.\n\nSe l'app carica molte immagini remote in liste, valuta più avanti pacchetti dedicati alla cache; per ora `Image.network` è più che sufficiente.","Image.network(\n  'https:\u002F\u002Fpicsum.photos\u002F600\u002F400',\n  width: double.infinity,\n  height: 200,\n  fit: BoxFit.cover,\n  loadingBuilder: (context, child, progress) {\n    if (progress == null) return child; \u002F\u002F download completato\n    final total = progress.expectedTotalBytes;\n    return SizedBox(\n      height: 200,\n      child: Center(\n        child: CircularProgressIndicator(\n          value: total != null\n              ? progress.cumulativeBytesLoaded \u002F total\n              : null,\n        ),\n      ),\n    );\n  },\n  errorBuilder: (context, error, stackTrace) => const SizedBox(\n    height: 200,\n    child: Center(child: Text('Immagine non disponibile')),\n  ),\n)","Durante il download compare un indicatore circolare, poi l'immagine remota. Senza connessione appare il messaggio di errore.",{"id":73,"position":74,"title":75,"body":76,"code_snippet":77,"code_language":56,"expected_result":78,"demo_url":41,"video_url":41},491,6,"Icone Material, icone personalizzate e Icon Button","Flutter include gratuitamente l'intero set Material Icons, disponibile grazie alla riga `uses-material-design: true` nel pubspec (se la rimuovi, le icone non vengono renderizzate).\n\nSi usano così:\n\n- `Icon(Icons.star, size: 20, color: Colors.amber)` per un'icona statica;\n- `IconButton(onPressed: ..., icon: Icon(...))` per un'icona cliccabile con effetto ripple;\n- `ImageIcon(AssetImage('assets\u002Fimages\u002Flogo.png'))` se vuoi usare un PNG monocromatico come icona, colorabile con `color`.\n\nPer i loghi vettoriali dei brand, di solito si aggiunge il pacchetto `flutter_svg` e si usa `SvgPicture.asset`, ricordando sempre di dichiarare il file `.svg` tra gli asset.\n\nMettiamo insieme icone, testo e immagini in una riga di valutazione del prodotto.","Row(\n  children: [\n    ...List.generate(\n      5,\n      (i) => Icon(\n        i \u003C 4 ? Icons.star_rounded : Icons.star_border_rounded,\n        size: 22,\n        color: Colors.amber.shade700,\n      ),\n    ),\n    const SizedBox(width: 8),\n    const Text('4.0 (128 recensioni)'),\n    const Spacer(),\n    IconButton(\n      tooltip: 'Aggiungi ai preferiti',\n      onPressed: () {\n        ScaffoldMessenger.of(context).showSnackBar(\n          const SnackBar(content: Text('Aggiunto ai preferiti')),\n        );\n      },\n      icon: const Icon(Icons.favorite_border),\n    ),\n  ],\n)","Compare una riga con quattro stelle piene, una vuota, il testo delle recensioni e un pulsante cuore che mostra uno SnackBar al tocco.",{"id":80,"position":81,"title":82,"body":83,"code_snippet":84,"code_language":56,"expected_result":85,"demo_url":41,"video_url":41},492,7,"Assemblare la schermata completa ed evitare gli errori tipici","Uniamo tutti i pezzi in un'unica schermata scrollabile. Usiamo `SingleChildScrollView` per evitare il famigerato overflow giallo\u002Fnero quando la tastiera o uno schermo piccolo riducono lo spazio.\n\n### Checklist degli errori più comuni\n\n1. **`Unable to load asset: assets\u002Fimages\u002Fprodotto.png`** → il file non è dichiarato nel pubspec, il percorso ha un refuso, oppure l'indentazione YAML è sbagliata.\n2. **Ho aggiunto l'asset ma non si vede** → l'hot reload non ricostruisce il bundle: ferma l'app e rilanciala con `flutter run` (full restart).\n3. **Il font non cambia** → il valore di `fontFamily` nel codice deve coincidere *esattamente* con `family` nel pubspec (maiuscole comprese).\n4. **`RenderFlex overflowed by N pixels`** → un `Column` troppo alto: avvolgilo in `SingleChildScrollView` o usa `Expanded`\u002F`Flexible` per i figli elastici.\n5. **Immagine deformata** → stai forzando `width` e `height` senza `fit`: aggiungi `BoxFit.cover` o `BoxFit.contain`.\n6. **App pesantissima** → PNG da 4000px per un'anteprima da 100px. Ridimensiona e comprimi le immagini prima di includerle.","class ProdottoPage extends StatelessWidget {\n  const ProdottoPage({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    final textTheme = Theme.of(context).textTheme;\n\n    return Scaffold(\n      appBar: AppBar(title: const Text('Scheda prodotto')),\n      body: SingleChildScrollView(\n        padding: const EdgeInsets.all(16),\n        child: Column(\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: [\n            ClipRRect(\n              borderRadius: BorderRadius.circular(16),\n              child: Image.asset(\n                'assets\u002Fimages\u002Fprodotto.png',\n                width: double.infinity,\n                height: 220,\n                fit: BoxFit.cover,\n                errorBuilder: (_, __, ___) => Container(\n                  height: 220,\n                  color: Colors.grey.shade200,\n                  alignment: Alignment.center,\n                  child: const Icon(Icons.broken_image_outlined, size: 48),\n                ),\n              ),\n            ),\n            const SizedBox(height: 16),\n            Text('Zaino da trekking 30L', style: textTheme.headlineSmall),\n            const SizedBox(height: 4),\n            Text(\n              '89,90 €',\n              style: textTheme.titleMedium?.copyWith(\n                color: Theme.of(context).colorScheme.primary,\n                fontWeight: FontWeight.w700,\n              ),\n            ),\n            const SizedBox(height: 12),\n            Row(\n              children: [\n                ...List.generate(\n                  5,\n                  (i) => Icon(\n                    i \u003C 4 ? Icons.star_rounded : Icons.star_border_rounded,\n                    size: 22,\n                    color: Colors.amber.shade700,\n                  ),\n                ),\n                const SizedBox(width: 8),\n                Text('4.0 (128)', style: textTheme.bodySmall),\n              ],\n            ),\n            const SizedBox(height: 16),\n            Text(\n              'Leggero, impermeabile e con schienale traspirante. '\n              'Perfetto per escursioni in giornata.',\n              style: textTheme.bodyMedium?.copyWith(height: 1.5),\n            ),\n            const SizedBox(height: 24),\n            Text('Galleria', style: textTheme.titleMedium),\n            const SizedBox(height: 8),\n            ClipRRect(\n              borderRadius: BorderRadius.circular(12),\n              child: Image.network(\n                'https:\u002F\u002Fpicsum.photos\u002F600\u002F400',\n                width: double.infinity,\n                height: 180,\n                fit: BoxFit.cover,\n                loadingBuilder: (context, child, progress) =>\n                    progress == null\n                        ? child\n                        : const SizedBox(\n                            height: 180,\n                            child: Center(child: CircularProgressIndicator()),\n                          ),\n                errorBuilder: (_, __, ___) => const SizedBox(\n                  height: 180,\n                  child: Center(child: Text('Immagine non disponibile')),\n                ),\n              ),\n            ),\n            const SizedBox(height: 24),\n            SizedBox(\n              width: double.infinity,\n              child: FilledButton.icon(\n                onPressed: () {},\n                icon: const Icon(Icons.shopping_cart_outlined),\n                label: const Text('Aggiungi al carrello'),\n              ),\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}","Una schermata prodotto scrollabile con immagine locale, prezzo, stelle, descrizione con font Poppins, immagine remota e pulsante d'acquisto, senza errori di overflow.",[],1789205525774]