mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Fix DropdownMenu focused item styles (#153159)
## Description This PR fixes the style resolution for selected dropdown menu items (make it possible to provide a custom style and avoid hardcoded values when possible). For the moment, I kept the default selected background which was previously set (`onSurface.withOpacity(0.12)`) to keep this PR focused on its goal which is to make it possible to overrides the defaults item style by providing a custom button style at the theme level or at the menu entry level. ## Related Issue Fixes https://github.com/flutter/flutter/issues/123736. ## Tests Adds 4 tests.
This commit is contained in:
parent
0eaeb0d1c5
commit
2e221e7308
@ -19,6 +19,7 @@ import 'input_border.dart';
|
||||
import 'input_decorator.dart';
|
||||
import 'material_state.dart';
|
||||
import 'menu_anchor.dart';
|
||||
import 'menu_button_theme.dart';
|
||||
import 'menu_style.dart';
|
||||
import 'text_field.dart';
|
||||
import 'theme.dart';
|
||||
@ -107,7 +108,6 @@ class DropdownMenuEntry<T> {
|
||||
final ButtonStyle? style;
|
||||
}
|
||||
|
||||
|
||||
/// A dropdown menu that can be opened from a [TextField]. The selected
|
||||
/// menu item is displayed in that field.
|
||||
///
|
||||
@ -643,14 +643,53 @@ class _DropdownMenuState<T> extends State<DropdownMenu<T>> {
|
||||
// paddings so its leading icon will be aligned with the leading icon of
|
||||
// the text field.
|
||||
final double padding = entry.leadingIcon == null ? (leadingPadding ?? _kDefaultHorizontalPadding) : _kDefaultHorizontalPadding;
|
||||
final ButtonStyle defaultStyle = switch (textDirection) {
|
||||
ButtonStyle effectiveStyle = entry.style ?? switch (textDirection) {
|
||||
TextDirection.rtl => MenuItemButton.styleFrom(padding: EdgeInsets.only(left: _kDefaultHorizontalPadding, right: padding)),
|
||||
TextDirection.ltr => MenuItemButton.styleFrom(padding: EdgeInsets.only(left: padding, right: _kDefaultHorizontalPadding)),
|
||||
};
|
||||
|
||||
ButtonStyle effectiveStyle = entry.style ?? defaultStyle;
|
||||
final Color focusedBackgroundColor = effectiveStyle.foregroundColor?.resolve(<MaterialState>{MaterialState.focused})
|
||||
?? Theme.of(context).colorScheme.onSurface;
|
||||
final ButtonStyle? themeStyle = MenuButtonTheme.of(context).style;
|
||||
|
||||
final WidgetStateProperty<Color?>? effectiveForegroundColor = entry.style?.foregroundColor ?? themeStyle?.foregroundColor;
|
||||
final WidgetStateProperty<Color?>? effectiveIconColor = entry.style?.iconColor ?? themeStyle?.iconColor;
|
||||
final WidgetStateProperty<Color?>? effectiveOverlayColor = entry.style?.overlayColor ?? themeStyle?.overlayColor;
|
||||
final WidgetStateProperty<Color?>? effectiveBackgroundColor = entry.style?.backgroundColor ?? themeStyle?.backgroundColor;
|
||||
|
||||
// Simulate the focused state because the text field should always be focused
|
||||
// during traversal. Include potential MenuItemButton theme in the focus
|
||||
// simulation for all colors in the theme.
|
||||
if (entry.enabled && i == focusedIndex) {
|
||||
// Query the Material 3 default style.
|
||||
// TODO(bleroux): replace once a standard way for accessing defaults will be defined.
|
||||
// See: https://github.com/flutter/flutter/issues/130135.
|
||||
final ButtonStyle defaultStyle = const MenuItemButton().defaultStyleOf(context);
|
||||
|
||||
Color? resolveFocusedColor(WidgetStateProperty<Color?>? colorStateProperty) {
|
||||
return colorStateProperty?.resolve(<MaterialState>{MaterialState.focused});
|
||||
}
|
||||
|
||||
final Color focusedForegroundColor = resolveFocusedColor(effectiveForegroundColor ?? defaultStyle.foregroundColor!)!;
|
||||
final Color focusedIconColor = resolveFocusedColor(effectiveIconColor ?? defaultStyle.iconColor!)!;
|
||||
final Color focusedOverlayColor = resolveFocusedColor(effectiveOverlayColor ?? defaultStyle.overlayColor!)!;
|
||||
// For the background color we can't rely on the default style which is transparent.
|
||||
// Defaults to onSurface.withOpacity(0.12).
|
||||
final Color focusedBackgroundColor = resolveFocusedColor(effectiveBackgroundColor)
|
||||
?? Theme.of(context).colorScheme.onSurface.withOpacity(0.12);
|
||||
|
||||
effectiveStyle = effectiveStyle.copyWith(
|
||||
backgroundColor: MaterialStatePropertyAll<Color>(focusedBackgroundColor),
|
||||
foregroundColor: MaterialStatePropertyAll<Color>(focusedForegroundColor),
|
||||
iconColor: MaterialStatePropertyAll<Color>(focusedIconColor),
|
||||
overlayColor: MaterialStatePropertyAll<Color>(focusedOverlayColor),
|
||||
);
|
||||
} else {
|
||||
effectiveStyle = effectiveStyle.copyWith(
|
||||
backgroundColor: effectiveBackgroundColor,
|
||||
foregroundColor: effectiveForegroundColor,
|
||||
iconColor: effectiveIconColor,
|
||||
overlayColor: effectiveOverlayColor,
|
||||
);
|
||||
}
|
||||
|
||||
Widget label = entry.labelWidget ?? Text(entry.label);
|
||||
if (widget.width != null) {
|
||||
@ -661,15 +700,6 @@ class _DropdownMenuState<T> extends State<DropdownMenu<T>> {
|
||||
);
|
||||
}
|
||||
|
||||
// Simulate the focused state because the text field should always be focused
|
||||
// during traversal. If the menu item has a custom foreground color, the "focused"
|
||||
// color will also change to foregroundColor.withOpacity(0.12).
|
||||
effectiveStyle = entry.enabled && i == focusedIndex
|
||||
? effectiveStyle.copyWith(
|
||||
backgroundColor: MaterialStatePropertyAll<Color>(focusedBackgroundColor.withOpacity(0.12))
|
||||
)
|
||||
: effectiveStyle;
|
||||
|
||||
final Widget menuItemButton = MenuItemButton(
|
||||
key: enableScrollToHighlight ? buttonItemKeys[i] : null,
|
||||
style: effectiveStyle,
|
||||
|
@ -13,12 +13,26 @@ void main() {
|
||||
|
||||
const String longText = 'one two three four five six seven eight nine ten eleven twelve';
|
||||
final List<DropdownMenuEntry<TestMenu>> menuChildren = <DropdownMenuEntry<TestMenu>>[];
|
||||
final List<DropdownMenuEntry<TestMenu>> menuChildrenWithIcons = <DropdownMenuEntry<TestMenu>>[];
|
||||
|
||||
for (final TestMenu value in TestMenu.values) {
|
||||
final DropdownMenuEntry<TestMenu> entry = DropdownMenuEntry<TestMenu>(value: value, label: value.label);
|
||||
menuChildren.add(entry);
|
||||
}
|
||||
|
||||
ValueKey<String> leadingIconKey(TestMenu menuEntry) => ValueKey<String>('leading-${menuEntry.label}');
|
||||
ValueKey<String> trailingIconKey(TestMenu menuEntry) => ValueKey<String>('trailing-${menuEntry.label}');
|
||||
|
||||
for (final TestMenu value in TestMenu.values) {
|
||||
final DropdownMenuEntry<TestMenu> entry = DropdownMenuEntry<TestMenu>(
|
||||
value: value,
|
||||
label: value.label,
|
||||
leadingIcon: Icon(key: leadingIconKey(value), Icons.alarm),
|
||||
trailingIcon: Icon(key: trailingIconKey(value), Icons.abc),
|
||||
);
|
||||
menuChildrenWithIcons.add(entry);
|
||||
}
|
||||
|
||||
Widget buildTest<T extends Enum>(ThemeData themeData, List<DropdownMenuEntry<T>> entries,
|
||||
{double? width, double? menuHeight, Widget? leadingIcon, Widget? label}) {
|
||||
return MaterialApp(
|
||||
@ -35,6 +49,13 @@ void main() {
|
||||
);
|
||||
}
|
||||
|
||||
Material getButtonMaterial(WidgetTester tester, String itemLabel) {
|
||||
return tester.widget<Material>(find.descendant(
|
||||
of: find.widgetWithText(MenuItemButton, itemLabel).last,
|
||||
matching: find.byType(Material),
|
||||
));
|
||||
}
|
||||
|
||||
testWidgets('DropdownMenu defaults', (WidgetTester tester) async {
|
||||
final ThemeData themeData = ThemeData();
|
||||
await tester.pumpWidget(buildTest(themeData, menuChildren));
|
||||
@ -83,6 +104,356 @@ void main() {
|
||||
expect(material.textStyle?.height, 1.43);
|
||||
});
|
||||
|
||||
group('Item style', () {
|
||||
const Color focusedBackgroundColor = Color(0xffff0000);
|
||||
const Color focusedForegroundColor = Color(0xff00ff00);
|
||||
const Color focusedIconColor = Color(0xff0000ff);
|
||||
const Color focusedOverlayColor = Color(0xffff00ff);
|
||||
const Color defaultBackgroundColor = Color(0xff00ffff);
|
||||
const Color defaultForegroundColor = Color(0xff000000);
|
||||
const Color defaultIconColor = Color(0xffffffff);
|
||||
const Color defaultOverlayColor = Color(0xffffff00);
|
||||
|
||||
final ButtonStyle customButtonStyle = ButtonStyle(
|
||||
backgroundColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.focused)) {
|
||||
return focusedBackgroundColor;
|
||||
}
|
||||
return defaultBackgroundColor;
|
||||
}),
|
||||
foregroundColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.focused)) {
|
||||
return focusedForegroundColor;
|
||||
}
|
||||
return defaultForegroundColor;
|
||||
}),
|
||||
iconColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.focused)) {
|
||||
return focusedIconColor;
|
||||
}
|
||||
return defaultIconColor;
|
||||
}),
|
||||
overlayColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.focused)) {
|
||||
return focusedOverlayColor;
|
||||
}
|
||||
return defaultOverlayColor;
|
||||
}),
|
||||
);
|
||||
|
||||
final List<DropdownMenuEntry<TestMenu>> styledMenuEntries = <DropdownMenuEntry<TestMenu>>[];
|
||||
for (final DropdownMenuEntry<TestMenu> entryWithIcons in menuChildrenWithIcons) {
|
||||
styledMenuEntries.add(DropdownMenuEntry<TestMenu>(
|
||||
value: entryWithIcons.value,
|
||||
label: entryWithIcons.label,
|
||||
leadingIcon: entryWithIcons.leadingIcon,
|
||||
trailingIcon: entryWithIcons.trailingIcon,
|
||||
style: customButtonStyle,
|
||||
));
|
||||
}
|
||||
|
||||
TextStyle? iconStyle(WidgetTester tester, Key key) {
|
||||
final RichText iconRichText = tester.widget<RichText>(
|
||||
find.descendant(of: find.byKey(key), matching: find.byType(RichText)).last,
|
||||
);
|
||||
return iconRichText.text.style;
|
||||
}
|
||||
|
||||
RenderObject overlayPainter(WidgetTester tester, TestMenu menuItem) {
|
||||
return tester.renderObject(find.descendant(
|
||||
of: find.widgetWithText(MenuItemButton, menuItem.label).last,
|
||||
matching: find.byElementPredicate(
|
||||
(Element element) => element.renderObject.runtimeType.toString() == '_RenderInkFeatures',
|
||||
),
|
||||
).last);
|
||||
}
|
||||
|
||||
testWidgets('defaults are correct', (WidgetTester tester) async {
|
||||
const TestMenu selectedItem = TestMenu.mainMenu3;
|
||||
const TestMenu nonSelectedItem = TestMenu.mainMenu2;
|
||||
|
||||
final ThemeData themeData = ThemeData();
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
theme: themeData,
|
||||
home: Scaffold(
|
||||
body: DropdownMenu<TestMenu>(
|
||||
initialSelection: selectedItem,
|
||||
dropdownMenuEntries: menuChildrenWithIcons,
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
// Open the menu.
|
||||
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
||||
await tester.pump();
|
||||
|
||||
final Material selectedButtonMaterial = getButtonMaterial(tester, selectedItem.label);
|
||||
expect(selectedButtonMaterial.color, themeData.colorScheme.onSurface.withOpacity(0.12));
|
||||
expect(selectedButtonMaterial.textStyle?.color, themeData.colorScheme.onSurface);
|
||||
expect(iconStyle(tester, leadingIconKey(selectedItem))?.color, themeData.colorScheme.onSurfaceVariant);
|
||||
|
||||
final Material nonSelectedButtonMaterial = getButtonMaterial(tester, nonSelectedItem.label);
|
||||
expect(nonSelectedButtonMaterial.color, Colors.transparent);
|
||||
expect(nonSelectedButtonMaterial.textStyle?.color, themeData.colorScheme.onSurface);
|
||||
expect(iconStyle(tester, leadingIconKey(nonSelectedItem))?.color, themeData.colorScheme.onSurfaceVariant);
|
||||
|
||||
// Hover the selected item.
|
||||
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
|
||||
addTearDown(() async {
|
||||
return gesture.removePointer();
|
||||
});
|
||||
await gesture.addPointer();
|
||||
await gesture.moveTo(tester.getCenter(find.widgetWithText(MenuItemButton, selectedItem.label).last));
|
||||
await tester.pump();
|
||||
|
||||
expect(
|
||||
overlayPainter(tester, selectedItem),
|
||||
paints..rect(color: themeData.colorScheme.onSurface.withOpacity(0.1).withAlpha(0)),
|
||||
);
|
||||
|
||||
// Hover a non-selected item.
|
||||
await gesture.moveTo(tester.getCenter(find.widgetWithText(MenuItemButton, nonSelectedItem.label).last));
|
||||
await tester.pump();
|
||||
|
||||
expect(
|
||||
overlayPainter(tester, nonSelectedItem),
|
||||
paints..rect(color: themeData.colorScheme.onSurface.withOpacity(0.08).withAlpha(0)),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('can be overridden at application theme level', (WidgetTester tester) async {
|
||||
const TestMenu selectedItem = TestMenu.mainMenu3;
|
||||
const TestMenu nonSelectedItem = TestMenu.mainMenu2;
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
theme: ThemeData(menuButtonTheme: MenuButtonThemeData(style: customButtonStyle)),
|
||||
home: Scaffold(
|
||||
body: DropdownMenu<TestMenu>(
|
||||
initialSelection: selectedItem,
|
||||
dropdownMenuEntries: menuChildrenWithIcons,
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
// Open the menu.
|
||||
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
||||
await tester.pump();
|
||||
|
||||
final Material selectedButtonMaterial = getButtonMaterial(tester, selectedItem.label);
|
||||
expect(selectedButtonMaterial.color, focusedBackgroundColor);
|
||||
expect(selectedButtonMaterial.textStyle?.color, focusedForegroundColor);
|
||||
expect(iconStyle(tester, leadingIconKey(selectedItem))?.color, focusedIconColor);
|
||||
|
||||
final Material nonSelectedButtonMaterial = getButtonMaterial(tester, nonSelectedItem.label);
|
||||
expect(nonSelectedButtonMaterial.color, defaultBackgroundColor);
|
||||
expect(nonSelectedButtonMaterial.textStyle?.color, defaultForegroundColor);
|
||||
expect(iconStyle(tester, leadingIconKey(nonSelectedItem))?.color, defaultIconColor);
|
||||
|
||||
// Hover the selected item.
|
||||
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
|
||||
addTearDown(() async {
|
||||
return gesture.removePointer();
|
||||
});
|
||||
await gesture.addPointer();
|
||||
await gesture.moveTo(tester.getCenter(find.widgetWithText(MenuItemButton, selectedItem.label).last));
|
||||
await tester.pump();
|
||||
|
||||
expect(
|
||||
overlayPainter(tester, selectedItem),
|
||||
paints..rect(color: focusedOverlayColor.withAlpha(0)),
|
||||
);
|
||||
|
||||
// Hover a non-selected item.
|
||||
await gesture.moveTo(tester.getCenter(find.widgetWithText(MenuItemButton, nonSelectedItem.label).last));
|
||||
await tester.pump();
|
||||
|
||||
expect(
|
||||
overlayPainter(tester, nonSelectedItem),
|
||||
paints..rect(color: defaultOverlayColor.withAlpha(0)),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('can be overridden at menu entry level', (WidgetTester tester) async {
|
||||
const TestMenu selectedItem = TestMenu.mainMenu3;
|
||||
const TestMenu nonSelectedItem = TestMenu.mainMenu2;
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: Scaffold(
|
||||
body: DropdownMenu<TestMenu>(
|
||||
initialSelection: selectedItem,
|
||||
dropdownMenuEntries: styledMenuEntries,
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
// Open the menu.
|
||||
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
||||
await tester.pump();
|
||||
|
||||
final Material selectedButtonMaterial = getButtonMaterial(tester, selectedItem.label);
|
||||
expect(selectedButtonMaterial.color, focusedBackgroundColor);
|
||||
expect(selectedButtonMaterial.textStyle?.color, focusedForegroundColor);
|
||||
expect(iconStyle(tester, leadingIconKey(selectedItem))?.color, focusedIconColor);
|
||||
|
||||
final Material nonSelectedButtonMaterial = getButtonMaterial(tester, nonSelectedItem.label);
|
||||
expect(nonSelectedButtonMaterial.color, defaultBackgroundColor);
|
||||
expect(nonSelectedButtonMaterial.textStyle?.color, defaultForegroundColor);
|
||||
expect(iconStyle(tester, leadingIconKey(nonSelectedItem))?.color, defaultIconColor);
|
||||
|
||||
// Hover the selected item.
|
||||
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
|
||||
addTearDown(() async {
|
||||
return gesture.removePointer();
|
||||
});
|
||||
await gesture.addPointer();
|
||||
await gesture.moveTo(tester.getCenter(find.widgetWithText(MenuItemButton, selectedItem.label).last));
|
||||
await tester.pump();
|
||||
|
||||
expect(
|
||||
overlayPainter(tester, selectedItem),
|
||||
paints..rect(color: focusedOverlayColor.withAlpha(0)),
|
||||
);
|
||||
|
||||
// Hover a non-selected item.
|
||||
await gesture.moveTo(tester.getCenter(find.widgetWithText(MenuItemButton, nonSelectedItem.label).last));
|
||||
await tester.pump();
|
||||
|
||||
expect(
|
||||
overlayPainter(tester, nonSelectedItem),
|
||||
paints..rect(color: defaultOverlayColor.withAlpha(0)),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('defined at menu entry level takes precedence', (WidgetTester tester) async {
|
||||
const TestMenu selectedItem = TestMenu.mainMenu3;
|
||||
const TestMenu nonSelectedItem = TestMenu.mainMenu2;
|
||||
|
||||
const Color luckyColor = Color(0xff777777);
|
||||
final ButtonStyle singleColorButtonStyle = ButtonStyle(
|
||||
backgroundColor: MaterialStateProperty.all(luckyColor),
|
||||
foregroundColor: MaterialStateProperty.all(luckyColor),
|
||||
iconColor: MaterialStateProperty.all(luckyColor),
|
||||
overlayColor: MaterialStateProperty.all(luckyColor),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
theme: ThemeData(menuButtonTheme: MenuButtonThemeData(style: singleColorButtonStyle)),
|
||||
home: Scaffold(
|
||||
body: DropdownMenu<TestMenu>(
|
||||
initialSelection: selectedItem,
|
||||
dropdownMenuEntries: styledMenuEntries,
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
// Open the menu.
|
||||
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
||||
await tester.pump();
|
||||
|
||||
final Material selectedButtonMaterial = getButtonMaterial(tester, selectedItem.label);
|
||||
expect(selectedButtonMaterial.color, focusedBackgroundColor);
|
||||
expect(selectedButtonMaterial.textStyle?.color, focusedForegroundColor);
|
||||
expect(iconStyle(tester, leadingIconKey(selectedItem))?.color, focusedIconColor);
|
||||
|
||||
final Material nonSelectedButtonMaterial = getButtonMaterial(tester, nonSelectedItem.label);
|
||||
expect(nonSelectedButtonMaterial.color, defaultBackgroundColor);
|
||||
expect(nonSelectedButtonMaterial.textStyle?.color, defaultForegroundColor);
|
||||
expect(iconStyle(tester, leadingIconKey(nonSelectedItem))?.color, defaultIconColor);
|
||||
|
||||
// Hover the selected item.
|
||||
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
|
||||
addTearDown(() async {
|
||||
return gesture.removePointer();
|
||||
});
|
||||
await gesture.addPointer();
|
||||
await gesture.moveTo(tester.getCenter(find.widgetWithText(MenuItemButton, selectedItem.label).last));
|
||||
await tester.pump();
|
||||
|
||||
expect(
|
||||
overlayPainter(tester, selectedItem),
|
||||
paints..rect(color: focusedOverlayColor.withAlpha(0)),
|
||||
);
|
||||
|
||||
// Hover a non-selected item.
|
||||
await gesture.moveTo(tester.getCenter(find.widgetWithText(MenuItemButton, nonSelectedItem.label).last));
|
||||
await tester.pump();
|
||||
|
||||
expect(
|
||||
overlayPainter(tester, nonSelectedItem),
|
||||
paints..rect(color: defaultOverlayColor.withAlpha(0)),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('defined at menu entry level and application level are merged', (WidgetTester tester) async {
|
||||
const TestMenu selectedItem = TestMenu.mainMenu3;
|
||||
const TestMenu nonSelectedItem = TestMenu.mainMenu2;
|
||||
|
||||
const Color luckyColor = Color(0xff777777);
|
||||
final ButtonStyle partialButtonStyle = ButtonStyle(
|
||||
backgroundColor: MaterialStateProperty.all(luckyColor),
|
||||
foregroundColor: MaterialStateProperty.all(luckyColor),
|
||||
);
|
||||
|
||||
final List<DropdownMenuEntry<TestMenu>> partiallyStyledMenuEntries = <DropdownMenuEntry<TestMenu>>[];
|
||||
for (final DropdownMenuEntry<TestMenu> entryWithIcons in menuChildrenWithIcons) {
|
||||
partiallyStyledMenuEntries.add(DropdownMenuEntry<TestMenu>(
|
||||
value: entryWithIcons.value,
|
||||
label: entryWithIcons.label,
|
||||
leadingIcon: entryWithIcons.leadingIcon,
|
||||
trailingIcon: entryWithIcons.trailingIcon,
|
||||
style: partialButtonStyle,
|
||||
));
|
||||
}
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
theme: ThemeData(menuButtonTheme: MenuButtonThemeData(style: customButtonStyle)),
|
||||
home: Scaffold(
|
||||
body: DropdownMenu<TestMenu>(
|
||||
initialSelection: selectedItem,
|
||||
dropdownMenuEntries: partiallyStyledMenuEntries,
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
// Open the menu.
|
||||
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
||||
await tester.pump();
|
||||
|
||||
final Material selectedButtonMaterial = getButtonMaterial(tester, selectedItem.label);
|
||||
expect(selectedButtonMaterial.color, luckyColor);
|
||||
expect(selectedButtonMaterial.textStyle?.color, luckyColor);
|
||||
expect(iconStyle(tester, leadingIconKey(selectedItem))?.color, focusedIconColor);
|
||||
|
||||
final Material nonSelectedButtonMaterial = getButtonMaterial(tester, nonSelectedItem.label);
|
||||
expect(nonSelectedButtonMaterial.color, luckyColor);
|
||||
expect(nonSelectedButtonMaterial.textStyle?.color, luckyColor);
|
||||
expect(iconStyle(tester, leadingIconKey(nonSelectedItem))?.color, defaultIconColor);
|
||||
|
||||
// Hover the selected item.
|
||||
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
|
||||
addTearDown(() async {
|
||||
return gesture.removePointer();
|
||||
});
|
||||
await gesture.addPointer();
|
||||
await gesture.moveTo(tester.getCenter(find.widgetWithText(MenuItemButton, selectedItem.label).last));
|
||||
await tester.pump();
|
||||
|
||||
expect(
|
||||
overlayPainter(tester, selectedItem),
|
||||
paints..rect(color: focusedOverlayColor.withAlpha(0)),
|
||||
);
|
||||
|
||||
// Hover a non-selected item.
|
||||
await gesture.moveTo(tester.getCenter(find.widgetWithText(MenuItemButton, nonSelectedItem.label).last));
|
||||
await tester.pump();
|
||||
|
||||
expect(
|
||||
overlayPainter(tester, nonSelectedItem),
|
||||
paints..rect(color: defaultOverlayColor.withAlpha(0)),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('Inner TextField is disabled when DropdownMenu is disabled', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
@ -344,7 +715,6 @@ void main() {
|
||||
final Rect dropdownMenuRect = tester.getRect(find.byType(TextField));
|
||||
expect(dropdownMenuRect.top, containerRect.top);
|
||||
|
||||
|
||||
await tester.tap(find.byType(TextField));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user