The SVG rendered correctly. The generated Flutter icon font did not.
After conversion with FlutterIcon.com, a location pin lost its center hole, a chat bubble lost its third dot, and a mail icon lost its inner cutout. The same source files still looked correct in browsers and design tools.
All three SVGs used fill-rule="evenodd" to form cutouts. To isolate the conversion, I rendered the source SVGs, the FlutterIcon TTF, and a GlyphPact OTF/CFF build inside the same Flutter app:
This comparison used GlyphPact v1.1.0. All three columns were rendered by the same Flutter app.
This is a three-file result, not a claim that every even-odd SVG fails in FlutterIcon.com. These files lost visible cutouts in my export. That was enough to make me look at the rest of my icon workflow.
The SVGs rendered correctly, but the conversion still changed them
An SVG renderer and an icon-font generator have different jobs.
The SVG renderer understands fill rules, strokes, transforms, clipping, paint, and the rest of the document. A monochrome icon font represents the artwork as glyph outlines and alpha coverage. Somewhere between those two representations, strokes may need outlining, transforms need flattening, and cutouts must survive as contour relationships.
FlutterIcon.com has a familiar warning for this class of problem:
If image looks not as expected please convert to compound path manually.
Skipped tags and attributes: ...
That advice can fix a file, but it sends the work back to the developer or designer. Open a vector editor, change the paths, export again, upload again, and check the result.
For one icon, fine. For an icon set that keeps changing, the repair loop becomes part of every release.
The icon set keeps changing
My icon sets rarely arrive once. More icons show up in later Figma handoffs, and every batch has to join the existing font without changing codepoints already used by the app.
The Dart API matters as much as the pictures:
Icon(AppIcons.location)
Icon(AppIcons.chat)
Icon(AppIcons.mail)
Each field points to a codepoint in the font. If regeneration assigns an old field to a new glyph, the Dart code still compiles. The app simply shows the wrong picture.
FlutterIcon.com can preserve assignments if you save its config.json and import it on the next visit. That works, but it depends on keeping the file and following the same process every time.
That was the weak point for my projects. Someone had to remember the previous config, upload the correct files, export the bundle, copy the right artifacts, and check that nothing moved. The repository could not tell us whether any step was missed.
I wanted the icon font to behave like generated source code.
Moving the build into the repository
The SVG directory should be the source of truth. The command should run locally and in CI. Existing codepoints should stay fixed without depending on a remembered browser session.
That became GlyphPact, a local SVG-to-Flutter icon compiler:
uv tool install glyphpact
glyphpact assets/icons \
--output lib/generated/app_icons \
--name AppIcons
The output includes an OpenType/CFF font, a const Dart IconData provider, a machine-readable report, and iconfont.lock.json.
The lock file is the API contract. Adding or reordering SVGs does not renumber existing icons. Removing an icon leaves a tombstone, so its codepoint cannot quietly return as a different picture later. A unique content-preserving rename keeps the same assignment and Dart name.
There is a longer explanation of why stable icon codepoints matter, including a lock-file diff.
CI can rebuild the candidate output without replacing the committed artifacts:
glyphpact --config icon_font.json --check
The command exits with code 3 when the generated output is stale. A missing icon build stops looking like a visual QA problem and starts behaving like any other failed code-generation check.
Strict builds are sometimes annoying
In strict mode, GlyphPact accepts supported SVG features only when it can preserve them. Approximations and skipped inputs require explicit flags. Malformed, unsafe, unknown, or over-budget input fails the build.
This is stricter than accepting whatever a generator can produce, and sometimes it gets in the way. I prefer that interruption in a pull request to finding a damaged icon after a release.
It also does not claim to support every valid SVG. Icon fonts represent monochrome alpha coverage, not the full SVG format. If source color, animation, filters, or browser-dependent painting matter, a font is the wrong output.
Known limits for large icon packs
GlyphPact automates bulk conversion, but each build still produces an icon font. OpenType glyph indexing has a practical ceiling of 65,534 usable glyphs per font, and GlyphPact enforces the same per-build limit.
By default, assignments use the BMP private-use range from U+E000 through U+F8FF. That range has 6,400 lifetime slots. Active icons and tombstones both consume slots because GlyphPact never recycles a removed icon's codepoint. The build report tracks the remaining capacity and warns when range use reaches 80%.
A larger pack can start in a supplementary private-use range, which provides up to 65,534 slots. That choice must be made before the first build because an established lock file rejects later start-codepoint changes.
Larger catalogs need to be split into multiple independently versioned fonts. GlyphPact makes those builds repeatable, but it cannot make an unlimited catalog fit into one mobile font.
The CLI remains the source of truth
Because the build runs locally from a checked-in config, the same workflow can be exposed to coding agents without changing the compiler.
The optional GlyphPact MCP integration exposes four local operations: audit an SVG pack, build from a checked-in config, check committed output for staleness, and page through a build report.
MCP is not required. The CLI remains the compiler. The MCP layer gives a coding agent a defined boundary for the same workflow instead of asking it to operate a browser or guess which generated files it owns.
I still ship plenty of SVG files
I would keep a multicolor illustration or a one-off static graphic as SVG and render it with flutter_svg. For animation, I would use a format and renderer suited to that asset.
FlutterIcon.com also remains convenient when I need a quick one-time font or want to choose from its bundled icon libraries.
GlyphPact is for the narrower case I kept running into: a maintained set of monochrome Flutter UI icons, repeated design deliveries, and generated artifacts that need to stay correct across releases.
GlyphPact's source is on GitHub. If you have an SVG that behaves badly during font conversion, especially one involving even-odd fills, strokes, or cutouts, open an issue with the file. Minimal examples of currently unsupported features are useful too. Awkward inputs are more useful to this compiler than clean demo icons.
_Disclosure: I built and maintain GlyphPact.

Top comments (0)