[{"data":1,"prerenderedAt":85},["ShallowReactive",2],{"tutorial-decorare-i-widget-in-flutter-con-boxdecoration-bordi-ombre-e-gradienti":3,"comments-tutorial-decorare-i-widget-in-flutter-con-boxdecoration-bordi-ombre-e-gradienti":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},100,"Decorare i widget in Flutter con BoxDecoration: bordi, ombre e gradienti","decorare-i-widget-in-flutter-con-boxdecoration-bordi-ombre-e-gradienti","Impara a usare BoxDecoration per trasformare un semplice Container in una card elegante: colori, bordi arrotondati, ombre, gradienti e immagini di sfondo, passo dopo passo.","Quando si inizia con Flutter, il `Container` è il widget che si usa più spesso: un rettangolo in cui mettere del contenuto. Ma finché gli si assegna solo un `color`, il risultato resta piatto e anonimo.\n\nLa vera potenza arriva con **`BoxDecoration`**: una singola classe che permette di definire colore di sfondo, bordi, angoli arrotondati, ombre, gradienti e immagini, senza dover installare alcun pacchetto esterno.\n\nIn questo tutorial costruiremo, passo dopo passo, una **card di profilo** partendo da un rettangolo grigio e arrivando a un componente rifinito e riutilizzabile. Impareremo anche gli errori più comuni (come il classico crash *\"Cannot provide both a color and a decoration\"*) e come evitarli.\n\n**Cosa ti serve:** Flutter 3.x installato, un editor (VS Code o Android Studio) e un progetto creato con `flutter create decorazioni_demo`.","https:\u002F\u002Fflutter.it\u002Fstorage\u002Ftutorials\u002F9eeeb79e-1cb6-436c-bad9-325eb80a390c.jpg","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1579546929593-b824f1e71808?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w5NzA2NTJ8MHwxfHJhbmRvbXx8fHx8fHx8fDE3ODk4Nzg2NzV8&ixlib=rb-4.1.0&q=80&w=1080",{"name":12,"author_url":13,"photo_url":14},"Codioful (Formerly Gradienta)","https:\u002F\u002Funsplash.com\u002F@codioful","https:\u002F\u002Funsplash.com\u002Fphotos\u002Fpurple-and-orange-gradient-1TCjZz-dnJc","https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=MxTAgxvXnDs",{"youtube_id":17,"duration_seconds":18,"published_at":19},"MxTAgxvXnDs",123,"2026-09-20T09:33:14+00:00","beginner",35,"3.x","published","2026-09-20T04:31:16+00:00","BoxDecoration in Flutter: bordi, ombre e gradienti","Guida per principianti a BoxDecoration in Flutter: colori, bordi arrotondati, BoxShadow, gradienti e immagini di sfondo per creare card eleganti.",{"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},667,"Preparare il progetto e il primo Container decorato","Crea un nuovo progetto e sostituisci il contenuto di `lib\u002Fmain.dart` con il codice qui sotto. Partiamo da una schermata con un `Container` centrale colorato.\n\n**Attenzione all'errore più comune:** un `Container` non può avere contemporaneamente la proprietà `color` e la proprietà `decoration`. Se le usi entrambe, l'app va in crash con il messaggio:\n\n> `Cannot provide both a color and a decoration. To provide both, use \"decoration: BoxDecoration(color: color)\".`\n\nLa regola è semplice: **appena ti serve una decorazione, sposta il colore dentro `BoxDecoration`**.","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: 'Decorazioni Demo',\n      debugShowCheckedModeBanner: false,\n      theme: ThemeData(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      backgroundColor: const Color(0xFFF2F4F8),\n      appBar: AppBar(title: const Text('BoxDecoration')),\n      body: Center(\n        child: Container(\n          width: 260,\n          height: 140,\n          \u002F\u002F NON usare anche \"color:\" qui fuori: il colore va dentro la decoration\n          decoration: const BoxDecoration(\n            color: Colors.white,\n          ),\n          child: const Center(child: Text('Il mio box')),\n        ),\n      ),\n    );\n  }\n}","dart","Avviando l'app vedi un rettangolo bianco 260x140 al centro di uno sfondo grigio chiaro, con la scritta \"Il mio box\".",null,{"id":43,"position":44,"title":45,"body":46,"code_snippet":47,"code_language":39,"expected_result":48,"demo_url":41,"video_url":41},668,2,"Aggiungere bordi e angoli arrotondati","Due proprietà di `BoxDecoration` fanno subito la differenza:\n\n- **`border`**: il contorno del box. `Border.all(...)` applica lo stesso bordo su tutti i lati, ma puoi anche usare `Border(top: BorderSide(...))` per un solo lato.\n- **`borderRadius`**: arrotonda gli angoli. `BorderRadius.circular(16)` li arrotonda tutti allo stesso modo, `BorderRadius.only(...)` ti permette di scegliere quali.\n\n> **Nota:** `borderRadius` funziona solo se la forma è un rettangolo (`shape: BoxShape.rectangle`, che è il default). Se imposti `shape: BoxShape.circle` devi rimuovere `borderRadius`, altrimenti ottieni un errore in fase di build.\n\nSostituisci il `Container` del passo precedente con questo.","Container(\n  width: 260,\n  height: 140,\n  decoration: BoxDecoration(\n    color: Colors.white,\n    borderRadius: BorderRadius.circular(20),\n    border: Border.all(\n      color: Colors.indigo.shade200,\n      width: 2,\n    ),\n  ),\n  child: const Center(\n    child: Text(\n      'Box con bordo',\n      style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600),\n    ),\n  ),\n)\n\n\u002F\u002F Variante: solo gli angoli superiori arrotondati\n\u002F\u002F borderRadius: const BorderRadius.only(\n\u002F\u002F   topLeft: Radius.circular(24),\n\u002F\u002F   topRight: Radius.circular(24),\n\u002F\u002F ),\n\n\u002F\u002F Variante: cerchio perfetto (niente borderRadius!)\n\u002F\u002F decoration: BoxDecoration(\n\u002F\u002F   shape: BoxShape.circle,\n\u002F\u002F   color: Colors.indigo,\n\u002F\u002F ),","Il box ora ha angoli arrotondati di 20 pixel e un sottile bordo indaco di 2 pixel.",{"id":50,"position":51,"title":52,"body":53,"code_snippet":54,"code_language":39,"expected_result":55,"demo_url":41,"video_url":41},669,3,"Dare profondità con le ombre (BoxShadow)","La proprietà `boxShadow` accetta una **lista** di `BoxShadow`: puoi quindi sovrapporne più di una per ottenere ombre morbide e realistiche.\n\nLe proprietà da conoscere:\n\n| Proprietà | A cosa serve |\n|---|---|\n| `color` | Colore dell'ombra, quasi sempre con opacità bassa |\n| `offset` | Spostamento in X e Y (`Offset(0, 8)` = ombra verso il basso) |\n| `blurRadius` | Quanto l'ombra è sfocata |\n| `spreadRadius` | Quanto l'ombra si espande prima di sfocare |\n\nUn buon punto di partenza per una card è un'ombra scura al 8-12% di opacità, leggermente spostata verso il basso.","Container(\n  width: 260,\n  height: 140,\n  decoration: BoxDecoration(\n    color: Colors.white,\n    borderRadius: BorderRadius.circular(20),\n    boxShadow: [\n      BoxShadow(\n        color: Colors.black.withOpacity(0.10),\n        offset: const Offset(0, 8),\n        blurRadius: 20,\n        spreadRadius: 0,\n      ),\n      BoxShadow(\n        color: Colors.black.withOpacity(0.04),\n        offset: const Offset(0, 2),\n        blurRadius: 4,\n      ),\n    ],\n  ),\n  child: const Center(child: Text('Box con ombra')),\n)","Il box appare sollevato dallo sfondo, con un'ombra morbida sotto: l'effetto è simile a quello di una Card di Material Design, ma completamente sotto il tuo controllo.",{"id":57,"position":58,"title":59,"body":60,"code_snippet":61,"code_language":39,"expected_result":62,"demo_url":41,"video_url":41},670,4,"Sfumature con LinearGradient e RadialGradient","Il campo `gradient` sostituisce il `color`: se li imposti entrambi, il gradiente vince e il colore viene ignorato (quindi meglio rimuoverlo per chiarezza).\n\nI gradienti più usati sono:\n\n- **`LinearGradient`**: sfumatura lungo una direzione, definita da `begin` e `end` (usa le costanti `Alignment.topLeft`, `Alignment.bottomRight`, ecc.).\n- **`RadialGradient`**: sfumatura circolare che parte dal centro.\n- **`SweepGradient`**: sfumatura \"a ventaglio\" attorno a un punto.\n\nCon `stops` puoi decidere in quale percentuale del percorso (da 0.0 a 1.0) si colloca ogni colore.","Container(\n  width: 260,\n  height: 140,\n  decoration: BoxDecoration(\n    borderRadius: BorderRadius.circular(20),\n    gradient: const LinearGradient(\n      begin: Alignment.topLeft,\n      end: Alignment.bottomRight,\n      colors: [\n        Color(0xFF6A11CB),\n        Color(0xFF2575FC),\n      ],\n      stops: [0.0, 1.0],\n    ),\n    boxShadow: [\n      BoxShadow(\n        color: const Color(0xFF2575FC).withOpacity(0.35),\n        offset: const Offset(0, 10),\n        blurRadius: 24,\n      ),\n    ],\n  ),\n  child: const Center(\n    child: Text(\n      'Gradiente',\n      style: TextStyle(\n        color: Colors.white,\n        fontSize: 20,\n        fontWeight: FontWeight.bold,\n      ),\n    ),\n  ),\n)","Il box mostra una sfumatura dal viola al blu in diagonale, con un'ombra colorata coordinata che accentua l'effetto.",{"id":64,"position":65,"title":66,"body":67,"code_snippet":68,"code_language":39,"expected_result":69,"demo_url":41,"video_url":41},671,5,"Immagine di sfondo con DecorationImage","`BoxDecoration` può usare un'immagine come sfondo tramite `image: DecorationImage(...)`. È utile per header e copertine.\n\nPunti chiave:\n\n- `fit: BoxFit.cover` riempie lo spazio mantenendo le proporzioni (è quasi sempre la scelta giusta).\n- `colorFilter` permette di scurire l'immagine così che il testo sopra resti leggibile.\n- L'immagine viene **automaticamente ritagliata** secondo il `borderRadius` della decorazione: non serve un `ClipRRect` esterno.\n\nPer usare un'immagine locale ricordati di dichiararla in `pubspec.yaml`:\n\n```yaml\nflutter:\n  assets:\n    - assets\u002Fimages\u002F\n```\n\nIn alternativa puoi usare `NetworkImage('https:\u002F\u002F...')` per un'immagine remota.","Container(\n  width: 260,\n  height: 160,\n  padding: const EdgeInsets.all(16),\n  alignment: Alignment.bottomLeft,\n  decoration: BoxDecoration(\n    borderRadius: BorderRadius.circular(20),\n    image: DecorationImage(\n      image: const NetworkImage(\n        'https:\u002F\u002Fpicsum.photos\u002F600\u002F400',\n      ),\n      fit: BoxFit.cover,\n      colorFilter: ColorFilter.mode(\n        Colors.black.withOpacity(0.35),\n        BlendMode.darken,\n      ),\n    ),\n    boxShadow: [\n      BoxShadow(\n        color: Colors.black.withOpacity(0.20),\n        offset: const Offset(0, 8),\n        blurRadius: 18,\n      ),\n    ],\n  ),\n  child: const Text(\n    'Copertina',\n    style: TextStyle(\n      color: Colors.white,\n      fontSize: 22,\n      fontWeight: FontWeight.bold,\n    ),\n  ),\n)","Il box mostra una foto di sfondo ritagliata con angoli arrotondati, leggermente scurita, con la scritta bianca \"Copertina\" in basso a sinistra.",{"id":71,"position":72,"title":73,"body":74,"code_snippet":75,"code_language":39,"expected_result":76,"demo_url":41,"video_url":41},672,6,"Mettere tutto insieme: una card di profilo riutilizzabile","Ora combiniamo quanto imparato in un widget riutilizzabile. Tre buone pratiche da seguire fin da subito:\n\n1. **Estrai il widget** in una classe separata (`ProfileCard`) invece di annidare tutto nel `build`.\n2. **Usa `const`** dove possibile: le decorazioni costanti vengono create una sola volta e migliorano le performance.\n3. **Definisci le decorazioni ricorrenti in un unico punto** (una classe `AppDecorations` con costanti statiche), così mantieni lo stile coerente in tutta l'app.\n\nSostituisci l'intero `HomePage` con il codice seguente.","class AppDecorations {\n  const AppDecorations._();\n\n  static BoxDecoration card = BoxDecoration(\n    color: Colors.white,\n    borderRadius: BorderRadius.circular(20),\n    border: Border.all(color: const Color(0xFFE6E8EF)),\n    boxShadow: [\n      BoxShadow(\n        color: Colors.black.withOpacity(0.08),\n        offset: const Offset(0, 6),\n        blurRadius: 16,\n      ),\n    ],\n  );\n\n  static const BoxDecoration avatar = BoxDecoration(\n    shape: BoxShape.circle,\n    gradient: LinearGradient(\n      begin: Alignment.topLeft,\n      end: Alignment.bottomRight,\n      colors: [Color(0xFF6A11CB), Color(0xFF2575FC)],\n    ),\n  );\n}\n\nclass ProfileCard extends StatelessWidget {\n  const ProfileCard({\n    super.key,\n    required this.nome,\n    required this.ruolo,\n    required this.iniziali,\n  });\n\n  final String nome;\n  final String ruolo;\n  final String iniziali;\n\n  @override\n  Widget build(BuildContext context) {\n    return Container(\n      padding: const EdgeInsets.all(16),\n      decoration: AppDecorations.card,\n      child: Row(\n        children: [\n          Container(\n            width: 56,\n            height: 56,\n            alignment: Alignment.center,\n            decoration: AppDecorations.avatar,\n            child: Text(\n              iniziali,\n              style: const TextStyle(\n                color: Colors.white,\n                fontWeight: FontWeight.bold,\n                fontSize: 18,\n              ),\n            ),\n          ),\n          const SizedBox(width: 16),\n          Expanded(\n            child: Column(\n              crossAxisAlignment: CrossAxisAlignment.start,\n              children: [\n                Text(\n                  nome,\n                  style: const TextStyle(\n                    fontSize: 18,\n                    fontWeight: FontWeight.w700,\n                  ),\n                ),\n                const SizedBox(height: 4),\n                Text(\n                  ruolo,\n                  style: TextStyle(color: Colors.grey.shade600),\n                ),\n              ],\n            ),\n          ),\n          const Icon(Icons.chevron_right),\n        ],\n      ),\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      backgroundColor: const Color(0xFFF2F4F8),\n      appBar: AppBar(title: const Text('BoxDecoration')),\n      body: ListView(\n        padding: const EdgeInsets.all(16),\n        children: const [\n          ProfileCard(\n            nome: 'Giulia Rossi',\n            ruolo: 'Flutter Developer',\n            iniziali: 'GR',\n          ),\n          SizedBox(height: 12),\n          ProfileCard(\n            nome: 'Marco Bianchi',\n            ruolo: 'UI Designer',\n            iniziali: 'MB',\n          ),\n        ],\n      ),\n    );\n  }\n}","Vedi una lista di card bianche con bordo sottile e ombra morbida, ognuna con un avatar circolare a gradiente contenente le iniziali, nome, ruolo e una freccia a destra.",{"id":78,"position":79,"title":80,"body":81,"code_snippet":82,"code_language":39,"expected_result":83,"demo_url":41,"video_url":41},673,7,"Rendere la card cliccabile senza perdere la decorazione","Un `Container` decorato non reagisce al tocco. Se avvolgi tutto in un `InkWell`, però, l'effetto ripple viene disegnato **sotto** il Container e non si vede.\n\nLa soluzione corretta in Material Design è:\n\n1. Usare un `Material` con `color: Colors.transparent` (o un `Ink` con la decorazione).\n2. Mettere l'`InkWell` sopra, con lo **stesso `borderRadius`** della decorazione, così il ripple resta dentro gli angoli arrotondati.\n\nIl trucco più pulito è il widget **`Ink`**, che accetta una `decoration` esattamente come `Container` e permette all'`InkWell` di disegnarci sopra.","\u002F\u002F Dentro ProfileCard, sostituisci il Container esterno con:\n\nMaterial(\n  color: Colors.transparent,\n  child: Ink(\n    decoration: AppDecorations.card,\n    child: InkWell(\n      borderRadius: BorderRadius.circular(20), \u002F\u002F stesso raggio della decoration\n      onTap: () {\n        ScaffoldMessenger.of(context).showSnackBar(\n          SnackBar(content: Text('Hai toccato $nome')),\n        );\n      },\n      child: Padding(\n        padding: const EdgeInsets.all(16),\n        child: Row(\n          \u002F\u002F ...stesso contenuto del passo precedente\n        ),\n      ),\n    ),\n  ),\n)","Toccando una card compare l'effetto ripple contenuto negli angoli arrotondati e viene mostrata una SnackBar con il nome selezionato. Hai ora una card decorata, riutilizzabile e interattiva, costruita solo con widget del framework.",[],1792316930148]