[{"data":1,"prerenderedAt":85},["ShallowReactive",2],{"tutorial-immagini-arrotondate-e-forme-personalizzate-in-flutter-con-cliprrect-clipoval-e-clippath":3,"comments-tutorial-immagini-arrotondate-e-forme-personalizzate-in-flutter-con-cliprrect-clipoval-e-clippath":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},114,"Immagini arrotondate e forme personalizzate in Flutter con ClipRRect, ClipOval e ClipPath","immagini-arrotondate-e-forme-personalizzate-in-flutter-con-cliprrect-clipoval-e-clippath","Impara a ritagliare widget e immagini in Flutter: angoli arrotondati con ClipRRect, avatar circolari con ClipOval e CircleAvatar, header a onda con ClipPath e un CustomClipper. Con consigli su antialiasing e performance.","In Flutter tutto è un widget, anche il ritaglio. Quando devi arrotondare gli angoli di un'immagine, creare un avatar circolare o disegnare un header con un taglio diagonale, non serve preparare PNG già ritagliati: bastano i widget della famiglia **Clip**.\n\nIn questo tutorial, pensato per chi è alle prime armi, vedremo:\n\n- `ClipRRect` per angoli arrotondati su immagini e contenitori\n- `ClipOval` e `CircleAvatar` per foto profilo circolari\n- `ClipRect` per mostrare solo una porzione di un widget\n- `ClipPath` con un `CustomClipper\u003CPath>` per forme libere (header a onda, taglio diagonale)\n- quando conviene usare `BoxDecoration` invece di un Clip, e come gestire `clipBehavior` e antialiasing\n\nAlla fine avrai una piccola schermata \"profilo\" con header curvo, avatar circolare e card con immagini arrotondate, realizzata interamente con widget standard di Flutter (nessun pacchetto esterno).","https:\u002F\u002Fflutter.it\u002Fstorage\u002Ftutorials\u002Fb9b2dd19-5a12-4655-b2be-078547c3abcf.jpg","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1683718522443-355f717f3b2a?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w5NzA2NTJ8MHwxfHJhbmRvbXx8fHx8fHx8fDE3OTEwODgyODR8&ixlib=rb-4.1.0&q=80&w=1080",{"name":12,"author_url":13,"photo_url":14},"Maxim Berg","https:\u002F\u002Funsplash.com\u002F@maxberg","https:\u002F\u002Funsplash.com\u002Fphotos\u002Fa-multicolored-image-of-a-circular-object-qpL0cRkLJn8","https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=faww6XQGCXQ",{"youtube_id":17,"duration_seconds":18,"published_at":19},"faww6XQGCXQ",124,"2026-10-04T10:51:48+00:00","beginner",30,"3.x","published","2026-10-04T04:31:24+00:00","ClipRRect, ClipOval e ClipPath in Flutter: guida","Guida beginner ai widget di ritaglio in Flutter: immagini arrotondate con ClipRRect, avatar con ClipOval, forme custom con ClipPath e CustomClipper.",{"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},763,"Preparare il progetto e la schermata di base","Crea un nuovo progetto (o usa uno esistente) e sostituisci il contenuto di `lib\u002Fmain.dart` con una schermata minimale che mostra un'immagine di rete **senza** alcun ritaglio: sarà il nostro punto di partenza per vedere la differenza.\n\n```bash\nflutter create clip_demo\ncd clip_demo\n```\n\nNota: usiamo `Image.network`, quindi su Android assicurati che il permesso Internet sia presente (nei progetti nuovi lo è già nei manifest di debug).","import 'package:flutter\u002Fmaterial.dart';\n\nvoid main() => runApp(const ClipDemoApp());\n\nclass ClipDemoApp extends StatelessWidget {\n  const ClipDemoApp({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return MaterialApp(\n      title: 'Clip Demo',\n      theme: ThemeData(\n        colorSchemeSeed: Colors.indigo,\n        useMaterial3: true,\n      ),\n      home: const HomePage(),\n    );\n  }\n}\n\nclass HomePage extends StatelessWidget {\n  const HomePage({super.key});\n\n  static const imageUrl =\n      'https:\u002F\u002Fpicsum.photos\u002F600\u002F400';\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(title: const Text('Ritagli in Flutter')),\n      body: ListView(\n        padding: const EdgeInsets.all(16),\n        children: [\n          const Text('Immagine senza ritaglio'),\n          const SizedBox(height: 8),\n          Image.network(\n            imageUrl,\n            height: 180,\n            width: double.infinity,\n            fit: BoxFit.cover,\n          ),\n        ],\n      ),\n    );\n  }\n}","dart","L'app mostra una lista con un'immagine rettangolare a tutta larghezza, con angoli vivi.",null,{"id":43,"position":44,"title":45,"body":46,"code_snippet":47,"code_language":39,"expected_result":48,"demo_url":41,"video_url":41},764,2,"Arrotondare gli angoli con ClipRRect","`ClipRRect` (Rounded Rectangle) ritaglia il figlio secondo un `BorderRadius`. È la soluzione standard per le immagini con angoli morbidi.\n\nPunti chiave:\n\n- `borderRadius` accetta `BorderRadius.circular(...)`, `BorderRadius.only(...)`, `BorderRadius.vertical(...)` ecc.\n- il ritaglio si applica a **qualsiasi** figlio, non solo alle immagini (puoi arrotondare anche un `Container` colorato o una `Column`)\n- usa `fit: BoxFit.cover` sull'immagine per evitare deformazioni\n\nAggiungi questi widget alla `ListView` creata nel passo precedente.","const SizedBox(height: 24),\nconst Text('ClipRRect: angoli arrotondati'),\nconst SizedBox(height: 8),\nClipRRect(\n  borderRadius: BorderRadius.circular(20),\n  child: Image.network(\n    imageUrl,\n    height: 180,\n    width: double.infinity,\n    fit: BoxFit.cover,\n  ),\n),\n\nconst SizedBox(height: 24),\nconst Text('ClipRRect: solo angoli superiori'),\nconst SizedBox(height: 8),\nClipRRect(\n  borderRadius: const BorderRadius.only(\n    topLeft: Radius.circular(32),\n    topRight: Radius.circular(32),\n  ),\n  child: Container(\n    height: 120,\n    width: double.infinity,\n    color: Colors.indigo,\n    alignment: Alignment.center,\n    child: const Text(\n      'Header arrotondato',\n      style: TextStyle(color: Colors.white, fontSize: 18),\n    ),\n  ),\n),","La seconda immagine ha gli angoli arrotondati di 20 px e sotto compare un contenitore indaco con solo i due angoli superiori smussati.",{"id":50,"position":51,"title":52,"body":53,"code_snippet":54,"code_language":39,"expected_result":55,"demo_url":41,"video_url":41},765,3,"Avatar circolari con ClipOval e CircleAvatar","Per una foto profilo hai due strade:\n\n1. **`ClipOval`**: ritaglia il figlio in un'ellisse inscritta nelle sue dimensioni. Se il figlio è quadrato, ottieni un cerchio perfetto.\n2. **`CircleAvatar`**: widget Material già pronto, con `backgroundImage`, `radius` e `child` per le iniziali di fallback.\n\nRegola pratica: usa `CircleAvatar` per gli avatar classici, `ClipOval` quando devi ritagliare un widget qualsiasi (un video, un `Container` con gradiente, una `Stack`).\n\nPer un bordo attorno all'avatar, avvolgi il tutto in un `Container` con `BoxDecoration(shape: BoxShape.circle, border: ...)` e un po' di padding.","const SizedBox(height: 24),\nconst Text('ClipOval e CircleAvatar'),\nconst SizedBox(height: 8),\nRow(\n  mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n  children: [\n    \u002F\u002F 1. ClipOval su un'immagine quadrata\n    ClipOval(\n      child: Image.network(\n        'https:\u002F\u002Fpicsum.photos\u002F200',\n        width: 90,\n        height: 90,\n        fit: BoxFit.cover,\n      ),\n    ),\n\n    \u002F\u002F 2. CircleAvatar con bordo\n    Container(\n      padding: const EdgeInsets.all(3),\n      decoration: BoxDecoration(\n        shape: BoxShape.circle,\n        border: Border.all(color: Colors.indigo, width: 2),\n      ),\n      child: const CircleAvatar(\n        radius: 42,\n        backgroundImage:\n            NetworkImage('https:\u002F\u002Fpicsum.photos\u002F201'),\n      ),\n    ),\n\n    \u002F\u002F 3. Fallback con iniziali\n    const CircleAvatar(\n      radius: 45,\n      backgroundColor: Colors.indigo,\n      child: Text(\n        'MR',\n        style: TextStyle(color: Colors.white, fontSize: 24),\n      ),\n    ),\n  ],\n),","Una riga con tre avatar circolari: immagine ritagliata, avatar con bordo indaco e avatar con le iniziali.",{"id":57,"position":58,"title":59,"body":60,"code_snippet":61,"code_language":39,"expected_result":62,"demo_url":41,"video_url":41},766,4,"Mostrare solo una porzione con ClipRect","`ClipRect` taglia il figlio sul suo rettangolo di layout. Da solo sembra inutile, ma diventa potente se combinato con `Align`: impostando `widthFactor` o `heightFactor` puoi mostrare solo una fetta del widget.\n\nQuesto è utile per:\n\n- anteprime \"a banda\" di un'immagine grande\n- effetti di reveal parziale\n- impedire che un figlio con `Transform.scale` o `OverflowBox` sbordi dal suo spazio\n\nNell'esempio mostriamo solo la metà superiore dell'immagine e la metà sinistra.","const SizedBox(height: 24),\nconst Text('ClipRect + Align: mezza immagine'),\nconst SizedBox(height: 8),\nRow(\n  children: [\n    Expanded(\n      child: ClipRect(\n        child: Align(\n          alignment: Alignment.topCenter,\n          heightFactor: 0.5, \u002F\u002F mostra solo il 50% in altezza\n          child: Image.network(\n            'https:\u002F\u002Fpicsum.photos\u002F400\u002F300',\n            fit: BoxFit.cover,\n          ),\n        ),\n      ),\n    ),\n    const SizedBox(width: 12),\n    Expanded(\n      child: ClipRect(\n        child: Align(\n          alignment: Alignment.centerLeft,\n          widthFactor: 0.5, \u002F\u002F mostra solo il 50% in larghezza\n          child: Image.network(\n            'https:\u002F\u002Fpicsum.photos\u002F400\u002F301',\n            fit: BoxFit.cover,\n          ),\n        ),\n      ),\n    ),\n  ],\n),","Due immagini affiancate: la prima mostra solo la fascia superiore, la seconda solo la metà sinistra, senza spazi vuoti o overflow.",{"id":64,"position":65,"title":66,"body":67,"code_snippet":68,"code_language":39,"expected_result":69,"demo_url":41,"video_url":41},767,5,"Forme libere con ClipPath e un CustomClipper","Quando la forma non è un rettangolo né un'ellisse, entra in gioco `ClipPath`. Devi fornirgli un `CustomClipper\u003CPath>` in cui disegni il contorno usando le coordinate del widget (`size.width`, `size.height`).\n\nMetodi utili sul `Path`:\n\n- `lineTo(x, y)`: linea retta\n- `quadraticBezierTo(cx, cy, x, y)`: curva con un punto di controllo\n- `close()`: chiude la forma\n\nIn `shouldReclip` restituisci `false` se la forma non cambia mai: eviti ricalcoli inutili.\n\nCreiamo due clipper: uno a onda per un header e uno con taglio diagonale.","class WaveClipper extends CustomClipper\u003CPath> {\n  @override\n  Path getClip(Size size) {\n    final path = Path();\n    path.lineTo(0, size.height - 40);\n    path.quadraticBezierTo(\n      size.width \u002F 4, size.height,      \u002F\u002F punto di controllo\n      size.width \u002F 2, size.height - 20, \u002F\u002F punto di arrivo\n    );\n    path.quadraticBezierTo(\n      size.width * 3 \u002F 4, size.height - 45,\n      size.width, size.height - 15,\n    );\n    path.lineTo(size.width, 0);\n    path.close();\n    return path;\n  }\n\n  @override\n  bool shouldReclip(covariant CustomClipper\u003CPath> oldClipper) => false;\n}\n\nclass DiagonalClipper extends CustomClipper\u003CPath> {\n  @override\n  Path getClip(Size size) {\n    return Path()\n      ..lineTo(0, size.height)\n      ..lineTo(size.width, size.height - 50)\n      ..lineTo(size.width, 0)\n      ..close();\n  }\n\n  @override\n  bool shouldReclip(covariant CustomClipper\u003CPath> oldClipper) => false;\n}\n\n\u002F\u002F --- uso nella ListView ---\n\u002F\u002F const SizedBox(height: 24),\n\u002F\u002F const Text('ClipPath: header a onda'),\n\u002F\u002F const SizedBox(height: 8),\n\u002F\u002F ClipPath(\n\u002F\u002F   clipper: WaveClipper(),\n\u002F\u002F   child: Container(\n\u002F\u002F     height: 160,\n\u002F\u002F     color: Colors.indigo,\n\u002F\u002F     alignment: Alignment.topCenter,\n\u002F\u002F     padding: const EdgeInsets.only(top: 32),\n\u002F\u002F     child: const Text('Benvenuto',\n\u002F\u002F         style: TextStyle(color: Colors.white, fontSize: 24)),\n\u002F\u002F   ),\n\u002F\u002F ),\n\u002F\u002F const SizedBox(height: 24),\n\u002F\u002F ClipPath(\n\u002F\u002F   clipper: DiagonalClipper(),\n\u002F\u002F   child: Image.network(imageUrl,\n\u002F\u002F       height: 180, width: double.infinity, fit: BoxFit.cover),\n\u002F\u002F ),","Compare un header indaco con il bordo inferiore ondulato e, sotto, un'immagine con il bordo inferiore tagliato in diagonale.",{"id":71,"position":72,"title":73,"body":74,"code_snippet":75,"code_language":39,"expected_result":76,"demo_url":41,"video_url":41},768,6,"Quando usare BoxDecoration al posto di un Clip","Non sempre serve un widget `Clip`. Se devi solo arrotondare uno sfondo (colore, gradiente o immagine), `Container` con `BoxDecoration` è più leggero perché evita un livello di ritaglio aggiuntivo.\n\nLinee guida:\n\n- **sfondo arrotondato** (colore\u002Fgradiente\u002Fimmagine di sfondo) → `BoxDecoration(borderRadius: ..., image: ...)`\n- **contenuto figlio da tagliare** (immagine `Image`, lista, video, `Stack`) → `ClipRRect`\n- `Card`, `Material`, `InkWell` hanno `shape`\u002F`borderRadius` e `clipBehavior`: usali invece di annidare Clip\n- se combini `BoxDecoration` arrotondata e figlio che sborda, imposta `clipBehavior: Clip.antiAlias` sul `Container`\n\nUn altro dettaglio: tutti i Clip accettano `clipBehavior`. Il default `Clip.antiAlias` va bene quasi sempre; `Clip.antiAliasWithSaveLayer` è il più costoso e serve solo in casi rari di fusione di colori.","\u002F\u002F A) Sfondo arrotondato: nessun Clip necessario\nContainer(\n  height: 160,\n  decoration: BoxDecoration(\n    borderRadius: BorderRadius.circular(24),\n    gradient: const LinearGradient(\n      colors: [Colors.indigo, Colors.purple],\n    ),\n    boxShadow: const [\n      BoxShadow(blurRadius: 12, color: Colors.black26, offset: Offset(0, 6)),\n    ],\n    image: const DecorationImage(\n      image: NetworkImage('https:\u002F\u002Fpicsum.photos\u002F600\u002F401'),\n      fit: BoxFit.cover,\n      opacity: 0.6,\n    ),\n  ),\n  alignment: Alignment.center,\n  child: const Text('BoxDecoration',\n      style: TextStyle(color: Colors.white, fontSize: 20)),\n),\n\nconst SizedBox(height: 16),\n\n\u002F\u002F B) Card che ritaglia da sola il contenuto\nCard(\n  clipBehavior: Clip.antiAlias,\n  shape: RoundedRectangleBorder(\n    borderRadius: BorderRadius.circular(20),\n  ),\n  child: Column(\n    crossAxisAlignment: CrossAxisAlignment.stretch,\n    children: [\n      Image.network('https:\u002F\u002Fpicsum.photos\u002F600\u002F402',\n          height: 150, fit: BoxFit.cover),\n      const Padding(\n        padding: EdgeInsets.all(12),\n        child: Text('Immagine ritagliata dalla Card'),\n      ),\n    ],\n  ),\n),","Il gradiente con immagine di sfondo è arrotondato senza usare Clip, e la Card ritaglia automaticamente l'immagine ai propri angoli.",{"id":78,"position":79,"title":80,"body":81,"code_snippet":82,"code_language":39,"expected_result":83,"demo_url":41,"video_url":41},769,7,"Mettere tutto insieme: una schermata profilo","Uniamo i pezzi in una schermata profilo realistica: header a onda, avatar circolare sovrapposto con `Stack`, e una griglia di immagini arrotondate.\n\nNota su `Stack`: l'avatar viene posizionato con `Positioned` e sporge oltre l'header; per questo diamo alla `Stack` un'altezza sufficiente e usiamo `clipBehavior: Clip.none` se vuoi permettere lo sbordo.\n\n**Consigli finali**\n\n- ogni Clip ha un costo: non annidarne più del necessario\n- preferisci `const` dove possibile per ridurre le ricostruzioni\n- verifica il risultato su schermi diversi: le forme di `ClipPath` dipendono da `size`, quindi si adattano automaticamente\n- per icone e loghi vettoriali complessi valuta un SVG invece di un `CustomClipper`","class ProfilePage extends StatelessWidget {\n  const ProfilePage({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      body: ListView(\n        padding: EdgeInsets.zero,\n        children: [\n          SizedBox(\n            height: 220,\n            child: Stack(\n              clipBehavior: Clip.none,\n              alignment: Alignment.topCenter,\n              children: [\n                ClipPath(\n                  clipper: WaveClipper(),\n                  child: Container(height: 170, color: Colors.indigo),\n                ),\n                Positioned(\n                  bottom: 0,\n                  child: Container(\n                    padding: const EdgeInsets.all(4),\n                    decoration: const BoxDecoration(\n                      shape: BoxShape.circle,\n                      color: Colors.white,\n                    ),\n                    child: const CircleAvatar(\n                      radius: 50,\n                      backgroundImage:\n                          NetworkImage('https:\u002F\u002Fpicsum.photos\u002F300'),\n                    ),\n                  ),\n                ),\n              ],\n            ),\n          ),\n          const SizedBox(height: 12),\n          const Center(\n            child: Text('Giulia Rossi',\n                style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold)),\n          ),\n          const SizedBox(height: 16),\n          GridView.builder(\n            shrinkWrap: true,\n            physics: const NeverScrollableScrollPhysics(),\n            padding: const EdgeInsets.all(16),\n            itemCount: 6,\n            gridDelegate:\n                const SliverGridDelegateWithFixedCrossAxisCount(\n              crossAxisCount: 3,\n              mainAxisSpacing: 10,\n              crossAxisSpacing: 10,\n            ),\n            itemBuilder: (context, index) => ClipRRect(\n              borderRadius: BorderRadius.circular(14),\n              child: Image.network(\n                'https:\u002F\u002Fpicsum.photos\u002F200?random=$index',\n                fit: BoxFit.cover,\n              ),\n            ),\n          ),\n        ],\n      ),\n    );\n  }\n}","Impostando `home: const ProfilePage()` vedi una schermata profilo con header curvo indaco, avatar circolare bordato di bianco a cavallo dell'onda e una griglia 3x3 di miniature con angoli arrotondati.",[],1792316927801]