Unverified Commit 3ce88d38 authored by Qun Cheng's avatar Qun Cheng Committed by GitHub

Replace menu defaults with tokens (#113963)

parent af34b104
......@@ -31,6 +31,7 @@ import 'package:gen_defaults/filter_chip_template.dart';
import 'package:gen_defaults/icon_button_template.dart';
import 'package:gen_defaults/input_chip_template.dart';
import 'package:gen_defaults/input_decorator_template.dart';
import 'package:gen_defaults/menu_template.dart';
import 'package:gen_defaults/navigation_bar_template.dart';
import 'package:gen_defaults/navigation_rail_template.dart';
import 'package:gen_defaults/popup_menu_template.dart';
......@@ -135,6 +136,7 @@ Future<void> main(List<String> args) async {
IconButtonTemplate('IconButton', '$materialLib/icon_button.dart', tokens).updateFile();
InputChipTemplate('InputChip', '$materialLib/input_chip.dart', tokens).updateFile();
InputDecoratorTemplate('InputDecorator', '$materialLib/input_decorator.dart', tokens).updateFile();
MenuTemplate('Menu', '$materialLib/menu_anchor.dart', tokens).updateFile();
NavigationBarTemplate('NavigationBar', '$materialLib/navigation_bar.dart', tokens).updateFile();
NavigationRailTemplate('NavigationRail', '$materialLib/navigation_rail.dart', tokens).updateFile();
PopupMenuTemplate('PopupMenu', '$materialLib/popup_menu.dart', tokens).updateFile();
......
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'template.dart';
class MenuTemplate extends TokenTemplate {
const MenuTemplate(super.blockName, super.fileName, super.tokens, {
super.colorSchemePrefix = '_colors.',
});
@override
String generate() => '''
class _MenuBarDefaultsM3 extends MenuStyle {
_MenuBarDefaultsM3(this.context)
: super(
elevation: const MaterialStatePropertyAll<double?>(${elevation('md.comp.menu.container')}),
shape: const MaterialStatePropertyAll<OutlinedBorder>(_defaultMenuBorder),
alignment: AlignmentDirectional.bottomStart,
);
static const RoundedRectangleBorder _defaultMenuBorder =
${shape('md.comp.menu.container', '')};
final BuildContext context;
late final ColorScheme _colors = Theme.of(context).colorScheme;
@override
MaterialStateProperty<Color?> get backgroundColor {
return MaterialStatePropertyAll<Color?>(${componentColor('md.comp.menu.container')});
}
@override
MaterialStateProperty<Color?>? get shadowColor {
return MaterialStatePropertyAll<Color?>(${color('md.comp.menu.container.shadow-color')});
}
@override
MaterialStateProperty<Color?>? get surfaceTintColor {
return MaterialStatePropertyAll<Color?>(${componentColor('md.comp.menu.container.surface-tint-layer')});
}
@override
MaterialStateProperty<EdgeInsetsGeometry?>? get padding {
return MaterialStatePropertyAll<EdgeInsetsGeometry>(
EdgeInsetsDirectional.symmetric(
horizontal: math.max(
_kTopLevelMenuHorizontalMinPadding,
2 + Theme.of(context).visualDensity.baseSizeAdjustment.dx,
),
),
);
}
}
class _MenuButtonDefaultsM3 extends ButtonStyle {
_MenuButtonDefaultsM3(this.context)
: super(
animationDuration: kThemeChangeDuration,
enableFeedback: true,
alignment: AlignmentDirectional.centerStart,
);
final BuildContext context;
late final ColorScheme _colors = Theme.of(context).colorScheme;
@override
MaterialStateProperty<Color?>? get backgroundColor {
return ButtonStyleButton.allOrNull<Color>(Colors.transparent);
}
// No default shadow color
// No default surface tint color
@override
MaterialStateProperty<double>? get elevation {
return ButtonStyleButton.allOrNull<double>(0.0);
}
@override
MaterialStateProperty<Color?>? get foregroundColor {
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
return ${componentColor('md.comp.menu.list-item.disabled.label-text')};
}
if (states.contains(MaterialState.pressed)) {
return ${componentColor('md.comp.menu.list-item.pressed.label-text')};
}
if (states.contains(MaterialState.hovered)) {
return ${componentColor('md.comp.menu.list-item.hover.label-text')};
}
if (states.contains(MaterialState.focused)) {
return ${componentColor('md.comp.menu.list-item.focus.label-text')};
}
return ${componentColor('md.comp.menu.list-item.label-text')};
});
}
@override
MaterialStateProperty<Color?>? get iconColor {
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
return ${componentColor('md.comp.menu.list-item.with-leading-icon.disabled.leading-icon')};
}
if (states.contains(MaterialState.pressed)) {
return ${componentColor('md.comp.menu.list-item.with-leading-icon.pressed.icon')};
}
if (states.contains(MaterialState.hovered)) {
return ${componentColor('md.comp.menu.list-item.with-leading-icon.hover.icon')};
}
if (states.contains(MaterialState.focused)) {
return ${componentColor('md.comp.menu.list-item.with-leading-icon.focus.icon')};
}
return ${componentColor('md.comp.menu.list-item.with-leading-icon.leading-icon')};
});
}
// No default fixedSize
@override
MaterialStateProperty<Size>? get maximumSize {
return ButtonStyleButton.allOrNull<Size>(Size.infinite);
}
@override
MaterialStateProperty<Size>? get minimumSize {
return ButtonStyleButton.allOrNull<Size>(const Size(64.0, ${tokens['md.comp.menu.list-item.container.height']}));
}
@override
MaterialStateProperty<MouseCursor?>? get mouseCursor {
return MaterialStateProperty.resolveWith(
(Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
return SystemMouseCursors.basic;
}
return SystemMouseCursors.click;
},
);
}
@override
MaterialStateProperty<Color?>? get overlayColor {
return MaterialStateProperty.resolveWith(
(Set<MaterialState> states) {
if (states.contains(MaterialState.pressed)) {
return ${componentColor('md.comp.menu.list-item.pressed.state-layer')};
}
if (states.contains(MaterialState.hovered)) {
return ${componentColor('md.comp.menu.list-item.hover.state-layer')};
}
if (states.contains(MaterialState.focused)) {
return ${componentColor('md.comp.menu.list-item.focus.state-layer')};
}
return Colors.transparent;
},
);
}
@override
MaterialStateProperty<EdgeInsetsGeometry>? get padding {
return ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(_scaledPadding(context));
}
// No default side
@override
MaterialStateProperty<OutlinedBorder>? get shape {
return ButtonStyleButton.allOrNull<OutlinedBorder>(const RoundedRectangleBorder());
}
@override
InteractiveInkFeatureFactory? get splashFactory => Theme.of(context).splashFactory;
@override
MaterialTapTargetSize? get tapTargetSize => Theme.of(context).materialTapTargetSize;
@override
MaterialStateProperty<TextStyle?> get textStyle {
return MaterialStatePropertyAll<TextStyle?>(${textStyle('md.comp.menu.list-item.label-text')});
}
@override
VisualDensity? get visualDensity => Theme.of(context).visualDensity;
// The horizontal padding number comes from the spec.
EdgeInsetsGeometry _scaledPadding(BuildContext context) {
return ButtonStyleButton.scaledPadding(
const EdgeInsets.symmetric(horizontal: 12),
const EdgeInsets.symmetric(horizontal: 8),
const EdgeInsets.symmetric(horizontal: 4),
MediaQuery.maybeOf(context)?.textScaleFactor ?? 1,
);
}
}
class _MenuDefaultsM3 extends MenuStyle {
_MenuDefaultsM3(this.context)
: super(
elevation: const MaterialStatePropertyAll<double?>(${elevation('md.comp.menu.container')}),
shape: const MaterialStatePropertyAll<OutlinedBorder>(_defaultMenuBorder),
alignment: AlignmentDirectional.topEnd,
);
static const RoundedRectangleBorder _defaultMenuBorder =
${shape('md.comp.menu.container', '')};
final BuildContext context;
late final ColorScheme _colors = Theme.of(context).colorScheme;
@override
MaterialStateProperty<Color?> get backgroundColor {
return MaterialStatePropertyAll<Color?>(${componentColor('md.comp.menu.container')});
}
@override
MaterialStateProperty<Color?>? get surfaceTintColor {
return MaterialStatePropertyAll<Color?>(${componentColor('md.comp.menu.container.surface-tint-layer')});
}
@override
MaterialStateProperty<Color?>? get shadowColor {
return MaterialStatePropertyAll<Color?>(${color('md.comp.menu.container.shadow-color')});
}
@override
MaterialStateProperty<EdgeInsetsGeometry?>? get padding {
return MaterialStatePropertyAll<EdgeInsetsGeometry>(
EdgeInsetsDirectional.symmetric(
vertical: math.max(
_kMenuVerticalMinPadding,
2 + Theme.of(context).visualDensity.baseSizeAdjustment.dy,
),
),
);
}
}
''';
}
......@@ -21,14 +21,14 @@ void main() {
await tester.sendKeyDownEvent(LogicalKeyboardKey.controlRight);
await tester.tapAt(const Offset(100, 200));
await tester.pump();
expect(tester.getRect(findMenu()), equals(const Rect.fromLTRB(100.0, 200.0, 404.0, 352.0)));
expect(tester.getRect(findMenu()), equals(const Rect.fromLTRB(100.0, 200.0, 388.0, 360.0)));
// Make sure tapping in a different place causes the menu to move.
await tester.tapAt(const Offset(200, 100));
await tester.pump();
await tester.sendKeyUpEvent(LogicalKeyboardKey.controlRight);
expect(tester.getRect(findMenu()), equals(const Rect.fromLTRB(200.0, 100.0, 504.0, 252.0)));
expect(tester.getRect(findMenu()), equals(const Rect.fromLTRB(200.0, 100.0, 488.0, 260.0)));
expect(find.text(example.MenuEntry.about.label), findsOneWidget);
expect(find.text(example.MenuEntry.showMessage.label), findsOneWidget);
......
......@@ -151,6 +151,7 @@ class ButtonStyle with Diagnosticable {
this.minimumSize,
this.fixedSize,
this.maximumSize,
this.iconColor,
this.iconSize,
this.side,
this.shape,
......@@ -230,6 +231,11 @@ class ButtonStyle with Diagnosticable {
/// This value must be greater than or equal to [minimumSize].
final MaterialStateProperty<Size?>? maximumSize;
/// The icon's color inside of the button.
///
/// If this is null, the icon color will be [foregroundColor].
final MaterialStateProperty<Color?>? iconColor;
/// The icon's size inside of the button.
final MaterialStateProperty<double?>? iconSize;
......@@ -323,6 +329,7 @@ class ButtonStyle with Diagnosticable {
MaterialStateProperty<Size?>? minimumSize,
MaterialStateProperty<Size?>? fixedSize,
MaterialStateProperty<Size?>? maximumSize,
MaterialStateProperty<Color?>? iconColor,
MaterialStateProperty<double?>? iconSize,
MaterialStateProperty<BorderSide?>? side,
MaterialStateProperty<OutlinedBorder?>? shape,
......@@ -346,6 +353,7 @@ class ButtonStyle with Diagnosticable {
minimumSize: minimumSize ?? this.minimumSize,
fixedSize: fixedSize ?? this.fixedSize,
maximumSize: maximumSize ?? this.maximumSize,
iconColor: iconColor ?? this.iconColor,
iconSize: iconSize ?? this.iconSize,
side: side ?? this.side,
shape: shape ?? this.shape,
......@@ -380,6 +388,7 @@ class ButtonStyle with Diagnosticable {
minimumSize: minimumSize ?? style.minimumSize,
fixedSize: fixedSize ?? style.fixedSize,
maximumSize: maximumSize ?? style.maximumSize,
iconColor: iconColor ?? style.iconColor,
iconSize: iconSize ?? style.iconSize,
side: side ?? style.side,
shape: shape ?? style.shape,
......@@ -407,6 +416,7 @@ class ButtonStyle with Diagnosticable {
minimumSize,
fixedSize,
maximumSize,
iconColor,
iconSize,
side,
shape,
......@@ -441,6 +451,7 @@ class ButtonStyle with Diagnosticable {
&& other.minimumSize == minimumSize
&& other.fixedSize == fixedSize
&& other.maximumSize == maximumSize
&& other.iconColor == iconColor
&& other.iconSize == iconSize
&& other.side == side
&& other.shape == shape
......@@ -467,6 +478,7 @@ class ButtonStyle with Diagnosticable {
properties.add(DiagnosticsProperty<MaterialStateProperty<Size?>>('minimumSize', minimumSize, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<Size?>>('fixedSize', fixedSize, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<Size?>>('maximumSize', maximumSize, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('iconColor', iconColor, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<double?>>('iconSize', iconSize, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<BorderSide?>>('side', side, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<OutlinedBorder?>>('shape', shape, defaultValue: null));
......@@ -496,6 +508,7 @@ class ButtonStyle with Diagnosticable {
minimumSize: MaterialStateProperty.lerp<Size?>(a?.minimumSize, b?.minimumSize, t, Size.lerp),
fixedSize: MaterialStateProperty.lerp<Size?>(a?.fixedSize, b?.fixedSize, t, Size.lerp),
maximumSize: MaterialStateProperty.lerp<Size?>(a?.maximumSize, b?.maximumSize, t, Size.lerp),
iconColor: MaterialStateProperty.lerp<Color?>(a?.iconColor, b?.iconColor, t, Color.lerp),
iconSize: MaterialStateProperty.lerp<double?>(a?.iconSize, b?.iconSize, t, lerpDouble),
side: _lerpSides(a?.side, b?.side, t),
shape: MaterialStateProperty.lerp<OutlinedBorder?>(a?.shape, b?.shape, t, OutlinedBorder.lerp),
......
......@@ -287,6 +287,7 @@ class _ButtonStyleState extends State<ButtonStyleButton> with TickerProviderStat
final Size? resolvedMinimumSize = resolve<Size?>((ButtonStyle? style) => style?.minimumSize);
final Size? resolvedFixedSize = resolve<Size?>((ButtonStyle? style) => style?.fixedSize);
final Size? resolvedMaximumSize = resolve<Size?>((ButtonStyle? style) => style?.maximumSize);
final Color? resolvedIconColor = resolve<Color?>((ButtonStyle? style) => style?.iconColor);
final double? resolvedIconSize = resolve<double?>((ButtonStyle? style) => style?.iconSize);
final BorderSide? resolvedSide = resolve<BorderSide?>((ButtonStyle? style) => style?.side);
final OutlinedBorder? resolvedShape = resolve<OutlinedBorder?>((ButtonStyle? style) => style?.shape);
......@@ -400,7 +401,7 @@ class _ButtonStyleState extends State<ButtonStyleButton> with TickerProviderStat
customBorder: resolvedShape.copyWith(side: resolvedSide),
statesController: statesController,
child: IconTheme.merge(
data: IconThemeData(color: resolvedForegroundColor, size: resolvedIconSize),
data: IconThemeData(color: resolvedIconColor ?? resolvedForegroundColor, size: resolvedIconSize),
child: Padding(
padding: padding,
child: Align(
......
......@@ -46,7 +46,7 @@ const double _kDefaultSubmenuIconSize = 24;
// The default spacing between the the leading icon, label, trailing icon, and
// shortcut label in a _MenuItemLabel.
const double _kLabelItemDefaultSpacing = 18;
const double _kLabelItemDefaultSpacing = 12;
// The minimum spacing between the the leading icon, label, trailing icon, and
// shortcut label in a _MenuItemLabel.
......@@ -66,7 +66,7 @@ const Map<ShortcutActivator, Intent> _kMenuTraversalShortcuts = <ShortcutActivat
};
// The minimum vertical spacing on the outside of menus.
const double _kMenuVerticalMinPadding = 4;
const double _kMenuVerticalMinPadding = 8;
// How close to the edge of the safe area the menu will be placed.
const double _kMenuViewPadding = 8;
......@@ -924,6 +924,7 @@ class MenuItemButton extends StatefulWidget {
Color? disabledBackgroundColor,
Color? shadowColor,
Color? surfaceTintColor,
Color? iconColor,
TextStyle? textStyle,
double? elevation,
EdgeInsetsGeometry? padding,
......@@ -948,6 +949,7 @@ class MenuItemButton extends StatefulWidget {
disabledForegroundColor: disabledForegroundColor,
shadowColor: shadowColor,
surfaceTintColor: surfaceTintColor,
iconColor: iconColor,
textStyle: textStyle,
elevation: elevation,
padding: padding,
......@@ -1021,10 +1023,11 @@ class _MenuItemButtonState extends State<MenuItemButton> {
// Since we don't want to use the theme style or default style from the
// TextButton, we merge the styles, merging them in the right order when
// each type of style exists. Each "*StyleOf" function is only called once.
final ButtonStyle mergedStyle =
widget.style?.merge(widget.themeStyleOf(context)?.merge(widget.defaultStyleOf(context))) ??
widget.themeStyleOf(context)?.merge(widget.defaultStyleOf(context)) ??
widget.defaultStyleOf(context);
ButtonStyle mergedStyle = widget.themeStyleOf(context)?.merge(widget.defaultStyleOf(context))
?? widget.defaultStyleOf(context);
if (widget.style != null) {
mergedStyle = widget.style!.merge(mergedStyle);
}
return TextButton(
onPressed: widget.enabled ? _handleSelect : null,
......@@ -1646,6 +1649,7 @@ class SubmenuButton extends StatefulWidget {
Color? disabledBackgroundColor,
Color? shadowColor,
Color? surfaceTintColor,
Color? iconColor,
TextStyle? textStyle,
double? elevation,
EdgeInsetsGeometry? padding,
......@@ -1670,6 +1674,7 @@ class SubmenuButton extends StatefulWidget {
disabledForegroundColor: disabledForegroundColor,
shadowColor: shadowColor,
surfaceTintColor: surfaceTintColor,
iconColor: iconColor,
textStyle: textStyle,
elevation: elevation,
padding: padding,
......@@ -1799,10 +1804,11 @@ class _SubmenuButtonState extends State<SubmenuButton> {
// TextButton, we merge the styles, merging them in the right order when
// each type of style exists. Each "*StyleOf" function is only called
// once.
final ButtonStyle mergedStyle =
widget.style?.merge(widget.themeStyleOf(context)?.merge(widget.defaultStyleOf(context))) ??
widget.themeStyleOf(context)?.merge(widget.defaultStyleOf(context)) ??
widget.defaultStyleOf(context);
ButtonStyle mergedStyle = widget.themeStyleOf(context)?.merge(widget.defaultStyleOf(context))
?? widget.defaultStyleOf(context);
if (widget.style != null) {
mergedStyle = widget.style!.merge(mergedStyle);
}
void toggleShowMenu(BuildContext context) {
if (controller.isOpen) {
......@@ -2519,14 +2525,13 @@ class _MenuItemLabel extends StatelessWidget {
padding: leadingIcon != null ? EdgeInsetsDirectional.only(start: horizontalPadding) : EdgeInsets.zero,
child: child,
),
if (trailingIcon != null)
Padding(
padding: EdgeInsetsDirectional.only(start: horizontalPadding),
child: trailingIcon,
),
],
),
if (showDecoration && (shortcut != null || hasSubmenu)) SizedBox(width: horizontalPadding),
if (trailingIcon != null)
Padding(
padding: EdgeInsetsDirectional.only(start: horizontalPadding),
child: trailingIcon,
),
if (showDecoration && shortcut != null)
Padding(
padding: EdgeInsetsDirectional.only(start: horizontalPadding),
......@@ -3056,17 +3061,24 @@ bool _debugMenuInfo(String message, [Iterable<String>? details]) {
return true;
}
// This class will eventually be auto-generated, so it should remain at the end
// of the file.
// BEGIN GENERATED TOKEN PROPERTIES - Menu
// Do not edit by hand. The code between the "BEGIN GENERATED" and
// "END GENERATED" comments are generated from data in the Material
// Design token database by the script:
// dev/tools/gen_defaults/bin/gen_defaults.dart.
// Token database version: v0_132
class _MenuBarDefaultsM3 extends MenuStyle {
_MenuBarDefaultsM3(this.context)
: super(
elevation: const MaterialStatePropertyAll<double?>(4),
shape: const MaterialStatePropertyAll<OutlinedBorder>(_defaultMenuBorder),
alignment: AlignmentDirectional.bottomStart,
);
: super(
elevation: const MaterialStatePropertyAll<double?>(3.0),
shape: const MaterialStatePropertyAll<OutlinedBorder>(_defaultMenuBorder),
alignment: AlignmentDirectional.bottomStart,
);
static const RoundedRectangleBorder _defaultMenuBorder =
RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.elliptical(2, 3)));
RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0)));
final BuildContext context;
......@@ -3077,6 +3089,16 @@ class _MenuBarDefaultsM3 extends MenuStyle {
return MaterialStatePropertyAll<Color?>(_colors.surface);
}
@override
MaterialStateProperty<Color?>? get shadowColor {
return MaterialStatePropertyAll<Color?>(_colors.shadow);
}
@override
MaterialStateProperty<Color?>? get surfaceTintColor {
return MaterialStatePropertyAll<Color?>(_colors.surfaceTint);
}
@override
MaterialStateProperty<EdgeInsetsGeometry?>? get padding {
return MaterialStatePropertyAll<EdgeInsetsGeometry>(
......@@ -3090,15 +3112,13 @@ class _MenuBarDefaultsM3 extends MenuStyle {
}
}
// This class will eventually be auto-generated, so it should remain at the end
// of the file.
class _MenuButtonDefaultsM3 extends ButtonStyle {
_MenuButtonDefaultsM3(this.context)
: super(
animationDuration: kThemeChangeDuration,
enableFeedback: true,
alignment: AlignmentDirectional.centerStart,
);
: super(
animationDuration: kThemeChangeDuration,
enableFeedback: true,
alignment: AlignmentDirectional.centerStart,
);
final BuildContext context;
late final ColorScheme _colors = Theme.of(context).colorScheme;
......@@ -3114,19 +3134,45 @@ class _MenuButtonDefaultsM3 extends ButtonStyle {
@override
MaterialStateProperty<double>? get elevation {
return ButtonStyleButton.allOrNull<double>(0);
return ButtonStyleButton.allOrNull<double>(0.0);
}
@override
MaterialStateProperty<Color?>? get foregroundColor {
return MaterialStateProperty.resolveWith(
(Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
return _colors.onSurface.withOpacity(0.38);
}
return _colors.primary;
},
);
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
return _colors.onSurface.withOpacity(0.38);
}
if (states.contains(MaterialState.pressed)) {
return _colors.onSurface;
}
if (states.contains(MaterialState.hovered)) {
return _colors.onSurface;
}
if (states.contains(MaterialState.focused)) {
return _colors.onSurface;
}
return _colors.onSurface;
});
}
@override
MaterialStateProperty<Color?>? get iconColor {
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
return _colors.onSurface.withOpacity(0.38);
}
if (states.contains(MaterialState.pressed)) {
return _colors.onSurfaceVariant;
}
if (states.contains(MaterialState.hovered)) {
return _colors.onSurfaceVariant;
}
if (states.contains(MaterialState.focused)) {
return _colors.onSurfaceVariant;
}
return _colors.onSurfaceVariant;
});
}
// No default fixedSize
......@@ -3138,7 +3184,7 @@ class _MenuButtonDefaultsM3 extends ButtonStyle {
@override
MaterialStateProperty<Size>? get minimumSize {
return ButtonStyleButton.allOrNull<Size>(const Size(64, 40));
return ButtonStyleButton.allOrNull<Size>(const Size(64.0, 48.0));
}
@override
......@@ -3157,16 +3203,16 @@ class _MenuButtonDefaultsM3 extends ButtonStyle {
MaterialStateProperty<Color?>? get overlayColor {
return MaterialStateProperty.resolveWith(
(Set<MaterialState> states) {
if (states.contains(MaterialState.pressed)) {
return _colors.onSurface.withOpacity(0.12);
}
if (states.contains(MaterialState.hovered)) {
return _colors.primary.withOpacity(0.08);
return _colors.onSurface.withOpacity(0.08);
}
if (states.contains(MaterialState.focused)) {
return _colors.primary.withOpacity(0.12);
}
if (states.contains(MaterialState.pressed)) {
return _colors.primary.withOpacity(0.12);
return _colors.onSurface.withOpacity(0.12);
}
return null;
return Colors.transparent;
},
);
}
......@@ -3197,9 +3243,10 @@ class _MenuButtonDefaultsM3 extends ButtonStyle {
@override
VisualDensity? get visualDensity => Theme.of(context).visualDensity;
// The horizontal padding number comes from the spec.
EdgeInsetsGeometry _scaledPadding(BuildContext context) {
return ButtonStyleButton.scaledPadding(
const EdgeInsets.all(8),
const EdgeInsets.symmetric(horizontal: 12),
const EdgeInsets.symmetric(horizontal: 8),
const EdgeInsets.symmetric(horizontal: 4),
MediaQuery.maybeOf(context)?.textScaleFactor ?? 1,
......@@ -3207,17 +3254,15 @@ class _MenuButtonDefaultsM3 extends ButtonStyle {
}
}
// This class will eventually be auto-generated, so it should remain at the end
// of the file.
class _MenuDefaultsM3 extends MenuStyle {
_MenuDefaultsM3(this.context)
: super(
elevation: const MaterialStatePropertyAll<double?>(4),
shape: const MaterialStatePropertyAll<OutlinedBorder>(_defaultMenuBorder),
alignment: AlignmentDirectional.topEnd,
);
: super(
elevation: const MaterialStatePropertyAll<double?>(3.0),
shape: const MaterialStatePropertyAll<OutlinedBorder>(_defaultMenuBorder),
alignment: AlignmentDirectional.topEnd,
);
static const RoundedRectangleBorder _defaultMenuBorder =
RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.elliptical(2, 3)));
RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0)));
final BuildContext context;
......@@ -3228,6 +3273,16 @@ class _MenuDefaultsM3 extends MenuStyle {
return MaterialStatePropertyAll<Color?>(_colors.surface);
}
@override
MaterialStateProperty<Color?>? get surfaceTintColor {
return MaterialStatePropertyAll<Color?>(_colors.surfaceTint);
}
@override
MaterialStateProperty<Color?>? get shadowColor {
return MaterialStatePropertyAll<Color?>(_colors.shadow);
}
@override
MaterialStateProperty<EdgeInsetsGeometry?>? get padding {
return MaterialStatePropertyAll<EdgeInsetsGeometry>(
......@@ -3240,3 +3295,5 @@ class _MenuDefaultsM3 extends MenuStyle {
);
}
}
// END GENERATED TOKEN PROPERTIES - Menu
......@@ -153,6 +153,8 @@ class TextButton extends ButtonStyleButton {
Color? disabledBackgroundColor,
Color? shadowColor,
Color? surfaceTintColor,
Color? iconColor,
Color? disabledIconColor,
double? elevation,
TextStyle? textStyle,
EdgeInsetsGeometry? padding,
......@@ -193,6 +195,11 @@ class TextButton extends ButtonStyleButton {
final MaterialStateProperty<Color?>? overlayColor = (foreground == null)
? null
: _TextButtonDefaultOverlay(foreground);
final MaterialStateProperty<Color?>? iconColorProp = (iconColor == null && disabledIconColor == null)
? null
: disabledIconColor == null
? ButtonStyleButton.allOrNull<Color?>(iconColor)
: _TextButtonDefaultIconColor(iconColor, disabledIconColor);
final MaterialStateProperty<MouseCursor>? mouseCursor = (enabledMouseCursor == null && disabledMouseCursor == null)
? null
: _TextButtonDefaultMouseCursor(enabledMouseCursor!, disabledMouseCursor!);
......@@ -204,6 +211,7 @@ class TextButton extends ButtonStyleButton {
overlayColor: overlayColor,
shadowColor: ButtonStyleButton.allOrNull<Color>(shadowColor),
surfaceTintColor: ButtonStyleButton.allOrNull<Color>(surfaceTintColor),
iconColor: iconColorProp,
elevation: ButtonStyleButton.allOrNull<double>(elevation),
padding: ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(padding),
minimumSize: ButtonStyleButton.allOrNull<Size>(minimumSize),
......@@ -422,6 +430,27 @@ class _TextButtonDefaultOverlay extends MaterialStateProperty<Color?> {
}
}
@immutable
class _TextButtonDefaultIconColor extends MaterialStateProperty<Color?> {
_TextButtonDefaultIconColor(this.iconColor, this.disabledIconColor);
final Color? iconColor;
final Color? disabledIconColor;
@override
Color? resolve(Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
return disabledIconColor;
}
return iconColor;
}
@override
String toString() {
return '{disabled: $disabledIconColor, color: $iconColor}';
}
}
@immutable
class _TextButtonDefaultMouseCursor extends MaterialStateProperty<MouseCursor> with Diagnosticable {
_TextButtonDefaultMouseCursor(this.enabledCursor, this.disabledCursor);
......
......@@ -27,6 +27,7 @@ void main() {
expect(style.minimumSize, null);
expect(style.fixedSize, null);
expect(style.maximumSize, null);
expect(style.iconColor, null);
expect(style.iconSize, null);
expect(style.side, null);
expect(style.shape, null);
......@@ -63,6 +64,7 @@ void main() {
minimumSize: MaterialStatePropertyAll<Size>(Size(1.0, 2.0)),
side: MaterialStatePropertyAll<BorderSide>(BorderSide(width: 4.0, color: Color(0xfffffff6))),
maximumSize: MaterialStatePropertyAll<Size>(Size(100.0, 200.0)),
iconColor: MaterialStatePropertyAll<Color>(Color(0xfffffff6)),
iconSize: MaterialStatePropertyAll<double>(48.1),
shape: MaterialStatePropertyAll<OutlinedBorder>(StadiumBorder()),
mouseCursor: MaterialStatePropertyAll<MouseCursor>(SystemMouseCursors.forbidden),
......@@ -87,6 +89,7 @@ void main() {
'padding: MaterialStatePropertyAll(EdgeInsets.all(1.0))',
'minimumSize: MaterialStatePropertyAll(Size(1.0, 2.0))',
'maximumSize: MaterialStatePropertyAll(Size(100.0, 200.0))',
'iconColor: MaterialStatePropertyAll(Color(0xfffffff6))',
'iconSize: MaterialStatePropertyAll(48.1)',
'side: MaterialStatePropertyAll(BorderSide(color: Color(0xfffffff6), width: 4.0))',
'shape: MaterialStatePropertyAll(StadiumBorder(BorderSide(width: 0.0, style: none)))',
......@@ -109,6 +112,7 @@ void main() {
const MaterialStateProperty<Size> minimumSize = MaterialStatePropertyAll<Size>(Size(1, 2));
const MaterialStateProperty<Size> fixedSize = MaterialStatePropertyAll<Size>(Size(3, 4));
const MaterialStateProperty<Size> maximumSize = MaterialStatePropertyAll<Size>(Size(5, 6));
const MaterialStateProperty<Color> iconColor = MaterialStatePropertyAll<Color>(Color(0xfffffff6));
const MaterialStateProperty<double> iconSize = MaterialStatePropertyAll<double>(48.0);
const MaterialStateProperty<BorderSide> side = MaterialStatePropertyAll<BorderSide>(BorderSide());
const MaterialStateProperty<OutlinedBorder> shape = MaterialStatePropertyAll<OutlinedBorder>(StadiumBorder());
......@@ -130,6 +134,7 @@ void main() {
minimumSize: minimumSize,
fixedSize: fixedSize,
maximumSize: maximumSize,
iconColor: iconColor,
iconSize: iconSize,
side: side,
shape: shape,
......@@ -154,6 +159,7 @@ void main() {
minimumSize: minimumSize,
fixedSize: fixedSize,
maximumSize: maximumSize,
iconColor: iconColor,
iconSize: iconSize,
side: side,
shape: shape,
......
......@@ -145,6 +145,159 @@ void main() {
);
}
testWidgets('menu defaults colors', (WidgetTester tester) async {
final ThemeData themeData = ThemeData();
await tester.pumpWidget(
MaterialApp(
theme: themeData,
home: Material(
child: MenuBar(
controller: controller,
children: createTestMenus(
onPressed: onPressed,
onOpen: onOpen,
onClose: onClose,
),
),
),
),
);
// menu bar(horizontal menu)
Finder menuMaterial = find.ancestor(
of: find.byType(TextButton),
matching: find.byType(Material),
).first;
Material material = tester.widget<Material>(menuMaterial);
expect(opened, isEmpty);
expect(material.color, themeData.colorScheme.surface);
expect(material.shadowColor, themeData.colorScheme.shadow);
expect(material.surfaceTintColor, themeData.colorScheme.surfaceTint);
expect(material.elevation, 3.0);
expect(material.shape, const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0))));
Finder buttonMaterial = find.descendant(
of: find.byType(TextButton),
matching: find.byType(Material),
).first;
material = tester.widget<Material>(buttonMaterial);
expect(material.color, Colors.transparent);
expect(material.elevation, 0.0);
expect(material.shape, const RoundedRectangleBorder());
expect(material.textStyle?.color, themeData.colorScheme.onSurface);
// vertical menu
await tester.tap(find.text(TestMenu.mainMenu1.label));
await tester.pump();
menuMaterial = find.ancestor(
of: find.widgetWithText(TextButton, TestMenu.subMenu10.label),
matching: find.byType(Material),
).first;
material = tester.widget<Material>(menuMaterial);
expect(opened.last, equals(TestMenu.mainMenu1));
expect(material.color, themeData.colorScheme.surface);
expect(material.shadowColor, themeData.colorScheme.shadow);
expect(material.surfaceTintColor, themeData.colorScheme.surfaceTint);
expect(material.elevation, 3.0);
expect(material.shape, const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0))));
buttonMaterial = find.descendant(
of: find.widgetWithText(TextButton, TestMenu.subMenu10.label),
matching: find.byType(Material),
).first;
material = tester.widget<Material>(buttonMaterial);
expect(material.color, Colors.transparent);
expect(material.elevation, 0.0);
expect(material.shape, const RoundedRectangleBorder());
expect(material.textStyle?.color, themeData.colorScheme.onSurface);
await tester.tap(find.text(TestMenu.mainMenu0.label));
await tester.pump();
expect(find.byIcon(Icons.add), findsOneWidget);
final RichText iconRichText = tester.widget<RichText>(
find.descendant(of: find.byIcon(Icons.add), matching: find.byType(RichText)),
);
expect(iconRichText.text.style?.color, themeData.colorScheme.onSurfaceVariant);
});
testWidgets('menu defaults - disabled', (WidgetTester tester) async {
final ThemeData themeData = ThemeData();
await tester.pumpWidget(
MaterialApp(
theme: themeData,
home: Material(
child: MenuBar(
controller: controller,
children: createTestMenus(
onPressed: onPressed,
onOpen: onOpen,
onClose: onClose,
),
),
),
),
);
// menu bar(horizontal menu)
Finder menuMaterial = find.ancestor(
of: find.widgetWithText(TextButton, TestMenu.mainMenu5.label),
matching: find.byType(Material),
).first;
Material material = tester.widget<Material>(menuMaterial);
expect(opened, isEmpty);
expect(material.color, themeData.colorScheme.surface);
expect(material.shadowColor, themeData.colorScheme.shadow);
expect(material.surfaceTintColor, themeData.colorScheme.surfaceTint);
expect(material.elevation, 3.0);
expect(material.shape, const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0))));
Finder buttonMaterial = find.descendant(
of: find.widgetWithText(TextButton, TestMenu.mainMenu5.label),
matching: find.byType(Material),
).first;
material = tester.widget<Material>(buttonMaterial);
expect(material.color, Colors.transparent);
expect(material.elevation, 0.0);
expect(material.shape, const RoundedRectangleBorder());
expect(material.textStyle?.color, themeData.colorScheme.onSurface.withOpacity(0.38));
// vertical menu
await tester.tap(find.text(TestMenu.mainMenu2.label));
await tester.pump();
menuMaterial = find.ancestor(
of: find.widgetWithText(TextButton, TestMenu.subMenu20.label),
matching: find.byType(Material),
).first;
material = tester.widget<Material>(menuMaterial);
expect(material.color, themeData.colorScheme.surface);
expect(material.shadowColor, themeData.colorScheme.shadow);
expect(material.surfaceTintColor, themeData.colorScheme.surfaceTint);
expect(material.elevation, 3.0);
expect(material.shape, const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0))));
buttonMaterial = find.descendant(
of: find.widgetWithText(TextButton, TestMenu.subMenu20.label),
matching: find.byType(Material),
).first;
material = tester.widget<Material>(buttonMaterial);
expect(material.color, Colors.transparent);
expect(material.elevation, 0.0);
expect(material.shape, const RoundedRectangleBorder());
expect(material.textStyle?.color, themeData.colorScheme.onSurface.withOpacity(0.38));
expect(find.byIcon(Icons.ac_unit), findsOneWidget);
final RichText iconRichText = tester.widget<RichText>(
find.descendant(of: find.byIcon(Icons.ac_unit), matching: find.byType(RichText)),
);
expect(iconRichText.text.style?.color, themeData.colorScheme.onSurface.withOpacity(0.38));
});
group('Menu functions', () {
testWidgets('basic menu structure', (WidgetTester tester) async {
await tester.pumpWidget(
......@@ -231,13 +384,13 @@ void main() {
expect(tester.getRect(find.byType(MenuBar)), equals(const Rect.fromLTRB(0, 0, 800, 48)));
expect(
tester.getRect(find.text(TestMenu.subMenu10.label)),
equals(const Rect.fromLTRB(112.0, 69.0, 266.0, 83.0)),
equals(const Rect.fromLTRB(124.0, 73.0, 278.0, 87.0)),
);
expect(
tester.getRect(
find.ancestor(of: find.text(TestMenu.subMenu10.label), matching: find.byType(Material)).at(1),
),
equals(const Rect.fromLTRB(104.0, 48.0, 334.0, 200.0)),
equals(const Rect.fromLTRB(112.0, 48.0, 326.0, 208.0)),
);
});
......@@ -276,13 +429,13 @@ void main() {
expect(tester.getRect(find.byType(MenuBar)), equals(const Rect.fromLTRB(0, 0, 800, 48)));
expect(
tester.getRect(find.text(TestMenu.subMenu10.label)),
equals(const Rect.fromLTRB(534.0, 69.0, 688.0, 83.0)),
equals(const Rect.fromLTRB(522.0, 73.0, 676.0, 87.0)),
);
expect(
tester.getRect(
find.ancestor(of: find.text(TestMenu.subMenu10.label), matching: find.byType(Material)).at(1),
),
equals(const Rect.fromLTRB(466.0, 48.0, 696.0, 200.0)),
equals(const Rect.fromLTRB(474.0, 48.0, 688.0, 208.0)),
);
// Close and make sure it goes back where it was.
......@@ -310,7 +463,7 @@ void main() {
expect(
tester.getRect(find.byType(MenuBar)),
equals(const Rect.fromLTRB(246.0, 0.0, 554.0, 48.0)),
equals(const Rect.fromLTRB(180.0, 0.0, 620.0, 48.0)),
);
});
......@@ -325,19 +478,19 @@ void main() {
// Open the menu and make sure things are the right size, in the right place.
await tester.tap(find.text('Press Me'));
await tester.pump();
expect(tester.getRect(findMenuScope), equals(const Rect.fromLTRB(328.0, 324.0, 618.0, 428.0)));
expect(tester.getRect(findMenuScope), equals(const Rect.fromLTRB(328.0, 324.0, 602.0, 436.0)));
await tester.pumpWidget(buildTestApp(alignment: AlignmentDirectional.topStart));
await tester.pump();
expect(tester.getRect(findMenuScope), equals(const Rect.fromLTRB(328.0, 276.0, 618.0, 380.0)));
expect(tester.getRect(findMenuScope), equals(const Rect.fromLTRB(328.0, 276.0, 602.0, 388.0)));
await tester.pumpWidget(buildTestApp(alignment: AlignmentDirectional.center));
await tester.pump();
expect(tester.getRect(findMenuScope), equals(const Rect.fromLTRB(400.0, 300.0, 690.0, 404.0)));
expect(tester.getRect(findMenuScope), equals(const Rect.fromLTRB(400.0, 300.0, 674.0, 412.0)));
await tester.pumpWidget(buildTestApp(alignment: AlignmentDirectional.bottomEnd));
await tester.pump();
expect(tester.getRect(findMenuScope), equals(const Rect.fromLTRB(472.0, 324.0, 762.0, 428.0)));
expect(tester.getRect(findMenuScope), equals(const Rect.fromLTRB(472.0, 324.0, 746.0, 436.0)));
await tester.pumpWidget(buildTestApp(alignment: AlignmentDirectional.topStart));
await tester.pump();
......@@ -369,20 +522,20 @@ void main() {
// Open the menu and make sure things are the right size, in the right place.
await tester.tap(find.text('Press Me'));
await tester.pump();
expect(tester.getRect(findMenuScope), equals(const Rect.fromLTRB(182.0, 324.0, 472.0, 428.0)));
expect(tester.getRect(findMenuScope), equals(const Rect.fromLTRB(198.0, 324.0, 472.0, 436.0)));
await tester.pumpWidget(buildTestApp(textDirection: TextDirection.rtl, alignment: AlignmentDirectional.topStart));
await tester.pump();
expect(tester.getRect(findMenuScope), equals(const Rect.fromLTRB(182.0, 276.0, 472.0, 380.0)));
expect(tester.getRect(findMenuScope), equals(const Rect.fromLTRB(198.0, 276.0, 472.0, 388.0)));
await tester.pumpWidget(buildTestApp(textDirection: TextDirection.rtl, alignment: AlignmentDirectional.center));
await tester.pump();
expect(tester.getRect(findMenuScope), equals(const Rect.fromLTRB(110.0, 300.0, 400.0, 404.0)));
expect(tester.getRect(findMenuScope), equals(const Rect.fromLTRB(126.0, 300.0, 400.0, 412.0)));
await tester
.pumpWidget(buildTestApp(textDirection: TextDirection.rtl, alignment: AlignmentDirectional.bottomEnd));
await tester.pump();
expect(tester.getRect(findMenuScope), equals(const Rect.fromLTRB(38.0, 324.0, 328.0, 428.0)));
expect(tester.getRect(findMenuScope), equals(const Rect.fromLTRB(54.0, 324.0, 328.0, 436.0)));
await tester.pumpWidget(buildTestApp(textDirection: TextDirection.rtl, alignment: AlignmentDirectional.topStart));
await tester.pump();
......@@ -411,13 +564,13 @@ void main() {
// Open the menu and make sure things are the right size, in the right place.
await tester.tap(find.text('Press Me'));
await tester.pump();
expect(tester.getRect(findMenuScope), equals(const Rect.fromLTRB(428.0, 374.0, 718.0, 478.0)));
expect(tester.getRect(findMenuScope), equals(const Rect.fromLTRB(428.0, 374.0, 702.0, 486.0)));
// Now move the menu by calling open() again with a local position on the
// anchor.
controller.open(position: const Offset(200, 200));
await tester.pump();
expect(tester.getRect(findMenuScope), equals(const Rect.fromLTRB(510.0, 476.0, 800.0, 580.0)));
expect(tester.getRect(findMenuScope), equals(const Rect.fromLTRB(526.0, 476.0, 800.0, 588.0)));
});
testWidgets('menu position in RTL', (WidgetTester tester) async {
......@@ -436,13 +589,13 @@ void main() {
// Open the menu and make sure things are the right size, in the right place.
await tester.tap(find.text('Press Me'));
await tester.pump();
expect(tester.getRect(findMenuScope), equals(const Rect.fromLTRB(82.0, 374.0, 372.0, 478.0)));
expect(tester.getRect(findMenuScope), equals(const Rect.fromLTRB(98.0, 374.0, 372.0, 486.0)));
// Now move the menu by calling open() again with a local position on the
// anchor.
controller.open(position: const Offset(400, 200));
await tester.pump();
expect(tester.getRect(findMenuScope), equals(const Rect.fromLTRB(510.0, 476.0, 800.0, 580.0)));
expect(tester.getRect(findMenuScope), equals(const Rect.fromLTRB(526.0, 476.0, 800.0, 588.0)));
});
testWidgets('works with Padding around menu and overlay', (WidgetTester tester) async {
......@@ -483,11 +636,11 @@ void main() {
expect(tester.getRect(find.byType(MenuBar)), equals(const Rect.fromLTRB(22.0, 22.0, 778.0, 70.0)));
expect(
tester.getRect(find.text(TestMenu.subMenu10.label)),
equals(const Rect.fromLTRB(134.0, 91.0, 288.0, 105.0)),
equals(const Rect.fromLTRB(146.0, 95.0, 300.0, 109.0)),
);
expect(
tester.getRect(find.ancestor(of: find.text(TestMenu.subMenu10.label), matching: find.byType(Material)).at(1)),
equals(const Rect.fromLTRB(126.0, 70.0, 356.0, 222.0)),
equals(const Rect.fromLTRB(134.0, 70.0, 348.0, 230.0)),
);
// Close and make sure it goes back where it was.
......@@ -538,11 +691,11 @@ void main() {
expect(tester.getRect(find.byType(MenuBar)), equals(const Rect.fromLTRB(22.0, 22.0, 778.0, 70.0)));
expect(
tester.getRect(find.text(TestMenu.subMenu10.label)),
equals(const Rect.fromLTRB(512.0, 91.0, 666.0, 105.0)),
equals(const Rect.fromLTRB(500.0, 95.0, 654.0, 109.0)),
);
expect(
tester.getRect(find.ancestor(of: find.text(TestMenu.subMenu10.label), matching: find.byType(Material)).at(1)),
equals(const Rect.fromLTRB(444.0, 70.0, 674.0, 222.0)),
equals(const Rect.fromLTRB(452.0, 70.0, 666.0, 230.0)),
);
// Close and make sure it goes back where it was.
......@@ -1447,12 +1600,13 @@ void main() {
await tester.pump();
expect(find.byType(MenuItemButton), findsNWidgets(6));
expect(find.byType(SubmenuButton), findsNWidgets(4));
expect(find.byType(SubmenuButton), findsNWidgets(5));
final List<Rect> menuRects = collectMenuRects();
expect(menuRects[0], equals(const Rect.fromLTRB(4.0, 0.0, 104.0, 48.0)));
expect(menuRects[1], equals(const Rect.fromLTRB(104.0, 0.0, 204.0, 48.0)));
expect(menuRects[2], equals(const Rect.fromLTRB(204.0, 0.0, 304.0, 48.0)));
expect(menuRects[3], equals(const Rect.fromLTRB(104.0, 100.0, 334.0, 148.0)));
expect(menuRects[0], equals(const Rect.fromLTRB(4.0, 0.0, 112.0, 48.0)));
expect(menuRects[1], equals(const Rect.fromLTRB(112.0, 0.0, 220.0, 48.0)));
expect(menuRects[2], equals(const Rect.fromLTRB(220.0, 0.0, 328.0, 48.0)));
expect(menuRects[3], equals(const Rect.fromLTRB(328.0, 0.0, 436.0, 48.0)));
expect(menuRects[4], equals(const Rect.fromLTRB(112.0, 104.0, 326.0, 152.0)));
});
testWidgets('unconstrained menus show up in the right place in RTL', (WidgetTester tester) async {
......@@ -1488,12 +1642,13 @@ void main() {
await tester.pump();
expect(find.byType(MenuItemButton), findsNWidgets(6));
expect(find.byType(SubmenuButton), findsNWidgets(4));
expect(find.byType(SubmenuButton), findsNWidgets(5));
final List<Rect> menuRects = collectMenuRects();
expect(menuRects[0], equals(const Rect.fromLTRB(696.0, 0.0, 796.0, 48.0)));
expect(menuRects[1], equals(const Rect.fromLTRB(596.0, 0.0, 696.0, 48.0)));
expect(menuRects[2], equals(const Rect.fromLTRB(496.0, 0.0, 596.0, 48.0)));
expect(menuRects[3], equals(const Rect.fromLTRB(466.0, 100.0, 696.0, 148.0)));
expect(menuRects[0], equals(const Rect.fromLTRB(688.0, 0.0, 796.0, 48.0)));
expect(menuRects[1], equals(const Rect.fromLTRB(580.0, 0.0, 688.0, 48.0)));
expect(menuRects[2], equals(const Rect.fromLTRB(472.0, 0.0, 580.0, 48.0)));
expect(menuRects[3], equals(const Rect.fromLTRB(364.0, 0.0, 472.0, 48.0)));
expect(menuRects[4], equals(const Rect.fromLTRB(474.0, 104.0, 688.0, 152.0)));
});
testWidgets('constrained menus show up in the right place in LTR', (WidgetTester tester) async {
......@@ -1527,12 +1682,13 @@ void main() {
await tester.pump();
expect(find.byType(MenuItemButton), findsNWidgets(6));
expect(find.byType(SubmenuButton), findsNWidgets(4));
expect(find.byType(SubmenuButton), findsNWidgets(5));
final List<Rect> menuRects = collectMenuRects();
expect(menuRects[0], equals(const Rect.fromLTRB(4.0, 0.0, 104.0, 48.0)));
expect(menuRects[1], equals(const Rect.fromLTRB(104.0, 0.0, 204.0, 48.0)));
expect(menuRects[2], equals(const Rect.fromLTRB(204.0, 0.0, 304.0, 48.0)));
expect(menuRects[3], equals(const Rect.fromLTRB(70.0, 100.0, 300.0, 148.0)));
expect(menuRects[0], equals(const Rect.fromLTRB(4.0, 0.0, 112.0, 48.0)));
expect(menuRects[1], equals(const Rect.fromLTRB(112.0, 0.0, 220.0, 48.0)));
expect(menuRects[2], equals(const Rect.fromLTRB(220.0, 0.0, 328.0, 48.0)));
expect(menuRects[3], equals(const Rect.fromLTRB(328.0, 0.0, 436.0, 48.0)));
expect(menuRects[4], equals(const Rect.fromLTRB(86.0, 104.0, 300.0, 152.0)));
});
testWidgets('constrained menus show up in the right place in RTL', (WidgetTester tester) async {
......@@ -1566,12 +1722,13 @@ void main() {
await tester.pump();
expect(find.byType(MenuItemButton), findsNWidgets(6));
expect(find.byType(SubmenuButton), findsNWidgets(4));
expect(find.byType(SubmenuButton), findsNWidgets(5));
final List<Rect> menuRects = collectMenuRects();
expect(menuRects[0], equals(const Rect.fromLTRB(196.0, 0.0, 296.0, 48.0)));
expect(menuRects[1], equals(const Rect.fromLTRB(96.0, 0.0, 196.0, 48.0)));
expect(menuRects[2], equals(const Rect.fromLTRB(-4.0, 0.0, 96.0, 48.0)));
expect(menuRects[3], equals(const Rect.fromLTRB(0.0, 100.0, 230.0, 148.0)));
expect(menuRects[0], equals(const Rect.fromLTRB(188.0, 0.0, 296.0, 48.0)));
expect(menuRects[1], equals(const Rect.fromLTRB(80.0, 0.0, 188.0, 48.0)));
expect(menuRects[2], equals(const Rect.fromLTRB(-28.0, 0.0, 80.0, 48.0)));
expect(menuRects[3], equals(const Rect.fromLTRB(-136.0, 0.0, -28.0, 48.0)));
expect(menuRects[4], equals(const Rect.fromLTRB(0.0, 104.0, 214.0, 152.0)));
});
});
......@@ -1783,6 +1940,7 @@ List<Widget> createTestMenus({
MenuItemButton(
onPressed: onPressed != null ? () => onPressed(TestMenu.subMenu00) : null,
shortcut: shortcuts[TestMenu.subMenu00],
leadingIcon: const Icon(Icons.add),
child: Text(TestMenu.subMenu00.label),
),
MenuItemButton(
......@@ -1849,6 +2007,7 @@ List<Widget> createTestMenus({
menuChildren: <Widget>[
MenuItemButton(
// Always disabled.
leadingIcon: const Icon(Icons.ac_unit),
shortcut: shortcuts[TestMenu.subMenu20],
child: Text(TestMenu.subMenu20.label),
),
......@@ -1895,6 +2054,12 @@ List<Widget> createTestMenus({
],
child: Text(TestMenu.mainMenu4.label),
),
SubmenuButton(
onOpen: onOpen != null ? () => onOpen(TestMenu.mainMenu5) : null,
onClose: onClose != null ? () => onClose(TestMenu.mainMenu5) : null,
menuChildren: const <Widget>[],
child: Text(TestMenu.mainMenu5.label),
),
];
return result;
}
......@@ -1905,6 +2070,7 @@ enum TestMenu {
mainMenu2('Menu 2'),
mainMenu3('Menu 3'),
mainMenu4('Menu 4'),
mainMenu5('Menu 5'),
subMenu00('Sub Menu 00'),
subMenu01('Sub Menu 01'),
subMenu02('Sub Menu 02'),
......
......@@ -87,13 +87,13 @@ void main() {
// Open a test menu.
await tester.tap(find.text(TestMenu.mainMenu1.label));
await tester.pump();
expect(tester.getRect(findMenuBarPanel().first), equals(const Rect.fromLTRB(240.0, 0.0, 560.0, 68.0)));
expect(tester.getRect(findMenuBarPanel().first), equals(const Rect.fromLTRB(228.0, 0.0, 572.0, 68.0)));
final Material menuBarMaterial = getMenuBarPanelMaterial(tester);
expect(menuBarMaterial.elevation, equals(15));
expect(menuBarMaterial.color, equals(Colors.red));
final Material subMenuMaterial = getSubmenuPanelMaterial(tester);
expect(tester.getRect(findSubmenuPanel()), equals(const Rect.fromLTRB(350.0, 58.0, 580.0, 210.0)));
expect(tester.getRect(findSubmenuPanel()), equals(const Rect.fromLTRB(346.0, 58.0, 560.0, 218.0)));
expect(subMenuMaterial.elevation, equals(20));
expect(subMenuMaterial.color, equals(Colors.green));
});
......@@ -160,19 +160,19 @@ void main() {
await tester.tap(find.text(TestMenu.mainMenu1.label));
await tester.pump();
expect(tester.getRect(findMenuBarPanel().first), equals(const Rect.fromLTRB(238.0, 0.0, 562.0, 72.0)));
expect(tester.getRect(findMenuBarPanel().first), equals(const Rect.fromLTRB(226.0, 0.0, 574.0, 72.0)));
final Material menuBarMaterial = getMenuBarPanelMaterial(tester);
expect(menuBarMaterial.elevation, equals(10.0));
expect(menuBarMaterial.color, equals(Colors.blue));
final Material subMenuMaterial = getSubmenuPanelMaterial(tester);
expect(tester.getRect(findSubmenuPanel()), equals(const Rect.fromLTRB(336.0, 60.0, 594.0, 232.0)));
expect(tester.getRect(findSubmenuPanel()), equals(const Rect.fromLTRB(332.0, 60.0, 574.0, 232.0)));
expect(subMenuMaterial.elevation, equals(18));
expect(subMenuMaterial.color, equals(Colors.cyan));
expect(subMenuMaterial.shape, equals(const BeveledRectangleBorder()));
final Finder menuItem = findSubMenuItem();
expect(tester.getRect(menuItem.first), equals(const Rect.fromLTRB(350.0, 74.0, 580.0, 122.0)));
expect(tester.getRect(menuItem.first), equals(const Rect.fromLTRB(346.0, 74.0, 560.0, 122.0)));
final Material menuItemMaterial = tester.widget<Material>(
find.ancestor(of: find.text(TestMenu.subMenu10.label), matching: find.byType(Material)).first);
expect(menuItemMaterial.color, equals(Colors.amber));
......
......@@ -261,20 +261,20 @@ void main() {
);
await tester.pump();
expect(tester.getRect(find.byType(MenuBar)), equals(const Rect.fromLTRB(240.0, 0.0, 560.0, 48.0)));
expect(tester.getRect(find.byType(MenuBar)), equals(const Rect.fromLTRB(228.0, 0.0, 572.0, 48.0)));
// Open and make sure things are the right size.
await tester.tap(find.text(TestMenu.mainMenu1.label));
await tester.pump();
expect(tester.getRect(find.byType(MenuBar)), equals(const Rect.fromLTRB(240.0, 0.0, 560.0, 48.0)));
expect(tester.getRect(find.byType(MenuBar)), equals(const Rect.fromLTRB(228.0, 0.0, 572.0, 48.0)));
expect(
tester.getRect(find.text(TestMenu.subMenu10.label)),
equals(const Rect.fromLTRB(366.0, 64.0, 520.0, 78.0)),
equals(const Rect.fromLTRB(366.0, 68.0, 520.0, 82.0)),
);
expect(
tester.getRect(find.ancestor(of: find.text(TestMenu.subMenu10.label), matching: find.byType(Material)).at(1)),
equals(const Rect.fromLTRB(350.0, 48.0, 602.0, 178.0)),
equals(const Rect.fromLTRB(346.0, 48.0, 579.0, 186.0)),
);
});
});
......
......@@ -87,13 +87,13 @@ void main() {
// Open a test menu.
await tester.tap(find.text(TestMenu.mainMenu1.label));
await tester.pump();
expect(tester.getRect(findMenuBarPanel().first), equals(const Rect.fromLTRB(246.0, 0.0, 554.0, 48.0)));
expect(tester.getRect(findMenuBarPanel().first), equals(const Rect.fromLTRB(234.0, 0.0, 566.0, 48.0)));
final Material menuBarMaterial = getMenuBarPanelMaterial(tester);
expect(menuBarMaterial.elevation, equals(20));
expect(menuBarMaterial.color, equals(Colors.green));
final Material subMenuMaterial = getSubmenuPanelMaterial(tester);
expect(tester.getRect(findSubmenuPanel()), equals(const Rect.fromLTRB(340.0, 48.0, 590.0, 212.0)));
expect(tester.getRect(findSubmenuPanel()), equals(const Rect.fromLTRB(336.0, 48.0, 570.0, 212.0)));
expect(subMenuMaterial.elevation, equals(15));
expect(subMenuMaterial.color, equals(Colors.red));
});
......@@ -160,19 +160,19 @@ void main() {
await tester.tap(find.text(TestMenu.mainMenu1.label));
await tester.pump();
expect(tester.getRect(findMenuBarPanel().first), equals(const Rect.fromLTRB(238.0, 0.0, 562.0, 72.0)));
expect(tester.getRect(findMenuBarPanel().first), equals(const Rect.fromLTRB(226.0, 0.0, 574.0, 72.0)));
final Material menuBarMaterial = getMenuBarPanelMaterial(tester);
expect(menuBarMaterial.elevation, equals(10.0));
expect(menuBarMaterial.color, equals(Colors.blue));
final Material subMenuMaterial = getSubmenuPanelMaterial(tester);
expect(tester.getRect(findSubmenuPanel()), equals(const Rect.fromLTRB(336.0, 60.0, 594.0, 232.0)));
expect(tester.getRect(findSubmenuPanel()), equals(const Rect.fromLTRB(332.0, 60.0, 574.0, 232.0)));
expect(subMenuMaterial.elevation, equals(18));
expect(subMenuMaterial.color, equals(Colors.cyan));
expect(subMenuMaterial.shape, equals(const BeveledRectangleBorder()));
final Finder menuItem = findSubMenuItem();
expect(tester.getRect(menuItem.first), equals(const Rect.fromLTRB(350.0, 74.0, 580.0, 122.0)));
expect(tester.getRect(menuItem.first), equals(const Rect.fromLTRB(346.0, 74.0, 560.0, 122.0)));
final Material menuItemMaterial = tester.widget<Material>(
find.ancestor(of: find.text(TestMenu.subMenu10.label), matching: find.byType(Material)).first);
expect(menuItemMaterial.color, equals(Colors.amber));
......
......@@ -1685,6 +1685,62 @@ void main() {
expect(controller.value, <MaterialState>{MaterialState.disabled});
expect(count, 1);
});
testWidgets('icon color can be different from the text color', (WidgetTester tester) async {
final Key iconButtonKey = UniqueKey();
const ColorScheme colorScheme = ColorScheme.light();
final ThemeData theme = ThemeData.from(colorScheme: colorScheme);
await tester.pumpWidget(
MaterialApp(
theme: theme,
home: Center(
child: TextButton.icon(
key: iconButtonKey,
style: TextButton.styleFrom(iconColor: Colors.red),
icon: const Icon(Icons.add),
onPressed: () {},
label: const Text('button'),
),
),
),
);
Finder buttonMaterial = find.descendant(
of: find.byKey(iconButtonKey),
matching: find.byType(Material),
);
Material material = tester.widget<Material>(buttonMaterial);
expect(material.textStyle!.color, colorScheme.primary);
Color? iconColor() => _iconStyle(tester, Icons.add)?.color;
expect(iconColor(), equals(Colors.red));
// disabled button
await tester.pumpWidget(
MaterialApp(
theme: theme,
home: Center(
child: TextButton.icon(
key: iconButtonKey,
style: TextButton.styleFrom(iconColor: Colors.red, disabledIconColor: Colors.blue),
icon: const Icon(Icons.add),
onPressed: null,
label: const Text('button'),
),
),
),
);
buttonMaterial = find.descendant(
of: find.byKey(iconButtonKey),
matching: find.byType(Material),
);
material = tester.widget<Material>(buttonMaterial);
expect(material.textStyle!.color, colorScheme.onSurface.withOpacity(0.38));
expect(iconColor(), equals(Colors.blue));
});
}
TextStyle? _iconStyle(WidgetTester tester, IconData icon) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment