Reducing Flutter app size for production
By Charlin Joe · 25 January 2025
App size affects install conversion rates, update adoption, and user retention — especially on markets with limited storage or slow connectivity. Here is how to measure and reduce Flutter app size.
Measure first
# Build and analyze
flutter build appbundle --analyze-size
flutter build ipa --analyze-size
# This prints a breakdown by category
# Also generates a .json file for devtools visualization
flutter pub global activate devtools
flutter pub global run devtools --appSizeBase=build/app-size.json
Enable obfuscation and tree-shaking
flutter build appbundle \
--obfuscate \
--split-debug-info=build/debug-info \
--tree-shake-icons
--obfuscate: renames Dart symbols, removes debug info from the binary--split-debug-info: puts debug symbols in a separate file (needed for crash symbolication later)--tree-shake-icons: removes unused Material icons from the binary (saves ~100-150 KB)
Android-specific: split APKs
Upload an App Bundle instead of a monolithic APK. The Play Store delivers only the architecture and resources the user's device needs:
flutter build appbundle # Preferred over flutter build apk
For testing outside Play Store, generate architecture-split APKs:
flutter build apk --split-per-abi
# Produces:
# build/app-armeabi-v7a-release.apk (~15 MB smaller)
# build/app-arm64-v8a-release.apk
# build/app-x86_64-release.apk
Compress and resize images
Images are usually the biggest win:
# Compress PNG assets
pngquant --quality 65-80 assets/images/*.png
# Compress JPEG
jpegoptim --max=80 assets/images/*.jpg
# Convert large PNGs to WebP
cwebp -q 80 input.png -o output.webp
Use flutter_svg for icons instead of PNG icon sheets — SVGs are much smaller.
Audit and remove unused dependencies
# Find packages that might be unused
flutter pub deps --json | python3 -c "
import json, sys
deps = json.load(sys.stdin)['packages']
for p in deps:
print(p['name'])
"
For each package, ask: does the app actually use this at runtime? Dev dependencies (dev_dependencies) are fine — they don't affect app size.
Deferred loading
For rarely-used features, load them on demand:
import 'package:my_app/features/advanced_charts.dart' deferred as charts;
// Only downloaded/loaded when first used
Future<void> _openCharts() async {
await charts.loadLibrary();
if (mounted) {
Navigator.push(context, MaterialPageRoute(
builder: (_) => charts.AdvancedChartsScreen(),
));
}
}
Note: deferred loading in Flutter only works on web currently. On mobile it's a no-op (loads at startup). Watch this issue for mobile support.
Font subsetting
If you bundle a custom font, subset it to only the characters you use:
# Keep only Latin characters
pyftsubset font.ttf --unicodes=U+0020-U+007F --output-file=font-subset.ttf
Or use Google Fonts which automatically subsets:
import 'package:google_fonts/google_fonts.dart';
// Downloads only the subset used in your app
Text('Hello', style: GoogleFonts.inter())
Remove unused locale data
If you only need English and Spanish:
# pubspec.yaml
flutter:
generate: true
# l10n.yaml
preferred-supported-locales:
- en
- es
Material widgets bundle locale data for 100+ languages. This trims unused locales.
Proguard / R8 on Android
R8 (the successor to Proguard) is enabled by default in release builds. It removes unused Java/Kotlin code from Android dependencies. If you add a large native SDK, check whether R8 rules are correctly configured:
android/app/proguard-rules.pro:
-keep class com.example.vendorsdk.** { *; } # Keep SDK classes R8 might remove
Size targets
Good: < 20 MB download size
Okay: 20-40 MB
Review: > 40 MB — identify what's large and cut it
Measure download size, not install size. Download size is what users see in the store.
Common pitfalls
Bundling hi-res images that are shown at small sizes. An 1024×1024 PNG used as a 48×48 icon wastes ~500 KB. Resize assets to the maximum size they'll be displayed at.
Including the same asset in multiple sizes without AssetVariants. Flutter picks the right resolution automatically if you place variants in 2.0x/ and 3.0x/ subdirectories. Don't include all sizes as separate named assets.
Measuring the debug build size. Debug builds include debug symbols, observatory service, and are not tree-shaken. Always measure release build size: flutter build appbundle --analyze-size.
Sign in to like, dislike, or report.