In Flutter, how do you ship custom brand icons as an icon font, and why must each IconData for that font be const?
answer
- SVGs merged into one font
- a fonts: family in pubspec
- static const fields in a class
- the release build scans constants
- IconData became final in 3.44
basics
~20 sGenerate one font from the icon SVGs, declare it as a family in pubspec.yaml, and expose each glyph as a static const IconData. Release builds subset icon fonts by finding const IconData values, so a non-const one stops the build.
solid answer
~50 sConvert the brand SVGs into a single `.ttf` with an icon-font generator, declare it under `flutter: fonts:` as a family such as `ClinicIcons`, and expose each glyph as `static const IconData(0xe801, fontFamily: 'ClinicIcons')` in a class annotated `@staticIconProvider`; add `fontPackage:` if the font lives in a package and `matchTextDirection: true` for directional glyphs. Const matters because release builds run the **icon tree shaker** by default: it finds every constant `IconData` and keeps only those glyphs. A non-const `IconData`, such as one built from a server-supplied integer, makes the build exit with 'This application cannot tree shake icons fonts' and a pointer to `--no-tree-shake-icons`. Since Flutter 3.44 `IconData` is a `final class` and its `codePoint`, `fontFamily` and `fontPackage` parameters are `@mustBeConst`, so the analyzer flags non-const arguments and the old `enum … implements IconData` pattern no longer compiles.
code
dart · 17 linesimport 'package:flutter/widgets.dart';
@staticIconProvider
abstract final class ClinicIcons {
static const String _family = 'ClinicIcons';
static const IconData pawHeart = IconData(0xe800, fontFamily: _family);
static const IconData syringe = IconData(0xe801, fontFamily: _family);
static const IconData carrier = IconData(0xe802, fontFamily: _family);
static const IconData backArrow = IconData(0xe803, fontFamily: _family, matchTextDirection: true);
}
IconData iconForVisit(String type) => switch (type) {
'vaccination' => ClinicIcons.syringe,
'transport' => ClinicIcons.carrier,
_ => ClinicIcons.pawHeart,
};go deeper
Know the steps: one font file, a pubspec family, and static const IconData fields that name that family.
Explain why const matters to the release build's glyph scan, what the build error says, and what fontPackage and matchTextDirection do.
Design server-driven icons as mappings to constants, and migrate pre-3.44 IconData enums without losing type safety.
Decide when a brand icon set belongs in an icon font versus vector assets, and who owns code-point stability as the set grows.
## Why a custom icon font A pet-clinic app has brand icons that no stock set contains: a paw with a heart, a syringe with a drop, a carrier crate. They are single-colour line drawings, which is exactly what an icon font is good at: every glyph scales cleanly, takes its colour from `IconTheme`, and the whole set costs one file. (Multi-colour marks belong in SVG instead.) ## Building and declaring the font 1. Export each icon as an SVG on the same canvas size. 2. Merge them into one `.ttf` with an icon-font generator, which assigns each glyph a code point in the Unicode private-use area and usually emits a code-point map. 3. Declare the font like any custom family: ```yaml flutter: fonts: - family: ClinicIcons fonts: - asset: fonts/ClinicIcons.ttf ``` 4. Expose each glyph as a constant: ```dart @staticIconProvider abstract final class ClinicIcons { static const String _family = 'ClinicIcons'; static const IconData pawHeart = IconData(0xe800, fontFamily: _family); static const IconData syringe = IconData(0xe801, fontFamily: _family); } ``` - **`fontFamily`** must equal the pubspec `family`. - **`fontPackage`** is required when the font is declared in a package, so `Icon` can resolve the namespaced family. - **`matchTextDirection: true`** makes `Icon` mirror a directional glyph in right-to-left locales. - **`@staticIconProvider`** marks a class that holds only `static const` icons, so the tree shaker can treat the declarations themselves as not being uses. ## Why every IconData must be const Release builds of Android, iOS, web and the other targets run the **icon tree shaker** by default (`--tree-shake-icons` is on; debug builds ignore it). The `flutter` tool asks a constant finder for every `IconData` instance in the compiled program, reads their code points, and subsets each icon font to those glyphs. The mechanism only works if every use is **known at compile time**: - A `const IconData(...)` is visible to the finder, with its code point. - `IconData(json['icon'] as int, fontFamily: 'ClinicIcons')` is not. The tool cannot know which glyphs it may need, so it stops with `This application cannot tree shake icons fonts. It has non-constant instances of IconData at the following locations:` and suggests building again with `--no-tree-shake-icons`. The fix is almost always to map dynamic keys to constants instead of constructing code points: ```dart IconData iconForVisit(String type) => switch (type) { 'vaccination' => ClinicIcons.syringe, _ => ClinicIcons.pawHeart, }; ``` Opting out with `--no-tree-shake-icons` works but ships every glyph of every icon font; how much that costs is an app-size question. ## Keeping code points stable The code point is the contract between the font file and the Dart constants. If a designer adds an icon and the generator renumbers the glyphs, `ClinicIcons.syringe` may silently draw the carrier crate instead — no compile error, no runtime error. Protect the mapping: - Commit the generator's code-point map next to the font and review changes to it like code. - Generate the Dart class from that map rather than editing constants by hand. - Append new glyphs at new code points; never reuse a retired one. - Keep a screenshot or golden check of an icon sheet so a swapped glyph is visible in review. ## What changed in Flutter 3.44 | Before 3.44 | Since 3.44 | |---|---| | `IconData` could be extended or implemented | `IconData` is a `final class` | | `enum AppIcons implements IconData` was a common pattern | that enum no longer compiles | | non-const arguments compiled silently | `codePoint`, `fontFamily` and `fontPackage` are `@mustBeConst`; the analyzer reports `non_const_argument_for_const_parameter` | The migration guide replaces the enum with a wrapper class holding `static const` instances and a small widget that renders `Icon(icon.iconData)`, keeping dot shorthands where the type can be inferred. If a truly dynamic icon is unavoidable, add `// ignore: non_const_argument_for_const_parameter` and accept that the build needs `--no-tree-shake-icons`. ## Checklist 1. One font per icon set, declared once. 2. Every glyph a `static const IconData` in an `@staticIconProvider` class. 3. Server- or database-driven icons mapped to constants, never built from integers. 4. Directional glyphs marked with `matchTextDirection: true`.
- The server sends an icon code point per clinic service. How do you render it without breaking the release build?Do not build `IconData` from the integer. Map a server key to one of your `static const IconData` fields with a `switch` or a const map, with a fallback icon for unknown keys. The tree shaker then sees every glyph you can show. Constructing `IconData(code)` at runtime makes the release build exit unless you pass `--no-tree-shake-icons`.
- Your team's enum AppIcons implements IconData stopped compiling on Flutter 3.44. What replaces it?`IconData` became a `final class`, so nothing outside its library can implement or extend it. Replace the enum with a class of `static const` wrapper instances that each hold a `const IconData`, plus a small widget that renders `Icon(icon.iconData)`. Keep a manual `values` list if tooling relied on the enum's.
saying these in an interview costs you the question
- A non-const IconData crashes the app at runtime on release builds.
- Custom icon fonts need no pubspec entry because Icon finds them automatically.
- Enums implementing IconData are still the recommended pattern in Flutter 3.47.
- fontPackage can be omitted even when the icon font lives in a package.
- Icon tree shaking also runs in debug builds.