← Writing

One non-const IconData ships every icon glyph in your Flutter app

Icon tree-shaking dies the moment any IconData is constructed non-const — my app was shipping ~7,447 glyphs for the 52 it used, and const getters cut one font from 1.22 MB to 3.8 KB.

While digging into app size on a production Flutter EHR, I found the builds were running with --no-tree-shake-icons. The flag had been added long ago because the build demanded it: just three non-const IconData constructions (in PDF theming code) were enough to make the icon tree-shaker refuse to run at all.

The cost of that flag: the app shipped roughly 7,447 icon glyphs from one icon pack when only 52 were actually used — about 3.7 MB of icon fonts in every install.

Fixing the three call sites and re-enabling tree-shaking shrank five of the six bundled fonts by ~98% immediately. But one vendored icon font stubbornly stayed at only a ~19% reduction. The reason taught me how the tree-shaker actually works: those icons were being resolved through runtime string-map lookups — a map from icon-name strings to IconData. The compiler can’t trace which entries a runtime string might hit, so it conservatively kept nearly everything.

Rewriting that map into const getters — one per icon actually referenced — made every usage statically traceable. That single font went from 1.22 MB to 3.8 KB, a 99.7% cut. Total icon-font weight across the app: ~3.7 MB down to ~48 KB.

The rules this left me with:

  • Treat --no-tree-shake-icons in your build scripts as a debt marker, not a setting. Find out why it’s there.
  • IconData must be const-constructible everywhere; one dynamic construction poisons the whole optimization.
  • Icon-by-string-name APIs are convenient and quietly cost megabytes. If you need dynamic icons, enumerate the allowed ones as consts and map to those.
← All writing Book a call →
Book a call → WhatsApp