Replace menu defaults with tokens (#113963)
This commit is contained in:
parent
af34b1041e
commit
3ce88d3813
@ -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();
|
||||
|
240
dev/tools/gen_defaults/lib/menu_template.dart
Normal file
240
dev/tools/gen_defaults/lib/menu_template.dart
Normal file
@ -0,0 +1,240 @@
|
||||
// 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);
|
||||
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 {
|
||||
@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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user