[{"data":1,"prerenderedAt":28},["ShallowReactive",2],{"articolo-ridurre-le-dimensioni-di-unapp-flutter-guida-pratica-allottimizzazione-del-bundle":3,"comments-article-ridurre-le-dimensioni-di-unapp-flutter-guida-pratica-allottimizzazione-del-bundle":27},{"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},102,"Ridurre le dimensioni di un'app Flutter: guida pratica all'ottimizzazione del bundle","ridurre-le-dimensioni-di-unapp-flutter-guida-pratica-allottimizzazione-del-bundle","Un'app pesante costa download e installazioni. Scopri come misurare il peso reale di un'app Flutter con --analyze-size e DevTools, e come ridurlo con App Bundle, split per ABI, ottimizzazione degli asset, R8 e deferred components.","## Perché il peso dell'app è un problema di business\n\nOgni megabyte in più incide sul tasso di installazione: su rete mobile gli utenti abbandonano i download lunghi, i dispositivi con poco spazio disinstallano per primi le app più grandi e gli store impongono limiti precisi (200 MB di dimensione di download per un Android App Bundle su Google Play, avviso di rete cellulare su iOS oltre i 200 MB).\n\nUn'app Flutter parte con un \"costo fisso\": il motore di rendering, la runtime Dart e il codice AOT compilato. Per una app vuota in release siamo nell'ordine di pochi megabyte per singola architettura, ma è sorprendentemente facile arrivare a 60-80 MB con qualche asset non compresso, tre SDK di analytics e una build sbagliata. La buona notizia è che gran parte del sovrappeso è recuperabile con interventi meccanici e ripetibili.\n\n## Prima regola: misurare, non indovinare\n\nFlutter include uno strumento di analisi dedicato che produce un report navigabile in DevTools.\n\n```bash\n# Android (APK per una singola architettura)\nflutter build apk --release --analyze-size --target-platform android-arm64\n\n# Android App Bundle\nflutter build appbundle --release --analyze-size\n\n# iOS\nflutter build ios --release --analyze-size\n```\n\nAl termine, il comando stampa un riepilogo a terminale e salva un file JSON in `.dart_tool\u002Fflutter_build\u002F` (il percorso esatto viene indicato nell'output). Per esplorarlo:\n\n```bash\nflutter pub global activate devtools\ndevtools\n```\n\nNella tab **App Size** si carica il file e si ottiene un treemap con il peso di ogni libreria Dart, di ogni package e di ogni asset. È l'unico modo serio per capire se il problema sono le immagini, i plugin nativi o il codice Dart.\n\nUn secondo strumento utile è l'**APK Analyzer** di Android Studio (`Build > Analyze APK`), che mostra la dimensione compressa reale del file consegnato all'utente, e `bundletool` per stimare il download effettivo di un AAB:\n\n```bash\nbundletool build-apks --bundle=app-release.aab --output=app.apks\nbundletool get-size total --apks=app.apks\n```\n\n> Attenzione: confrontate sempre la **dimensione di download** (compressa, per singolo dispositivo) e non il peso del file universale, altrimenti ottimizzerete il numero sbagliato.\n\n## Le build giuste: i guadagni facili\n\n### App Bundle invece di APK universale\n\nUn APK universale contiene le librerie native per tutte le architetture (arm64, armeabi-v7a, x86_64). Il formato AAB permette a Play di generare l'APK su misura del dispositivo:\n\n```bash\nflutter build appbundle --release\n```\n\nSe dovete distribuire APK (store alternativi, sideload), usate almeno lo split per ABI:\n\n```bash\nflutter build apk --release --split-per-abi\n```\n\nIl risultato sono tre APK separati, ciascuno tipicamente 30-40% più leggero dell'universale.\n\n### Offuscamento e separazione dei simboli di debug\n\n```bash\nflutter build appbundle --release \\\n  --obfuscate \\\n  --split-debug-info=build\u002Fsymbols\n```\n\nI simboli di debug vengono estratti dal binario e salvati a parte: l'app si alleggerisce e il codice diventa più difficile da analizzare. **Conservate la cartella `build\u002Fsymbols` per ogni release**: senza quei file non sarete in grado di simbolicare gli stack trace provenienti da Crashlytics o Sentry.\n\n### Tree shaking delle icone\n\nIn release Flutter rimuove automaticamente i glifi non usati dai font di icone (`--tree-shake-icons` è attivo di default). Il tree shaking però funziona solo se gli `IconData` sono costanti note a compile time. Questo pattern lo disattiva silenziosamente:\n\n```dart\n\u002F\u002F ❌ Impedisce il tree shaking: l'intero font Material Icons finisce nel bundle\nIcon(IconData(codePoint, fontFamily: 'MaterialIcons'));\n```\n\n```dart\n\u002F\u002F ✅ Mappa esplicita di costanti: il compilatore sa esattamente cosa serve\nconst _icone = \u003CString, IconData>{\n  'home': Icons.home,\n  'search': Icons.search,\n  'profile': Icons.person,\n};\n\nIcon(_icone[chiave] ?? Icons.help_outline);\n```\n\nIl font completo di Material Icons pesa oltre 1,5 MB: è uno dei regali più costosi che si possano fare a un'app.\n\n### R8 per il codice nativo Android\n\nI plugin (Firebase, Google Play Services, librerie di terze parti) portano con sé molto codice Java\u002FKotlin. Abilitare R8 e la rimozione delle risorse inutilizzate può far risparmiare diversi MB:\n\n```kotlin\n\u002F\u002F android\u002Fapp\u002Fbuild.gradle.kts\nandroid {\n    buildTypes {\n        release {\n            isMinifyEnabled = true\n            isShrinkResources = true\n            proguardFiles(\n                getDefaultProguardFile(\"proguard-android-optimize.txt\"),\n                \"proguard-rules.pro\"\n            )\n        }\n    }\n}\n```\n\nTestate **sempre** la build offuscata su dispositivo reale: se una libreria usa reflection (tipicamente parser JSON nativi o SDK di pagamento) servono regole di keep nel file `proguard-rules.pro`.\n\n## Gli asset: dove si nasconde il peso vero\n\nNella maggior parte dei progetti che ho analizzato, il codice Dart è la parte piccola del bundle. Il grosso sono immagini e font.\n\n**Immagini**\n\n- Convertite PNG\u002FJPEG in **WebP**: risparmio tipico del 25-35% a parità di qualità.\n  ```bash\n  cwebp -q 82 assets\u002Fhero.png -o assets\u002Fhero.webp\n  ```\n- Non spedite immagini più grandi di quanto verranno mostrate: un'immagine 4000×3000 per una card da 320 px è puro spreco (e costa anche RAM a runtime).\n- Per illustrazioni e icone complesse preferite **SVG precompilato** con `vector_graphics`: il formato `.vec` è compatto e si disegna senza parsing XML a runtime.\n\n**pubspec.yaml selettivo**\n\nDichiarare una cartella intera include *tutto* quello che c'è dentro, comprese le versioni di lavorazione dimenticate:\n\n```yaml\nflutter:\n  assets:\n    # ❌ include anche i file .psd, i backup e le prove\n    # - assets\u002F\n    # ✅ solo quello che serve davvero\n    - assets\u002Fimages\u002Flogo.webp\n    - assets\u002Ficons\u002F\n```\n\n**Font**\n\nUn font di testo con tutti i pesi (regular, medium, bold, italic) può superare il megabyte. Includete solo i pesi effettivamente usati e valutate il subsetting con `fonttools`:\n\n```bash\npyftsubset Inter.ttf --unicodes=\"U+0000-00FF,U+20AC\" --output-file=Inter-subset.ttf\n```\n\nAttenzione al package `google_fonts`: di default scarica i font a runtime (bundle più leggero, ma richiede rete al primo avvio e può far comparire un flash di testo). Se vi serve il font offline, includetelo negli asset e disattivate il fetch runtime.\n\n## Dipendenze: ogni package ha un prezzo\n\nPrima di aggiungere un package, chiedetevi quanto codice nativo si porta dietro. Alcuni esempi tipici:\n\n- i modelli **bundled** di Google ML Kit possono aggiungere decine di MB: se possibile usate le varianti \"unbundled\" servite da Google Play Services;\n- SDK di mappe, video streaming e pagamenti includono librerie `.so` per ogni ABI;\n- molte utility con una sola funzione si sostituiscono con venti righe di Dart.\n\nUn audit periodico aiuta:\n\n```bash\ndart pub deps --style=compact\nflutter pub outdated\n```\n\nE ricordate che il tree shaking di Dart AOT è efficace solo sul codice statisticamente raggiungibile: registrazioni dinamiche, grosse mappe di factory e `switch` su stringhe che istanziano mezzo progetto mantengono vivo codice che non userete mai.\n\n## Deferred components: scaricare le feature su richiesta\n\nPer funzionalità voluminose e usate da una minoranza di utenti (editor avanzato, modelli ML, moduli AR) Android supporta il download on demand tramite Play Feature Delivery. Lato Dart si usa l'import differito:\n\n```dart\nimport 'package:my_app\u002Feditor\u002Feditor.dart' deferred as editor;\n\nFuture\u003Cvoid> apriEditor(BuildContext context) async {\n  await editor.loadLibrary(); \u002F\u002F scarica e carica il modulo\n  if (!context.mounted) return;\n  Navigator.of(context).push(\n    MaterialPageRoute(builder: (_) => editor.EditorPage()),\n  );\n}\n```\n\nNel `pubspec.yaml` si dichiarano i componenti:\n\n```yaml\nflutter:\n  deferred-components:\n    - name: editorComponent\n      libraries:\n        - package:my_app\u002Feditor\u002Feditor.dart\n      assets:\n        - assets\u002Feditor\u002Fbrushes.webp\n```\n\nSi compila con `flutter build appbundle --release`, che valida la configurazione e, la prima volta, suggerisce i file Android da generare. Mostrate sempre uno stato di caricamento e gestite l'errore di rete: `loadLibrary()` può fallire.\n\nSu **Flutter Web** lo stesso meccanismo produce chunk JavaScript separati, ed è uno degli strumenti più efficaci per ridurre il tempo al primo frame.\n\n## Nota su Flutter Web\n\nIl peso iniziale sul web si governa con leve diverse:\n\n```bash\nflutter build web --release --wasm --pwa-strategy=offline-first\n```\n\nPunti chiave: sfruttare la cache del service worker, servire tutto con compressione **Brotli** lato server, evitare di pubblicare le source map in produzione e usare gli import differiti per le rotte secondarie.\n\n## Checklist operativa\n\n1. Misurare con `--analyze-size` e DevTools **prima** di toccare qualsiasi cosa.\n2. Distribuire un App Bundle (o APK split per ABI).\n3. Aggiungere `--obfuscate --split-debug-info` e archiviare i simboli.\n4. Verificare che il tree shaking delle icone non sia disattivato da `IconData` dinamici.\n5. Abilitare R8 + `shrinkResources` e testare la build su dispositivo.\n6. Convertire le immagini in WebP e ridimensionarle alla risoluzione reale.\n7. Dichiarare asset e pesi dei font in modo selettivo nel `pubspec.yaml`.\n8. Fare l'audit delle dipendenze e rimuovere quelle inutilizzate.\n9. Spostare le feature pesanti in deferred components.\n10. Aggiungere al CI un controllo che fallisca se la dimensione supera una soglia.\n\nQuest'ultimo punto è ciò che rende il lavoro permanente: senza un guardrail automatico, il bundle torna a gonfiarsi nel giro di pochi sprint. Una riga nel workflow di CI che confronta la dimensione dell'artefatto con quella della release precedente costa dieci minuti e protegge mesi di ottimizzazioni.","https:\u002F\u002Fflutter.it\u002Fstorage\u002Farticles\u002F97d32eb4-3f03-4c11-8900-372f022fbd8b.jpg","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1434626881859-194d67b2b86f?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w5NzA2NTJ8MHwxfHJhbmRvbXx8fHx8fHx8fDE3ODk3OTA0OTB8&ixlib=rb-4.1.0&q=80&w=1080",{"name":12,"author_url":13,"photo_url":14},"Firmbee.com","https:\u002F\u002Funsplash.com\u002F@firmbee","https:\u002F\u002Funsplash.com\u002Fphotos\u002Fperson-holding-white-samsung-galaxy-tab-jrh5lAq-mIs",null,"published","2026-09-19T04:01:31+00:00","Ridurre il peso di un'app Flutter: guida pratica","Come misurare e ridurre le dimensioni di un'app Flutter: --analyze-size, DevTools, App Bundle, offuscamento, R8, asset WebP e deferred components.",{"id":21,"name":22,"slug":23},3,"Best practice","best-practice",{"id":25,"name":26},1,"Flutter Bot",[],1792316910069]