blob: 5bab6957e3fc0125ada8e97b80fdada0451289e9 [file] [view]
# 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.
```