Flutter and Dart — Cross-Platform Development: Why Your APK Ships at 80 MB and How to Cut It Without Breaking the App

A build that lands on Google Play at 70–80 MB is not a rare failure — it is the default outcome of a Flutter project that nobody measured. The download feels fine on your office Wi-Fi and your Pixel, and then a user on a three-year-old Android device with 2 GB of RAM waits, installs, and leaves a one-star review about "slow and heavy." The fix does not start with a tip list. It starts with a number. For a deeper dive into this topic, see Cambridge Admissions.

Myth one: "Flutter apps are just big, that's the framework"

Flutter does ship a runtime, but the framework is a fixed cost you cannot remove — what you can remove is everything you added on top and never audited. The honest first step is flutter build appbundle --analyze-size (or --analyze-size on an APK). It writes a size profile you open in DevTools, and the tree breaks down by package: your Dart code, the Flutter engine, assets, native libraries, fonts.

Most teams that run this for the first time find the same three culprits: uncompressed PNGs and JPEGs sitting in assets/, a full icon font pulled in for eight glyphs, and two or three Firebase SDKs added "just in case" during a sprint and never removed.

Measure before you touch anything Run the size analyzer on the exact build you ship to the store, not a debug build. Debug builds carry the observatory and are not representative — you will optimise the wrong things.

Myth two: "split-per-abi is a legacy Android trick"

It is not legacy, it is still one of the cheapest wins available. A universal APK bundles arm64-v8a, armeabi-v7a and x86_64 native libraries in one file. flutter build apk --split-per-abi produces three APKs, each carrying only its own architecture — the arm64 one is typically a third smaller than the universal build.

But the better default in 2026 is the Android App Bundle. flutter build appbundle lets Play deliver per-device splits automatically, and you keep one artifact. The catch people miss: if you test only the universal APK locally, you never see what a real device downloads, and you never notice that a native dependency is shipping libraries for architectures you do not support. Upload to an internal testing track and check the download size Play reports — that number, not the APK on your disk, is what the user pays for.

Myth three: "assets are small, they don't matter"

A single hero image exported from a design tool at 3 MB is worth more than every code tweak combined. Convert to WebP, cap resolution at what the layout actually needs, and check with the size analyzer which assets dominate. The same goes for fonts: a variable font file with full Cyrillic and Latin coverage can weigh more than your Dart code. Subset it, or drop to the weights you use.

Icons are the quiet one. flutter build tree-shakes Icons from Material by default and prints how many glyphs it removed — read that line in the build log. If you pulled in a custom icon font or a package that bundles its own, tree-shaking does not touch it. That is where a 400 KB font ships for six icons.

Myth four: "iOS thinning is Apple's problem"

App thinning on iOS is real, but it only helps if your build is configured for it. Enable bitcode is gone as an option in recent Xcode, and the practical lever left is asset catalogs: images placed in Assets.xcassets with proper scale variants get thinned per device, images dumped into a Flutter assets/ folder do not. If your iOS download size looks bloated next to Android, look there first before blaming the engine.

Two more levers worth knowing:

  • --split-debug-info and --obfuscate strip symbol tables from the release binary. You keep the symbols file for crash deobfuscation — lose it and your stack traces become noise, so archive it in CI.
  • Deferred components let you ship a feature as a separate download on Android. Powerful, but it adds a loading state and a failure path you now have to test. Do not reach for it until the simple wins are done.

What a realistic before/after looks like

Numbers below are illustrative for a mid-sized app with Firebase Auth, Firestore, a maps screen and a media-heavy feed — your tree will differ, the ratios usually hold.

Lever Before After
Universal APK, arm64 device ~78 MB —
App Bundle, per-device download — ~34 MB
Assets (PNG → WebP, resized) ~14 MB ~4 MB
Unused Firebase SDKs removed ~6 MB 0
Icon font → tree-shaken Material ~0.5 MB ~0.05 MB

The part that actually bites: verifying nothing broke

Every size win is a behaviour change until proven otherwise. The one that catches teams is offline: you compressed assets, swapped formats, moved a font — and the app that worked online now shows grey boxes in airplane mode. Test the release build, not debug, with the network off, on a real low-end device. Check that images render, icons appear, and any cached-first screens still populate.

The 60-second pre-release check Install the store-signed build on a budget Android phone, enable airplane mode, open every tab once. If anything is blank, you optimised a file the app needed at runtime.

Where structured learning actually helps

You can find every command above in the Flutter docs — the barrier is not information, it is knowing which lever applies to your tree and in what order, and having someone sanity-check the trade-off before you ship. Self-study gets you the commands; it rarely gets you feedback on a decision like "is deferred components worth the loading state here?"

The Flutter and Dart — Cross-Platform Development course covers the build pipeline, Firebase integration, asset handling and store publishing in the same order you hit them in a real project, with lessons adapted to your level — useful if you want the release side covered rather than discovering it on launch day. For a deeper dive into this topic, see 12 LLM Prompts for Mobile Release Day.

Size is a budget, not a one-time cleanup. Set a ceiling, check the download number Play and App Store Connect report each release, and treat a jump as a signal to open the analyzer again.

← All posts

Ask AI about this article

Ask a question about this article — ASI Biont will answer based on its text.