Skip to content

[material_ui] Lazily initialize default component themes in ThemeData - #13516

Draft
kevmoo wants to merge 1 commit into
flutter:mainfrom
kevmoo:themedata-lazy-component-themes
Draft

kevmoo wants to merge 1 commit into
flutter:mainfrom
kevmoo:themedata-lazy-component-themes

Conversation

@kevmoo

@kevmoo kevmoo commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Avoid eagerly instantiating all 47 default component theme objects (AppBarThemeData, BottomSheetThemeData, CardThemeData, DatePickerThemeData, DialogThemeData, SliderThemeData, TimePickerThemeData, etc.) inside factory ThemeData(...) when those sub-themes are not customized by the application.

Background

Currently, factory ThemeData initializes all 47 component theme parameters with fooTheme ??= const FooThemeData() and stores them in non-nullable fields on ThemeData. Even when an application only uses a handful of Material widgets, instantiating all 47 FooThemeData classes in ThemeData (and unconditionally calling FooThemeData.lerp(a.fooTheme, b.fooTheme, t) in ThemeData.lerp, which is always reachable via MaterialApp -> AnimatedTheme -> ThemeDataTween.lerp) prevents Dart's whole-program Type Flow Analysis (TFA) in gen_snapshot and dart2wasm from tree-shaking unused *ThemeData classes, their .lerp() methods, and their operator == / hashCode implementations.

Changes

  • Stores the 47 component theme properties in private nullable backing fields (final FooThemeData? _fooTheme;) on ThemeData (initialized via required FooThemeData? fooTheme in const ThemeData.raw) and lazily returns _fooTheme ?? const FooThemeData() from each public non-nullable getter.
  • Preserves null backing fields across factory ThemeData(...) and ThemeData.copyWith(...) when a component theme is not explicitly provided.
  • Short-circuits ThemeData.lerp (a._fooTheme == null && b._fooTheme == null ? null : FooThemeData.lerp(a.fooTheme, b.fooTheme, t)), ThemeData.== ((other._fooTheme == null && _fooTheme == null) || other.fooTheme == fooTheme), and ThemeData.hashCode (if (_fooTheme != null && _fooTheme != const FooThemeData()) _fooTheme) so:
    1. When a component theme is never configured in an application, Dart TFA infers _fooTheme has type Null and folds the null check to true, tree-shaking const FooThemeData(), FooThemeData.==, and FooThemeData.lerp.
    2. When a component theme is explicitly passed as const FooThemeData() (or copied via theme.copyWith(fooTheme: theme.fooTheme)), ==, hashCode, and lerp behave identically to an omitted (null) backing field.

Measured Binary Size Impact (kevmoo/slide_puzzle, stacked on #13515)

  • Native Linux x64 AOT (libapp.so, gen_snapshot): -81,920 B (-2.72%, 3,015,640 B -> 2,933,720 B; -180,224 B / -5.79% combined with #13515)
  • Raw .wasm binary (flutter build web --wasm --source-maps): -37,053 B (-1.51%, 2,453,498 B -> 2,416,445 B; -81,255 B / -3.25% combined with #13515)
  • Wasm code section (--no-source-maps): -27,339 B (-2.78%, -118 compiled functions; -45,866 B / -4.57% combined with #13515)
  • ThemeDataTween.lerp: 9,559 B -> 2,070 B (-7,489 B / -78.34%)
  • ThemeData.==: 1,820 B -> 888 B (-932 B / -51.21%)

Part of flutter/flutter#196349
Related to flutter/flutter#91772
Design RFC: flutter/rfc#43

Pre-Review Checklist

If you need help, consider asking for advice on the #hackers-new channel on Discord.

Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the gemini-code-assist bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.

Footnotes

  1. Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling. ↩ ↩2

Stores nullable private backing fields (`_fooTheme`) for the 47 default-constructed component theme properties on `ThemeData`, returning `const FooThemeData()` lazily from each public getter when null and short-circuiting `ThemeData.lerp`, `operator ==`, and `hashCode` when unconfigured so Dart whole-program TFA can tree-shake unused component themes and their `.lerp` methods.
@github-actions github-actions Bot added p: material_ui triage-design Should be looked at in design triage labels Nov 1, 2026
@kevmoo
kevmoo marked this pull request as ready for review November 1, 2026 04:50

@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 refactors ThemeData to lazily initialize default component theme data, converting component theme fields into getters backed by nullable private fields to enable tree-shaking of unused themes. It updates copyWith, lerp, equality checks, and hashing to support this new structure, and adds a test suite to verify behavior preservation. The review feedback identifies redundant type casts to InputDecorationThemeData? in both the constructor and the copyWith method.

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

Labels

CICD Run CI/CD p: material_ui triage-design Should be looked at in design triage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants