mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
un-break ThemeData
equality (#154695)
This PR is _almost_ able to close issue #89127. Sadly, no `InheritedModel` or custom `RenderObject`s today; instead the [WidgetState operators](https://main-api.flutter.dev/flutter/widgets/WidgetStateOperators.html) have been restructured to support equality checks. `WidgetStateProperty.fromMap()` is now capable of accurate equality checks, and all of the `.styleFrom()` methods have been refactored to use that constructor. (Equality checks are still broken for `WidgetStateProperty.resolveWith()`, and any other non-`const` objects that implement the interface.) <br><br> credit for this idea goes to @justinmc: https://github.com/flutter/flutter/issues/89127#issuecomment-2313187703
This commit is contained in:
parent
18c325af17
commit
bfa04edca6
@ -120,31 +120,24 @@ class _${blockName}DefaultsM3 extends SegmentedButtonThemeData {
|
||||
@override
|
||||
Widget? get selectedIcon => const Icon(Icons.check);
|
||||
|
||||
static MaterialStateProperty<Color?> resolveStateColor(Color? unselectedColor, Color? selectedColor, Color? overlayColor){
|
||||
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.selected)) {
|
||||
if (states.contains(MaterialState.pressed)) {
|
||||
return (overlayColor ?? selectedColor)?.withOpacity(0.1);
|
||||
}
|
||||
if (states.contains(MaterialState.hovered)) {
|
||||
return (overlayColor ?? selectedColor)?.withOpacity(0.08);
|
||||
}
|
||||
if (states.contains(MaterialState.focused)) {
|
||||
return (overlayColor ?? selectedColor)?.withOpacity(0.1);
|
||||
}
|
||||
} else {
|
||||
if (states.contains(MaterialState.pressed)) {
|
||||
return (overlayColor ?? unselectedColor)?.withOpacity(0.1);
|
||||
}
|
||||
if (states.contains(MaterialState.hovered)) {
|
||||
return (overlayColor ?? unselectedColor)?.withOpacity(0.08);
|
||||
}
|
||||
if (states.contains(MaterialState.focused)) {
|
||||
return (overlayColor ?? unselectedColor)?.withOpacity(0.1);
|
||||
}
|
||||
}
|
||||
return Colors.transparent;
|
||||
});
|
||||
static WidgetStateProperty<Color?> resolveStateColor(
|
||||
Color? unselectedColor,
|
||||
Color? selectedColor,
|
||||
Color? overlayColor,
|
||||
) {
|
||||
final Color? selected = overlayColor ?? selectedColor;
|
||||
final Color? unselected = overlayColor ?? unselectedColor;
|
||||
return WidgetStateProperty<Color?>.fromMap(
|
||||
<WidgetStatesConstraint, Color?>{
|
||||
WidgetState.selected & WidgetState.pressed: selected?.withOpacity(0.1),
|
||||
WidgetState.selected & WidgetState.hovered: selected?.withOpacity(0.08),
|
||||
WidgetState.selected & WidgetState.focused: selected?.withOpacity(0.1),
|
||||
WidgetState.pressed: unselected?.withOpacity(0.1),
|
||||
WidgetState.hovered: unselected?.withOpacity(0.08),
|
||||
WidgetState.focused: unselected?.withOpacity(0.1),
|
||||
WidgetState.any: Colors.transparent,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
''';
|
||||
|
@ -30,8 +30,8 @@ class MaterialStateExample extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return TextFormField(
|
||||
initialValue: 'abc',
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: const Icon(Icons.person),
|
||||
decoration: const InputDecoration(
|
||||
prefixIcon: Icon(Icons.person),
|
||||
prefixIconColor: WidgetStateColor.fromMap(
|
||||
<WidgetStatesConstraint, Color>{
|
||||
WidgetState.focused: Colors.green,
|
||||
|
@ -32,7 +32,7 @@ class MaterialStateExample extends StatelessWidget {
|
||||
return Theme(
|
||||
data: themeData.copyWith(
|
||||
inputDecorationTheme: themeData.inputDecorationTheme.copyWith(
|
||||
prefixIconColor: WidgetStateColor.fromMap(
|
||||
prefixIconColor: const WidgetStateColor.fromMap(
|
||||
<WidgetStatesConstraint, Color>{
|
||||
WidgetState.error: Colors.red,
|
||||
WidgetState.focused: Colors.blue,
|
||||
|
@ -45,7 +45,7 @@ class _ListTileExampleState extends State<ListTileExample> {
|
||||
_selected = !_selected;
|
||||
});
|
||||
},
|
||||
iconColor: WidgetStateColor.fromMap(<WidgetStatesConstraint, Color>{
|
||||
iconColor: const WidgetStateColor.fromMap(<WidgetStatesConstraint, Color>{
|
||||
WidgetState.disabled: Colors.red,
|
||||
WidgetState.selected: Colors.green,
|
||||
WidgetState.any: Colors.black,
|
||||
|
@ -246,6 +246,23 @@ abstract class ButtonStyleButton extends StatefulWidget {
|
||||
/// A convenience method for subclasses.
|
||||
static MaterialStateProperty<T>? allOrNull<T>(T? value) => value == null ? null : MaterialStatePropertyAll<T>(value);
|
||||
|
||||
/// Returns null if [enabled] and [disabled] are null.
|
||||
/// Otherwise, returns a [WidgetStateProperty] that resolves to [disabled]
|
||||
/// when [WidgetState.disabled] is active, and [enabled] otherwise.
|
||||
///
|
||||
/// A convenience method for subclasses.
|
||||
static WidgetStateProperty<Color?>? defaultColor(Color? enabled, Color? disabled) {
|
||||
if ((enabled ?? disabled) == null) {
|
||||
return null;
|
||||
}
|
||||
return WidgetStateProperty<Color?>.fromMap(
|
||||
<WidgetStatesConstraint, Color?>{
|
||||
WidgetState.disabled: disabled,
|
||||
WidgetState.any: enabled,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// A convenience method used by subclasses in the framework, that returns an
|
||||
/// interpolated value based on the [fontSizeMultiplier] parameter:
|
||||
///
|
||||
|
@ -228,37 +228,39 @@ class ElevatedButton extends ButtonStyleButton {
|
||||
ButtonLayerBuilder? backgroundBuilder,
|
||||
ButtonLayerBuilder? foregroundBuilder,
|
||||
}) {
|
||||
final MaterialStateProperty<Color?>? foregroundColorProp = switch ((foregroundColor, disabledForegroundColor)) {
|
||||
(null, null) => null,
|
||||
(_, _) => _ElevatedButtonDefaultColor(foregroundColor, disabledForegroundColor),
|
||||
};
|
||||
final MaterialStateProperty<Color?>? backgroundColorProp = switch ((backgroundColor, disabledBackgroundColor)) {
|
||||
(null, null) => null,
|
||||
(_, _) => _ElevatedButtonDefaultColor(backgroundColor, disabledBackgroundColor),
|
||||
};
|
||||
final MaterialStateProperty<Color?>? iconColorProp = switch ((iconColor, disabledIconColor)) {
|
||||
(null, null) => null,
|
||||
(_, _) => _ElevatedButtonDefaultColor(iconColor, disabledIconColor),
|
||||
};
|
||||
final MaterialStateProperty<Color?>? overlayColorProp = switch ((foregroundColor, overlayColor)) {
|
||||
(null, null) => null,
|
||||
(_, final Color overlayColor) when overlayColor.value == 0 => const MaterialStatePropertyAll<Color?>(Colors.transparent),
|
||||
(_, _) => _ElevatedButtonDefaultOverlay((overlayColor ?? foregroundColor)!),
|
||||
(_, Color(a: 0.0)) => WidgetStatePropertyAll<Color?>(overlayColor),
|
||||
(_, final Color color) || (final Color color, _) => WidgetStateProperty<Color?>.fromMap(
|
||||
<WidgetState, Color?>{
|
||||
WidgetState.pressed: color.withOpacity(0.1),
|
||||
WidgetState.hovered: color.withOpacity(0.08),
|
||||
WidgetState.focused: color.withOpacity(0.1),
|
||||
},
|
||||
),
|
||||
};
|
||||
final MaterialStateProperty<double>? elevationValue = switch (elevation) {
|
||||
null => null,
|
||||
_ => _ElevatedButtonDefaultElevation(elevation),
|
||||
};
|
||||
final MaterialStateProperty<MouseCursor?> mouseCursor = _ElevatedButtonDefaultMouseCursor(enabledMouseCursor, disabledMouseCursor);
|
||||
|
||||
WidgetStateProperty<double>? elevationValue;
|
||||
if (elevation != null) {
|
||||
elevationValue = WidgetStateProperty<double>.fromMap(
|
||||
<WidgetStatesConstraint, double>{
|
||||
WidgetState.disabled: 0,
|
||||
WidgetState.pressed: elevation + 6,
|
||||
WidgetState.hovered: elevation + 2,
|
||||
WidgetState.focused: elevation + 2,
|
||||
WidgetState.any: elevation,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return ButtonStyle(
|
||||
textStyle: MaterialStatePropertyAll<TextStyle?>(textStyle),
|
||||
backgroundColor: backgroundColorProp,
|
||||
foregroundColor: foregroundColorProp,
|
||||
backgroundColor: ButtonStyleButton.defaultColor(backgroundColor, disabledBackgroundColor),
|
||||
foregroundColor: ButtonStyleButton.defaultColor(foregroundColor, disabledForegroundColor),
|
||||
overlayColor: overlayColorProp,
|
||||
shadowColor: ButtonStyleButton.allOrNull<Color>(shadowColor),
|
||||
surfaceTintColor: ButtonStyleButton.allOrNull<Color>(surfaceTintColor),
|
||||
iconColor: iconColorProp,
|
||||
iconColor: ButtonStyleButton.defaultColor(iconColor, disabledIconColor),
|
||||
elevation: elevationValue,
|
||||
padding: ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(padding),
|
||||
minimumSize: ButtonStyleButton.allOrNull<Size>(minimumSize),
|
||||
@ -266,7 +268,12 @@ class ElevatedButton extends ButtonStyleButton {
|
||||
maximumSize: ButtonStyleButton.allOrNull<Size>(maximumSize),
|
||||
side: ButtonStyleButton.allOrNull<BorderSide>(side),
|
||||
shape: ButtonStyleButton.allOrNull<OutlinedBorder>(shape),
|
||||
mouseCursor: mouseCursor,
|
||||
mouseCursor: WidgetStateProperty<MouseCursor?>.fromMap(
|
||||
<WidgetStatesConstraint, MouseCursor?>{
|
||||
WidgetState.disabled: disabledMouseCursor,
|
||||
WidgetState.any: enabledMouseCursor,
|
||||
},
|
||||
),
|
||||
visualDensity: visualDensity,
|
||||
tapTargetSize: tapTargetSize,
|
||||
animationDuration: animationDuration,
|
||||
@ -453,83 +460,6 @@ EdgeInsetsGeometry _scaledPadding(BuildContext context) {
|
||||
);
|
||||
}
|
||||
|
||||
@immutable
|
||||
class _ElevatedButtonDefaultColor extends MaterialStateProperty<Color?> with Diagnosticable {
|
||||
_ElevatedButtonDefaultColor(this.color, this.disabled);
|
||||
|
||||
final Color? color;
|
||||
final Color? disabled;
|
||||
|
||||
@override
|
||||
Color? resolve(Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return disabled;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
class _ElevatedButtonDefaultOverlay extends MaterialStateProperty<Color?> with Diagnosticable {
|
||||
_ElevatedButtonDefaultOverlay(this.overlay);
|
||||
|
||||
final Color overlay;
|
||||
|
||||
@override
|
||||
Color? resolve(Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.pressed)) {
|
||||
return overlay.withOpacity(0.1);
|
||||
}
|
||||
if (states.contains(MaterialState.hovered)) {
|
||||
return overlay.withOpacity(0.08);
|
||||
}
|
||||
if (states.contains(MaterialState.focused)) {
|
||||
return overlay.withOpacity(0.1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
class _ElevatedButtonDefaultElevation extends MaterialStateProperty<double> with Diagnosticable {
|
||||
_ElevatedButtonDefaultElevation(this.elevation);
|
||||
|
||||
final double elevation;
|
||||
|
||||
@override
|
||||
double resolve(Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return 0;
|
||||
}
|
||||
if (states.contains(MaterialState.pressed)) {
|
||||
return elevation + 6;
|
||||
}
|
||||
if (states.contains(MaterialState.hovered)) {
|
||||
return elevation + 2;
|
||||
}
|
||||
if (states.contains(MaterialState.focused)) {
|
||||
return elevation + 2;
|
||||
}
|
||||
return elevation;
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
class _ElevatedButtonDefaultMouseCursor extends MaterialStateProperty<MouseCursor?> with Diagnosticable {
|
||||
_ElevatedButtonDefaultMouseCursor(this.enabledCursor, this.disabledCursor);
|
||||
|
||||
final MouseCursor? enabledCursor;
|
||||
final MouseCursor? disabledCursor;
|
||||
|
||||
@override
|
||||
MouseCursor? resolve(Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return disabledCursor;
|
||||
}
|
||||
return enabledCursor;
|
||||
}
|
||||
}
|
||||
|
||||
class _ElevatedButtonWithIcon extends ElevatedButton {
|
||||
_ElevatedButtonWithIcon({
|
||||
super.key,
|
||||
|
@ -291,33 +291,26 @@ class FilledButton extends ButtonStyleButton {
|
||||
ButtonLayerBuilder? backgroundBuilder,
|
||||
ButtonLayerBuilder? foregroundBuilder,
|
||||
}) {
|
||||
final MaterialStateProperty<Color?>? foregroundColorProp = switch ((foregroundColor, disabledForegroundColor)) {
|
||||
(null, null) => null,
|
||||
(_, _) => _FilledButtonDefaultColor(foregroundColor, disabledForegroundColor),
|
||||
};
|
||||
final MaterialStateProperty<Color?>? backgroundColorProp = switch ((backgroundColor, disabledBackgroundColor)) {
|
||||
(null, null) => null,
|
||||
(_, _) => _FilledButtonDefaultColor(backgroundColor, disabledBackgroundColor),
|
||||
};
|
||||
final MaterialStateProperty<Color?>? iconColorProp = switch ((iconColor, disabledIconColor)) {
|
||||
(null, null) => null,
|
||||
(_, _) => _FilledButtonDefaultColor(iconColor, disabledIconColor),
|
||||
};
|
||||
final MaterialStateProperty<Color?>? overlayColorProp = switch ((foregroundColor, overlayColor)) {
|
||||
(null, null) => null,
|
||||
(_, final Color overlayColor) when overlayColor.value == 0 => const MaterialStatePropertyAll<Color?>(Colors.transparent),
|
||||
(_, _) => _FilledButtonDefaultOverlay((overlayColor ?? foregroundColor)!),
|
||||
(_, Color(a: 0.0)) => WidgetStatePropertyAll<Color?>(overlayColor),
|
||||
(_, final Color color) || (final Color color, _) => WidgetStateProperty<Color?>.fromMap(
|
||||
<WidgetState, Color?>{
|
||||
WidgetState.pressed: color.withOpacity(0.1),
|
||||
WidgetState.hovered: color.withOpacity(0.08),
|
||||
WidgetState.focused: color.withOpacity(0.1),
|
||||
},
|
||||
),
|
||||
};
|
||||
final MaterialStateProperty<MouseCursor?> mouseCursor = _FilledButtonDefaultMouseCursor(enabledMouseCursor, disabledMouseCursor);
|
||||
|
||||
return ButtonStyle(
|
||||
textStyle: MaterialStatePropertyAll<TextStyle?>(textStyle),
|
||||
backgroundColor: backgroundColorProp,
|
||||
foregroundColor: foregroundColorProp,
|
||||
backgroundColor: ButtonStyleButton.defaultColor(backgroundColor, disabledBackgroundColor),
|
||||
foregroundColor: ButtonStyleButton.defaultColor(foregroundColor, disabledForegroundColor),
|
||||
overlayColor: overlayColorProp,
|
||||
shadowColor: ButtonStyleButton.allOrNull<Color>(shadowColor),
|
||||
surfaceTintColor: ButtonStyleButton.allOrNull<Color>(surfaceTintColor),
|
||||
iconColor: iconColorProp,
|
||||
iconColor: ButtonStyleButton.defaultColor(iconColor, disabledIconColor),
|
||||
elevation: ButtonStyleButton.allOrNull(elevation),
|
||||
padding: ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(padding),
|
||||
minimumSize: ButtonStyleButton.allOrNull<Size>(minimumSize),
|
||||
@ -325,7 +318,12 @@ class FilledButton extends ButtonStyleButton {
|
||||
maximumSize: ButtonStyleButton.allOrNull<Size>(maximumSize),
|
||||
side: ButtonStyleButton.allOrNull<BorderSide>(side),
|
||||
shape: ButtonStyleButton.allOrNull<OutlinedBorder>(shape),
|
||||
mouseCursor: mouseCursor,
|
||||
mouseCursor: WidgetStateProperty<MouseCursor?>.fromMap(
|
||||
<WidgetStatesConstraint, MouseCursor?>{
|
||||
WidgetState.disabled: disabledMouseCursor,
|
||||
WidgetState.any: enabledMouseCursor,
|
||||
},
|
||||
),
|
||||
visualDensity: visualDensity,
|
||||
tapTargetSize: tapTargetSize,
|
||||
animationDuration: animationDuration,
|
||||
@ -483,59 +481,6 @@ EdgeInsetsGeometry _scaledPadding(BuildContext context) {
|
||||
);
|
||||
}
|
||||
|
||||
@immutable
|
||||
class _FilledButtonDefaultColor extends MaterialStateProperty<Color?> with Diagnosticable {
|
||||
_FilledButtonDefaultColor(this.color, this.disabled);
|
||||
|
||||
final Color? color;
|
||||
final Color? disabled;
|
||||
|
||||
@override
|
||||
Color? resolve(Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return disabled;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
class _FilledButtonDefaultOverlay extends MaterialStateProperty<Color?> with Diagnosticable {
|
||||
_FilledButtonDefaultOverlay(this.overlay);
|
||||
|
||||
final Color overlay;
|
||||
|
||||
@override
|
||||
Color? resolve(Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.pressed)) {
|
||||
return overlay.withOpacity(0.1);
|
||||
}
|
||||
if (states.contains(MaterialState.hovered)) {
|
||||
return overlay.withOpacity(0.08);
|
||||
}
|
||||
if (states.contains(MaterialState.focused)) {
|
||||
return overlay.withOpacity(0.1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
class _FilledButtonDefaultMouseCursor extends MaterialStateProperty<MouseCursor?> with Diagnosticable {
|
||||
_FilledButtonDefaultMouseCursor(this.enabledCursor, this.disabledCursor);
|
||||
|
||||
final MouseCursor? enabledCursor;
|
||||
final MouseCursor? disabledCursor;
|
||||
|
||||
@override
|
||||
MouseCursor? resolve(Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return disabledCursor;
|
||||
}
|
||||
return enabledCursor;
|
||||
}
|
||||
}
|
||||
|
||||
class _FilledButtonWithIcon extends FilledButton {
|
||||
_FilledButtonWithIcon({
|
||||
super.key,
|
||||
|
@ -643,24 +643,24 @@ class IconButton extends StatelessWidget {
|
||||
AlignmentGeometry? alignment,
|
||||
InteractiveInkFeatureFactory? splashFactory,
|
||||
}) {
|
||||
final MaterialStateProperty<Color?>? buttonBackgroundColor = (backgroundColor == null && disabledBackgroundColor == null)
|
||||
? null
|
||||
: _IconButtonDefaultBackground(backgroundColor, disabledBackgroundColor);
|
||||
final MaterialStateProperty<Color?>? buttonForegroundColor = (foregroundColor == null && disabledForegroundColor == null)
|
||||
? null
|
||||
: _IconButtonDefaultForeground(foregroundColor, disabledForegroundColor);
|
||||
final MaterialStateProperty<Color?>? overlayColorProp = (foregroundColor == null &&
|
||||
hoverColor == null && focusColor == null && highlightColor == null && overlayColor == null)
|
||||
? null
|
||||
: switch (overlayColor) {
|
||||
(final Color overlayColor) when overlayColor.value == 0 => const MaterialStatePropertyAll<Color?>(Colors.transparent),
|
||||
_ => _IconButtonDefaultOverlay(foregroundColor, focusColor, hoverColor, highlightColor, overlayColor),
|
||||
};
|
||||
final MaterialStateProperty<MouseCursor?> mouseCursor = _IconButtonDefaultMouseCursor(enabledMouseCursor, disabledMouseCursor);
|
||||
final Color? overlayFallback = overlayColor ?? foregroundColor;
|
||||
WidgetStateProperty<Color?>? overlayColorProp;
|
||||
if ((hoverColor ?? focusColor ?? highlightColor ?? overlayFallback) != null) {
|
||||
overlayColorProp = switch (overlayColor) {
|
||||
Color(a: 0.0) => WidgetStatePropertyAll<Color>(overlayColor),
|
||||
_ => WidgetStateProperty<Color?>.fromMap(
|
||||
<WidgetState, Color?>{
|
||||
WidgetState.pressed: highlightColor ?? overlayFallback?.withOpacity(0.1),
|
||||
WidgetState.hovered: hoverColor ?? overlayFallback?.withOpacity(0.08),
|
||||
WidgetState.focused: focusColor ?? overlayFallback?.withOpacity(0.1),
|
||||
},
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
return ButtonStyle(
|
||||
backgroundColor: buttonBackgroundColor,
|
||||
foregroundColor: buttonForegroundColor,
|
||||
backgroundColor: ButtonStyleButton.defaultColor(backgroundColor, disabledBackgroundColor),
|
||||
foregroundColor: ButtonStyleButton.defaultColor(foregroundColor, disabledForegroundColor),
|
||||
overlayColor: overlayColorProp,
|
||||
shadowColor: ButtonStyleButton.allOrNull<Color>(shadowColor),
|
||||
surfaceTintColor: ButtonStyleButton.allOrNull<Color>(surfaceTintColor),
|
||||
@ -672,7 +672,12 @@ class IconButton extends StatelessWidget {
|
||||
iconSize: ButtonStyleButton.allOrNull<double>(iconSize),
|
||||
side: ButtonStyleButton.allOrNull<BorderSide>(side),
|
||||
shape: ButtonStyleButton.allOrNull<OutlinedBorder>(shape),
|
||||
mouseCursor: mouseCursor,
|
||||
mouseCursor: WidgetStateProperty<MouseCursor?>.fromMap(
|
||||
<WidgetStatesConstraint, MouseCursor?>{
|
||||
WidgetState.disabled: disabledMouseCursor,
|
||||
WidgetState.any: enabledMouseCursor,
|
||||
},
|
||||
),
|
||||
visualDensity: visualDensity,
|
||||
tapTargetSize: tapTargetSize,
|
||||
animationDuration: animationDuration,
|
||||
@ -993,111 +998,6 @@ class _IconButtonM3 extends ButtonStyleButton {
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
class _IconButtonDefaultBackground extends MaterialStateProperty<Color?> {
|
||||
_IconButtonDefaultBackground(this.background, this.disabledBackground);
|
||||
|
||||
final Color? background;
|
||||
final Color? disabledBackground;
|
||||
|
||||
@override
|
||||
Color? resolve(Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return disabledBackground;
|
||||
}
|
||||
return background;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '{disabled: $disabledBackground, otherwise: $background}';
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
class _IconButtonDefaultForeground extends MaterialStateProperty<Color?> {
|
||||
_IconButtonDefaultForeground(this.foregroundColor, this.disabledForegroundColor);
|
||||
|
||||
final Color? foregroundColor;
|
||||
final Color? disabledForegroundColor;
|
||||
|
||||
@override
|
||||
Color? resolve(Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return disabledForegroundColor;
|
||||
}
|
||||
return foregroundColor;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '{disabled: $disabledForegroundColor, otherwise: $foregroundColor}';
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
class _IconButtonDefaultOverlay extends MaterialStateProperty<Color?> {
|
||||
_IconButtonDefaultOverlay(
|
||||
this.foregroundColor,
|
||||
this.focusColor,
|
||||
this.hoverColor,
|
||||
this.highlightColor,
|
||||
this.overlayColor,
|
||||
);
|
||||
|
||||
final Color? foregroundColor;
|
||||
final Color? focusColor;
|
||||
final Color? hoverColor;
|
||||
final Color? highlightColor;
|
||||
final Color? overlayColor;
|
||||
|
||||
@override
|
||||
Color? resolve(Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.selected)) {
|
||||
if (states.contains(MaterialState.pressed)) {
|
||||
return highlightColor ?? (overlayColor ?? foregroundColor)?.withOpacity(0.1);
|
||||
}
|
||||
if (states.contains(MaterialState.hovered)) {
|
||||
return hoverColor ?? (overlayColor ?? foregroundColor)?.withOpacity(0.08);
|
||||
}
|
||||
if (states.contains(MaterialState.focused)) {
|
||||
return focusColor ?? (overlayColor ?? foregroundColor)?.withOpacity(0.1);
|
||||
}
|
||||
}
|
||||
if (states.contains(MaterialState.pressed)) {
|
||||
return highlightColor ?? (overlayColor ?? foregroundColor)?.withOpacity(0.1);
|
||||
}
|
||||
if (states.contains(MaterialState.hovered)) {
|
||||
return hoverColor ?? (overlayColor ?? foregroundColor)?.withOpacity(0.08);
|
||||
}
|
||||
if (states.contains(MaterialState.focused)) {
|
||||
return focusColor ?? (overlayColor ?? foregroundColor)?.withOpacity(0.1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '{hovered: $hoverColor, focused: $focusColor, pressed: $highlightColor, otherwise: null}';
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
class _IconButtonDefaultMouseCursor extends MaterialStateProperty<MouseCursor?> with Diagnosticable {
|
||||
_IconButtonDefaultMouseCursor(this.enabledCursor, this.disabledCursor);
|
||||
|
||||
final MouseCursor? enabledCursor;
|
||||
final MouseCursor? disabledCursor;
|
||||
|
||||
@override
|
||||
MouseCursor? resolve(Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return disabledCursor;
|
||||
}
|
||||
return enabledCursor;
|
||||
}
|
||||
}
|
||||
|
||||
// BEGIN GENERATED TOKEN PROPERTIES - IconButton
|
||||
|
||||
// Do not edit by hand. The code between the "BEGIN GENERATED" and
|
||||
|
@ -215,34 +215,30 @@ class OutlinedButton extends ButtonStyleButton {
|
||||
ButtonLayerBuilder? backgroundBuilder,
|
||||
ButtonLayerBuilder? foregroundBuilder,
|
||||
}) {
|
||||
final MaterialStateProperty<Color?>? foregroundColorProp = switch ((foregroundColor, disabledForegroundColor)) {
|
||||
(null, null) => null,
|
||||
(_, _) => _OutlinedButtonDefaultColor(foregroundColor, disabledForegroundColor),
|
||||
};
|
||||
final MaterialStateProperty<Color?>? backgroundColorProp = switch ((backgroundColor, disabledBackgroundColor)) {
|
||||
(null, null) => null,
|
||||
(_, null) => MaterialStatePropertyAll<Color?>(backgroundColor),
|
||||
(_, _) => _OutlinedButtonDefaultColor(backgroundColor, disabledBackgroundColor),
|
||||
};
|
||||
final MaterialStateProperty<Color?>? iconColorProp = switch ((iconColor, disabledIconColor)) {
|
||||
(null, null) => null,
|
||||
(_, _) => _OutlinedButtonDefaultColor(iconColor, disabledIconColor),
|
||||
(_?, null) => WidgetStatePropertyAll<Color?>(backgroundColor),
|
||||
(_, _) => ButtonStyleButton.defaultColor(backgroundColor, disabledBackgroundColor),
|
||||
};
|
||||
final MaterialStateProperty<Color?>? overlayColorProp = switch ((foregroundColor, overlayColor)) {
|
||||
(null, null) => null,
|
||||
(_, final Color overlayColor) when overlayColor.value == 0 => const MaterialStatePropertyAll<Color?>(Colors.transparent),
|
||||
(_, _) => _OutlinedButtonDefaultOverlay((overlayColor ?? foregroundColor)!),
|
||||
(_, Color(a: 0.0)) => WidgetStatePropertyAll<Color?>(overlayColor),
|
||||
(_, final Color color) || (final Color color, _) => WidgetStateProperty<Color?>.fromMap(
|
||||
<WidgetState, Color?>{
|
||||
WidgetState.pressed: color.withOpacity(0.1),
|
||||
WidgetState.hovered: color.withOpacity(0.08),
|
||||
WidgetState.focused: color.withOpacity(0.1),
|
||||
},
|
||||
),
|
||||
};
|
||||
final MaterialStateProperty<MouseCursor?> mouseCursor = _OutlinedButtonDefaultMouseCursor(enabledMouseCursor, disabledMouseCursor);
|
||||
|
||||
return ButtonStyle(
|
||||
textStyle: ButtonStyleButton.allOrNull<TextStyle>(textStyle),
|
||||
foregroundColor: foregroundColorProp,
|
||||
foregroundColor: ButtonStyleButton.defaultColor(foregroundColor, disabledForegroundColor),
|
||||
backgroundColor: backgroundColorProp,
|
||||
overlayColor: overlayColorProp,
|
||||
shadowColor: ButtonStyleButton.allOrNull<Color>(shadowColor),
|
||||
surfaceTintColor: ButtonStyleButton.allOrNull<Color>(surfaceTintColor),
|
||||
iconColor: iconColorProp,
|
||||
iconColor: ButtonStyleButton.defaultColor(iconColor, disabledIconColor),
|
||||
elevation: ButtonStyleButton.allOrNull<double>(elevation),
|
||||
padding: ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(padding),
|
||||
minimumSize: ButtonStyleButton.allOrNull<Size>(minimumSize),
|
||||
@ -250,7 +246,12 @@ class OutlinedButton extends ButtonStyleButton {
|
||||
maximumSize: ButtonStyleButton.allOrNull<Size>(maximumSize),
|
||||
side: ButtonStyleButton.allOrNull<BorderSide>(side),
|
||||
shape: ButtonStyleButton.allOrNull<OutlinedBorder>(shape),
|
||||
mouseCursor: mouseCursor,
|
||||
mouseCursor: WidgetStateProperty<MouseCursor?>.fromMap(
|
||||
<WidgetStatesConstraint, MouseCursor?>{
|
||||
WidgetState.disabled: disabledMouseCursor,
|
||||
WidgetState.any: enabledMouseCursor,
|
||||
},
|
||||
),
|
||||
visualDensity: visualDensity,
|
||||
tapTargetSize: tapTargetSize,
|
||||
animationDuration: animationDuration,
|
||||
@ -408,59 +409,6 @@ EdgeInsetsGeometry _scaledPadding(BuildContext context) {
|
||||
);
|
||||
}
|
||||
|
||||
@immutable
|
||||
class _OutlinedButtonDefaultColor extends MaterialStateProperty<Color?> with Diagnosticable {
|
||||
_OutlinedButtonDefaultColor(this.color, this.disabled);
|
||||
|
||||
final Color? color;
|
||||
final Color? disabled;
|
||||
|
||||
@override
|
||||
Color? resolve(Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return disabled;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
class _OutlinedButtonDefaultOverlay extends MaterialStateProperty<Color?> with Diagnosticable {
|
||||
_OutlinedButtonDefaultOverlay(this.foreground);
|
||||
|
||||
final Color foreground;
|
||||
|
||||
@override
|
||||
Color? resolve(Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.pressed)) {
|
||||
return foreground.withOpacity(0.1);
|
||||
}
|
||||
if (states.contains(MaterialState.hovered)) {
|
||||
return foreground.withOpacity(0.08);
|
||||
}
|
||||
if (states.contains(MaterialState.focused)) {
|
||||
return foreground.withOpacity(0.1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
class _OutlinedButtonDefaultMouseCursor extends MaterialStateProperty<MouseCursor?> with Diagnosticable {
|
||||
_OutlinedButtonDefaultMouseCursor(this.enabledCursor, this.disabledCursor);
|
||||
|
||||
final MouseCursor? enabledCursor;
|
||||
final MouseCursor? disabledCursor;
|
||||
|
||||
@override
|
||||
MouseCursor? resolve(Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return disabledCursor;
|
||||
}
|
||||
return enabledCursor;
|
||||
}
|
||||
}
|
||||
|
||||
class _OutlinedButtonWithIcon extends OutlinedButton {
|
||||
_OutlinedButtonWithIcon({
|
||||
super.key,
|
||||
|
@ -291,14 +291,6 @@ class SegmentedButton<T> extends StatefulWidget {
|
||||
AlignmentGeometry? alignment,
|
||||
InteractiveInkFeatureFactory? splashFactory,
|
||||
}) {
|
||||
final MaterialStateProperty<Color?>? foregroundColorProp =
|
||||
(foregroundColor == null && disabledForegroundColor == null && selectedForegroundColor == null)
|
||||
? null
|
||||
: _SegmentButtonDefaultColor(foregroundColor, disabledForegroundColor, selectedForegroundColor);
|
||||
final MaterialStateProperty<Color?>? backgroundColorProp =
|
||||
(backgroundColor == null && disabledBackgroundColor == null && selectedBackgroundColor == null)
|
||||
? null
|
||||
: _SegmentButtonDefaultColor(backgroundColor, disabledBackgroundColor, selectedBackgroundColor);
|
||||
final MaterialStateProperty<Color?>? overlayColorProp = (foregroundColor == null &&
|
||||
selectedForegroundColor == null && overlayColor == null)
|
||||
? null
|
||||
@ -326,12 +318,25 @@ class SegmentedButton<T> extends StatefulWidget {
|
||||
alignment: alignment,
|
||||
splashFactory: splashFactory,
|
||||
).copyWith(
|
||||
foregroundColor: foregroundColorProp,
|
||||
backgroundColor: backgroundColorProp,
|
||||
foregroundColor: _defaultColor(foregroundColor, disabledForegroundColor, selectedForegroundColor),
|
||||
backgroundColor: _defaultColor(backgroundColor, disabledBackgroundColor, selectedBackgroundColor),
|
||||
overlayColor: overlayColorProp,
|
||||
);
|
||||
}
|
||||
|
||||
static WidgetStateProperty<Color?>? _defaultColor(Color? enabled, Color? disabled, Color? selected) {
|
||||
if ((selected ?? enabled ?? disabled) == null) {
|
||||
return null;
|
||||
}
|
||||
return WidgetStateProperty<Color?>.fromMap(
|
||||
<WidgetStatesConstraint, Color?>{
|
||||
WidgetState.disabled: disabled,
|
||||
WidgetState.selected: selected,
|
||||
WidgetState.any: enabled,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Customizes this button's appearance.
|
||||
///
|
||||
/// The following style properties apply to the entire segmented button:
|
||||
@ -589,26 +594,6 @@ class SegmentedButtonState<T> extends State<SegmentedButton<T>> {
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
class _SegmentButtonDefaultColor extends MaterialStateProperty<Color?> with Diagnosticable {
|
||||
_SegmentButtonDefaultColor(this.color, this.disabled, this.selected);
|
||||
|
||||
final Color? color;
|
||||
final Color? disabled;
|
||||
final Color? selected;
|
||||
|
||||
@override
|
||||
Color? resolve(Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return disabled;
|
||||
}
|
||||
if (states.contains(MaterialState.selected)) {
|
||||
return selected;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
}
|
||||
|
||||
class _SegmentedButtonRenderWidget<T> extends MultiChildRenderObjectWidget {
|
||||
const _SegmentedButtonRenderWidget({
|
||||
super.key,
|
||||
@ -1081,31 +1066,24 @@ class _SegmentedButtonDefaultsM3 extends SegmentedButtonThemeData {
|
||||
@override
|
||||
Widget? get selectedIcon => const Icon(Icons.check);
|
||||
|
||||
static MaterialStateProperty<Color?> resolveStateColor(Color? unselectedColor, Color? selectedColor, Color? overlayColor){
|
||||
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.selected)) {
|
||||
if (states.contains(MaterialState.pressed)) {
|
||||
return (overlayColor ?? selectedColor)?.withOpacity(0.1);
|
||||
}
|
||||
if (states.contains(MaterialState.hovered)) {
|
||||
return (overlayColor ?? selectedColor)?.withOpacity(0.08);
|
||||
}
|
||||
if (states.contains(MaterialState.focused)) {
|
||||
return (overlayColor ?? selectedColor)?.withOpacity(0.1);
|
||||
}
|
||||
} else {
|
||||
if (states.contains(MaterialState.pressed)) {
|
||||
return (overlayColor ?? unselectedColor)?.withOpacity(0.1);
|
||||
}
|
||||
if (states.contains(MaterialState.hovered)) {
|
||||
return (overlayColor ?? unselectedColor)?.withOpacity(0.08);
|
||||
}
|
||||
if (states.contains(MaterialState.focused)) {
|
||||
return (overlayColor ?? unselectedColor)?.withOpacity(0.1);
|
||||
}
|
||||
}
|
||||
return Colors.transparent;
|
||||
});
|
||||
static WidgetStateProperty<Color?> resolveStateColor(
|
||||
Color? unselectedColor,
|
||||
Color? selectedColor,
|
||||
Color? overlayColor,
|
||||
) {
|
||||
final Color? selected = overlayColor ?? selectedColor;
|
||||
final Color? unselected = overlayColor ?? unselectedColor;
|
||||
return WidgetStateProperty<Color?>.fromMap(
|
||||
<WidgetStatesConstraint, Color?>{
|
||||
WidgetState.selected & WidgetState.pressed: selected?.withOpacity(0.1),
|
||||
WidgetState.selected & WidgetState.hovered: selected?.withOpacity(0.08),
|
||||
WidgetState.selected & WidgetState.focused: selected?.withOpacity(0.1),
|
||||
WidgetState.pressed: unselected?.withOpacity(0.1),
|
||||
WidgetState.hovered: unselected?.withOpacity(0.08),
|
||||
WidgetState.focused: unselected?.withOpacity(0.1),
|
||||
WidgetState.any: Colors.transparent,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -222,30 +222,29 @@ class TextButton extends ButtonStyleButton {
|
||||
ButtonLayerBuilder? backgroundBuilder,
|
||||
ButtonLayerBuilder? foregroundBuilder,
|
||||
}) {
|
||||
final MaterialStateProperty<Color?>? foregroundColorProp = switch ((foregroundColor, disabledForegroundColor)) {
|
||||
(null, null) => null,
|
||||
(_, _) => _TextButtonDefaultColor(foregroundColor, disabledForegroundColor),
|
||||
};
|
||||
final MaterialStateProperty<Color?>? backgroundColorProp = switch ((backgroundColor, disabledBackgroundColor)) {
|
||||
(null, null) => null,
|
||||
(_, null) => MaterialStatePropertyAll<Color?>(backgroundColor),
|
||||
(_, _) => _TextButtonDefaultColor(backgroundColor, disabledBackgroundColor),
|
||||
(_?, null) => MaterialStatePropertyAll<Color?>(backgroundColor),
|
||||
(_, _) => ButtonStyleButton.defaultColor(backgroundColor, disabledBackgroundColor),
|
||||
};
|
||||
final MaterialStateProperty<Color?>? iconColorProp = switch ((iconColor, disabledIconColor)) {
|
||||
(null, null) => null,
|
||||
(_, null) => MaterialStatePropertyAll<Color?>(iconColor),
|
||||
(_, _) => _TextButtonDefaultColor(iconColor, disabledIconColor),
|
||||
(_?, null) => MaterialStatePropertyAll<Color?>(iconColor),
|
||||
(_, _) => ButtonStyleButton.defaultColor(iconColor, disabledIconColor),
|
||||
};
|
||||
final MaterialStateProperty<Color?>? overlayColorProp = switch ((foregroundColor, overlayColor)) {
|
||||
(null, null) => null,
|
||||
(_, final Color overlayColor) when overlayColor.value == 0 => const MaterialStatePropertyAll<Color?>(Colors.transparent),
|
||||
(_, _) => _TextButtonDefaultOverlay((overlayColor ?? foregroundColor)!),
|
||||
(_, Color(a: 0.0)) => WidgetStatePropertyAll<Color?>(overlayColor),
|
||||
(_, final Color color) || (final Color color, _) => WidgetStateProperty<Color?>.fromMap(
|
||||
<WidgetState, Color?>{
|
||||
WidgetState.pressed: color.withOpacity(0.1),
|
||||
WidgetState.hovered: color.withOpacity(0.08),
|
||||
WidgetState.focused: color.withOpacity(0.1),
|
||||
},
|
||||
),
|
||||
};
|
||||
final MaterialStateProperty<MouseCursor?> mouseCursor = _TextButtonDefaultMouseCursor(enabledMouseCursor, disabledMouseCursor);
|
||||
|
||||
return ButtonStyle(
|
||||
textStyle: ButtonStyleButton.allOrNull<TextStyle>(textStyle),
|
||||
foregroundColor: foregroundColorProp,
|
||||
foregroundColor: ButtonStyleButton.defaultColor(foregroundColor, disabledForegroundColor),
|
||||
backgroundColor: backgroundColorProp,
|
||||
overlayColor: overlayColorProp,
|
||||
shadowColor: ButtonStyleButton.allOrNull<Color>(shadowColor),
|
||||
@ -258,7 +257,12 @@ class TextButton extends ButtonStyleButton {
|
||||
maximumSize: ButtonStyleButton.allOrNull<Size>(maximumSize),
|
||||
side: ButtonStyleButton.allOrNull<BorderSide>(side),
|
||||
shape: ButtonStyleButton.allOrNull<OutlinedBorder>(shape),
|
||||
mouseCursor: mouseCursor,
|
||||
mouseCursor: WidgetStateProperty<MouseCursor?>.fromMap(
|
||||
<WidgetStatesConstraint, MouseCursor?>{
|
||||
WidgetState.disabled: disabledMouseCursor,
|
||||
WidgetState.any: enabledMouseCursor,
|
||||
},
|
||||
),
|
||||
visualDensity: visualDensity,
|
||||
tapTargetSize: tapTargetSize,
|
||||
animationDuration: animationDuration,
|
||||
@ -434,69 +438,6 @@ EdgeInsetsGeometry _scaledPadding(BuildContext context) {
|
||||
);
|
||||
}
|
||||
|
||||
@immutable
|
||||
class _TextButtonDefaultColor extends MaterialStateProperty<Color?> {
|
||||
_TextButtonDefaultColor(this.color, this.disabled);
|
||||
|
||||
final Color? color;
|
||||
final Color? disabled;
|
||||
|
||||
@override
|
||||
Color? resolve(Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return disabled;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '{disabled: $disabled, otherwise: $color}';
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
class _TextButtonDefaultOverlay extends MaterialStateProperty<Color?> {
|
||||
_TextButtonDefaultOverlay(this.primary);
|
||||
|
||||
final Color primary;
|
||||
|
||||
@override
|
||||
Color? resolve(Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.pressed)) {
|
||||
return primary.withOpacity(0.1);
|
||||
}
|
||||
if (states.contains(MaterialState.hovered)) {
|
||||
return primary.withOpacity(0.08);
|
||||
}
|
||||
if (states.contains(MaterialState.focused)) {
|
||||
return primary.withOpacity(0.1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '{hovered: ${primary.withOpacity(0.04)}, focused,pressed: ${primary.withOpacity(0.12)}, otherwise: null}';
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
class _TextButtonDefaultMouseCursor extends MaterialStateProperty<MouseCursor?> with Diagnosticable {
|
||||
_TextButtonDefaultMouseCursor(this.enabledCursor, this.disabledCursor);
|
||||
|
||||
final MouseCursor? enabledCursor;
|
||||
final MouseCursor? disabledCursor;
|
||||
|
||||
@override
|
||||
MouseCursor? resolve(Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return disabledCursor;
|
||||
}
|
||||
return enabledCursor;
|
||||
}
|
||||
}
|
||||
|
||||
class _TextButtonWithIcon extends TextButton {
|
||||
_TextButtonWithIcon({
|
||||
super.key,
|
||||
|
@ -6,6 +6,7 @@
|
||||
/// @docImport 'package:flutter/scheduler.dart';
|
||||
library;
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@ -45,14 +46,77 @@ abstract interface class WidgetStatesConstraint {
|
||||
bool isSatisfiedBy(Set<WidgetState> states);
|
||||
}
|
||||
|
||||
// A private class, used in [WidgetStateOperators].
|
||||
class _WidgetStateOperation implements WidgetStatesConstraint {
|
||||
const _WidgetStateOperation(this._isSatisfiedBy);
|
||||
@immutable
|
||||
sealed class _WidgetStateCombo implements WidgetStatesConstraint {
|
||||
const _WidgetStateCombo(this.first, this.second);
|
||||
|
||||
final bool Function(Set<WidgetState> states) _isSatisfiedBy;
|
||||
final WidgetStatesConstraint first;
|
||||
final WidgetStatesConstraint second;
|
||||
|
||||
@override
|
||||
bool isSatisfiedBy(Set<WidgetState> states) => _isSatisfiedBy(states);
|
||||
// ignore: hash_and_equals, since == is defined in subclasses
|
||||
int get hashCode => Object.hash(first, second);
|
||||
}
|
||||
|
||||
class _WidgetStateAnd extends _WidgetStateCombo {
|
||||
const _WidgetStateAnd(super.first, super.second);
|
||||
|
||||
@override
|
||||
bool isSatisfiedBy(Set<WidgetState> states) {
|
||||
return first.isSatisfiedBy(states) && second.isSatisfiedBy(states);
|
||||
}
|
||||
|
||||
@override
|
||||
// ignore: hash_and_equals, hashCode is defined in the sealed super-class
|
||||
bool operator ==(Object other) {
|
||||
return other is _WidgetStateAnd
|
||||
&& other.first == first
|
||||
&& other.second == second;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => '($first & $second)';
|
||||
}
|
||||
|
||||
class _WidgetStateOr extends _WidgetStateCombo {
|
||||
const _WidgetStateOr(super.first, super.second);
|
||||
|
||||
@override
|
||||
bool isSatisfiedBy(Set<WidgetState> states) {
|
||||
return first.isSatisfiedBy(states) || second.isSatisfiedBy(states);
|
||||
}
|
||||
|
||||
@override
|
||||
// ignore: hash_and_equals, hashCode is defined in the sealed super-class
|
||||
bool operator ==(Object other) {
|
||||
return other is _WidgetStateOr
|
||||
&& other.first == first
|
||||
&& other.second == second;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => '($first | $second)';
|
||||
}
|
||||
|
||||
@immutable
|
||||
class _WidgetStateNot implements WidgetStatesConstraint {
|
||||
const _WidgetStateNot(this.value);
|
||||
|
||||
final WidgetStatesConstraint value;
|
||||
|
||||
@override
|
||||
bool isSatisfiedBy(Set<WidgetState> states) => !value.isSatisfiedBy(states);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is _WidgetStateNot && other.value == value;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => value.hashCode;
|
||||
|
||||
@override
|
||||
String toString() => '~$value';
|
||||
}
|
||||
|
||||
/// These operators can be used inside a [WidgetStateMap] to combine states
|
||||
@ -67,33 +131,24 @@ class _WidgetStateOperation implements WidgetStatesConstraint {
|
||||
/// the operators can be used without being directly inherited.
|
||||
extension WidgetStateOperators on WidgetStatesConstraint {
|
||||
/// Combines two [WidgetStatesConstraint] values using logical "and".
|
||||
WidgetStatesConstraint operator &(WidgetStatesConstraint other) {
|
||||
return _WidgetStateOperation(
|
||||
(Set<WidgetState> states) => isSatisfiedBy(states) && other.isSatisfiedBy(states),
|
||||
);
|
||||
}
|
||||
WidgetStatesConstraint operator &(WidgetStatesConstraint other) => _WidgetStateAnd(this, other);
|
||||
|
||||
/// Combines two [WidgetStatesConstraint] values using logical "or".
|
||||
WidgetStatesConstraint operator |(WidgetStatesConstraint other) {
|
||||
return _WidgetStateOperation(
|
||||
(Set<WidgetState> states) => isSatisfiedBy(states) || other.isSatisfiedBy(states),
|
||||
);
|
||||
}
|
||||
WidgetStatesConstraint operator |(WidgetStatesConstraint other) => _WidgetStateOr(this, other);
|
||||
|
||||
/// Takes a [WidgetStatesConstraint] and applies the logical "not".
|
||||
WidgetStatesConstraint operator ~() {
|
||||
return _WidgetStateOperation(
|
||||
(Set<WidgetState> states) => !isSatisfiedBy(states),
|
||||
);
|
||||
}
|
||||
WidgetStatesConstraint operator ~() => _WidgetStateNot(this);
|
||||
}
|
||||
|
||||
// A private class, used to create [WidgetState.any].
|
||||
class _AlwaysMatch implements WidgetStatesConstraint {
|
||||
const _AlwaysMatch();
|
||||
class _AnyWidgetStates implements WidgetStatesConstraint {
|
||||
const _AnyWidgetStates();
|
||||
|
||||
@override
|
||||
bool isSatisfiedBy(Set<WidgetState> states) => true;
|
||||
|
||||
@override
|
||||
String toString() => 'WidgetState.any';
|
||||
}
|
||||
|
||||
/// Interactive states that some of the widgets can take on when receiving input
|
||||
@ -183,7 +238,7 @@ enum WidgetState implements WidgetStatesConstraint {
|
||||
/// isn't satisfied by the given set of states, consier adding
|
||||
/// [WidgetState.any] as the final [WidgetStateMap] key.
|
||||
/// {@endtemplate}
|
||||
static const WidgetStatesConstraint any = _AlwaysMatch();
|
||||
static const WidgetStatesConstraint any = _AnyWidgetStates();
|
||||
|
||||
@override
|
||||
bool isSatisfiedBy(Set<WidgetState> states) => states.contains(this);
|
||||
@ -268,7 +323,7 @@ abstract class WidgetStateColor extends Color implements WidgetStateProperty<Col
|
||||
/// [Set] of [WidgetState]s will be selected.
|
||||
///
|
||||
/// {@macro flutter.widgets.WidgetState.any}
|
||||
factory WidgetStateColor.fromMap(WidgetStateMap<Color> map) = _WidgetStateColorMapper;
|
||||
const factory WidgetStateColor.fromMap(WidgetStateMap<Color> map) = _WidgetStateColorMapper;
|
||||
|
||||
/// Returns a [Color] that's to be used when a component is in the specified
|
||||
/// state.
|
||||
@ -290,18 +345,6 @@ class _WidgetStateColor extends WidgetStateColor {
|
||||
Color resolve(Set<WidgetState> states) => _resolve(states);
|
||||
}
|
||||
|
||||
class _WidgetStateColorMapper extends WidgetStateColor {
|
||||
_WidgetStateColorMapper(this.map)
|
||||
: super(_WidgetStateMapper<Color>(map).resolve(_defaultStates).value);
|
||||
|
||||
final WidgetStateMap<Color> map;
|
||||
|
||||
static const Set<WidgetState> _defaultStates = <WidgetState>{};
|
||||
|
||||
@override
|
||||
Color resolve(Set<WidgetState> states) => _WidgetStateMapper<Color>(map).resolve(states);
|
||||
}
|
||||
|
||||
class _WidgetStateColorTransparent extends WidgetStateColor {
|
||||
const _WidgetStateColorTransparent() : super(0x00000000);
|
||||
|
||||
@ -309,6 +352,26 @@ class _WidgetStateColorTransparent extends WidgetStateColor {
|
||||
Color resolve(Set<WidgetState> states) => const Color(0x00000000);
|
||||
}
|
||||
|
||||
@immutable
|
||||
class _WidgetStateColorMapper extends _WidgetStateColorTransparent {
|
||||
const _WidgetStateColorMapper(this.map);
|
||||
|
||||
final WidgetStateMap<Color> map;
|
||||
|
||||
_WidgetStateMapper<Color> get _mapper => _WidgetStateMapper<Color>(map);
|
||||
|
||||
@override
|
||||
Color resolve(Set<WidgetState> states) => _mapper.resolve(states);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is _WidgetStateColorMapper && other.map == map;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => map.hashCode;
|
||||
}
|
||||
|
||||
/// Defines a [MouseCursor] whose value depends on a set of [WidgetState]s which
|
||||
/// represent the interactive state of a component.
|
||||
///
|
||||
@ -548,8 +611,18 @@ class _WidgetBorderSideMapper extends WidgetStateBorderSide {
|
||||
|
||||
final WidgetStateMap<BorderSide?> map;
|
||||
|
||||
_WidgetStateMapper<BorderSide?> get _mapper => _WidgetStateMapper<BorderSide?>(map);
|
||||
|
||||
@override
|
||||
BorderSide? resolve(Set<WidgetState> states) => _WidgetStateMapper<BorderSide?>(map).resolve(states);
|
||||
BorderSide? resolve(Set<WidgetState> states) => _mapper.resolve(states);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is _WidgetBorderSideMapper && other.map == map;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => map.hashCode;
|
||||
}
|
||||
|
||||
/// Defines an [OutlinedBorder] whose value depends on a set of [WidgetState]s
|
||||
@ -660,8 +733,18 @@ class _WidgetTextStyleMapper extends WidgetStateTextStyle {
|
||||
|
||||
final WidgetStateMap<TextStyle> map;
|
||||
|
||||
_WidgetStateMapper<TextStyle> get _mapper => _WidgetStateMapper<TextStyle>(map);
|
||||
|
||||
@override
|
||||
TextStyle resolve(Set<WidgetState> states) => _WidgetStateMapper<TextStyle>(map).resolve(states);
|
||||
TextStyle resolve(Set<WidgetState> states) => _mapper.resolve(states);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is _WidgetTextStyleMapper && other.map == map;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => map.hashCode;
|
||||
}
|
||||
|
||||
/// Interface for classes that [resolve] to a value of type `T` based
|
||||
@ -847,6 +930,7 @@ class _WidgetStatePropertyWith<T> implements WidgetStateProperty<T> {
|
||||
typedef WidgetStateMap<T> = Map<WidgetStatesConstraint, T>;
|
||||
|
||||
// A private class, used to create the [WidgetStateProperty.fromMap] constructor.
|
||||
@immutable
|
||||
class _WidgetStateMapper<T> implements WidgetStateProperty<T> {
|
||||
const _WidgetStateMapper(this.map);
|
||||
|
||||
@ -872,6 +956,17 @@ class _WidgetStateMapper<T> implements WidgetStateProperty<T> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is _WidgetStateMapper && mapEquals(map, other.map);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => MapEquality<WidgetStatesConstraint, T>().hash(map);
|
||||
|
||||
@override
|
||||
String toString() => '$map';
|
||||
}
|
||||
|
||||
/// Convenience class for creating a [WidgetStateProperty] that
|
||||
@ -881,6 +976,7 @@ class _WidgetStateMapper<T> implements WidgetStateProperty<T> {
|
||||
///
|
||||
/// * [MaterialStatePropertyAll], the Material specific version of
|
||||
/// `WidgetStatePropertyAll`.
|
||||
@immutable
|
||||
class WidgetStatePropertyAll<T> implements WidgetStateProperty<T> {
|
||||
|
||||
/// Constructs a [WidgetStateProperty] that always resolves to the given
|
||||
@ -901,6 +997,16 @@ class WidgetStatePropertyAll<T> implements WidgetStateProperty<T> {
|
||||
return 'WidgetStatePropertyAll($value)';
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is WidgetStatePropertyAll<T>
|
||||
&& other.runtimeType == runtimeType
|
||||
&& other.value == value;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => value.hashCode;
|
||||
}
|
||||
|
||||
/// Manages a set of [WidgetState]s and notifies listeners of changes.
|
||||
|
@ -21,6 +21,44 @@ void main() {
|
||||
expect(dawn.primaryColor, Color.lerp(dark.primaryColor, light.primaryColor, 0.25));
|
||||
});
|
||||
|
||||
test('ThemeData objects with .styleFrom() members are equal', () {
|
||||
ThemeData createThemeData() {
|
||||
return ThemeData(
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ElevatedButton.styleFrom(
|
||||
foregroundColor: Colors.black,
|
||||
backgroundColor: Colors.black,
|
||||
elevation: 1.0,
|
||||
),
|
||||
),
|
||||
filledButtonTheme: FilledButtonThemeData(
|
||||
style: FilledButton.styleFrom(
|
||||
foregroundColor: Colors.black,
|
||||
disabledForegroundColor: Colors.black,
|
||||
backgroundColor: Colors.black,
|
||||
disabledBackgroundColor: Colors.black,
|
||||
overlayColor: Colors.black,
|
||||
),
|
||||
),
|
||||
iconButtonTheme: IconButtonThemeData(
|
||||
style: IconButton.styleFrom(
|
||||
hoverColor: Colors.black,
|
||||
focusColor: Colors.black,
|
||||
highlightColor: Colors.black,
|
||||
),
|
||||
),
|
||||
textButtonTheme: TextButtonThemeData(
|
||||
style: TextButton.styleFrom(
|
||||
enabledMouseCursor: MouseCursor.defer,
|
||||
disabledMouseCursor: MouseCursor.uncontrolled,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
expect(createThemeData() == createThemeData(), isTrue);
|
||||
});
|
||||
|
||||
test('Defaults to the default typography for the platform', () {
|
||||
for (final TargetPlatform platform in TargetPlatform.values) {
|
||||
final ThemeData theme = ThemeData(platform: platform, useMaterial3: false);
|
||||
|
Loading…
Reference in New Issue
Block a user