| # Material 3 Expressive Component Migration Checklist |
| |
| ## Goal |
| |
| This document provides actionable steps for migrating `material_ui` components to Material 3 Expressive. |
| |
| For the rationale behind the migration strategy, check the Useful References section. The docs listed there explain why Material and Cupertino are moving into standalone packages, why Material 3 Expressive work happens in `material_ui`, and why this checklist uses component-level opt-in during the migration. |
| |
| ## Scope |
| |
| Migrate at most one component per PR. |
| |
| Keep Material 3 as the default. Add Material 3 Expressive behind a component-level opt-in using the component theme. |
| |
| Do not add a global `ThemeData` expressive variant yet. |
| |
| Start from existing generated token files under `packages/material_ui/tool/gen_defaults/data/`. The component migration PR should consume those tokens through a template and generate defaults. |
| |
| ## Useful References |
| |
| - [Architecture of `material_ui` and `cupertino_ui`](https://docs.google.com/document/d/1TW42GcjEWoPZK7Y2cZr38zjoRQ1T4gJBujXIqPL_lOw/edit?tab=t.0) |
| - [Material 3 Expressive migration planning](https://docs.google.com/document/d/15XBE5xraSiMh_Oep_hclrX8rnT7rhM69L1kaMN94gdo/edit?tab=t.0) |
| - [Material 3 Expressive project](https://github.com/orgs/flutter/projects/250) |
| |
| ## 1. Confirm the Component Changes |
| |
| - Check the Material Design component page for the component's M3 Expressive update. |
| - Check the existing token files under `packages/material_ui/tool/gen_defaults/data/`. |
| - List the M3E changes the PR will implement: colors, sizes, widths, shapes, states, elevation, motion, or typography. |
| |
| ## 2. Create the M3E Template |
| |
| Create or update the component template under `packages/material_ui/tool/gen_defaults/templates/`. |
| |
| Use the existing component template file when the component already has one. Otherwise, create: |
| |
| `packages/material_ui/tool/gen_defaults/templates/<component>_template.dart` |
| |
| Create an M3E template class that extends `TokenTemplateM3E`. |
| |
| Use the existing `M3E` suffix pattern for generated defaults class names. |
| |
| Example pattern: |
| |
| ```dart |
| class IconButtonTemplateM3E extends TokenTemplateM3E { |
| const IconButtonTemplateM3E(); |
| |
| @override |
| String get name => 'Icon Button'; |
| |
| @override |
| String get parentFilePath => 'icon_button.dart'; |
| |
| @override |
| String generateContents(String className) { |
| return ''' |
| class $className { |
| const $className(this.context); |
| |
| final BuildContext context; |
| |
| // generated defaults |
| } |
| '''; |
| } |
| } |
| ``` |
| |
| Generate defaults from token data whenever possible. If a required value is missing from the available token files but is specified in the Material Design documentation, use a localized hardcoded value and add a comment with a link to the source. |
| |
| Do not hardcode values in component code. Keep fallback values in the template or generated-defaults path so they can be replaced when tokens become available. |
| |
| ## 3. Register the Template and Generate Defaults |
| |
| Update `packages/material_ui/tool/gen_defaults/bin/gen_defaults.dart`. |
| |
| Add the template import: |
| |
| ```dart |
| import '../templates/<component>_template.dart'; |
| ``` |
| |
| Register the M3E template for the migrated component. |
| |
| Example: |
| |
| ```dart |
| import '../templates/icon_button_template.dart'; |
| |
| // ... |
| |
| const IconButtonTemplateM3E().generateFile(verbose: verbose); |
| ``` |
| |
| Run the generator from the `material_ui` package directory: |
| |
| ```shell |
| cd packages/material_ui |
| dart run tool/gen_defaults/bin/gen_defaults.dart |
| ``` |
| |
| Confirm that the generated defaults are written under `packages/material_ui/lib/src/generated/` and use the filename pattern `<component>_defaults_m3e.g.dart`. |
| |
| Do not manually edit generated defaults. |
| |
| ## 4. Add Component Theme Opt-In |
| |
| Add `StyleVariant? variant` to the component theme data class. |
| |
| Example: |
| |
| ```dart |
| class IconButtonThemeData with Diagnosticable { |
| const IconButtonThemeData({ |
| this.style, |
| this.variant, |
| }); |
| |
| final ButtonStyle? style; |
| |
| /// The style variant of Material Design used by [IconButton]. |
| /// |
| /// Set this to [StyleVariant.material3Expressive] to opt icon buttons into |
| /// the Material 3 Expressive style. |
| final StyleVariant? variant; |
| } |
| ``` |
| |
| Update the component theme data class support methods, such as `lerp`, `==`, `hashCode`, `debugFillProperties`, etc. Update `copyWith` if the class already has one. |
| |
| Default null to `StyleVariant.material3` in component behavior. |
| |
| Do not add extra component theme flags unless the component needs them. |
| |
| ## 5. Wire the Component to M3E Defaults |
| |
| Add the generated M3E defaults as a `part` directive in the component file. |
| |
| Example: |
| |
| ```dart |
| part 'generated/icon_button_defaults_m3e.g.dart'; |
| ``` |
| |
| Select defaults using the component theme variant. |
| |
| Example pattern: |
| |
| ```dart |
| final StyleVariant effectiveVariant = |
| <Component>Theme.of(context).variant ?? StyleVariant.material3; |
| |
| return switch (effectiveVariant) { |
| StyleVariant.material3 => _<Component>DefaultsM3(context), |
| StyleVariant.material3Expressive => _<Component>DefaultsM3E(context), |
| }; |
| ``` |
| |
| Keep existing Material 3 defaults unchanged. |
| |
| Ensure M3E defaults apply only when the component theme variant is `StyleVariant.material3Expressive`. |
| |
| ## 6. Add Public Style APIs Only When Required |
| |
| Prefer existing style fields in the corresponding component theme data first. Add a new style field only when an M3E update cannot be represented by existing APIs. |
| |
| For example, Material 3 Expressive IconButton provides size variants that control multiple values, such as icon size and container size. Since `IconButtonThemeData.style` did not already have a single property for selecting those token groups, the migration introduced `ButtonStyle.sizeVariant`. |
| |
| Use the [IconButton migration](https://github.com/flutter/packages/pull/12832) as a reference for adding `ButtonStyle.sizeVariant` and related button variant APIs. |
| |
| When adding new style fields: |
| |
| - Add constructor parameters. |
| - Add fields. |
| - Update `copyWith`, if the class already has one. |
| - Update `merge`. |
| - Update `lerp`. |
| - Update equality and `hashCode`. |
| - Update `debugFillProperties`. |
| - Add API docs. |
| - Add tests. |
| |
| Use shared property names across related components when the variants have the same meaning. For example, if multiple button components expose size variants, prefer a shared `sizeVariant` property name instead of component-specific names. |
| |
| ## 7. Add API Docs |
| |
| Document how to opt into M3E for the component. |
| |
| If the component docs already describe Material 3 default values, update that documentation to include the Material 3 Expressive defaults as well. |
| |
| Keep Material 3 and Material 3 Expressive defaults easy to distinguish. |
| |
| Document new style fields at the API declaration site. |
| |
| Avoid exposing token implementation details in user-facing docs unless the API itself is token-oriented. |
| |
| ## 8. Add Tests |
| |
| Add focused tests for: |
| |
| - Component theme `variant` opt-in. |
| - Generated M3E defaults. |
| - New style properties. |
| - Theme-level style defaults. |
| - Widget-level style overrides. |
| - Enabled, disabled, hovered, focused, pressed, selected, and unselected states when the component has state-specific tokens. |
| - Theme/style equality, `hashCode`, `lerp`, and diagnostics when public APIs changed. |
| |
| ## 9. Add ThemeData Documentation |
| |
| Create a separate PR for a centralized `ThemeData` documentation section if the component migration PR is already large. |
| |
| Add a list of components that support M3E component-level opt-in. |
| |
| Update the list whenever a component migration lands. |
| |
| Include the exact opt-in API for each migrated component. |
| |
| Example entry: |
| |
| ```md |
| - IconButton: use `IconButtonThemeData(variant: StyleVariant.material3Expressive)`. |
| ``` |
| |
| ## 10. Add an Example |
| |
| Create a separate PR for examples if the migration PR is already large. The example should show the Material 3 Expressive opt-in component, such as all new M3E size, width, shape, color, and state variants that apply. |
| |
| Place examples under `packages/material_ui/example/`. |
| |
| Add or update example tests if required by the package. Remember to list the example path in the component documentation. |
| |
| Example: |
| |
| ```dart |
| /// <callout-box> |
| /// This sample shows creation of [IconButton] widgets for standard, filled, |
| /// filled tonal and outlined types, as described in: https://m3.material.io/components/icon-buttons/overview |
| /// |
| /// {@macro material_ui.dartpad_guide} |
| /// {@example /example/lib/icon_button/icon_button.2.dart#body} |
| /// </callout-box> |
| ``` |
| |
| ## 11. Add Changelog and Version Update |
| |
| Create a pending changelog file that includes the changelog entry and version bump. |
| |
| Make a copy of the [`material_ui` `template.yaml`](https://github.com/flutter/packages/blob/main/packages/material_ui/pending_changelogs/template.yaml), then fill out the details. Use `version: minor` when the PR adds public API or new M3E component support. |
| |
| For manually created pending changelog files, use one of: |
| |
| - `version: major` |
| - `version: minor` |
| - `version: patch` |
| - `version: skip` |
| |
| Alternatively, use the Flutter Packages Tool. Follow the [configuration instructions](https://github.com/flutter/packages/tree/main/script/tool#flutter-plugin-tools), then set up the local tool from the repository root: |
| |
| ```shell |
| dart pub get -C script/tool |
| ``` |
| |
| Run this from the `material_ui` package directory: |
| |
| ```shell |
| dart run ../../script/tool/bin/flutter_plugin_tools.dart update-release-info \ |
| --current-package \ |
| --base-branch=origin/main \ |
| --version=minor \ |
| --changelog="Adds Material 3 Expressive support for <Component>." |
| ``` |
| |
| For the Flutter Packages Tool command, use `--version=bugfix` for patch-level changes or `--version=next` for changelog-only changes that should not publish a package version. |
| |
| ## 12. Run Verification |
| |
| Set up the Flutter Packages Tool from the repository root: |
| |
| ```shell |
| dart pub get -C script/tool |
| ``` |
| |
| Run formatting: |
| |
| ```shell |
| dart run script/tool/bin/flutter_plugin_tools.dart format \ |
| --packages material_ui \ |
| --base-branch=origin/main \ |
| --no-clang-format \ |
| --no-kotlin \ |
| --no-java \ |
| --no-swift |
| ``` |
| |
| Run analysis: |
| |
| ```shell |
| dart run script/tool/bin/flutter_plugin_tools.dart analyze \ |
| --packages material_ui \ |
| --base-branch=origin/main |
| ``` |
| |
| Run the relevant component tests from the `material_ui` package directory. For example: |
| |
| ```shell |
| cd packages/material_ui |
| flutter test test/icon_button_test.dart |
| ``` |
| |
| Run generator tests from the `gen_defaults` package directory if generator code changed: |
| |
| ```shell |
| cd packages/material_ui/tool/gen_defaults |
| flutter test test |
| ``` |
| |
| ## 13. PR Description Checklist |
| |
| Include: |
| |
| - Component migrated. |
| - New public APIs. |
| - New component theme opt-in. |
| - Follow-up PRs needed. |
| |
| Example: |
| |
| ```md |
| ## Description |
| |
| Adds Material 3 Expressive support for `<Component>` behind an explicit component-level opt-in. |
| |
| ## Changes |
| |
| - Adds `<Component>ThemeData.variant`. |
| - Adds new style APIs: `<new style APIs>`. |
| - Adds `<Component>TemplateM3E`. |
| - Adds generated defaults in `packages/material_ui/lib/src/generated/<component>_defaults_m3e.g.dart`. |
| - Adds tests for M3E defaults and opt-in behavior. |
| |
| ## Follow-ups |
| |
| - Add or update the ThemeData M3E component list. |
| - Add an example demonstrating the M3E `<Component>`. |
| - Add follow-up PRs for any M3E behavior that cannot be included in this PR. |
| ``` |