flutter / flutter/flutter

Icon tree shaking ships unreferenced package icon fonts at full size

Open
#191,733 6 comments 0 reactions 1 assignee Claimed by @dcharkes View on GitHub
found in release: 3.47 has reproducible steps P2 team-tool tool triaged-tool
Dominant language
Dart
Stars
179k
Forks
31.1k
PR merge metrics
PR metrics pending

Description

If you have multiple icon font weights and one goes unused, no `const IconData(...)` for that weight survives compilation. With zero references, Flutter cannot infer that it is an icon font and it remains un-tree-shaken.

Related to https://github.com/flutter/flutter/issues/190902.

## Steps to reproduce

1. Create a Flutter app that depends on an icon package bundling multiple weights as separate font families, e.g. [`forui_phosphor 0.26.0`](https://pub.dev/packages/forui_phosphor) (regular, thin, light, bold, fill, each its own ttf and `@staticIconProvider` class).
2. Reference icons from one family only:
```dart
const Icon(FPhosphorIcons.acorn)
```
3. `flutter build ios --release --analyze-size`.
4. Inspect the bundled fonts in the size analysis JSON or the built bundle.

### Expected results

Unreferenced icon font families are tree-shaken.

### Actual results

Only families with at least one discovered `const IconData` are tree-shaken. Families with zero references are copied through at full size.

With reference to light icon:
Image

[ios-code-size-analysis_light_reference.json](https://github.com/user-attachments/files/31426121/ios-code-size-analysis_light_reference.json)

With no reference to light icon:
Image

[ios-code-size-analysis_no_reference.json](https://github.com/user-attachments/files/31426180/ios-code-size-analysis_no_reference.json)

Please inspect attached size_analysis.jsons using `dart devtools --appSizeBase=`.

### Code sample

Code sample

```dart
import 'package:flutter/widgets.dart';

import 'package:forui_phosphor/forui_phosphor.dart';

// MVE for icon fonts with zero IconData references shipping at full size.
//
// forui_phosphor bundles 5 weights as separate font families. Only regular and light are referenced below; thin, bold
// and fill ship at full size (~450-540 KB each) while the referenced families are subsetted to ~1-5 KB.
void main() => runApp(const Application());

class Application extends StatelessWidget {
const new({super.key});

@override
Widget build(BuildContext context) => const Directionality(
textDirection: TextDirection.ltr,
child: ColoredBox(
color: Color(0xFF000000),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(FPhosphorIcons.acorn, size: 48, color: Color(0xFFFFFFFF)),
// Toggle this single reference to observe light.ttf shipping at ~1 KB (subsetted) vs ~537 KB (whole).
Icon(FPhosphorLightIcons.acorn, size: 48, color: Color(0xFFFFFFFF)),
],
),
),
);
}
```

### Flutter Doctor output

Doctor output

```console
Flutter 3.47.1 • channel stable
Framework • revision 6655482ec0 • 2026-08-19
Engine • hash 11d79658c444477b06513d32b52c8c4ccb7276b0
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.