Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
81482e7
Add M3E IconButton
QuncCccccc May 29, 2026
bd6ac5b
Rename IconButton M3E template
QuncCccccc Aug 8, 2026
b58d013
Add IconButton xSmall tap target coverage
QuncCccccc Aug 17, 2026
ad3c453
Rename IconButton M3E style variants
QuncCccccc Aug 17, 2026
e0f40a0
Rename M3E IconButton default internals
QuncCccccc Aug 17, 2026
310555b
Use padded tap target for M3E IconButton defaults
QuncCccccc Aug 17, 2026
f4b6db1
Document M3E IconButton tap target defaults
QuncCccccc Aug 17, 2026
9aec3af
Expand ButtonStyle variant documentation
QuncCccccc Aug 17, 2026
9a7f441
Clarify ButtonStyle variant enum docs
QuncCccccc Aug 18, 2026
1d07dcd
Move IconButton variant defaults to IconButton docs
QuncCccccc Aug 18, 2026
a592aa8
Refine ButtonSize API documentation
QuncCccccc Aug 18, 2026
6a60a93
Rephrase ButtonStyle size variant docs
QuncCccccc Aug 18, 2026
7076f83
Simplify ButtonStyle size variant docs
QuncCccccc Aug 18, 2026
42e7a80
Clarify IconButton width variant docs
QuncCccccc Aug 18, 2026
885832a
Clarify ButtonStyle shape variant docs
QuncCccccc Aug 18, 2026
4e7b7c1
Polish IconButton variant style docs
QuncCccccc Aug 18, 2026
2822adc
Clarify IconButton M3E option docs
QuncCccccc Aug 18, 2026
e877ff8
Simplify IconButton M3E option docs
QuncCccccc Aug 18, 2026
39f1cc6
Separate IconButton M3E default docs
QuncCccccc Aug 18, 2026
8595f5a
Inline IconButton variant style lookup
QuncCccccc Aug 18, 2026
4d9f13d
Clean up IconButton M3E default variants
QuncCccccc Aug 18, 2026
05e63bb
Move IconButton M3E variant getters
QuncCccccc Aug 18, 2026
b9567d9
Clarify IconButton M3E default constructors
QuncCccccc Aug 18, 2026
701ac2f
Rename IconButton M3E variant enums
QuncCccccc Aug 18, 2026
7128649
Clarify IconButton M3 defaults docs
QuncCccccc Aug 18, 2026
30150ea
Move IconButton useMaterial3 TODO
QuncCccccc Aug 18, 2026
e5471d3
Simplify IconButton variant docs
QuncCccccc Aug 18, 2026
99177d2
Clean up IconButton M3E tests
QuncCccccc Aug 18, 2026
0509c4b
Apply M3E test theme defaults
QuncCccccc Aug 18, 2026
07e81d1
Simplify IconButton M3E test helper
QuncCccccc Aug 18, 2026
6e002ef
Add IconButton M3E release info
QuncCccccc Sep 10, 2026
3ced364
Address Gemini comment
QuncCccccc Sep 17, 2026
00046d2
Split IconButton M3E generated defaults
QuncCccccc Sep 18, 2026
7a30d30
Fix IconButton template lint after rebase
QuncCccccc Sep 22, 2026
e5b7688
Address feedback
QuncCccccc Sep 23, 2026
0889ca3
Reformat to fix Linux repo checks
QuncCccccc Sep 23, 2026
9186a12
Replace deprecated withOpacity with withValues
QuncCccccc Sep 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 114 additions & 1 deletion packages/material_ui/lib/src/button_style.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,55 @@ import 'theme_data.dart';
// late BuildContext context;
// typedef MyAppHome = Placeholder;

/// Defines size variants for Material 3 Expressive button components.
///
/// Each size variant represents a named visual scale. For example, a Material
/// 3 Expressive [IconButton] uses this value to choose its container size, icon
/// size, and padding.
enum ButtonSizeVariant {
/// The smallest button size.
xSmall,

/// A small button size.
small,

/// A medium button size.
medium,

/// A large button size.
large,

/// The largest button size.
xLarge,
}

/// Defines the width variants for Material 3 Expressive [IconButton].
enum IconButtonWidthVariant {
/// A narrower icon button.
narrow,

/// The standard icon button width.
standard,

/// A wider icon button.
wide,
}

/// Defines shape variants for Material 3 Expressive button components.
enum ButtonShapeVariant {
/// A rounded button shape.
///
/// For example, a Material 3 Expressive [IconButton] with this variant uses
/// rounder default container shapes.
round,

/// A squared button shape.
///
/// For example, a Material 3 Expressive [IconButton] with this variant uses
/// more squared default container shapes.
square,
}

/// The type for [ButtonStyle.backgroundBuilder] and [ButtonStyle.foregroundBuilder].
///
/// The [states] parameter is the button's current pressed/hovered/etc state. The [child] is
Expand Down Expand Up @@ -196,6 +245,9 @@ class ButtonStyle with Diagnosticable {
this.splashFactory,
this.backgroundBuilder,
this.foregroundBuilder,
this.sizeVariant,
this.iconButtonWidth,
this.shapeVariant,
});

/// The style for a button's [Text] widget descendants.
Expand Down Expand Up @@ -432,6 +484,42 @@ class ButtonStyle with Diagnosticable {
/// configuring clipping.
final ButtonLayerBuilder? foregroundBuilder;

/// The Material 3 Expressive size variant for this button.
///
/// A size variant selects a component-defined token set rather than a single
/// dimension. For example, an [IconButton] uses this value to choose its
/// default container size, padding, and icon size when it is using
/// [StyleVariant.material3Expressive].
///
/// Use [minimumSize], [fixedSize], [maximumSize], [padding], or [iconSize] to
/// directly override those individual style properties.
final ButtonSizeVariant? sizeVariant;

/// The Material 3 Expressive width variant for an [IconButton].
///
/// A width variant selects the leading and trailing space tokens used to
/// compute an icon button's default horizontal padding and minimum width when
/// it is using [StyleVariant.material3Expressive]. It does not change the
/// button's height or icon size.
///
/// This property is only used by icon buttons. Use [padding],
/// [minimumSize], [fixedSize], or [maximumSize] to directly override the
/// resulting layout.
final IconButtonWidthVariant? iconButtonWidth;

/// The Material 3 Expressive shape variant for this button.
///
/// A shape variant selects between component-defined token families, such as
/// round and square shapes. For [IconButton], this affects the default
/// container and selected-state shapes when it is using
/// [StyleVariant.material3Expressive]; state-specific tokens, such as the
/// pressed shape, are still resolved by the component defaults.
///
/// Use [shape] to provide a specific [OutlinedBorder] or stateful shape
/// override instead of selecting one of the Material 3 Expressive shape
/// variants.
final ButtonShapeVariant? shapeVariant;

/// Returns a copy of this ButtonStyle with the given fields replaced with
/// the new values.
ButtonStyle copyWith({
Expand Down Expand Up @@ -460,6 +548,9 @@ class ButtonStyle with Diagnosticable {
InteractiveInkFeatureFactory? splashFactory,
ButtonLayerBuilder? backgroundBuilder,
ButtonLayerBuilder? foregroundBuilder,
ButtonSizeVariant? sizeVariant,
IconButtonWidthVariant? iconButtonWidth,
ButtonShapeVariant? shapeVariant,
Comment on lines +551 to +553

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should these new properties get added to the button_style_test?

Also should these be WidgetStateProperties? Ooc, what does WidgetStateProperty mean and how do we decide which properties are WidgetStateProperties?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

A WidgetStateProperty represents a value that can resolve differently depending on states such as pressed, hovered, focused, disabled, or selected.

For example, components' overlayColor will change a little bit based on different states, so we will have something like

  WidgetStateProperty<Color?>? get overlayColor =>    WidgetStateProperty.resolveWith((Set<WidgetState> states) {
      if (states.contains(WidgetState.selected)) {
        if (states.contains(WidgetState.pressed)) {
          return _colors.onInverseSurface.withOpacity(0.1);
        }
        if (states.contains(WidgetState.hovered)) {
          return _colors.onInverseSurface.withOpacity(0.08);
        }
        if (states.contains(WidgetState.focused)) {
          return _colors.onInverseSurface.withOpacity(0.08);
        }
      }
      ...
    });

The new properties do not change by widget state. They select a button configuration. For example, ButtonSizeVariant.small selects a coordinated set of values for properties such as ButtonStyle.iconSize, ButtonStyle.padding, ButtonStyle.minimumSize, and ButtonStyle.shape. Those resulting properties are WidgetStatePropertys where state-dependent resolution is needed. Let me know if this doesn't make sense:)!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Should these new properties get added to the button_style_test?

Added tests!

}) {
return ButtonStyle(
textStyle: textStyle ?? this.textStyle,
Expand Down Expand Up @@ -487,6 +578,9 @@ class ButtonStyle with Diagnosticable {
splashFactory: splashFactory ?? this.splashFactory,
backgroundBuilder: backgroundBuilder ?? this.backgroundBuilder,
foregroundBuilder: foregroundBuilder ?? this.foregroundBuilder,
sizeVariant: sizeVariant ?? this.sizeVariant,
iconButtonWidth: iconButtonWidth ?? this.iconButtonWidth,
shapeVariant: shapeVariant ?? this.shapeVariant,
);
}

Expand Down Expand Up @@ -525,6 +619,9 @@ class ButtonStyle with Diagnosticable {
splashFactory: splashFactory ?? style.splashFactory,
backgroundBuilder: backgroundBuilder ?? style.backgroundBuilder,
foregroundBuilder: foregroundBuilder ?? style.foregroundBuilder,
sizeVariant: sizeVariant ?? style.sizeVariant,
iconButtonWidth: iconButtonWidth ?? style.iconButtonWidth,
shapeVariant: shapeVariant ?? style.shapeVariant,
);
}

Expand Down Expand Up @@ -556,6 +653,9 @@ class ButtonStyle with Diagnosticable {
splashFactory,
backgroundBuilder,
foregroundBuilder,
sizeVariant,
iconButtonWidth,
shapeVariant,
];
return Object.hashAll(values);
}
Expand Down Expand Up @@ -593,7 +693,10 @@ class ButtonStyle with Diagnosticable {
other.alignment == alignment &&
other.splashFactory == splashFactory &&
other.backgroundBuilder == backgroundBuilder &&
other.foregroundBuilder == foregroundBuilder;
other.foregroundBuilder == foregroundBuilder &&
other.sizeVariant == sizeVariant &&
other.iconButtonWidth == iconButtonWidth &&
other.shapeVariant == shapeVariant;
}

@override
Expand Down Expand Up @@ -715,6 +818,13 @@ class ButtonStyle with Diagnosticable {
defaultValue: null,
),
);
properties.add(EnumProperty<ButtonSizeVariant>('sizeVariant', sizeVariant, defaultValue: null));
properties.add(
EnumProperty<IconButtonWidthVariant>('iconButtonWidth', iconButtonWidth, defaultValue: null),
);
properties.add(
EnumProperty<ButtonShapeVariant>('shapeVariant', shapeVariant, defaultValue: null),
);
}

/// Linearly interpolate between two [ButtonStyle]s.
Expand Down Expand Up @@ -778,6 +888,9 @@ class ButtonStyle with Diagnosticable {
splashFactory: t < 0.5 ? a?.splashFactory : b?.splashFactory,
backgroundBuilder: t < 0.5 ? a?.backgroundBuilder : b?.backgroundBuilder,
foregroundBuilder: t < 0.5 ? a?.foregroundBuilder : b?.foregroundBuilder,
sizeVariant: t < 0.5 ? a?.sizeVariant : b?.sizeVariant,
iconButtonWidth: t < 0.5 ? a?.iconButtonWidth : b?.iconButtonWidth,
shapeVariant: t < 0.5 ? a?.shapeVariant : b?.shapeVariant,
);
}
}
Loading
Loading