[{"data":1,"prerenderedAt":78},["ShallowReactive",2],{"tutorial-i-pulsanti-in-flutter-elevatedbutton-textbutton-outlinedbutton-e-iconbutton":3,"comments-tutorial-i-pulsanti-in-flutter-elevatedbutton-textbutton-outlinedbutton-e-iconbutton":77},{"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},84,"I pulsanti in Flutter: ElevatedButton, TextButton, OutlinedButton e IconButton","i-pulsanti-in-flutter-elevatedbutton-textbutton-outlinedbutton-e-iconbutton","Impara a usare e personalizzare i pulsanti di Material 3 in Flutter: ElevatedButton, FilledButton, TextButton, OutlinedButton, IconButton, stati disabilitati e stile globale nel tema.","I pulsanti sono il primo punto di contatto tra l'utente e la logica della tua app: senza un tocco, non succede nulla.\n\nFlutter offre una famiglia completa di pulsanti Material 3 — `ElevatedButton`, `FilledButton`, `TextButton`, `OutlinedButton`, `IconButton` — che condividono la stessa API e si differenziano solo per l'enfasi visiva.\n\nIn questo tutorial per principianti vedremo:\n\n- quale pulsante usare in base all'importanza dell'azione;\n- come gestire il callback `onPressed` e lo stato **disabilitato**;\n- come aggiungere un'icona con `.icon()`;\n- come personalizzare colori, forma e padding con `ButtonStyle` e `styleFrom`;\n- come definire uno **stile globale** nel `ThemeData` per non ripetere il codice.\n\nAl termine avrai una schermata dimostrativa con tutti i tipi di pulsante e un pulsante \"Salva\" che mostra un indicatore di caricamento mentre lavora.","https:\u002F\u002Fflutter.it\u002Fstorage\u002Ftutorials\u002F4735b535-fc0a-4da8-a4d9-a55347b1785a.jpg","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1603731955457-b45eaf9e564e?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w5NzA2NTJ8MHwxfHJhbmRvbXx8fHx8fHx8fDE3ODg0OTYyNjd8&ixlib=rb-4.1.0&q=80&w=1080",{"name":12,"author_url":13,"photo_url":14},"Brett Jordan","https:\u002F\u002Funsplash.com\u002F@brett_jordan","https:\u002F\u002Funsplash.com\u002Fphotos\u002Fwhite-and-green-iphone-screen-ShEPzAZctBQ","https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=sGFjD-VUuIY",{"youtube_id":17,"duration_seconds":18,"published_at":19},"sGFjD-VUuIY",134,"2026-09-04T09:14:55+00:00","beginner",30,"3.x","published","2026-09-04T04:31:08+00:00","Pulsanti in Flutter: guida completa per principianti","Guida pratica ai pulsanti Flutter: ElevatedButton, FilledButton, TextButton, OutlinedButton, IconButton, stati disabilitati e stile nel tema.",{"id":28,"name":29,"slug":30},1,"Guide","guide",{"id":28,"name":32},"Flutter Bot",[34,42,49,56,63,70],{"id":35,"position":28,"title":36,"body":37,"code_snippet":38,"code_language":39,"expected_result":40,"demo_url":41,"video_url":41},560,"Preparare il progetto e la schermata di prova","Crea un nuovo progetto (o apri il tuo) e sostituisci il contenuto di `lib\u002Fmain.dart` con una struttura minima: `MaterialApp` con Material 3 attivo e uno `Scaffold` che ospiterà i nostri pulsanti.\n\nUsiamo `ColorScheme.fromSeed` per generare una palette coerente: i pulsanti Material 3 prendono automaticamente i colori dal tema, quindi cambiando il `seedColor` cambierà l'aspetto di tutti i bottoni.\n\nIl `body` è un `ListView` con `padding`: così, anche aggiungendo molti pulsanti, la pagina resta scorrevole e non genera errori di overflow.","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: 'Pulsanti Flutter',\n      debugShowCheckedModeBanner: false,\n      theme: ThemeData(\n        useMaterial3: true,\n        colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo),\n      ),\n      home: const ButtonsPage(),\n    );\n  }\n}\n\nclass ButtonsPage extends StatelessWidget {\n  const ButtonsPage({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(title: const Text('Pulsanti in Flutter')),\n      body: ListView(\n        padding: const EdgeInsets.all(16),\n        children: const [\n          Text('Qui aggiungeremo i pulsanti'),\n        ],\n      ),\n    );\n  }\n}","dart","L'app si avvia mostrando una AppBar con il titolo \"Pulsanti in Flutter\" e il testo segnaposto nel corpo.",null,{"id":43,"position":44,"title":45,"body":46,"code_snippet":47,"code_language":39,"expected_result":48,"demo_url":41,"video_url":41},561,2,"I quattro pulsanti base e la loro gerarchia visiva","Material 3 propone una scala di enfasi. Scegli il pulsante in base a **quanto è importante l'azione**:\n\n| Widget | Enfasi | Quando usarlo |\n|---|---|---|\n| `FilledButton` | Massima | Azione principale della schermata (Salva, Acquista) |\n| `ElevatedButton` | Alta | Azione importante su sfondi \"pieni\" o con contenuti sotto |\n| `OutlinedButton` | Media | Azione secondaria (Annulla, Modifica) |\n| `TextButton` | Bassa | Azioni terziarie, link, dialog |\n\nTutti accettano gli stessi parametri fondamentali:\n\n- `onPressed`: la funzione chiamata al tocco;\n- `child`: il contenuto, di solito un `Text`.\n\n**Regola d'oro**: se passi `null` a `onPressed`, il pulsante diventa automaticamente **disabilitato** e grigio. Non serve nessuna proprietà `enabled`.\n\nSostituisci i `children` del `ListView` con l'elenco qui sotto.","children: [\n  FilledButton(\n    onPressed: () => debugPrint('FilledButton premuto'),\n    child: const Text('Filled (azione principale)'),\n  ),\n  const SizedBox(height: 12),\n  ElevatedButton(\n    onPressed: () => debugPrint('ElevatedButton premuto'),\n    child: const Text('Elevated'),\n  ),\n  const SizedBox(height: 12),\n  OutlinedButton(\n    onPressed: () => debugPrint('OutlinedButton premuto'),\n    child: const Text('Outlined (secondaria)'),\n  ),\n  const SizedBox(height: 12),\n  TextButton(\n    onPressed: () => debugPrint('TextButton premuto'),\n    child: const Text('Text (terziaria)'),\n  ),\n  const SizedBox(height: 24),\n  const ElevatedButton(\n    onPressed: null, \u002F\u002F disabilitato\n    child: Text('Disabilitato'),\n  ),\n],","Vedi quattro pulsanti con enfasi crescente\u002Fdecrescente e uno grigio non cliccabile. Toccandoli, i messaggi compaiono nella console di debug.",{"id":50,"position":51,"title":52,"body":53,"code_snippet":54,"code_language":39,"expected_result":55,"demo_url":41,"video_url":41},562,3,"Pulsanti con icona e IconButton","Ogni pulsante ha un costruttore `.icon()` che affianca un'icona all'etichetta: `ElevatedButton.icon`, `FilledButton.icon`, `OutlinedButton.icon`, `TextButton.icon`. I parametri sono `icon` e `label`.\n\nQuando invece vuoi **solo** l'icona (tipico nelle `AppBar` o nelle liste) usi `IconButton`, che richiede `icon` e opzionalmente `tooltip` per l'accessibilità.\n\nPer allineare più pulsanti sulla stessa riga usa un `Row` con `MainAxisAlignment`, oppure `Wrap` se vuoi che vadano a capo su schermi stretti: è la scelta più sicura per evitare overflow.","Wrap(\n  spacing: 12,\n  runSpacing: 12,\n  children: [\n    FilledButton.icon(\n      onPressed: () {},\n      icon: const Icon(Icons.download),\n      label: const Text('Scarica'),\n    ),\n    OutlinedButton.icon(\n      onPressed: () {},\n      icon: const Icon(Icons.share),\n      label: const Text('Condividi'),\n    ),\n    IconButton(\n      onPressed: () {},\n      tooltip: 'Preferiti',\n      icon: const Icon(Icons.favorite_border),\n    ),\n    IconButton.filledTonal(\n      onPressed: () {},\n      tooltip: 'Elimina',\n      icon: const Icon(Icons.delete_outline),\n    ),\n  ],\n),","Una riga di pulsanti con icona che va a capo automaticamente sugli schermi stretti; tenendo premuto su un IconButton compare il tooltip.",{"id":57,"position":58,"title":59,"body":60,"code_snippet":61,"code_language":39,"expected_result":62,"demo_url":41,"video_url":41},563,4,"Personalizzare l'aspetto con styleFrom","Per cambiare colori, forma, dimensioni e padding di un singolo pulsante usa il metodo statico `styleFrom` della classe corrispondente (`ElevatedButton.styleFrom`, `FilledButton.styleFrom`, ecc.). Restituisce un `ButtonStyle` da passare al parametro `style`.\n\nLe proprietà più usate:\n\n- `backgroundColor` \u002F `foregroundColor`: sfondo e colore di testo e icona;\n- `padding`: spazio interno;\n- `shape`: forma del bordo, tipicamente `RoundedRectangleBorder`;\n- `minimumSize`: dimensione minima, utile per un pulsante a tutta larghezza con `Size.fromHeight(52)`;\n- `textStyle`: stile del testo.\n\n💡 **Suggerimento**: per un pulsante largo quanto lo schermo, `minimumSize: const Size.fromHeight(52)` è più pulito che avvolgerlo in un `SizedBox(width: double.infinity)`.","FilledButton(\n  style: FilledButton.styleFrom(\n    backgroundColor: Colors.deepOrange,\n    foregroundColor: Colors.white,\n    minimumSize: const Size.fromHeight(52),\n    padding: const EdgeInsets.symmetric(horizontal: 24),\n    shape: RoundedRectangleBorder(\n      borderRadius: BorderRadius.circular(12),\n    ),\n    textStyle: const TextStyle(\n      fontSize: 16,\n      fontWeight: FontWeight.bold,\n    ),\n  ),\n  onPressed: () {},\n  child: const Text('Acquista ora'),\n),\nconst SizedBox(height: 12),\nOutlinedButton(\n  style: OutlinedButton.styleFrom(\n    foregroundColor: Colors.deepOrange,\n    side: const BorderSide(color: Colors.deepOrange, width: 1.5),\n    minimumSize: const Size.fromHeight(52),\n    shape: const StadiumBorder(),\n  ),\n  onPressed: () {},\n  child: const Text('Aggiungi al carrello'),\n),","Un pulsante arancione a tutta larghezza con angoli arrotondati e, sotto, un pulsante bordato a forma di pillola.",{"id":64,"position":65,"title":66,"body":67,"code_snippet":68,"code_language":39,"expected_result":69,"demo_url":41,"video_url":41},564,5,"Un pulsante con stato di caricamento","Un errore classico è permettere all'utente di premere più volte lo stesso pulsante mentre un'operazione è in corso (ad esempio un salvataggio in rete).\n\nLa soluzione è semplice: in uno `StatefulWidget` teniamo una variabile booleana `_loading`. Quando è `true` passiamo `null` a `onPressed` (pulsante disabilitato) e mostriamo un `CircularProgressIndicator` al posto del testo.\n\nNota due dettagli importanti:\n\n- l'indicatore va incapsulato in un `SizedBox` di dimensioni piccole (es. 20×20), altrimenti \"gonfia\" il pulsante;\n- dopo un `await` verifica sempre `if (!mounted) return;` prima di chiamare `setState`, perché l'utente potrebbe aver lasciato la schermata.","class SaveButton extends StatefulWidget {\n  const SaveButton({super.key});\n\n  @override\n  State\u003CSaveButton> createState() => _SaveButtonState();\n}\n\nclass _SaveButtonState extends State\u003CSaveButton> {\n  bool _loading = false;\n\n  Future\u003Cvoid> _save() async {\n    setState(() => _loading = true);\n    await Future.delayed(const Duration(seconds: 2)); \u002F\u002F simula la rete\n    if (!mounted) return;\n    setState(() => _loading = false);\n    ScaffoldMessenger.of(context).showSnackBar(\n      const SnackBar(content: Text('Salvataggio completato')),\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return FilledButton(\n      onPressed: _loading ? null : _save,\n      style: FilledButton.styleFrom(\n        minimumSize: const Size.fromHeight(52),\n      ),\n      child: _loading\n          ? const SizedBox(\n              width: 20,\n              height: 20,\n              child: CircularProgressIndicator(\n                strokeWidth: 2,\n                color: Colors.white,\n              ),\n            )\n          : const Text('Salva'),\n    );\n  }\n}","Premendo \"Salva\" il pulsante si disabilita e mostra uno spinner per 2 secondi, poi appare una SnackBar di conferma.",{"id":71,"position":72,"title":73,"body":74,"code_snippet":75,"code_language":39,"expected_result":76,"demo_url":41,"video_url":41},565,6,"Definire lo stile globale nel tema","Se tutti i pulsanti dell'app devono avere lo stesso raggio degli angoli e la stessa altezza, ripetere `styleFrom` ovunque è una cattiva pratica. Meglio configurarlo **una sola volta** nel `ThemeData` con i temi dedicati:\n\n- `filledButtonTheme` → `FilledButtonThemeData`\n- `elevatedButtonTheme` → `ElevatedButtonThemeData`\n- `outlinedButtonTheme` → `OutlinedButtonThemeData`\n- `textButtonTheme` → `TextButtonThemeData`\n- `iconButtonTheme` → `IconButtonThemeData`\n\nDa quel momento ogni pulsante eredita lo stile; se in un caso specifico serve un'eccezione, basta passare comunque un `style` locale, che ha la precedenza.\n\nRicompila e verifica: tutti i pulsanti senza `style` esplicito adottano il nuovo aspetto.","theme: ThemeData(\n  useMaterial3: true,\n  colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo),\n  filledButtonTheme: FilledButtonThemeData(\n    style: FilledButton.styleFrom(\n      minimumSize: const Size.fromHeight(52),\n      shape: RoundedRectangleBorder(\n        borderRadius: BorderRadius.circular(12),\n      ),\n      textStyle: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),\n    ),\n  ),\n  outlinedButtonTheme: OutlinedButtonThemeData(\n    style: OutlinedButton.styleFrom(\n      minimumSize: const Size.fromHeight(52),\n      shape: RoundedRectangleBorder(\n        borderRadius: BorderRadius.circular(12),\n      ),\n    ),\n  ),\n  textButtonTheme: TextButtonThemeData(\n    style: TextButton.styleFrom(\n      textStyle: const TextStyle(fontWeight: FontWeight.w600),\n    ),\n  ),\n),","Tutti i pulsanti dell'app hanno altezza 52 e angoli arrotondati a 12 px senza ripetere codice, tranne quelli con uno stile locale personalizzato.",[],1789120582333]