[{"data":1,"prerenderedAt":27},["ShallowReactive",2],{"articolo-font-personalizzati-in-flutter-variable-font-google-fonts-e-icone-custom":3,"comments-article-font-personalizzati-in-flutter-variable-font-google-fonts-e-icone-custom":26},{"id":4,"title":5,"slug":6,"excerpt":7,"body":8,"cover_image":9,"cover_remote_url":10,"cover_credit":11,"video_url":15,"status":16,"published_at":17,"meta_title":18,"meta_description":19,"category":20,"author":24},137,"Font personalizzati in Flutter: variable font, google_fonts e icone custom","font-personalizzati-in-flutter-variable-font-google-fonts-e-icone-custom","Come integrare font personalizzati in un'app Flutter: dichiarazione in pubspec, variable font con FontVariation, google_fonts senza download a runtime, fallback per emoji e CJK, icone custom e accessibilità del testo.","## Perché la tipografia merita attenzione\n\nLa tipografia è uno degli elementi che distingue di più un'app \"generica\" da un prodotto curato. Flutter rende semplice usare font personalizzati, ma dietro alla sezione `fonts:` del `pubspec.yaml` ci sono parecchi dettagli che fanno la differenza: pesi mappati male, variable font trattati come statici, download a runtime che falliscono offline, icone custom che gonfiano il bundle.\n\nIn questa guida vediamo come gestire font e icone in modo solido, dal setup di base fino agli aspetti di performance e accessibilità.\n\n## Dichiarare un font nel pubspec\n\nIl punto di partenza è inserire i file (`.ttf` o `.otf`) negli asset e dichiararli nel `pubspec.yaml`:\n\n```yaml\nflutter:\n  fonts:\n    - family: Inter\n      fonts:\n        - asset: assets\u002Ffonts\u002FInter-Regular.ttf\n          weight: 400\n        - asset: assets\u002Ffonts\u002FInter-Medium.ttf\n          weight: 500\n        - asset: assets\u002Ffonts\u002FInter-Bold.ttf\n          weight: 700\n        - asset: assets\u002Ffonts\u002FInter-Italic.ttf\n          weight: 400\n          style: italic\n```\n\nTre regole da ricordare:\n\n- il valore di `weight` deve essere un multiplo di 100 tra 100 e 900 e deve corrispondere al peso reale del file;\n- se dichiari un solo file e poi chiedi `FontWeight.w700`, Flutter applica un **bold sintetico** (lo \"finto\" grassetto calcolato dal motore di rendering): funziona, ma il risultato è visivamente peggiore del vero taglio bold;\n- `style: italic` va dichiarato esplicitamente, altrimenti otterrai un corsivo sintetico ottenuto inclinando i glifi.\n\n### Applicare il font al tema\n\nIl modo più pulito è impostarlo una volta sola nel `ThemeData`, così tutti i widget di testo lo ereditano:\n\n```dart\nMaterialApp(\n  theme: ThemeData(\n    colorSchemeSeed: const Color(0xFF3F51B5),\n    fontFamily: 'Inter',\n    textTheme: const TextTheme(\n      displaySmall: TextStyle(fontWeight: FontWeight.w700, height: 1.15),\n      bodyMedium: TextStyle(fontSize: 15, height: 1.45, letterSpacing: 0.1),\n    ),\n  ),\n  home: const HomePage(),\n);\n```\n\nSe hai un font diverso per i titoli e uno per il corpo, puoi usare `TextTheme.apply`:\n\n```dart\nfinal base = ThemeData(colorSchemeSeed: Colors.indigo);\n\nfinal theme = base.copyWith(\n  textTheme: base.textTheme.apply(fontFamily: 'Inter').copyWith(\n        displayLarge: base.textTheme.displayLarge?.copyWith(fontFamily: 'Fraunces'),\n        displayMedium: base.textTheme.displayMedium?.copyWith(fontFamily: 'Fraunces'),\n        headlineLarge: base.textTheme.headlineLarge?.copyWith(fontFamily: 'Fraunces'),\n      ),\n);\n```\n\n## Variable font: un solo file, infiniti pesi\n\nUn variable font contiene **assi** (axes) che permettono di interpolare dinamicamente peso, larghezza, inclinazione. L'asse più comune è `wght`. Il vantaggio è doppio: un unico file al posto di sei o sette, e la possibilità di usare pesi intermedi (es. 450) non disponibili con i tagli statici.\n\nIn Flutter si dichiara il file una volta sola e si usa `fontVariations` nello stile:\n\n```yaml\nflutter:\n  fonts:\n    - family: InterVariable\n      fonts:\n        - asset: assets\u002Ffonts\u002FInter-VariableFont_wght.ttf\n```\n\n```dart\nText(\n  'Peso intermedio 450',\n  style: TextStyle(\n    fontFamily: 'InterVariable',\n    fontSize: 18,\n    fontVariations: const [FontVariation('wght', 450)],\n  ),\n);\n```\n\nAttenzione a un dettaglio che genera spesso confusione: con i variable font, `fontWeight` e `fontVariations` **non sono la stessa cosa**. Su alcune piattaforme `fontWeight` da solo può produrre un bold sintetico invece di spostare l'asse. La strategia più sicura è impostarli entrambi in modo coerente:\n\n```dart\nTextStyle weightedStyle(double weight) => TextStyle(\n      fontFamily: 'InterVariable',\n      fontWeight: FontWeight.values.firstWhere(\n        (w) => w.value == (weight ~\u002F 100) * 100,\n        orElse: () => FontWeight.normal,\n      ),\n      fontVariations: [FontVariation('wght', weight)],\n    );\n```\n\nPoiché `fontVariations` è una lista di valori numerici, puoi anche **animare** il peso del testo con un `TweenAnimationBuilder`, cosa impossibile con i font statici:\n\n```dart\nTweenAnimationBuilder\u003Cdouble>(\n  tween: Tween(begin: 300, end: _selected ? 700 : 300),\n  duration: const Duration(milliseconds: 250),\n  builder: (context, weight, _) => Text(\n    'Voce di menu',\n    style: TextStyle(\n      fontFamily: 'InterVariable',\n      fontVariations: [FontVariation('wght', weight)],\n    ),\n  ),\n);\n```\n\nAltri assi frequenti sono `wdth` (larghezza), `slnt` (inclinazione) e `opsz` (optical size): basta aggiungerli alla lista, purché il font li supporti.\n\n## google_fonts: comodo, ma configuralo bene\n\nIl pacchetto `google_fonts` è ottimo in fase di prototipazione perché evita di scaricare manualmente i file:\n\n```dart\nimport 'package:google_fonts\u002Fgoogle_fonts.dart';\n\nfinal theme = ThemeData(\n  textTheme: GoogleFonts.interTextTheme(ThemeData.light().textTheme),\n);\n```\n\nIl problema è il comportamento di default: se il font non è presente tra gli asset, viene **scaricato a runtime** dal web e messo in cache. Significa primo avvio più lento, dipendenza dalla rete e possibile \"flash\" di testo con un font diverso. In produzione la scelta migliore è quasi sempre fare il bundling dei file e disattivare il fetch:\n\n```dart\nvoid main() {\n  GoogleFonts.config.allowRuntimeFetching = false;\n  runApp(const MyApp());\n}\n```\n\nCon il fetch disattivato, `google_fonts` userà solo i file dichiarati nel `pubspec.yaml` (nominati secondo la convenzione del pacchetto, ad esempio `Inter-Regular.ttf`). Ricorda anche di includere le licenze OFL nel registro delle licenze dell'app:\n\n```dart\nLicenseRegistry.addLicense(() async* {\n  final license = await rootBundle.loadString('assets\u002Ffonts\u002FOFL.txt');\n  yield LicenseEntryWithLineBreaks(['google_fonts'], license);\n});\n```\n\n## Fallback: emoji, alfabeti non latini e caratteri mancanti\n\nNessun font copre tutti i glifi. Se la tua app mostra contenuti generati dagli utenti, prima o poi incontrerai emoji, cirillico o ideogrammi che il font principale non contiene: il risultato sono i famigerati \"tofu\" (rettangoli vuoti).\n\nLa soluzione è `fontFamilyFallback`, una catena di famiglie consultate in ordine:\n\n```dart\nconst appTextStyle = TextStyle(\n  fontFamily: 'Inter',\n  fontFamilyFallback: ['NotoSansSC', 'NotoColorEmoji'],\n);\n```\n\nPuoi impostarlo anche a livello di tema con `ThemeData(fontFamilyFallback: [...])`. Su Android e iOS i font di sistema coprono già molto, ma su **Flutter Web** e su alcuni device Android personalizzati il fallback esplicito evita brutte sorprese. Attenzione al peso: un Noto Sans CJK completo può superare i 10 MB, valuta l'uso di versioni ridotte (subset) con gli strumenti di `fonttools`.\n\n## Icone personalizzate come font\n\nLe icone vettoriali monocromatiche possono essere distribuite come font: un solo file, scalabile, colorabile e perfettamente allineato al testo. Dopo aver generato il `.ttf` (con strumenti come FlutterIcon o IcoMoon), si dichiara come un font qualsiasi e si creano le costanti:\n\n```yaml\nflutter:\n  fonts:\n    - family: AppIcons\n      fonts:\n        - asset: assets\u002Ffonts\u002FAppIcons.ttf\n```\n\n```dart\nabstract final class AppIcons {\n  static const _family = 'AppIcons';\n\n  static const wallet = IconData(0xe900, fontFamily: _family);\n  static const invoice = IconData(0xe901, fontFamily: _family);\n  static const support = IconData(0xe902, fontFamily: _family);\n}\n\n\u002F\u002F Uso\nIcon(AppIcons.wallet, size: 24, color: Theme.of(context).colorScheme.primary);\n```\n\nSe il font delle icone è distribuito da un package riutilizzabile, aggiungi il parametro `fontPackage`:\n\n```dart\nstatic const wallet = IconData(\n  0xe900,\n  fontFamily: 'AppIcons',\n  fontPackage: 'my_design_system',\n);\n```\n\n### Tree shaking delle icone\n\nIn release Flutter esegue il **tree shaking dei font di icone**: rimuove i glifi non utilizzati, riducendo drasticamente il peso. Funziona solo se gli `IconData` sono costanti note a compile time. Costruzioni dinamiche come `IconData(codePoint, fontFamily: 'AppIcons')` con `codePoint` calcolato a runtime fanno fallire la build con un errore esplicito, costringendoti a compilare con `--no-tree-shake-icons` e a portarti dietro l'intero set. Se devi mappare icone ricevute dal backend, usa una mappa di costanti:\n\n```dart\nconst iconByName = \u003CString, IconData>{\n  'wallet': AppIcons.wallet,\n  'invoice': AppIcons.invoice,\n};\n\nIcon(iconByName[response.icon] ?? Icons.help_outline);\n```\n\n## Caricare font a runtime con FontLoader\n\nIn scenari di white labeling o server-driven UI può servire caricare un font scaricato dalla rete. L'API è `FontLoader`:\n\n```dart\nFuture\u003Cvoid> loadRemoteFont(String family, Uint8List bytes) async {\n  final loader = FontLoader(family)\n    ..addFont(Future.value(ByteData.view(bytes.buffer)));\n  await loader.load();\n}\n```\n\nDopo il `load()` il font è disponibile per tutta l'app usando `fontFamily: family`. Ricorda di scaricare i byte **prima** di mostrare la schermata (o di gestire un rebuild), di salvarli in cache su disco e di avere sempre un font locale di fallback in caso di errore di rete.\n\n## Accessibilità: niente layout a misura di font\n\nUn font personalizzato non deve rompere l'ingrandimento del testo di sistema. Alcune accortezze:\n\n- non disattivare mai lo scaling con `TextScaler.noScaling` sull'intera app: al massimo limitalo con `clamp` nei punti critici;\n- preferisci altezze minime (`ConstraintedBox`, `minHeight`) a dimensioni fisse per bottoni e card contenenti testo;\n- usa la proprietà `height` del `TextStyle` per l'interlinea invece di `SizedBox` tra le righe.\n\n```dart\nMediaQuery(\n  data: MediaQuery.of(context).copyWith(\n    textScaler: MediaQuery.textScalerOf(context).clamp(\n      minScaleFactor: 1.0,\n      maxScaleFactor: 1.6,\n    ),\n  ),\n  child: child,\n);\n```\n\nUn'altra impostazione utile quando il font ha metriche verticali generose è `TextHeightBehavior`, che permette di eliminare lo spazio extra sopra la prima riga e sotto l'ultima:\n\n```dart\nText(\n  'Titolo perfettamente allineato',\n  style: const TextStyle(height: 1.2),\n  textHeightBehavior: const TextHeightBehavior(\n    applyHeightToFirstAscent: false,\n    applyHeightToLastDescent: false,\n  ),\n);\n```\n\n## Checklist finale\n\n- Dichiara un file per ogni peso reale, oppure usa un variable font con `fontVariations`.\n- In produzione, bundle dei font e `allowRuntimeFetching = false` se usi `google_fonts`.\n- Includi le licenze dei font nel `LicenseRegistry`.\n- Configura `fontFamilyFallback` se gestisci contenuti multilingua o emoji.\n- Mantieni gli `IconData` costanti per non perdere il tree shaking.\n- Verifica la UI con il testo ingrandito al 160% prima di rilasciare.\n\nCurare questi dettagli costa poche ore e si traduce in un'app che appare immediatamente più professionale, più leggera e accessibile a tutti.","https:\u002F\u002Fflutter.it\u002Fstorage\u002Farticles\u002Fcb9058ca-99bf-4143-8551-4839323367d5.jpg","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1523837157348-ffbdaccfc7de?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w5NzA2NTJ8MHwxfHJhbmRvbXx8fHx8fHx8fDE3OTI4MTQ0ODl8&ixlib=rb-4.1.0&q=80&w=1080",{"name":12,"author_url":13,"photo_url":14},"Nikhil Mitra","https:\u002F\u002Funsplash.com\u002F@nikhilmitra","https:\u002F\u002Funsplash.com\u002Fphotos\u002Fcreative-decor-Q_6BS8IN0J8",null,"published","2026-10-24T04:01:30+00:00","Font personalizzati in Flutter: guida pratica","Come usare font personalizzati in Flutter: pubspec, variable font con FontVariation, google_fonts offline, fallback emoji, icone custom e accessibilità.",{"id":21,"name":22,"slug":23},1,"Guide","guide",{"id":21,"name":25},"Flutter Bot",[],1793098107460]