Flutter Android App Size in 2026: A Size-Review Routine for Solo Developers (App Bundle, Split Debug Info, Shrinking)
App size is one of those things a solo developer only notices when it's already a problem: a Play Console warning, a user review saying “too big for what it does”, or a release that suddenly grew after adding one plugin. The good news is that a Flutter Android app has a small number of size levers, and most of them are build flags you set once. This is the size-review routine I use for my Flutter and Firebase apps: how to measure properly, where the bytes usually go, the flags worth enabling, and how to keep size from creeping up release after release. Every command and setting below comes from the official Flutter and Android documentation, checked on 2026-10-10.
Measure the right thing first
Flutter's guide to measuring your app's size starts with a warning that's easy to ignore: debug builds are not representative. They include debugging support and aren't optimised, so a debug APK says almost nothing about what users download. Measure release builds only.
The second trap is measuring a universal APK. A single APK that contains native code for every CPU architecture and assets for every screen density is much bigger than what any one device installs. Google Play generates optimised APKs from your Android App Bundle for each device configuration, so the number that matters is the download size per device, which Play Console reports for each release.
Break down where the bytes go
Flutter has a built-in size analysis tool. Per the same docs, you invoke it by passing --analyze-size when building, for example flutter build appbundle --analyze-size. The build records code size usage by Dart package, prints a summary in the terminal, and leaves a *-code-size-analysis_*.json file you can open in the DevTools app size tool for a deeper, tree-style view.
In my apps, the breakdown usually points to one of four places:
- Native libraries: the Flutter engine and any plugin that ships native code. These are per-architecture, which is why App Bundles matter so much.
- Dart code (the AOT snapshot): your code plus every package you import, including the parts you barely use.
- Assets: images, fonts, animations and bundled JSON. This is the most common source of sudden jumps.
- Android-side Java/Kotlin code and resources pulled in by plugins and SDKs.
The levers, in the order I apply them
1. Ship an Android App Bundle
Google's App Bundle documentation says that since August 2021 new apps are required to publish with the Android App Bundle on Google Play. The bundle lets Play deliver only the code and resources each device needs, so a phone doesn't download native code for architectures it can't run. Build it with flutter build appbundle, as described in Flutter's Android deployment guide.
2. Split debug info out of the binary
Flutter's obfuscation guide shows building in release mode with --obfuscate and --split-debug-info=/<symbols-directory>. That moves debug symbols out of the app into a symbols directory, and it obfuscates Dart identifiers. Keep the symbol files for every release, because you need them to read obfuscated stack traces; I store them next to the release tag. If you use Crashlytics, upload them as its Flutter docs describe so crash reports stay readable.
3. Turn on code and resource shrinking for the Android side
Android's guide to shrinking, obfuscating and optimising your app recommends enabling both code optimisation and resource shrinking for release builds. On newer Android Gradle Plugin versions this is a single optimization { enable = true } block; on older versions it's isMinifyEnabled = true and isShrinkResources = true in the release build type. Check your AGP version and use the matching form. Then run a full test pass: shrinking can strip classes that are only reached through reflection, and plugins sometimes need keep rules.
4. Audit assets
- Convert large PNG and JPEG images to WebP where quality allows.
- Remove unused images and old onboarding screens left in
pubspec.yaml. - Bundle only the font weights you actually use, or load fonts at runtime if that suits your app.
- Move rarely used large content (big JSON, long animations) to a download on demand, for example from Firebase Storage or Remote Config, if it isn't needed on first launch.
5. Audit dependencies
Every package adds code, and some pull in big native SDKs. Once a quarter I look at the size analysis by package and ask, for each of the biggest ones: do I still use it, is there a lighter alternative, and am I importing a whole SDK for one small feature? Removing one unused plugin can do more than an afternoon of image compression.
Keep size from creeping back
Size grows one release at a time, so I treat it as part of the release checklist rather than a one-off project:
- Record the per-device download size from Play Console for every release in your changelog.
- Run
--analyze-sizebefore any release that adds a dependency and compare it with the previous JSON file. - Set a personal budget, for example “no release grows by more than a few percent without a reason I can name”.
- Review Play Console's app size reports; Google's Play Console help on app size explains the limits and the size data Play shows.
This fits naturally next to the other routine checks I run, like the weekly Crashlytics triage and the target API upgrade plan, which itself can change size when you bump AGP and plugins.
A worked routine for one release
Here's how the pieces fit together on a normal release day for one of my apps, without any specific numbers, because yours will differ:
- Build the release bundle with
--analyze-size,--obfuscateand--split-debug-info, and archive the symbols folder under the version number. - Open the size analysis JSON in DevTools and compare the top packages with the previous release's file.
- If something grew, find out why before uploading: a new plugin, a new asset, or a dependency upgrade.
- Upload to the internal testing track, install from Play on a real device, and run the screens most likely to break under shrinking.
- After rollout, copy Play Console's download size into the changelog.
Common mistakes
- Comparing a debug build to a release build and celebrating a “huge reduction”.
- Enabling shrinking without testing, then shipping a crash on a screen that uses reflection.
- Losing the symbol files from
--split-debug-info, making crash reports unreadable. - Optimising images while a forgotten SDK accounts for most of the growth.
FAQ
Why is my Flutter APK so much bigger than the Play download size?
A universal APK includes native code and resources for all devices. Play delivers a smaller, device-specific set generated from your App Bundle.
Does obfuscation make the app smaller?
The main size effect comes from moving debug info out with --split-debug-info; obfuscation itself is mainly about making reverse engineering harder. Measure your own build to see the difference.
Is code shrinking safe for Flutter apps?
Usually, but test thoroughly. Some plugins or SDKs need keep rules, which their documentation normally lists.
How often should I review app size?
Check the number every release and do a deeper dependency and asset audit every few months.
Sources
- Flutter docs: measuring your app's size (checked 2026-10-10)
- Flutter docs: obfuscating Dart code (checked 2026-10-10)
- Android Developers: about Android App Bundles (checked 2026-10-10)
- Android Developers: shrink, obfuscate and optimise your app (checked 2026-10-10)
- Play Console Help: optimise your app's size (checked 2026-10-10)
More guides: the GSPTeck blog.