Skip to content

[record_use] Migrate IconTreeShaker to package:record_use - #190225

Merged
auto-submit[bot] merged 8 commits into
flutter:masterfrom
dcharkes:icon-tree-shaking-record-use
Aug 4, 2026
Merged

auto-submit[bot] merged 8 commits into
flutter:masterfrom
dcharkes:icon-tree-shaking-record-use

Conversation

@dcharkes

@dcharkes dcharkes commented Jul 29, 2026 •

Copy link
Copy Markdown
Contributor

Migrates IconTreeShaker from const_finder to package:record_use.

Using the Dart compiler feature record use gives us the ability to record all constants reliably:

// supported before and after this PR:
const IconData(0xe84e, fontFamily: 'MaterialIcons')

// supported after this PR:
IconData(0xe84e, fontFamily: 'MaterialIcons')

Should fix:

Interesting implementation details:

  • Web has wasm with js fallback. We're taking one of the recorded uses of either compiler. If one compiler can prove code is dead it should not be reachable in the other.
  • Retains Artifact.constFinder for g3 compatibility. (We can clean this up after we migrate g3.)

Interesting roll details:

  • This adds a dependency on package:record_use in Flutter in the g3 build, but the package rolls with the Dart SDK into g3. This means we cannot do any breaking changes to the package anymore. (We marked the package 1.0.)

Testing:

  • Updated the relevant unit test.
  • Updated one integration test to see the error messages still surface if tree shaking fails.

@github-actions github-actions Bot added tool Affects the "flutter" command-line tool. See also t: labels. framework flutter/packages/flutter repository. See also f: labels. labels Jul 29, 2026
@dcharkes dcharkes added the CICD Run CI/CD label Jul 29, 2026
@dcharkes
dcharkes force-pushed the icon-tree-shaking-record-use branch 3 times, most recently from 3806d21 to afc7956 Compare July 29, 2026 20:44
@dcharkes
dcharkes force-pushed the icon-tree-shaking-record-use branch 2 times, most recently from 4864af0 to 047150c Compare July 30, 2026 08:00
@github-actions github-actions Bot added a: tests "flutter test", flutter_test, or one of our tests d: examples Sample code and demos a: desktop Running on desktop f: integration_test The flutter/packages/integration_test plugin team-ios Owned by iOS platform team labels Jul 30, 2026
@dcharkes
dcharkes force-pushed the icon-tree-shaking-record-use branch 2 times, most recently from 88b4347 to 842b07d Compare July 30, 2026 08:12
@github-actions github-actions Bot removed a: tests "flutter test", flutter_test, or one of our tests d: examples Sample code and demos a: desktop Running on desktop f: integration_test The flutter/packages/integration_test plugin team-ios Owned by iOS platform team labels Jul 30, 2026
@dcharkes
dcharkes force-pushed the icon-tree-shaking-record-use branch 3 times, most recently from 71eca96 to 51cfbb8 Compare July 30, 2026 08:39
@github-actions github-actions Bot added the d: examples Sample code and demos label Jul 30, 2026
@dcharkes
dcharkes force-pushed the icon-tree-shaking-record-use branch from 51cfbb8 to 6e50967 Compare July 30, 2026 08:39
@github-actions github-actions Bot removed the d: examples Sample code and demos label Jul 30, 2026
@dcharkes
dcharkes marked this pull request as ready for review July 31, 2026 07:40

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request migrates the icon tree-shaking mechanism from using ConstFinder to using the RecordUse metadata. It annotates IconData with @RecordUse(), updates KernelSnapshot to always generate recorded uses in AOT mode, and refactors IconTreeShaker to parse recorded uses JSON files instead of analyzing the kernel file. Review feedback suggests optimizing the empty-file check by using file.lengthSync() > 0 instead of reading the file, and strictly matching the IconData class name and library URI to prevent false positives during definition checks.

Comment thread packages/flutter_tools/lib/src/build_system/targets/icon_tree_shaker.dart Outdated
@mkustermann

mkustermann commented Jul 31, 2026 •

Copy link
Copy Markdown
Member

(For dart2wasm, IIRC there's also some issues with record use and deferred loading. I surfaced those concerns on some gerrit CL but I cannot find it atm)

Flutter does not do tree-shaking of fonts based on loading units. All characters of one font stay in one font file.

Tree shaking is a compile-time concept. Deferred loading is a bundling + runtime concept.
Maybe what you're saying is that all tree shaken fonts are loaded eagerly by flutter, before main is run?

(A flutter app may use deferred loading of some UI screens. If only those UI screens need a certain font, then it would be sufficient to load the font at the time when the deferred code for those UI screens is loaded - and once the deferred UI screens actually render they are guaranteed the font and it's icons are available)

A higher level tangential question: I can see that this PR changes the bespoke const_finder with the record uses file (+1). Are there also plans to go to the next step of

  • allowing hook/build.dart to bundle fonts (and legacy pubspec.yaml:flutter.fonts declarations are implicitly added as well)
  • sending all those to a common linke (e.g. pkg/flutter/hook/link.dart accepting fonts to tree shake)
  • make that linker run the tree shaking of icons based on record_uses.json?

(That would remove more of the bespoke logic in the flutter build system internals - e.g. invoking the C++ binary that does the font tree shaking)

@dcharkes

Copy link
Copy Markdown
Contributor Author

Tree shaking is a compile-time concept. Deferred loading is a bundling + runtime concept. Maybe what you're saying is that all tree shaken fonts are loaded eagerly by flutter, before main is run?

Yes

(A flutter app may use deferred loading of some UI screens. If only those UI screens need a certain font, then it would be sufficient to load the font at the time when the deferred code for those UI screens is loaded - and once the deferred UI screens actually render they are guaranteed the font and it's icons are available)

Indeed

A higher level tangential question: I can see that this PR changes the bespoke const_finder with the record uses file (+1). Are there also plans to go to the next step of

  • allowing hook/build.dart to bundle fonts (and legacy pubspec.yaml:flutter.fonts declarations are implicitly added as well)
  • sending all those to a common linke (e.g. pkg/flutter/hook/link.dart accepting fonts to tree shake)
  • make that linker run the tree shaking of icons based on record_uses.json?

(That would remove more of the bespoke logic in the flutter build system internals - e.g. invoking the C++ binary that does the font tree shaking)

Yes, I'd love to build them on top of data assets. I explored how we can do that in:

It should just be a package, and should work also outside Flutter 👌

auto-submit Bot pushed a commit to dart-lang/native that referenced this pull request Jul 31, 2026
So we can combine two recordings.

Use case dart2wasm + dart2js compilation fallbacks in Flutter. Conditional imports can make it so that they are not necessarily a super set of each other, they might be disjoint. So picking one to default to does not give the right semantics.

Context:

* flutter/flutter#190225 (comment)
var hasNonConstant = false;

for (final MapEntry<DefinitionWithInstances, List<InstanceReference>> entry
in recordings.instances.entries) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: consider using object destructuring here

for (final MapEntry(:key, :value) in recordings.instances.entries) {

str.startsWith('package:flutter/src/widgets/icon_data.dart::IconData.');
}

({Constant? codePoint, Constant? fontFamily, Constant? fontPackage})? _extractIconDataConstants(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should define a typedef for this record type. It's pretty verbose :)

({Constant? codePoint, Constant? fontFamily, Constant? fontPackage})? _extractIconDataConstants(
InstanceReference reference,
) {
if (reference is InstanceConstantReference) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another opportunity for object destructuring:

if (reference case InstanceConstantReference(
      instanceConstant: InstanceConstant(:final fields),
    )) {
  return (
    codePoint: fields[_codePointFieldName],
    fontFamily: fields[_fontFamilyFieldName],
    fontPackage: fields[_fontPackageFieldName],
  );
}

'pubspec.yaml, or you are missing "uses-material-design: true".',
} else if (reference is InstanceCreationReference) {
final List<MaybeConstant> positional = reference.positionalArguments;
final Map<String, MaybeConstant> named = reference.namedArguments;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here:

else if (reference case InstanceCreationReference(
      :final positionalArguments,
      :final namedArguments,
    )) {
  // Use `positionalArguments` and `namedArguments` directly here
}

or

else if (reference case InstanceCreationReference(
      positionalArguments: final positional,
      namedArguments: final named,
    )) {
  // Use `positional` and `named` directly here
}

/// are combined in the returned [Recordings].
Recordings merge(Recordings other) {
final newCalls = <DefinitionWithStaticCalls, List<CallReference>>{};
for (final MapEntry<DefinitionWithStaticCalls, List<CallReference>> entry in calls.entries) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer using object destructuring for MapEntrys, here and elsewhere.

contains('Expected to find fontFamily for constant IconData with codepoint: 4660'),
);
}
if (target.length == 2 && target[0] == 'web' && target[1] == '--wasm') {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (target case ['web', '--wasm'])

@dcharkes
dcharkes requested a review from bkonyi August 3, 2026 13:19
@gaaclarke

Copy link
Copy Markdown
Member

@dcharkes fyi this increased the web side 100K in the following benchmark: https://flutter-flutter-perf.luci.app/e/?begin=1785858784&end=1786384856&keys=Xdd3fcae2aac0e95f0a61f49a23781826&num_commits=50&request_type=1&xbaroffset=2809046

I'll let you decide if that is a fair tradeoff =)

@dcharkes

Copy link
Copy Markdown
Contributor Author

@dcharkes fyi this increased the web side 100K in the following benchmark: https://flutter-flutter-perf.luci.app/e/?begin=1785858784&end=1786384856&keys=Xdd3fcae2aac0e95f0a61f49a23781826&num_commits=50&request_type=1&xbaroffset=2809046

I'll let you decide if that is a fair tradeoff =)

Thanks for letting me know. I'll investigate. (Note that the web icon tree shaking was broken before and sometimes omitted icons, so I'll need to see if this is a regression, or bug fix.)

auto-submit Bot pushed a commit to flutter/packages that referenced this pull request Aug 10, 2026
…12420)

Manual roll requested by [email protected]

flutter/flutter@b766512...27b0988

2026-08-05 [email protected] reland(tool): remove redundant --enable-experiment=record-use flag (flutter/flutter#190591)
2026-08-05 [email protected] Windows: Propagate enabled accessibility state (flutter/flutter#190507)
2026-08-05 [email protected] Roll Fuchsia Test Scripts from ltbuIH9Z3T_yOuigu... to vcANVO8VIDQHasH1X... (flutter/flutter#190589)
2026-08-05 [email protected] Document frozen embedder API structs (flutter/flutter#186842)
2026-08-05 [email protected] Document super call order for State.didChangeDependencies (flutter/flutter#185945)
2026-08-05 [email protected] Move examples of `flutter/widgets` widgets out from `flutter/material` (flutter/flutter#189532)
2026-08-05 [email protected] [web] Use thread local strike caches in skwasm (flutter/flutter#190048)
2026-08-05 [email protected] doc: fix typo in see also section for PrimaryScrollController.maybeOf (flutter/flutter#190386)
2026-08-05 [email protected] Migrate the shell unit tests from legacy Dart native functions to FFI (flutter/flutter#190473)
2026-08-05 [email protected] render proxy box now defaults baseline calculation to null (flutter/flutter#190269)
2026-08-05 [email protected] Update Widgets Localizations from Translation Console (flutter/flutter#190503)
2026-08-04 [email protected] Revert: fix(tool): remove redundant --enable-experiment=record-use flag (flutter/flutter#190583)
2026-08-04 [email protected] Roll Fuchsia Test Scripts from 1frGe_KltAJKkeyPg... to ltbuIH9Z3T_yOuigu... (flutter/flutter#190561)
2026-08-04 [email protected] iOS,macOS: add tsan and ubsan support for Swift (flutter/flutter#190497)
2026-08-04 [email protected] fix: update on_message_ to nullptr after window destroy so that dart gets destroy message  (flutter/flutter#185807)
2026-08-04 [email protected] [flutter_tools] Gracefully handle locked Windows files during clean (flutter/flutter#190095)
2026-08-04 [email protected] iOS,macOS: make Logger thread-safe, conform to Sendable (flutter/flutter#190488)
2026-08-04 [email protected] iOS: Eliminate use of IOSContextNoop in platform view tests (reland) (flutter/flutter#190509)
2026-08-04 [email protected] fix(tool): remove redundant --enable-experiment=record-use flag (flutter/flutter#190475)
2026-08-04 [email protected] android_hardware_smoke_test: Detect blank image failures or EGL initialization warnings and retry (flutter/flutter#190110)
2026-08-04 [email protected] Bumps text gamma on windows to match skia. (flutter/flutter#190477)
2026-08-04 [email protected] Roll Skia from 48b58ee222f1 to a8583a0a2c11 (2 revisions) (flutter/flutter#190537)
2026-08-04 [email protected] [tool][web] Intercept dart2wasm errors & append JS migration footers (flutter/flutter#190476)
2026-08-04 [email protected] [record_use] Migrate IconTreeShaker to `package:record_use` (flutter/flutter#190225)
2026-08-04 [email protected] Handle unexpected exceptions during Azure metadata detection (flutter/flutter#189457)
2026-08-04 [email protected] Fix merge conflict from flutter/flutter#190369 (flutter/flutter#190544)
2026-08-04 [email protected] Prepare device support symbols (flutter/flutter#190369)
2026-08-04 [email protected] Roll Packages from ac87e65 to 3498b9d (1 revision) (flutter/flutter#190532)
2026-08-04 [email protected] Roll Skia from a08d918ebd6a to 48b58ee222f1 (11 revisions) (flutter/flutter#190527)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-packages
Please CC [email protected],[email protected] on the revert to ensure that a human
is aware of the problem.

To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
@dcharkes

Copy link
Copy Markdown
Contributor Author

@gaaclarke This is a funny one: The Cupertino icons were retaining 7 icons in the previous implementation, and 0 icons in the new implementation. Now with 0 icons retained, The Flutter icon tree shaker does not know if some asset is an icon font or a normal font (that should be retained). So it decided to retain the full font. 🙈 😄

if (!families.contains(familyKey)) {
continue;
}

So, I think we have to specify if something is an icon font or not.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CICD Run CI/CD framework flutter/packages/flutter repository. See also f: labels. tool Affects the "flutter" command-line tool. See also t: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants