[{"data":1,"prerenderedAt":75},["ShallowReactive",2],{"tutorial-estrarre-codice-riutilizzabile-in-flutter-con-le-extension-methods-di-dart":3,"comments-tutorial-estrarre-codice-riutilizzabile-in-flutter-con-le-extension-methods-di-dart":74},{"id":4,"title":5,"slug":6,"excerpt":7,"intro":8,"cover_image":9,"video_url":10,"difficulty":11,"estimated_minutes":12,"flutter_version":13,"status":14,"published_at":15,"meta_title":16,"meta_description":17,"category":18,"author":22,"steps":25},52,"Estrarre codice riutilizzabile in Flutter con le extension methods di Dart","estrarre-codice-riutilizzabile-in-flutter-con-le-extension-methods-di-dart","Impara a scrivere codice più pulito e leggibile in Flutter usando le extension methods di Dart per aggiungere funzionalità a classi esistenti come String, BuildContext e DateTime.","Le **extension methods** sono una delle funzionalità più potenti e sottovalutate di Dart. Permettono di aggiungere nuovi metodi e proprietà a classi già esistenti — anche a quelle della libreria standard o di package di terze parti — senza modificarne il codice sorgente né ricorrere all'ereditarietà.\n\nIn questo tutorial vedremo come usarle in un progetto Flutter reale per ridurre il boilerplate: aggiungeremo scorciatoie a `BuildContext` per accedere a tema e dimensioni, arricchiremo `String` con validazioni utili e formatteremo le date con `DateTime`. Alla fine avrai un piccolo insieme di extension pronte da riusare in tutti i tuoi progetti.","https:\u002F\u002Fflutter.it\u002Fstorage\u002Ftutorials\u002F07e9c162-1f35-4e25-8bab-0bacec203d42.jpg",null,"intermediate",25,"3.x","published","2026-07-29T04:30:56+00:00","Extension methods in Flutter e Dart: codice più pulito","Guida pratica alle extension methods di Dart in Flutter: aggiungi metodi a String, BuildContext e DateTime per un codice più leggibile e riutilizzabile.",{"id":19,"name":20,"slug":21},3,"Best practice","best-practice",{"id":23,"name":24},1,"Flutter Bot",[26,33,40,46,53,60,67],{"id":27,"position":23,"title":28,"body":29,"code_snippet":30,"code_language":31,"expected_result":32,"demo_url":10,"video_url":10},350,"Capire cos'è una extension method","Una extension aggiunge funzionalità a un tipo esistente senza sottoclassarlo. La sintassi di base è:\n\n```\nextension NomeExtension on TipoEsistente {\n  \u002F\u002F metodi e getter\n}\n```\n\nIl `NomeExtension` è opzionale ma consigliato: serve per risolvere eventuali conflitti e per importare selettivamente l'extension. Dentro il corpo puoi usare la parola chiave `this` per riferirti all'istanza del tipo esteso.\n\nIniziamo con un esempio semplice su `int` per prendere confidenza con la sintassi.","extension IntUtils on int {\n  bool get isEven => this % 2 == 0;\n\n  Duration get seconds => Duration(seconds: this);\n\n  int doubled() => this * 2;\n}\n\nvoid main() {\n  print(4.isEven);   \u002F\u002F true\n  print(3.doubled()); \u002F\u002F 6\n  print(5.seconds);   \u002F\u002F 0:00:05.000000\n}","dart","Il codice compila ed esegue stampando true, 6 e la durata di 5 secondi, dimostrando che i metodi sono ora disponibili direttamente sugli interi.",{"id":34,"position":35,"title":36,"body":37,"code_snippet":38,"code_language":31,"expected_result":39,"demo_url":10,"video_url":10},351,2,"Creare scorciatoie utili su BuildContext","In Flutter accediamo spessissimo a tema, colori e dimensioni tramite `Theme.of(context)` e `MediaQuery.of(context)`. Sono chiamate lunghe e ripetitive. Con una extension su `BuildContext` possiamo creare delle scorciatoie leggibili.\n\nCrea un file `lib\u002Fextensions\u002Fcontext_extensions.dart` e definisci l'extension seguente.","import 'package:flutter\u002Fmaterial.dart';\n\nextension ContextExtensions on BuildContext {\n  \u002F\u002F Tema\n  ThemeData get theme => Theme.of(this);\n  TextTheme get textTheme => theme.textTheme;\n  ColorScheme get colors => theme.colorScheme;\n\n  \u002F\u002F Dimensioni schermo\n  Size get screenSize => MediaQuery.sizeOf(this);\n  double get screenWidth => screenSize.width;\n  double get screenHeight => screenSize.height;\n  bool get isTablet => screenWidth >= 600;\n\n  \u002F\u002F Navigazione rapida\n  void pop\u003CT>([T? result]) => Navigator.of(this).pop(result);\n\n  Future\u003CT?> push\u003CT>(Widget page) =>\n      Navigator.of(this).push\u003CT>(MaterialPageRoute(builder: (_) => page));\n}","Il file compila senza errori ed esporta un'extension con getter per tema, colori e dimensioni oltre a metodi di navigazione.",{"id":41,"position":19,"title":42,"body":43,"code_snippet":44,"code_language":31,"expected_result":45,"demo_url":10,"video_url":10},352,"Usare l'extension di BuildContext in un widget","Vediamo la differenza in un widget reale. Importa il file creato e usa le nuove scorciatoie: il codice diventa molto più compatto e leggibile.","import 'package:flutter\u002Fmaterial.dart';\nimport 'extensions\u002Fcontext_extensions.dart';\n\nclass ProfileHeader extends StatelessWidget {\n  const ProfileHeader({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return Container(\n      width: context.screenWidth,\n      padding: const EdgeInsets.all(16),\n      color: context.colors.primaryContainer,\n      child: Column(\n        crossAxisAlignment: CrossAxisAlignment.start,\n        children: [\n          Text(\n            'Benvenuto',\n            style: context.textTheme.headlineSmall,\n          ),\n          if (context.isTablet)\n            Text('Layout tablet attivo', style: context.textTheme.bodySmall),\n        ],\n      ),\n    );\n  }\n}","Il widget mostra un header colorato con il colore del tema e adatta il testo mostrato in base alla larghezza dello schermo, senza chiamate verbose a Theme.of e MediaQuery.of.",{"id":47,"position":48,"title":49,"body":50,"code_snippet":51,"code_language":31,"expected_result":52,"demo_url":10,"video_url":10},353,4,"Arricchire String con validazioni e formattazioni","Le validazioni di form e la manipolazione di testo sono operazioni comuni. Con un'extension su `String` (e `String?` per gestire i valori nullable) centralizziamo la logica in un unico punto.\n\nCrea `lib\u002Fextensions\u002Fstring_extensions.dart`.","extension StringExtensions on String {\n  bool get isValidEmail {\n    final regex = RegExp(r'^[\\w.+-]+@[\\w-]+\\.[\\w.-]+$');\n    return regex.hasMatch(this);\n  }\n\n  String get capitalized {\n    if (isEmpty) return this;\n    return '${this[0].toUpperCase()}${substring(1)}';\n  }\n\n  String truncate(int maxLength) {\n    if (length \u003C= maxLength) return this;\n    return '${substring(0, maxLength)}\\u2026';\n  }\n}\n\nextension NullableStringExtensions on String? {\n  bool get isNullOrBlank => this == null || this!.trim().isEmpty;\n}","Ottieni metodi come 'mario@test.it'.isValidEmail (true), 'ciao'.capitalized ('Ciao') e il controllo isNullOrBlank su stringhe nullable.",{"id":54,"position":55,"title":56,"body":57,"code_snippet":58,"code_language":31,"expected_result":59,"demo_url":10,"video_url":10},354,5,"Formattare le date con un'extension su DateTime","Formattare date è ripetitivo: usiamo il package `intl` incapsulandolo dentro extension dedicate, così da non dover ricreare i `DateFormat` ovunque.\n\nAggiungi la dipendenza con:\n\n```bash\nflutter pub add intl\n```\n\nPoi crea `lib\u002Fextensions\u002Fdate_extensions.dart`.","import 'package:intl\u002Fintl.dart';\n\nextension DateTimeExtensions on DateTime {\n  String get shortDate => DateFormat('dd\u002FMM\u002Fyyyy').format(this);\n\n  String get readable => DateFormat('d MMMM yyyy', 'it_IT').format(this);\n\n  bool get isToday {\n    final now = DateTime.now();\n    return year == now.year && month == now.month && day == now.day;\n  }\n\n  String get relative {\n    final diff = DateTime.now().difference(this);\n    if (diff.inMinutes \u003C 1) return 'adesso';\n    if (diff.inHours \u003C 1) return '${diff.inMinutes} min fa';\n    if (diff.inDays \u003C 1) return '${diff.inHours} ore fa';\n    return shortDate;\n  }\n}","Puoi scrivere DateTime.now().shortDate per ottenere '05\u002F06\u002F2024' o .relative per '3 min fa', semplificando la formattazione delle date in tutta l'app.",{"id":61,"position":62,"title":63,"body":64,"code_snippet":65,"code_language":31,"expected_result":66,"demo_url":10,"video_url":10},355,6,"Organizzare le extension con un file barrel","Per evitare import multipli in ogni file, raggruppa tutte le tue extension in un unico **barrel file**. Così importi una sola riga e hai accesso a tutto il tuo toolkit.\n\nCrea `lib\u002Fextensions\u002Fextensions.dart`.","\u002F\u002F lib\u002Fextensions\u002Fextensions.dart\nexport 'context_extensions.dart';\nexport 'string_extensions.dart';\nexport 'date_extensions.dart';\n\n\u002F\u002F In qualsiasi widget:\n\u002F\u002F import 'package:mia_app\u002Fextensions\u002Fextensions.dart';\n\u002F\u002F\n\u002F\u002F Text(user.name.capitalized, style: context.textTheme.titleLarge)\n\u002F\u002F Text(order.date.relative)\n\u002F\u002F if (email.isValidEmail) { ... }","Con un solo import hai a disposizione tutte le extension, mantenendo il codice dei widget pulito e conciso.",{"id":68,"position":69,"title":70,"body":71,"code_snippet":72,"code_language":31,"expected_result":73,"demo_url":10,"video_url":10},356,7,"Best practice e limiti da conoscere","Le extension sono potenti ma vanno usate con criterio. Ecco alcune linee guida:\n\n- **Non abusarne**: crea extension solo per logica genuinamente riutilizzabile, non per nascondere complessità di dominio.\n- **Sono risolte staticamente**: il metodo chiamato dipende dal *tipo statico* della variabile, non da quello runtime. Un'extension su `Animal` non verrà applicata se la variabile è tipizzata come `dynamic`.\n- **Nessun override**: se il tipo ha già un metodo con lo stesso nome, vince sempre il metodo originale della classe.\n- **Conflitti**: se due extension importate definiscono lo stesso metodo, usa `NomeExtension(oggetto).metodo()` per disambiguare.\n- **Nessuno stato**: le extension non possono dichiarare campi di istanza, solo metodi e getter\u002Fsetter.\n\nSeguendo queste regole otterrai un codice più espressivo senza sorprese.","\u002F\u002F Disambiguare due extension in conflitto\n\u002F\u002F StringExtensions('ciao').capitalized;\n\n\u002F\u002F Attenzione al tipo statico:\ndynamic value = 4;\n\u002F\u002F value.doubled(); \u002F\u002F ERRORE: extension non applicata su dynamic\nint number = 4;\nnumber.doubled(); \u002F\u002F OK","Comprendi quando e come usare le extension in modo sicuro, evitando i due errori più comuni: l'uso su dynamic e i conflitti tra nomi.",[],1785306094806]