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'; ...@@ -31,6 +31,7 @@ import 'package:gen_defaults/filter_chip_template.dart';
import 'package:gen_defaults/icon_button_template.dart'; import 'package:gen_defaults/icon_button_template.dart';
import 'package:gen_defaults/input_chip_template.dart'; import 'package:gen_defaults/input_chip_template.dart';
import 'package:gen_defaults/input_decorator_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_bar_template.dart';
import 'package:gen_defaults/navigation_rail_template.dart'; import 'package:gen_defaults/navigation_rail_template.dart';
import 'package:gen_defaults/popup_menu_template.dart'; import 'package:gen_defaults/popup_menu_template.dart';
...@@ -135,6 +136,7 @@ Future<void> main(List<String> args) async { ...@@ -135,6 +136,7 @@ Future<void> main(List<String> args) async {
IconButtonTemplate('IconButton', '$materialLib/icon_button.dart', tokens).updateFile(); IconButtonTemplate('IconButton', '$materialLib/icon_button.dart', tokens).updateFile();
InputChipTemplate('InputChip', '$materialLib/input_chip.dart', tokens).updateFile(); InputChipTemplate('InputChip', '$materialLib/input_chip.dart', tokens).updateFile();
InputDecoratorTemplate('InputDecorator', '$materialLib/input_decorator.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(); NavigationBarTemplate('NavigationBar', '$materialLib/navigation_bar.dart', tokens).updateFile();
NavigationRailTemplate('NavigationRail', '$materialLib/navigation_rail.dart', tokens).updateFile(); NavigationRailTemplate('NavigationRail', '$materialLib/navigation_rail.dart', tokens).updateFile();
PopupMenuTemplate('PopupMenu', '$materialLib/popup_menu.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() { ...@@ -21,14 +21,14 @@ void main() {
await tester.sendKeyDownEvent(LogicalKeyboardKey.controlRight); await tester.sendKeyDownEvent(LogicalKeyboardKey.controlRight);
await tester.tapAt(const Offset(100, 200)); await tester.tapAt(const Offset(100, 200));
await tester.pump(); 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. // Make sure tapping in a different place causes the menu to move.
await tester.tapAt(const Offset(200, 100)); await tester.tapAt(const Offset(200, 100));
await tester.pump(); await tester.pump();
await tester.sendKeyUpEvent(LogicalKeyboardKey.controlRight); 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.about.label), findsOneWidget);
expect(find.text(example.MenuEntry.showMessage.label), findsOneWidget); expect(find.text(example.MenuEntry.showMessage.label), findsOneWidget);
......
...@@ -151,6 +151,7 @@ class ButtonStyle with Diagnosticable { ...@@ -151,6 +151,7 @@ class ButtonStyle with Diagnosticable {
this.minimumSize, this.minimumSize,
this.fixedSize, this.fixedSize,
this.maximumSize, this.maximumSize,
this.iconColor,
this.iconSize, this.iconSize,
this.side, this.side,
this.shape, this.shape,
...@@ -230,6 +231,11 @@ class ButtonStyle with Diagnosticable { ...@@ -230,6 +231,11 @@ class ButtonStyle with Diagnosticable {
/// This value must be greater than or equal to [minimumSize]. /// This value must be greater than or equal to [minimumSize].
final MaterialStateProperty<Size?>? maximumSize; 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. /// The icon's size inside of the button.
final MaterialStateProperty<double?>? iconSize; final MaterialStateProperty<double?>? iconSize;
...@@ -323,6 +329,7 @@ class ButtonStyle with Diagnosticable { ...@@ -323,6 +329,7 @@ class ButtonStyle with Diagnosticable {
MaterialStateProperty<Size?>? minimumSize, MaterialStateProperty<Size?>? minimumSize,
MaterialStateProperty<Size?>? fixedSize, MaterialStateProperty<Size?>? fixedSize,
MaterialStateProperty<Size?>? maximumSize, MaterialStateProperty<Size?>? maximumSize,
MaterialStateProperty<Color?>? iconColor,
MaterialStateProperty<double?>? iconSize, MaterialStateProperty<double?>? iconSize,
MaterialStateProperty<BorderSide?>? side, MaterialStateProperty<BorderSide?>? side,
MaterialStateProperty<OutlinedBorder?>? shape, MaterialStateProperty<OutlinedBorder?>? shape,
...@@ -346,6 +353,7 @@ class ButtonStyle with Diagnosticable { ...@@ -346,6 +353,7 @@ class ButtonStyle with Diagnosticable {
minimumSize: minimumSize ?? this.minimumSize, minimumSize: minimumSize ?? this.minimumSize,
fixedSize: fixedSize ?? this.fixedSize, fixedSize: fixedSize ?? this.fixedSize,
maximumSize: maximumSize ?? this.maximumSize, maximumSize: maximumSize ?? this.maximumSize,
iconColor: iconColor ?? this.iconColor,
iconSize: iconSize ?? this.iconSize, iconSize: iconSize ?? this.iconSize,
side: side ?? this.side, side: side ?? this.side,
shape: shape ?? this.shape, shape: shape ?? this.shape,
...@@ -380,6 +388,7 @@ class ButtonStyle with Diagnosticable { ...@@ -380,6 +388,7 @@ class ButtonStyle with Diagnosticable {
minimumSize: minimumSize ?? style.minimumSize, minimumSize: minimumSize ?? style.minimumSize,
fixedSize: fixedSize ?? style.fixedSize, fixedSize: fixedSize ?? style.fixedSize,
maximumSize: maximumSize ?? style.maximumSize, maximumSize: maximumSize ?? style.maximumSize,
iconColor: iconColor ?? style.iconColor,
iconSize: iconSize ?? style.iconSize, iconSize: iconSize ?? style.iconSize,
side: side ?? style.side, side: side ?? style.side,
shape: shape ?? style.shape, shape: shape ?? style.shape,
...@@ -407,6 +416,7 @@ class ButtonStyle with Diagnosticable { ...@@ -407,6 +416,7 @@ class ButtonStyle with Diagnosticable {
minimumSize, minimumSize,
fixedSize, fixedSize,
maximumSize, maximumSize,
iconColor,
iconSize, iconSize,
side, side,
shape, shape,
...@@ -441,6 +451,7 @@ class ButtonStyle with Diagnosticable { ...@@ -441,6 +451,7 @@ class ButtonStyle with Diagnosticable {
&& other.minimumSize == minimumSize && other.minimumSize == minimumSize
&& other.fixedSize == fixedSize && other.fixedSize == fixedSize
&& other.maximumSize == maximumSize && other.maximumSize == maximumSize
&& other.iconColor == iconColor
&& other.iconSize == iconSize && other.iconSize == iconSize
&& other.side == side && other.side == side
&& other.shape == shape && other.shape == shape
...@@ -467,6 +478,7 @@ class ButtonStyle with Diagnosticable { ...@@ -467,6 +478,7 @@ class ButtonStyle with Diagnosticable {
properties.add(DiagnosticsProperty<MaterialStateProperty<Size?>>('minimumSize', minimumSize, defaultValue: null)); properties.add(DiagnosticsProperty<MaterialStateProperty<Size?>>('minimumSize', minimumSize, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<Size?>>('fixedSize', fixedSize, defaultValue: null)); properties.add(DiagnosticsProperty<MaterialStateProperty<Size?>>('fixedSize', fixedSize, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<Size?>>('maximumSize', maximumSize, 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<double?>>('iconSize', iconSize, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<BorderSide?>>('side', side, defaultValue: null)); properties.add(DiagnosticsProperty<MaterialStateProperty<BorderSide?>>('side', side, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<OutlinedBorder?>>('shape', shape, defaultValue: null)); properties.add(DiagnosticsProperty<MaterialStateProperty<OutlinedBorder?>>('shape', shape, defaultValue: null));
...@@ -496,6 +508,7 @@ class ButtonStyle with Diagnosticable { ...@@ -496,6 +508,7 @@ class ButtonStyle with Diagnosticable {
minimumSize: MaterialStateProperty.lerp<Size?>(a?.minimumSize, b?.minimumSize, t, Size.lerp), minimumSize: MaterialStateProperty.lerp<Size?>(a?.minimumSize, b?.minimumSize, t, Size.lerp),
fixedSize: MaterialStateProperty.lerp<Size?>(a?.fixedSize, b?.fixedSize, 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), 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), iconSize: MaterialStateProperty.lerp<double?>(a?.iconSize, b?.iconSize, t, lerpDouble),
side: _lerpSides(a?.side, b?.side, t), side: _lerpSides(a?.side, b?.side, t),
shape: MaterialStateProperty.lerp<OutlinedBorder?>(a?.shape, b?.shape, t, OutlinedBorder.lerp), shape: MaterialStateProperty.lerp<OutlinedBorder?>(a?.shape, b?.shape, t, OutlinedBorder.lerp),
......
...@@ -287,6 +287,7 @@ class _ButtonStyleState extends State<ButtonStyleButton> with TickerProviderStat ...@@ -287,6 +287,7 @@ class _ButtonStyleState extends State<ButtonStyleButton> with TickerProviderStat
final Size? resolvedMinimumSize = resolve<Size?>((ButtonStyle? style) => style?.minimumSize); final Size? resolvedMinimumSize = resolve<Size?>((ButtonStyle? style) => style?.minimumSize);
final Size? resolvedFixedSize = resolve<Size?>((ButtonStyle? style) => style?.fixedSize); final Size? resolvedFixedSize = resolve<Size?>((ButtonStyle? style) => style?.fixedSize);
final Size? resolvedMaximumSize = resolve<Size?>((ButtonStyle? style) => style?.maximumSize); 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 double? resolvedIconSize = resolve<double?>((ButtonStyle? style) => style?.iconSize);
final BorderSide? resolvedSide = resolve<BorderSide?>((ButtonStyle? style) => style?.side); final BorderSide? resolvedSide = resolve<BorderSide?>((ButtonStyle? style) => style?.side);
final OutlinedBorder? resolvedShape = resolve<OutlinedBorder?>((ButtonStyle? style) => style?.shape); final OutlinedBorder? resolvedShape = resolve<OutlinedBorder?>((ButtonStyle? style) => style?.shape);
...@@ -400,7 +401,7 @@ class _ButtonStyleState extends State<ButtonStyleButton> with TickerProviderStat ...@@ -400,7 +401,7 @@ class _ButtonStyleState extends State<ButtonStyleButton> with TickerProviderStat
customBorder: resolvedShape.copyWith(side: resolvedSide), customBorder: resolvedShape.copyWith(side: resolvedSide),
statesController: statesController, statesController: statesController,
child: IconTheme.merge( child: IconTheme.merge(
data: IconThemeData(color: resolvedForegroundColor, size: resolvedIconSize), data: IconThemeData(color: resolvedIconColor ?? resolvedForegroundColor, size: resolvedIconSize),
child: Padding( child: Padding(
padding: padding, padding: padding,
child: Align( child: Align(
......
...@@ -46,7 +46,7 @@ const double _kDefaultSubmenuIconSize = 24; ...@@ -46,7 +46,7 @@ const double _kDefaultSubmenuIconSize = 24;
// The default spacing between the the leading icon, label, trailing icon, and // The default spacing between the the leading icon, label, trailing icon, and
// shortcut label in a _MenuItemLabel. // 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 // The minimum spacing between the the leading icon, label, trailing icon, and
// shortcut label in a _MenuItemLabel. // shortcut label in a _MenuItemLabel.
...@@ -66,7 +66,7 @@ const Map<ShortcutActivator, Intent> _kMenuTraversalShortcuts = <ShortcutActivat ...@@ -66,7 +66,7 @@ const Map<ShortcutActivator, Intent> _kMenuTraversalShortcuts = <ShortcutActivat
}; };
// The minimum vertical spacing on the outside of menus. // 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. // How close to the edge of the safe area the menu will be placed.
const double _kMenuViewPadding = 8; const double _kMenuViewPadding = 8;
...@@ -924,6 +924,7 @@ class MenuItemButton extends StatefulWidget { ...@@ -924,6 +924,7 @@ class MenuItemButton extends StatefulWidget {
Color? disabledBackgroundColor, Color? disabledBackgroundColor,
Color? shadowColor, Color? shadowColor,
Color? surfaceTintColor, Color? surfaceTintColor,
Color? iconColor,
TextStyle? textStyle, TextStyle? textStyle,
double? elevation, double? elevation,
EdgeInsetsGeometry? padding, EdgeInsetsGeometry? padding,
...@@ -948,6 +949,7 @@ class MenuItemButton extends StatefulWidget { ...@@ -948,6 +949,7 @@ class MenuItemButton extends StatefulWidget {
disabledForegroundColor: disabledForegroundColor, disabledForegroundColor: disabledForegroundColor,
shadowColor: shadowColor, shadowColor: shadowColor,
surfaceTintColor: surfaceTintColor, surfaceTintColor: surfaceTintColor,
iconColor: iconColor,
textStyle: textStyle, textStyle: textStyle,
elevation: elevation, elevation: elevation,
padding: padding, padding: padding,
...@@ -1021,10 +1023,11 @@ class _MenuItemButtonState extends State<MenuItemButton> { ...@@ -1021,10 +1023,11 @@ class _MenuItemButtonState extends State<MenuItemButton> {
// Since we don't want to use the theme style or default style from the // 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 // TextButton, we merge the styles, merging them in the right order when
// each type of style exists. Each "*StyleOf" function is only called once. // each type of style exists. Each "*StyleOf" function is only called once.
final ButtonStyle mergedStyle = ButtonStyle mergedStyle = widget.themeStyleOf(context)?.merge(widget.defaultStyleOf(context))
widget.style?.merge(widget.themeStyleOf(context)?.merge(widget.defaultStyleOf(context))) ?? ?? widget.defaultStyleOf(context);
widget.themeStyleOf(context)?.merge(widget.defaultStyleOf(context)) ?? if (widget.style != null) {
widget.defaultStyleOf(context); mergedStyle = widget.style!.merge(mergedStyle);
}
return TextButton( return TextButton(
onPressed: widget.enabled ? _handleSelect : null, onPressed: widget.enabled ? _handleSelect : null,
...@@ -1646,6 +1649,7 @@ class SubmenuButton extends StatefulWidget { ...@@ -1646,6 +1649,7 @@ class SubmenuButton extends StatefulWidget {
Color? disabledBackgroundColor, Color? disabledBackgroundColor,
Color? shadowColor, Color? shadowColor,
Color? surfaceTintColor, Color? surfaceTintColor,
Color? iconColor,
TextStyle? textStyle, TextStyle? textStyle,
double? elevation, double? elevation,
EdgeInsetsGeometry? padding, EdgeInsetsGeometry? padding,
...@@ -1670,6 +1674,7 @@ class SubmenuButton extends StatefulWidget { ...@@ -1670,6 +1674,7 @@ class SubmenuButton extends StatefulWidget {
disabledForegroundColor: disabledForegroundColor, disabledForegroundColor: disabledForegroundColor,
shadowColor: shadowColor, shadowColor: shadowColor,
surfaceTintColor: surfaceTintColor, surfaceTintColor: surfaceTintColor,
iconColor: iconColor,
textStyle: textStyle, textStyle: textStyle,
elevation: elevation, elevation: elevation,
padding: padding, padding: padding,
...@@ -1799,10 +1804,11 @@ class _SubmenuButtonState extends State<SubmenuButton> { ...@@ -1799,10 +1804,11 @@ class _SubmenuButtonState extends State<SubmenuButton> {
// TextButton, we merge the styles, merging them in the right order when // TextButton, we merge the styles, merging them in the right order when
// each type of style exists. Each "*StyleOf" function is only called // each type of style exists. Each "*StyleOf" function is only called
// once. // once.
final ButtonStyle mergedStyle = ButtonStyle mergedStyle = widget.themeStyleOf(context)?.merge(widget.defaultStyleOf(context))
widget.style?.merge(widget.themeStyleOf(context)?.merge(widget.defaultStyleOf(context))) ?? ?? widget.defaultStyleOf(context);
widget.themeStyleOf(context)?.merge(widget.defaultStyleOf(context)) ?? if (widget.style != null) {
widget.defaultStyleOf(context); mergedStyle = widget.style!.merge(mergedStyle);
}
void toggleShowMenu(BuildContext context) { void toggleShowMenu(BuildContext context) {
if (controller.isOpen) { if (controller.isOpen) {
...@@ -2519,14 +2525,13 @@ class _MenuItemLabel extends StatelessWidget { ...@@ -2519,14 +2525,13 @@ class _MenuItemLabel extends StatelessWidget {
padding: leadingIcon != null ? EdgeInsetsDirectional.only(start: horizontalPadding) : EdgeInsets.zero, padding: leadingIcon != null ? EdgeInsetsDirectional.only(start: horizontalPadding) : EdgeInsets.zero,
child: child, child: child,
), ),
],
),
if (trailingIcon != null) if (trailingIcon != null)
Padding( Padding(
padding: EdgeInsetsDirectional.only(start: horizontalPadding), padding: EdgeInsetsDirectional.only(start: horizontalPadding),
child: trailingIcon, child: trailingIcon,
), ),
],
),
if (showDecoration && (shortcut != null || hasSubmenu)) SizedBox(width: horizontalPadding),
if (showDecoration && shortcut != null) if (showDecoration && shortcut != null)
Padding( Padding(
padding: EdgeInsetsDirectional.only(start: horizontalPadding), padding: EdgeInsetsDirectional.only(start: horizontalPadding),
...@@ -3056,17 +3061,24 @@ bool _debugMenuInfo(String message, [Iterable<String>? details]) { ...@@ -3056,17 +3061,24 @@ bool _debugMenuInfo(String message, [Iterable<String>? details]) {
return true; return true;
} }
// This class will eventually be auto-generated, so it should remain at the end // BEGIN GENERATED TOKEN PROPERTIES - Menu
// of the file.
// 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 { class _MenuBarDefaultsM3 extends MenuStyle {
_MenuBarDefaultsM3(this.context) _MenuBarDefaultsM3(this.context)
: super( : super(
elevation: const MaterialStatePropertyAll<double?>(4), elevation: const MaterialStatePropertyAll<double?>(3.0),
shape: const MaterialStatePropertyAll<OutlinedBorder>(_defaultMenuBorder), shape: const MaterialStatePropertyAll<OutlinedBorder>(_defaultMenuBorder),
alignment: AlignmentDirectional.bottomStart, alignment: AlignmentDirectional.bottomStart,
); );
static const RoundedRectangleBorder _defaultMenuBorder = static const RoundedRectangleBorder _defaultMenuBorder =
RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.elliptical(2, 3))); RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0)));
final BuildContext context; final BuildContext context;
...@@ -3077,6 +3089,16 @@ class _MenuBarDefaultsM3 extends MenuStyle { ...@@ -3077,6 +3089,16 @@ class _MenuBarDefaultsM3 extends MenuStyle {
return MaterialStatePropertyAll<Color?>(_colors.surface); 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 @override
MaterialStateProperty<EdgeInsetsGeometry?>? get padding { MaterialStateProperty<EdgeInsetsGeometry?>? get padding {
return MaterialStatePropertyAll<EdgeInsetsGeometry>( return MaterialStatePropertyAll<EdgeInsetsGeometry>(
...@@ -3090,8 +3112,6 @@ class _MenuBarDefaultsM3 extends MenuStyle { ...@@ -3090,8 +3112,6 @@ 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 { class _MenuButtonDefaultsM3 extends ButtonStyle {
_MenuButtonDefaultsM3(this.context) _MenuButtonDefaultsM3(this.context)
: super( : super(
...@@ -3114,19 +3134,45 @@ class _MenuButtonDefaultsM3 extends ButtonStyle { ...@@ -3114,19 +3134,45 @@ class _MenuButtonDefaultsM3 extends ButtonStyle {
@override @override
MaterialStateProperty<double>? get elevation { MaterialStateProperty<double>? get elevation {
return ButtonStyleButton.allOrNull<double>(0); return ButtonStyleButton.allOrNull<double>(0.0);
} }
@override @override
MaterialStateProperty<Color?>? get foregroundColor { MaterialStateProperty<Color?>? get foregroundColor {
return MaterialStateProperty.resolveWith( return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
(Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) { if (states.contains(MaterialState.disabled)) {
return _colors.onSurface.withOpacity(0.38); return _colors.onSurface.withOpacity(0.38);
} }
return _colors.primary; 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 // No default fixedSize
...@@ -3138,7 +3184,7 @@ class _MenuButtonDefaultsM3 extends ButtonStyle { ...@@ -3138,7 +3184,7 @@ class _MenuButtonDefaultsM3 extends ButtonStyle {
@override @override
MaterialStateProperty<Size>? get minimumSize { MaterialStateProperty<Size>? get minimumSize {
return ButtonStyleButton.allOrNull<Size>(const Size(64, 40)); return ButtonStyleButton.allOrNull<Size>(const Size(64.0, 48.0));
} }
@override @override
...@@ -3157,16 +3203,16 @@ class _MenuButtonDefaultsM3 extends ButtonStyle { ...@@ -3157,16 +3203,16 @@ class _MenuButtonDefaultsM3 extends ButtonStyle {
MaterialStateProperty<Color?>? get overlayColor { MaterialStateProperty<Color?>? get overlayColor {
return MaterialStateProperty.resolveWith( return MaterialStateProperty.resolveWith(
(Set<MaterialState> states) { (Set<MaterialState> states) {
if (states.contains(MaterialState.pressed)) {
return _colors.onSurface.withOpacity(0.12);
}
if (states.contains(MaterialState.hovered)) { if (states.contains(MaterialState.hovered)) {
return _colors.primary.withOpacity(0.08); return _colors.onSurface.withOpacity(0.08);
} }
if (states.contains(MaterialState.focused)) { if (states.contains(MaterialState.focused)) {
return _colors.primary.withOpacity(0.12); return _colors.onSurface.withOpacity(0.12);
}
if (states.contains(MaterialState.pressed)) {
return _colors.primary.withOpacity(0.12);
} }
return null; return Colors.transparent;
}, },
); );
} }
...@@ -3197,9 +3243,10 @@ class _MenuButtonDefaultsM3 extends ButtonStyle { ...@@ -3197,9 +3243,10 @@ class _MenuButtonDefaultsM3 extends ButtonStyle {
@override @override
VisualDensity? get visualDensity => Theme.of(context).visualDensity; VisualDensity? get visualDensity => Theme.of(context).visualDensity;
// The horizontal padding number comes from the spec.
EdgeInsetsGeometry _scaledPadding(BuildContext context) { EdgeInsetsGeometry _scaledPadding(BuildContext context) {
return ButtonStyleButton.scaledPadding( return ButtonStyleButton.scaledPadding(
const EdgeInsets.all(8), const EdgeInsets.symmetric(horizontal: 12),
const EdgeInsets.symmetric(horizontal: 8), const EdgeInsets.symmetric(horizontal: 8),
const EdgeInsets.symmetric(horizontal: 4), const EdgeInsets.symmetric(horizontal: 4),
MediaQuery.maybeOf(context)?.textScaleFactor ?? 1, MediaQuery.maybeOf(context)?.textScaleFactor ?? 1,
...@@ -3207,17 +3254,15 @@ class _MenuButtonDefaultsM3 extends ButtonStyle { ...@@ -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 { class _MenuDefaultsM3 extends MenuStyle {
_MenuDefaultsM3(this.context) _MenuDefaultsM3(this.context)
: super( : super(
elevation: const MaterialStatePropertyAll<double?>(4), elevation: const MaterialStatePropertyAll<double?>(3.0),
shape: const MaterialStatePropertyAll<OutlinedBorder>(_defaultMenuBorder), shape: const MaterialStatePropertyAll<OutlinedBorder>(_defaultMenuBorder),
alignment: AlignmentDirectional.topEnd, alignment: AlignmentDirectional.topEnd,
); );
static const RoundedRectangleBorder _defaultMenuBorder = static const RoundedRectangleBorder _defaultMenuBorder =
RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.elliptical(2, 3))); RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0)));
final BuildContext context; final BuildContext context;
...@@ -3228,6 +3273,16 @@ class _MenuDefaultsM3 extends MenuStyle { ...@@ -3228,6 +3273,16 @@ class _MenuDefaultsM3 extends MenuStyle {
return MaterialStatePropertyAll<Color?>(_colors.surface); 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 @override
MaterialStateProperty<EdgeInsetsGeometry?>? get padding { MaterialStateProperty<EdgeInsetsGeometry?>? get padding {
return MaterialStatePropertyAll<EdgeInsetsGeometry>( return MaterialStatePropertyAll<EdgeInsetsGeometry>(
...@@ -3240,3 +3295,5 @@ class _MenuDefaultsM3 extends MenuStyle { ...@@ -3240,3 +3295,5 @@ class _MenuDefaultsM3 extends MenuStyle {
); );
} }
} }
// END GENERATED TOKEN PROPERTIES - Menu
...@@ -153,6 +153,8 @@ class TextButton extends ButtonStyleButton { ...@@ -153,6 +153,8 @@ class TextButton extends ButtonStyleButton {
Color? disabledBackgroundColor, Color? disabledBackgroundColor,
Color? shadowColor, Color? shadowColor,
Color? surfaceTintColor, Color? surfaceTintColor,
Color? iconColor,
Color? disabledIconColor,
double? elevation, double? elevation,
TextStyle? textStyle, TextStyle? textStyle,
EdgeInsetsGeometry? padding, EdgeInsetsGeometry? padding,
...@@ -193,6 +195,11 @@ class TextButton extends ButtonStyleButton { ...@@ -193,6 +195,11 @@ class TextButton extends ButtonStyleButton {
final MaterialStateProperty<Color?>? overlayColor = (foreground == null) final MaterialStateProperty<Color?>? overlayColor = (foreground == null)
? null ? null
: _TextButtonDefaultOverlay(foreground); : _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) final MaterialStateProperty<MouseCursor>? mouseCursor = (enabledMouseCursor == null && disabledMouseCursor == null)
? null ? null
: _TextButtonDefaultMouseCursor(enabledMouseCursor!, disabledMouseCursor!); : _TextButtonDefaultMouseCursor(enabledMouseCursor!, disabledMouseCursor!);
...@@ -204,6 +211,7 @@ class TextButton extends ButtonStyleButton { ...@@ -204,6 +211,7 @@ class TextButton extends ButtonStyleButton {
overlayColor: overlayColor, overlayColor: overlayColor,
shadowColor: ButtonStyleButton.allOrNull<Color>(shadowColor), shadowColor: ButtonStyleButton.allOrNull<Color>(shadowColor),
surfaceTintColor: ButtonStyleButton.allOrNull<Color>(surfaceTintColor), surfaceTintColor: ButtonStyleButton.allOrNull<Color>(surfaceTintColor),
iconColor: iconColorProp,
elevation: ButtonStyleButton.allOrNull<double>(elevation), elevation: ButtonStyleButton.allOrNull<double>(elevation),
padding: ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(padding), padding: ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(padding),
minimumSize: ButtonStyleButton.allOrNull<Size>(minimumSize), minimumSize: ButtonStyleButton.allOrNull<Size>(minimumSize),
...@@ -422,6 +430,27 @@ class _TextButtonDefaultOverlay extends MaterialStateProperty<Color?> { ...@@ -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 @immutable
class _TextButtonDefaultMouseCursor extends MaterialStateProperty<MouseCursor> with Diagnosticable { class _TextButtonDefaultMouseCursor extends MaterialStateProperty<MouseCursor> with Diagnosticable {
_TextButtonDefaultMouseCursor(this.enabledCursor, this.disabledCursor); _TextButtonDefaultMouseCursor(this.enabledCursor, this.disabledCursor);
......
...@@ -27,6 +27,7 @@ void main() { ...@@ -27,6 +27,7 @@ void main() {
expect(style.minimumSize, null); expect(style.minimumSize, null);
expect(style.fixedSize, null); expect(style.fixedSize, null);
expect(style.maximumSize, null); expect(style.maximumSize, null);
expect(style.iconColor, null);
expect(style.iconSize, null); expect(style.iconSize, null);
expect(style.side, null); expect(style.side, null);
expect(style.shape, null); expect(style.shape, null);
...@@ -63,6 +64,7 @@ void main() { ...@@ -63,6 +64,7 @@ void main() {
minimumSize: MaterialStatePropertyAll<Size>(Size(1.0, 2.0)), minimumSize: MaterialStatePropertyAll<Size>(Size(1.0, 2.0)),
side: MaterialStatePropertyAll<BorderSide>(BorderSide(width: 4.0, color: Color(0xfffffff6))), side: MaterialStatePropertyAll<BorderSide>(BorderSide(width: 4.0, color: Color(0xfffffff6))),
maximumSize: MaterialStatePropertyAll<Size>(Size(100.0, 200.0)), maximumSize: MaterialStatePropertyAll<Size>(Size(100.0, 200.0)),
iconColor: MaterialStatePropertyAll<Color>(Color(0xfffffff6)),
iconSize: MaterialStatePropertyAll<double>(48.1), iconSize: MaterialStatePropertyAll<double>(48.1),
shape: MaterialStatePropertyAll<OutlinedBorder>(StadiumBorder()), shape: MaterialStatePropertyAll<OutlinedBorder>(StadiumBorder()),
mouseCursor: MaterialStatePropertyAll<MouseCursor>(SystemMouseCursors.forbidden), mouseCursor: MaterialStatePropertyAll<MouseCursor>(SystemMouseCursors.forbidden),
...@@ -87,6 +89,7 @@ void main() { ...@@ -87,6 +89,7 @@ void main() {
'padding: MaterialStatePropertyAll(EdgeInsets.all(1.0))', 'padding: MaterialStatePropertyAll(EdgeInsets.all(1.0))',
'minimumSize: MaterialStatePropertyAll(Size(1.0, 2.0))', 'minimumSize: MaterialStatePropertyAll(Size(1.0, 2.0))',
'maximumSize: MaterialStatePropertyAll(Size(100.0, 200.0))', 'maximumSize: MaterialStatePropertyAll(Size(100.0, 200.0))',
'iconColor: MaterialStatePropertyAll(Color(0xfffffff6))',
'iconSize: MaterialStatePropertyAll(48.1)', 'iconSize: MaterialStatePropertyAll(48.1)',
'side: MaterialStatePropertyAll(BorderSide(color: Color(0xfffffff6), width: 4.0))', 'side: MaterialStatePropertyAll(BorderSide(color: Color(0xfffffff6), width: 4.0))',
'shape: MaterialStatePropertyAll(StadiumBorder(BorderSide(width: 0.0, style: none)))', 'shape: MaterialStatePropertyAll(StadiumBorder(BorderSide(width: 0.0, style: none)))',
...@@ -109,6 +112,7 @@ void main() { ...@@ -109,6 +112,7 @@ void main() {
const MaterialStateProperty<Size> minimumSize = MaterialStatePropertyAll<Size>(Size(1, 2)); const MaterialStateProperty<Size> minimumSize = MaterialStatePropertyAll<Size>(Size(1, 2));
const MaterialStateProperty<Size> fixedSize = MaterialStatePropertyAll<Size>(Size(3, 4)); const MaterialStateProperty<Size> fixedSize = MaterialStatePropertyAll<Size>(Size(3, 4));
const MaterialStateProperty<Size> maximumSize = MaterialStatePropertyAll<Size>(Size(5, 6)); 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<double> iconSize = MaterialStatePropertyAll<double>(48.0);
const MaterialStateProperty<BorderSide> side = MaterialStatePropertyAll<BorderSide>(BorderSide()); const MaterialStateProperty<BorderSide> side = MaterialStatePropertyAll<BorderSide>(BorderSide());
const MaterialStateProperty<OutlinedBorder> shape = MaterialStatePropertyAll<OutlinedBorder>(StadiumBorder()); const MaterialStateProperty<OutlinedBorder> shape = MaterialStatePropertyAll<OutlinedBorder>(StadiumBorder());
...@@ -130,6 +134,7 @@ void main() { ...@@ -130,6 +134,7 @@ void main() {
minimumSize: minimumSize, minimumSize: minimumSize,
fixedSize: fixedSize, fixedSize: fixedSize,
maximumSize: maximumSize, maximumSize: maximumSize,
iconColor: iconColor,
iconSize: iconSize, iconSize: iconSize,
side: side, side: side,
shape: shape, shape: shape,
...@@ -154,6 +159,7 @@ void main() { ...@@ -154,6 +159,7 @@ void main() {
minimumSize: minimumSize, minimumSize: minimumSize,
fixedSize: fixedSize, fixedSize: fixedSize,
maximumSize: maximumSize, maximumSize: maximumSize,
iconColor: iconColor,
iconSize: iconSize, iconSize: iconSize,
side: side, side: side,
shape: shape, shape: shape,
......
...@@ -145,6 +145,159 @@ void main() { ...@@ -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', () { group('Menu functions', () {
testWidgets('basic menu structure', (WidgetTester tester) async { testWidgets('basic menu structure', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
...@@ -231,13 +384,13 @@ void main() { ...@@ -231,13 +384,13 @@ void main() {
expect(tester.getRect(find.byType(MenuBar)), equals(const Rect.fromLTRB(0, 0, 800, 48))); expect(tester.getRect(find.byType(MenuBar)), equals(const Rect.fromLTRB(0, 0, 800, 48)));
expect( expect(
tester.getRect(find.text(TestMenu.subMenu10.label)), 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( expect(
tester.getRect( tester.getRect(
find.ancestor(of: find.text(TestMenu.subMenu10.label), matching: find.byType(Material)).at(1), 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() { ...@@ -276,13 +429,13 @@ void main() {
expect(tester.getRect(find.byType(MenuBar)), equals(const Rect.fromLTRB(0, 0, 800, 48))); expect(tester.getRect(find.byType(MenuBar)), equals(const Rect.fromLTRB(0, 0, 800, 48)));
expect( expect(
tester.getRect(find.text(TestMenu.subMenu10.label)), 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( expect(
tester.getRect( tester.getRect(
find.ancestor(of: find.text(TestMenu.subMenu10.label), matching: find.byType(Material)).at(1), 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. // Close and make sure it goes back where it was.
...@@ -310,7 +463,7 @@ void main() { ...@@ -310,7 +463,7 @@ void main() {
expect( expect(
tester.getRect(find.byType(MenuBar)), 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() { ...@@ -325,19 +478,19 @@ void main() {
// Open the menu and make sure things are the right size, in the right place. // Open the menu and make sure things are the right size, in the right place.
await tester.tap(find.text('Press Me')); await tester.tap(find.text('Press Me'));
await tester.pump(); 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.pumpWidget(buildTestApp(alignment: AlignmentDirectional.topStart));
await tester.pump(); 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.pumpWidget(buildTestApp(alignment: AlignmentDirectional.center));
await tester.pump(); 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.pumpWidget(buildTestApp(alignment: AlignmentDirectional.bottomEnd));
await tester.pump(); 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.pumpWidget(buildTestApp(alignment: AlignmentDirectional.topStart));
await tester.pump(); await tester.pump();
...@@ -369,20 +522,20 @@ void main() { ...@@ -369,20 +522,20 @@ void main() {
// Open the menu and make sure things are the right size, in the right place. // Open the menu and make sure things are the right size, in the right place.
await tester.tap(find.text('Press Me')); await tester.tap(find.text('Press Me'));
await tester.pump(); 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.pumpWidget(buildTestApp(textDirection: TextDirection.rtl, alignment: AlignmentDirectional.topStart));
await tester.pump(); 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.pumpWidget(buildTestApp(textDirection: TextDirection.rtl, alignment: AlignmentDirectional.center));
await tester.pump(); 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 await tester
.pumpWidget(buildTestApp(textDirection: TextDirection.rtl, alignment: AlignmentDirectional.bottomEnd)); .pumpWidget(buildTestApp(textDirection: TextDirection.rtl, alignment: AlignmentDirectional.bottomEnd));
await tester.pump(); 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.pumpWidget(buildTestApp(textDirection: TextDirection.rtl, alignment: AlignmentDirectional.topStart));
await tester.pump(); await tester.pump();
...@@ -411,13 +564,13 @@ void main() { ...@@ -411,13 +564,13 @@ void main() {
// Open the menu and make sure things are the right size, in the right place. // Open the menu and make sure things are the right size, in the right place.
await tester.tap(find.text('Press Me')); await tester.tap(find.text('Press Me'));
await tester.pump(); 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 // Now move the menu by calling open() again with a local position on the
// anchor. // anchor.
controller.open(position: const Offset(200, 200)); controller.open(position: const Offset(200, 200));
await tester.pump(); 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 { testWidgets('menu position in RTL', (WidgetTester tester) async {
...@@ -436,13 +589,13 @@ void main() { ...@@ -436,13 +589,13 @@ void main() {
// Open the menu and make sure things are the right size, in the right place. // Open the menu and make sure things are the right size, in the right place.
await tester.tap(find.text('Press Me')); await tester.tap(find.text('Press Me'));
await tester.pump(); 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 // Now move the menu by calling open() again with a local position on the
// anchor. // anchor.
controller.open(position: const Offset(400, 200)); controller.open(position: const Offset(400, 200));
await tester.pump(); 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 { testWidgets('works with Padding around menu and overlay', (WidgetTester tester) async {
...@@ -483,11 +636,11 @@ void main() { ...@@ -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.byType(MenuBar)), equals(const Rect.fromLTRB(22.0, 22.0, 778.0, 70.0)));
expect( expect(
tester.getRect(find.text(TestMenu.subMenu10.label)), 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( expect(
tester.getRect(find.ancestor(of: find.text(TestMenu.subMenu10.label), matching: find.byType(Material)).at(1)), 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. // Close and make sure it goes back where it was.
...@@ -538,11 +691,11 @@ void main() { ...@@ -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.byType(MenuBar)), equals(const Rect.fromLTRB(22.0, 22.0, 778.0, 70.0)));
expect( expect(
tester.getRect(find.text(TestMenu.subMenu10.label)), 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( expect(
tester.getRect(find.ancestor(of: find.text(TestMenu.subMenu10.label), matching: find.byType(Material)).at(1)), 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. // Close and make sure it goes back where it was.
...@@ -1447,12 +1600,13 @@ void main() { ...@@ -1447,12 +1600,13 @@ void main() {
await tester.pump(); await tester.pump();
expect(find.byType(MenuItemButton), findsNWidgets(6)); expect(find.byType(MenuItemButton), findsNWidgets(6));
expect(find.byType(SubmenuButton), findsNWidgets(4)); expect(find.byType(SubmenuButton), findsNWidgets(5));
final List<Rect> menuRects = collectMenuRects(); final List<Rect> menuRects = collectMenuRects();
expect(menuRects[0], equals(const Rect.fromLTRB(4.0, 0.0, 104.0, 48.0))); expect(menuRects[0], equals(const Rect.fromLTRB(4.0, 0.0, 112.0, 48.0)));
expect(menuRects[1], equals(const Rect.fromLTRB(104.0, 0.0, 204.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(204.0, 0.0, 304.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(104.0, 100.0, 334.0, 148.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 { testWidgets('unconstrained menus show up in the right place in RTL', (WidgetTester tester) async {
...@@ -1488,12 +1642,13 @@ void main() { ...@@ -1488,12 +1642,13 @@ void main() {
await tester.pump(); await tester.pump();
expect(find.byType(MenuItemButton), findsNWidgets(6)); expect(find.byType(MenuItemButton), findsNWidgets(6));
expect(find.byType(SubmenuButton), findsNWidgets(4)); expect(find.byType(SubmenuButton), findsNWidgets(5));
final List<Rect> menuRects = collectMenuRects(); final List<Rect> menuRects = collectMenuRects();
expect(menuRects[0], equals(const Rect.fromLTRB(696.0, 0.0, 796.0, 48.0))); expect(menuRects[0], equals(const Rect.fromLTRB(688.0, 0.0, 796.0, 48.0)));
expect(menuRects[1], equals(const Rect.fromLTRB(596.0, 0.0, 696.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(496.0, 0.0, 596.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(466.0, 100.0, 696.0, 148.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 { testWidgets('constrained menus show up in the right place in LTR', (WidgetTester tester) async {
...@@ -1527,12 +1682,13 @@ void main() { ...@@ -1527,12 +1682,13 @@ void main() {
await tester.pump(); await tester.pump();
expect(find.byType(MenuItemButton), findsNWidgets(6)); expect(find.byType(MenuItemButton), findsNWidgets(6));
expect(find.byType(SubmenuButton), findsNWidgets(4)); expect(find.byType(SubmenuButton), findsNWidgets(5));
final List<Rect> menuRects = collectMenuRects(); final List<Rect> menuRects = collectMenuRects();
expect(menuRects[0], equals(const Rect.fromLTRB(4.0, 0.0, 104.0, 48.0))); expect(menuRects[0], equals(const Rect.fromLTRB(4.0, 0.0, 112.0, 48.0)));
expect(menuRects[1], equals(const Rect.fromLTRB(104.0, 0.0, 204.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(204.0, 0.0, 304.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(70.0, 100.0, 300.0, 148.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 { testWidgets('constrained menus show up in the right place in RTL', (WidgetTester tester) async {
...@@ -1566,12 +1722,13 @@ void main() { ...@@ -1566,12 +1722,13 @@ void main() {
await tester.pump(); await tester.pump();
expect(find.byType(MenuItemButton), findsNWidgets(6)); expect(find.byType(MenuItemButton), findsNWidgets(6));
expect(find.byType(SubmenuButton), findsNWidgets(4)); expect(find.byType(SubmenuButton), findsNWidgets(5));
final List<Rect> menuRects = collectMenuRects(); final List<Rect> menuRects = collectMenuRects();
expect(menuRects[0], equals(const Rect.fromLTRB(196.0, 0.0, 296.0, 48.0))); expect(menuRects[0], equals(const Rect.fromLTRB(188.0, 0.0, 296.0, 48.0)));
expect(menuRects[1], equals(const Rect.fromLTRB(96.0, 0.0, 196.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(-4.0, 0.0, 96.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(0.0, 100.0, 230.0, 148.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({ ...@@ -1783,6 +1940,7 @@ List<Widget> createTestMenus({
MenuItemButton( MenuItemButton(
onPressed: onPressed != null ? () => onPressed(TestMenu.subMenu00) : null, onPressed: onPressed != null ? () => onPressed(TestMenu.subMenu00) : null,
shortcut: shortcuts[TestMenu.subMenu00], shortcut: shortcuts[TestMenu.subMenu00],
leadingIcon: const Icon(Icons.add),
child: Text(TestMenu.subMenu00.label), child: Text(TestMenu.subMenu00.label),
), ),
MenuItemButton( MenuItemButton(
...@@ -1849,6 +2007,7 @@ List<Widget> createTestMenus({ ...@@ -1849,6 +2007,7 @@ List<Widget> createTestMenus({
menuChildren: <Widget>[ menuChildren: <Widget>[
MenuItemButton( MenuItemButton(
// Always disabled. // Always disabled.
leadingIcon: const Icon(Icons.ac_unit),
shortcut: shortcuts[TestMenu.subMenu20], shortcut: shortcuts[TestMenu.subMenu20],
child: Text(TestMenu.subMenu20.label), child: Text(TestMenu.subMenu20.label),
), ),
...@@ -1895,6 +2054,12 @@ List<Widget> createTestMenus({ ...@@ -1895,6 +2054,12 @@ List<Widget> createTestMenus({
], ],
child: Text(TestMenu.mainMenu4.label), 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; return result;
} }
...@@ -1905,6 +2070,7 @@ enum TestMenu { ...@@ -1905,6 +2070,7 @@ enum TestMenu {
mainMenu2('Menu 2'), mainMenu2('Menu 2'),
mainMenu3('Menu 3'), mainMenu3('Menu 3'),
mainMenu4('Menu 4'), mainMenu4('Menu 4'),
mainMenu5('Menu 5'),
subMenu00('Sub Menu 00'), subMenu00('Sub Menu 00'),
subMenu01('Sub Menu 01'), subMenu01('Sub Menu 01'),
subMenu02('Sub Menu 02'), subMenu02('Sub Menu 02'),
......
...@@ -87,13 +87,13 @@ void main() { ...@@ -87,13 +87,13 @@ void main() {
// Open a test menu. // Open a test menu.
await tester.tap(find.text(TestMenu.mainMenu1.label)); await tester.tap(find.text(TestMenu.mainMenu1.label));
await tester.pump(); 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); final Material menuBarMaterial = getMenuBarPanelMaterial(tester);
expect(menuBarMaterial.elevation, equals(15)); expect(menuBarMaterial.elevation, equals(15));
expect(menuBarMaterial.color, equals(Colors.red)); expect(menuBarMaterial.color, equals(Colors.red));
final Material subMenuMaterial = getSubmenuPanelMaterial(tester); 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.elevation, equals(20));
expect(subMenuMaterial.color, equals(Colors.green)); expect(subMenuMaterial.color, equals(Colors.green));
}); });
...@@ -160,19 +160,19 @@ void main() { ...@@ -160,19 +160,19 @@ void main() {
await tester.tap(find.text(TestMenu.mainMenu1.label)); await tester.tap(find.text(TestMenu.mainMenu1.label));
await tester.pump(); 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); final Material menuBarMaterial = getMenuBarPanelMaterial(tester);
expect(menuBarMaterial.elevation, equals(10.0)); expect(menuBarMaterial.elevation, equals(10.0));
expect(menuBarMaterial.color, equals(Colors.blue)); expect(menuBarMaterial.color, equals(Colors.blue));
final Material subMenuMaterial = getSubmenuPanelMaterial(tester); 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.elevation, equals(18));
expect(subMenuMaterial.color, equals(Colors.cyan)); expect(subMenuMaterial.color, equals(Colors.cyan));
expect(subMenuMaterial.shape, equals(const BeveledRectangleBorder())); expect(subMenuMaterial.shape, equals(const BeveledRectangleBorder()));
final Finder menuItem = findSubMenuItem(); 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>( final Material menuItemMaterial = tester.widget<Material>(
find.ancestor(of: find.text(TestMenu.subMenu10.label), matching: find.byType(Material)).first); find.ancestor(of: find.text(TestMenu.subMenu10.label), matching: find.byType(Material)).first);
expect(menuItemMaterial.color, equals(Colors.amber)); expect(menuItemMaterial.color, equals(Colors.amber));
......
...@@ -261,20 +261,20 @@ void main() { ...@@ -261,20 +261,20 @@ void main() {
); );
await tester.pump(); 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. // Open and make sure things are the right size.
await tester.tap(find.text(TestMenu.mainMenu1.label)); await tester.tap(find.text(TestMenu.mainMenu1.label));
await tester.pump(); 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( expect(
tester.getRect(find.text(TestMenu.subMenu10.label)), 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( expect(
tester.getRect(find.ancestor(of: find.text(TestMenu.subMenu10.label), matching: find.byType(Material)).at(1)), 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() { ...@@ -87,13 +87,13 @@ void main() {
// Open a test menu. // Open a test menu.
await tester.tap(find.text(TestMenu.mainMenu1.label)); await tester.tap(find.text(TestMenu.mainMenu1.label));
await tester.pump(); 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); final Material menuBarMaterial = getMenuBarPanelMaterial(tester);
expect(menuBarMaterial.elevation, equals(20)); expect(menuBarMaterial.elevation, equals(20));
expect(menuBarMaterial.color, equals(Colors.green)); expect(menuBarMaterial.color, equals(Colors.green));
final Material subMenuMaterial = getSubmenuPanelMaterial(tester); 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.elevation, equals(15));
expect(subMenuMaterial.color, equals(Colors.red)); expect(subMenuMaterial.color, equals(Colors.red));
}); });
...@@ -160,19 +160,19 @@ void main() { ...@@ -160,19 +160,19 @@ void main() {
await tester.tap(find.text(TestMenu.mainMenu1.label)); await tester.tap(find.text(TestMenu.mainMenu1.label));
await tester.pump(); 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); final Material menuBarMaterial = getMenuBarPanelMaterial(tester);
expect(menuBarMaterial.elevation, equals(10.0)); expect(menuBarMaterial.elevation, equals(10.0));
expect(menuBarMaterial.color, equals(Colors.blue)); expect(menuBarMaterial.color, equals(Colors.blue));
final Material subMenuMaterial = getSubmenuPanelMaterial(tester); 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.elevation, equals(18));
expect(subMenuMaterial.color, equals(Colors.cyan)); expect(subMenuMaterial.color, equals(Colors.cyan));
expect(subMenuMaterial.shape, equals(const BeveledRectangleBorder())); expect(subMenuMaterial.shape, equals(const BeveledRectangleBorder()));
final Finder menuItem = findSubMenuItem(); 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>( final Material menuItemMaterial = tester.widget<Material>(
find.ancestor(of: find.text(TestMenu.subMenu10.label), matching: find.byType(Material)).first); find.ancestor(of: find.text(TestMenu.subMenu10.label), matching: find.byType(Material)).first);
expect(menuItemMaterial.color, equals(Colors.amber)); expect(menuItemMaterial.color, equals(Colors.amber));
......
...@@ -1685,6 +1685,62 @@ void main() { ...@@ -1685,6 +1685,62 @@ void main() {
expect(controller.value, <MaterialState>{MaterialState.disabled}); expect(controller.value, <MaterialState>{MaterialState.disabled});
expect(count, 1); 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) { 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