mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
907 lines
34 KiB
Dart
907 lines
34 KiB
Dart
// 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 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
void main() {
|
|
final List<DropdownMenuEntry<TestMenu>> menuChildren = <DropdownMenuEntry<TestMenu>>[];
|
|
|
|
for (final TestMenu value in TestMenu.values) {
|
|
final DropdownMenuEntry<TestMenu> entry = DropdownMenuEntry<TestMenu>(value: value, label: value.label);
|
|
menuChildren.add(entry);
|
|
}
|
|
|
|
Widget buildTest<T extends Enum>(ThemeData themeData, List<DropdownMenuEntry<T>> entries,
|
|
{double? width, double? menuHeight, Widget? leadingIcon, Widget? label}) {
|
|
return MaterialApp(
|
|
theme: themeData,
|
|
home: Scaffold(
|
|
body: DropdownMenu<T>(
|
|
label: label,
|
|
leadingIcon: leadingIcon,
|
|
width: width,
|
|
menuHeight: menuHeight,
|
|
dropdownMenuEntries: entries,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
testWidgets('DropdownMenu defaults', (WidgetTester tester) async {
|
|
final ThemeData themeData = ThemeData();
|
|
await tester.pumpWidget(buildTest(themeData, menuChildren));
|
|
|
|
final EditableText editableText = tester.widget(find.byType(EditableText));
|
|
expect(editableText.style.color, themeData.textTheme.labelLarge!.color);
|
|
expect(editableText.style.background, themeData.textTheme.labelLarge!.background);
|
|
expect(editableText.style.shadows, themeData.textTheme.labelLarge!.shadows);
|
|
expect(editableText.style.decoration, themeData.textTheme.labelLarge!.decoration);
|
|
expect(editableText.style.locale, themeData.textTheme.labelLarge!.locale);
|
|
expect(editableText.style.wordSpacing, themeData.textTheme.labelLarge!.wordSpacing);
|
|
|
|
final TextField textField = tester.widget(find.byType(TextField));
|
|
expect(textField.decoration?.border, const OutlineInputBorder());
|
|
|
|
await tester.tap(find.widgetWithIcon(IconButton, Icons.arrow_drop_down).first);
|
|
await tester.pump();
|
|
expect(find.byType(MenuAnchor), findsOneWidget);
|
|
|
|
final Finder menuMaterial = find.ancestor(
|
|
of: find.widgetWithText(TextButton, TestMenu.mainMenu0.label),
|
|
matching: find.byType(Material),
|
|
).last;
|
|
Material 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))));
|
|
|
|
final Finder buttonMaterial = find.descendant(
|
|
of: find.byType(TextButton),
|
|
matching: find.byType(Material),
|
|
).last;
|
|
|
|
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);
|
|
});
|
|
|
|
testWidgets('DropdownMenu can be disabled', (WidgetTester tester) async {
|
|
final ThemeData themeData = ThemeData();
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: themeData,
|
|
home: Scaffold(
|
|
body: SafeArea(
|
|
child: DropdownMenu<TestMenu>(
|
|
enabled: false,
|
|
dropdownMenuEntries: menuChildren,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final TextField textField = tester.widget(find.byType(TextField));
|
|
expect(textField.decoration?.enabled, false);
|
|
final Finder menuMaterial = find.ancestor(
|
|
of: find.byType(SingleChildScrollView),
|
|
matching: find.byType(Material),
|
|
);
|
|
expect(menuMaterial, findsNothing);
|
|
|
|
await tester.tap(find.byType(TextField));
|
|
await tester.pump();
|
|
final Finder updatedMenuMaterial = find.ancestor(
|
|
of: find.byType(SingleChildScrollView),
|
|
matching: find.byType(Material),
|
|
);
|
|
expect(updatedMenuMaterial, findsNothing);
|
|
});
|
|
|
|
testWidgets('The width of the text field should always be the same as the menu view',
|
|
(WidgetTester tester) async {
|
|
|
|
final ThemeData themeData = ThemeData();
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: themeData,
|
|
home: Scaffold(
|
|
body: SafeArea(
|
|
child: DropdownMenu<TestMenu>(
|
|
dropdownMenuEntries: menuChildren,
|
|
),
|
|
),
|
|
),
|
|
)
|
|
);
|
|
|
|
final Finder textField = find.byType(TextField);
|
|
final Size anchorSize = tester.getSize(textField);
|
|
expect(anchorSize, const Size(180.0, 54.0));
|
|
|
|
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
|
await tester.pumpAndSettle();
|
|
|
|
final Finder menuMaterial = find.ancestor(
|
|
of: find.byType(SingleChildScrollView),
|
|
matching: find.byType(Material),
|
|
);
|
|
final Size menuSize = tester.getSize(menuMaterial);
|
|
expect(menuSize, const Size(180.0, 304.0));
|
|
|
|
// The text field should have same width as the menu
|
|
// when the width property is not null.
|
|
await tester.pumpWidget(buildTest(themeData, menuChildren, width: 200.0));
|
|
|
|
final Finder anchor = find.byType(TextField);
|
|
final Size size = tester.getSize(anchor);
|
|
expect(size, const Size(200.0, 54.0));
|
|
|
|
await tester.tap(anchor);
|
|
await tester.pumpAndSettle();
|
|
|
|
final Finder updatedMenu = find.ancestor(
|
|
of: find.byType(SingleChildScrollView),
|
|
matching: find.byType(Material),
|
|
);
|
|
final Size updatedMenuSize = tester.getSize(updatedMenu);
|
|
expect(updatedMenuSize, const Size(200.0, 304.0));
|
|
});
|
|
|
|
testWidgets('The width property can customize the width of the dropdown menu', (WidgetTester tester) async {
|
|
final ThemeData themeData = ThemeData();
|
|
final List<DropdownMenuEntry<ShortMenu>> shortMenuItems = <DropdownMenuEntry<ShortMenu>>[];
|
|
|
|
for (final ShortMenu value in ShortMenu.values) {
|
|
final DropdownMenuEntry<ShortMenu> entry = DropdownMenuEntry<ShortMenu>(value: value, label: value.label);
|
|
shortMenuItems.add(entry);
|
|
}
|
|
|
|
const double customBigWidth = 250.0;
|
|
await tester.pumpWidget(buildTest(themeData, shortMenuItems, width: customBigWidth));
|
|
RenderBox box = tester.firstRenderObject(find.byType(DropdownMenu<ShortMenu>));
|
|
expect(box.size.width, customBigWidth);
|
|
|
|
await tester.tap(find.byType(DropdownMenu<ShortMenu>));
|
|
await tester.pump();
|
|
expect(find.byType(MenuItemButton), findsNWidgets(6));
|
|
Size buttonSize = tester.getSize(find.widgetWithText(MenuItemButton, 'I0').last);
|
|
expect(buttonSize.width, customBigWidth);
|
|
|
|
// reset test
|
|
await tester.pumpWidget(Container());
|
|
const double customSmallWidth = 100.0;
|
|
await tester.pumpWidget(buildTest(themeData, shortMenuItems, width: customSmallWidth));
|
|
box = tester.firstRenderObject(find.byType(DropdownMenu<ShortMenu>));
|
|
expect(box.size.width, customSmallWidth);
|
|
|
|
await tester.tap(find.byType(DropdownMenu<ShortMenu>));
|
|
await tester.pump();
|
|
expect(find.byType(MenuItemButton), findsNWidgets(6));
|
|
buttonSize = tester.getSize(find.widgetWithText(MenuItemButton, 'I0').last);
|
|
expect(buttonSize.width, customSmallWidth);
|
|
});
|
|
|
|
testWidgets('The menuHeight property can be used to show a shorter scrollable menu list instead of the complete list',
|
|
(WidgetTester tester) async {
|
|
final ThemeData themeData = ThemeData();
|
|
await tester.pumpWidget(buildTest(themeData, menuChildren));
|
|
|
|
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
|
await tester.pumpAndSettle();
|
|
|
|
final Element firstItem = tester.element(find.widgetWithText(MenuItemButton, 'Item 0').last);
|
|
final RenderBox firstBox = firstItem.renderObject! as RenderBox;
|
|
final Offset topLeft = firstBox.localToGlobal(firstBox.size.topLeft(Offset.zero));
|
|
final Element lastItem = tester.element(find.widgetWithText(MenuItemButton, 'Item 5').last);
|
|
final RenderBox lastBox = lastItem.renderObject! as RenderBox;
|
|
final Offset bottomRight = lastBox.localToGlobal(lastBox.size.bottomRight(Offset.zero));
|
|
// height = height of MenuItemButton * 6 = 48 * 6
|
|
expect(bottomRight.dy - topLeft.dy, 288.0);
|
|
|
|
final Finder menuView = find.ancestor(
|
|
of: find.byType(SingleChildScrollView),
|
|
matching: find.byType(Padding),
|
|
).first;
|
|
final Size menuViewSize = tester.getSize(menuView);
|
|
expect(menuViewSize, const Size(180.0, 304.0)); // 304 = 288 + vertical padding(2 * 8)
|
|
|
|
// Constrains the menu height.
|
|
await tester.pumpWidget(Container());
|
|
await tester.pumpWidget(buildTest(themeData, menuChildren, menuHeight: 100));
|
|
await tester.pumpAndSettle();
|
|
|
|
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
|
await tester.pumpAndSettle();
|
|
|
|
final Finder updatedMenu = find.ancestor(
|
|
of: find.byType(SingleChildScrollView),
|
|
matching: find.byType(Padding),
|
|
).first;
|
|
|
|
final Size updatedMenuSize = tester.getSize(updatedMenu);
|
|
expect(updatedMenuSize, const Size(180.0, 100.0));
|
|
});
|
|
|
|
testWidgets('The text in the menu button should be aligned with the text of '
|
|
'the text field - LTR', (WidgetTester tester) async {
|
|
final ThemeData themeData = ThemeData();
|
|
// Default text field (without leading icon).
|
|
await tester.pumpWidget(buildTest(themeData, menuChildren, label: const Text('label')));
|
|
|
|
final Finder label = find.text('label');
|
|
final Offset labelTopLeft = tester.getTopLeft(label);
|
|
|
|
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
|
await tester.pumpAndSettle();
|
|
final Finder itemText = find.text('Item 0').last;
|
|
final Offset itemTextTopLeft = tester.getTopLeft(itemText);
|
|
|
|
expect(labelTopLeft.dx, equals(itemTextTopLeft.dx));
|
|
|
|
// Test when the text field has a leading icon.
|
|
await tester.pumpWidget(Container());
|
|
await tester.pumpWidget(buildTest(themeData, menuChildren,
|
|
leadingIcon: const Icon(Icons.search),
|
|
label: const Text('label'),
|
|
));
|
|
|
|
final Finder leadingIcon = find.widgetWithIcon(Container, Icons.search);
|
|
final double iconWidth = tester.getSize(leadingIcon).width;
|
|
final Finder updatedLabel = find.text('label');
|
|
final Offset updatedLabelTopLeft = tester.getTopLeft(updatedLabel);
|
|
|
|
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
|
await tester.pumpAndSettle();
|
|
final Finder updatedItemText = find.text('Item 0').last;
|
|
final Offset updatedItemTextTopLeft = tester.getTopLeft(updatedItemText);
|
|
|
|
|
|
expect(updatedLabelTopLeft.dx, equals(updatedItemTextTopLeft.dx));
|
|
expect(updatedLabelTopLeft.dx, equals(iconWidth));
|
|
|
|
// Test when then leading icon is a widget with a bigger size.
|
|
await tester.pumpWidget(Container());
|
|
await tester.pumpWidget(buildTest(themeData, menuChildren,
|
|
leadingIcon: const SizedBox(
|
|
width: 75.0,
|
|
child: Icon(Icons.search)),
|
|
label: const Text('label'),
|
|
));
|
|
|
|
final Finder largeLeadingIcon = find.widgetWithIcon(Container, Icons.search);
|
|
final double largeIconWidth = tester.getSize(largeLeadingIcon).width;
|
|
final Finder updatedLabel1 = find.text('label');
|
|
final Offset updatedLabelTopLeft1 = tester.getTopLeft(updatedLabel1);
|
|
|
|
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
|
await tester.pumpAndSettle();
|
|
final Finder updatedItemText1 = find.text('Item 0').last;
|
|
final Offset updatedItemTextTopLeft1 = tester.getTopLeft(updatedItemText1);
|
|
|
|
|
|
expect(updatedLabelTopLeft1.dx, equals(updatedItemTextTopLeft1.dx));
|
|
expect(updatedLabelTopLeft1.dx, equals(largeIconWidth));
|
|
});
|
|
|
|
testWidgets('The text in the menu button should be aligned with the text of '
|
|
'the text field - RTL', (WidgetTester tester) async {
|
|
final ThemeData themeData = ThemeData();
|
|
// Default text field (without leading icon).
|
|
await tester.pumpWidget(MaterialApp(
|
|
theme: themeData,
|
|
home: Scaffold(
|
|
body: Directionality(
|
|
textDirection: TextDirection.rtl,
|
|
child: DropdownMenu<TestMenu>(
|
|
label: const Text('label'),
|
|
dropdownMenuEntries: menuChildren,
|
|
),
|
|
),
|
|
),
|
|
));
|
|
|
|
final Finder label = find.text('label');
|
|
final Offset labelTopRight = tester.getTopRight(label);
|
|
|
|
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
|
await tester.pumpAndSettle();
|
|
final Finder itemText = find.text('Item 0').last;
|
|
final Offset itemTextTopRight = tester.getTopRight(itemText);
|
|
|
|
expect(labelTopRight.dx, equals(itemTextTopRight.dx));
|
|
|
|
// Test when the text field has a leading icon.
|
|
await tester.pumpWidget(Container());
|
|
await tester.pumpWidget(MaterialApp(
|
|
theme: themeData,
|
|
home: Scaffold(
|
|
body: Directionality(
|
|
textDirection: TextDirection.rtl,
|
|
child: DropdownMenu<TestMenu>(
|
|
leadingIcon: const Icon(Icons.search),
|
|
label: const Text('label'),
|
|
dropdownMenuEntries: menuChildren,
|
|
),
|
|
),
|
|
),
|
|
));
|
|
await tester.pump();
|
|
|
|
final Finder leadingIcon = find.widgetWithIcon(Container, Icons.search);
|
|
final double iconWidth = tester.getSize(leadingIcon).width;
|
|
final Offset dropdownMenuTopRight = tester.getTopRight(find.byType(DropdownMenu<TestMenu>));
|
|
final Finder updatedLabel = find.text('label');
|
|
final Offset updatedLabelTopRight = tester.getTopRight(updatedLabel);
|
|
|
|
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
|
await tester.pumpAndSettle();
|
|
final Finder updatedItemText = find.text('Item 0').last;
|
|
final Offset updatedItemTextTopRight = tester.getTopRight(updatedItemText);
|
|
|
|
|
|
expect(updatedLabelTopRight.dx, equals(updatedItemTextTopRight.dx));
|
|
expect(updatedLabelTopRight.dx, equals(dropdownMenuTopRight.dx - iconWidth));
|
|
|
|
// Test when then leading icon is a widget with a bigger size.
|
|
await tester.pumpWidget(Container());
|
|
await tester.pumpWidget(MaterialApp(
|
|
theme: themeData,
|
|
home: Scaffold(
|
|
body: Directionality(
|
|
textDirection: TextDirection.rtl,
|
|
child: DropdownMenu<TestMenu>(
|
|
leadingIcon: const SizedBox(width: 75.0, child: Icon(Icons.search)),
|
|
label: const Text('label'),
|
|
dropdownMenuEntries: menuChildren,
|
|
),
|
|
),
|
|
),
|
|
));
|
|
await tester.pump();
|
|
|
|
final Finder largeLeadingIcon = find.widgetWithIcon(Container, Icons.search);
|
|
final double largeIconWidth = tester.getSize(largeLeadingIcon).width;
|
|
final Offset updatedDropdownMenuTopRight = tester.getTopRight(find.byType(DropdownMenu<TestMenu>));
|
|
final Finder updatedLabel1 = find.text('label');
|
|
final Offset updatedLabelTopRight1 = tester.getTopRight(updatedLabel1);
|
|
|
|
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
|
await tester.pumpAndSettle();
|
|
final Finder updatedItemText1 = find.text('Item 0').last;
|
|
final Offset updatedItemTextTopRight1 = tester.getTopRight(updatedItemText1);
|
|
|
|
|
|
expect(updatedLabelTopRight1.dx, equals(updatedItemTextTopRight1.dx));
|
|
expect(updatedLabelTopRight1.dx, equals(updatedDropdownMenuTopRight.dx - largeIconWidth));
|
|
});
|
|
|
|
testWidgets('DropdownMenu has default trailing icon button', (WidgetTester tester) async {
|
|
final ThemeData themeData = ThemeData();
|
|
await tester.pumpWidget(buildTest(themeData, menuChildren));
|
|
await tester.pump();
|
|
|
|
final Finder iconButton = find.widgetWithIcon(IconButton, Icons.arrow_drop_down).first;
|
|
expect(iconButton, findsOneWidget);
|
|
|
|
await tester.tap(iconButton);
|
|
await tester.pump();
|
|
|
|
final Finder menuMaterial = find.ancestor(
|
|
of: find.widgetWithText(MenuItemButton, TestMenu.mainMenu0.label),
|
|
matching: find.byType(Material),
|
|
).last;
|
|
expect(menuMaterial, findsOneWidget);
|
|
});
|
|
|
|
testWidgets('DropdownMenu can customize trailing icon button', (WidgetTester tester) async {
|
|
final ThemeData themeData = ThemeData();
|
|
await tester.pumpWidget(MaterialApp(
|
|
theme: themeData,
|
|
home: Scaffold(
|
|
body: DropdownMenu<TestMenu>(
|
|
trailingIcon: const Icon(Icons.ac_unit),
|
|
dropdownMenuEntries: menuChildren,
|
|
),
|
|
),
|
|
));
|
|
await tester.pump();
|
|
|
|
final Finder iconButton = find.widgetWithIcon(IconButton, Icons.ac_unit).first;
|
|
expect(iconButton, findsOneWidget);
|
|
|
|
await tester.tap(iconButton);
|
|
await tester.pump();
|
|
|
|
final Finder menuMaterial = find.ancestor(
|
|
of: find.widgetWithText(MenuItemButton, TestMenu.mainMenu0.label),
|
|
matching: find.byType(Material),
|
|
).last;
|
|
expect(menuMaterial, findsOneWidget);
|
|
});
|
|
|
|
testWidgets('Down key can highlight the menu item', (WidgetTester tester) async {
|
|
final ThemeData themeData = ThemeData();
|
|
await tester.pumpWidget(MaterialApp(
|
|
theme: themeData,
|
|
home: Scaffold(
|
|
body: DropdownMenu<TestMenu>(
|
|
trailingIcon: const Icon(Icons.ac_unit),
|
|
dropdownMenuEntries: menuChildren,
|
|
),
|
|
),
|
|
));
|
|
|
|
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
|
await tester.pump();
|
|
|
|
await simulateKeyDownEvent(LogicalKeyboardKey.arrowDown);
|
|
await tester.pumpAndSettle();
|
|
Finder button0Material = find.descendant(
|
|
of: find.widgetWithText(MenuItemButton, 'Item 0').last,
|
|
matching: find.byType(Material),
|
|
);
|
|
|
|
Material item0material = tester.widget<Material>(button0Material);
|
|
expect(item0material.color, themeData.colorScheme.onSurface.withOpacity(0.12));
|
|
|
|
// Press down key one more time, the highlight should move to the next item.
|
|
await simulateKeyDownEvent(LogicalKeyboardKey.arrowDown);
|
|
await tester.pumpAndSettle();
|
|
final Finder button1Material = find.descendant(
|
|
of: find.widgetWithText(MenuItemButton, 'Menu 1').last,
|
|
matching: find.byType(Material),
|
|
);
|
|
final Material item1material = tester.widget<Material>(button1Material);
|
|
expect(item1material.color, themeData.colorScheme.onSurface.withOpacity(0.12));
|
|
button0Material = find.descendant(
|
|
of: find.widgetWithText(MenuItemButton, 'Item 0').last,
|
|
matching: find.byType(Material),
|
|
);
|
|
item0material = tester.widget<Material>(button0Material);
|
|
expect(item0material.color, Colors.transparent); // the previous item should not be highlighted.
|
|
});
|
|
|
|
testWidgets('Up key can highlight the menu item', (WidgetTester tester) async {
|
|
final ThemeData themeData = ThemeData();
|
|
await tester.pumpWidget(MaterialApp(
|
|
theme: themeData,
|
|
home: Scaffold(
|
|
body: DropdownMenu<TestMenu>(
|
|
dropdownMenuEntries: menuChildren,
|
|
),
|
|
),
|
|
));
|
|
|
|
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
|
await tester.pump();
|
|
|
|
await simulateKeyDownEvent(LogicalKeyboardKey.arrowUp);
|
|
await tester.pumpAndSettle();
|
|
Finder button5Material = find.descendant(
|
|
of: find.widgetWithText(MenuItemButton, 'Item 5').last,
|
|
matching: find.byType(Material),
|
|
);
|
|
|
|
Material item5material = tester.widget<Material>(button5Material);
|
|
expect(item5material.color, themeData.colorScheme.onSurface.withOpacity(0.12));
|
|
|
|
// Press up key one more time, the highlight should move up to the item 4.
|
|
await simulateKeyDownEvent(LogicalKeyboardKey.arrowUp);
|
|
await tester.pumpAndSettle();
|
|
final Finder button4Material = find.descendant(
|
|
of: find.widgetWithText(MenuItemButton, 'Item 4').last,
|
|
matching: find.byType(Material),
|
|
);
|
|
final Material item4material = tester.widget<Material>(button4Material);
|
|
expect(item4material.color, themeData.colorScheme.onSurface.withOpacity(0.12));
|
|
button5Material = find.descendant(
|
|
of: find.widgetWithText(MenuItemButton, 'Item 5').last,
|
|
matching: find.byType(Material),
|
|
);
|
|
|
|
item5material = tester.widget<Material>(button5Material);
|
|
expect(item5material.color, Colors.transparent); // the previous item should not be highlighted.
|
|
});
|
|
|
|
testWidgets('The text input should match the label of the menu item while pressing down key', (WidgetTester tester) async {
|
|
final ThemeData themeData = ThemeData();
|
|
await tester.pumpWidget(MaterialApp(
|
|
theme: themeData,
|
|
home: Scaffold(
|
|
body: DropdownMenu<TestMenu>(
|
|
dropdownMenuEntries: menuChildren,
|
|
),
|
|
),
|
|
));
|
|
|
|
// Open the menu
|
|
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
|
await tester.pump();
|
|
|
|
await simulateKeyDownEvent(LogicalKeyboardKey.arrowDown);
|
|
await tester.pump();
|
|
expect(find.widgetWithText(TextField, 'Item 0'), findsOneWidget);
|
|
|
|
// Press down key one more time to the next item.
|
|
await simulateKeyDownEvent(LogicalKeyboardKey.arrowDown);
|
|
await tester.pump();
|
|
expect(find.widgetWithText(TextField, 'Menu 1'), findsOneWidget);
|
|
|
|
// Press down to the next item.
|
|
await simulateKeyDownEvent(LogicalKeyboardKey.arrowDown);
|
|
await tester.pump();
|
|
expect(find.widgetWithText(TextField, 'Item 2'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('The text input should match the label of the menu item while pressing up key', (WidgetTester tester) async {
|
|
final ThemeData themeData = ThemeData();
|
|
await tester.pumpWidget(MaterialApp(
|
|
theme: themeData,
|
|
home: Scaffold(
|
|
body: DropdownMenu<TestMenu>(
|
|
dropdownMenuEntries: menuChildren,
|
|
),
|
|
),
|
|
));
|
|
|
|
// Open the menu
|
|
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
|
await tester.pump();
|
|
|
|
await simulateKeyDownEvent(LogicalKeyboardKey.arrowUp);
|
|
await tester.pump();
|
|
expect(find.widgetWithText(TextField, 'Item 5'), findsOneWidget);
|
|
|
|
// Press up key one more time to the upper item.
|
|
await simulateKeyDownEvent(LogicalKeyboardKey.arrowUp);
|
|
await tester.pump();
|
|
expect(find.widgetWithText(TextField, 'Item 4'), findsOneWidget);
|
|
|
|
// Press up to the upper item.
|
|
await simulateKeyDownEvent(LogicalKeyboardKey.arrowUp);
|
|
await tester.pump();
|
|
expect(find.widgetWithText(TextField, 'Item 3'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('Disabled button will be skipped while pressing up/down key', (WidgetTester tester) async {
|
|
final ThemeData themeData = ThemeData();
|
|
final List<DropdownMenuEntry<TestMenu>> menuWithDisabledItems = <DropdownMenuEntry<TestMenu>>[
|
|
const DropdownMenuEntry<TestMenu>(value: TestMenu.mainMenu0, label: 'Item 0'),
|
|
const DropdownMenuEntry<TestMenu>(value: TestMenu.mainMenu1, label: 'Item 1', enabled: false),
|
|
const DropdownMenuEntry<TestMenu>(value: TestMenu.mainMenu2, label: 'Item 2', enabled: false),
|
|
const DropdownMenuEntry<TestMenu>(value: TestMenu.mainMenu3, label: 'Item 3'),
|
|
const DropdownMenuEntry<TestMenu>(value: TestMenu.mainMenu4, label: 'Item 4'),
|
|
const DropdownMenuEntry<TestMenu>(value: TestMenu.mainMenu5, label: 'Item 5', enabled: false),
|
|
];
|
|
await tester.pumpWidget(MaterialApp(
|
|
theme: themeData,
|
|
home: Scaffold(
|
|
body: DropdownMenu<TestMenu>(
|
|
dropdownMenuEntries: menuWithDisabledItems,
|
|
),
|
|
),
|
|
));
|
|
await tester.pump();
|
|
|
|
// Open the menu
|
|
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
|
await tester.pumpAndSettle();
|
|
|
|
await simulateKeyDownEvent(LogicalKeyboardKey.arrowDown);
|
|
await tester.pumpAndSettle();
|
|
final Finder button0Material = find.descendant(
|
|
of: find.widgetWithText(MenuItemButton, 'Item 0').last,
|
|
matching: find.byType(Material),
|
|
);
|
|
final Material item0Material = tester.widget<Material>(button0Material);
|
|
expect(item0Material.color, themeData.colorScheme.onSurface.withOpacity(0.12)); // first item can be highlighted as it's enabled.
|
|
|
|
// Continue to press down key. Item 3 should be highlighted as Menu 1 and Item 2 are both disabled.
|
|
await simulateKeyDownEvent(LogicalKeyboardKey.arrowDown);
|
|
await tester.pumpAndSettle();
|
|
final Finder button3Material = find.descendant(
|
|
of: find.widgetWithText(MenuItemButton, 'Item 3').last,
|
|
matching: find.byType(Material),
|
|
);
|
|
final Material item3Material = tester.widget<Material>(button3Material);
|
|
expect(item3Material.color, themeData.colorScheme.onSurface.withOpacity(0.12));
|
|
});
|
|
|
|
testWidgets('Searching is enabled by default', (WidgetTester tester) async {
|
|
final ThemeData themeData = ThemeData();
|
|
await tester.pumpWidget(MaterialApp(
|
|
theme: themeData,
|
|
home: Scaffold(
|
|
body: DropdownMenu<TestMenu>(
|
|
dropdownMenuEntries: menuChildren,
|
|
),
|
|
),
|
|
));
|
|
|
|
// Open the menu
|
|
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
|
await tester.pump();
|
|
await tester.enterText(find.byType(TextField).first, 'Menu 1');
|
|
await tester.pumpAndSettle();
|
|
final Finder buttonMaterial = find.descendant(
|
|
of: find.widgetWithText(MenuItemButton, 'Menu 1').last,
|
|
matching: find.byType(Material),
|
|
);
|
|
final Material itemMaterial = tester.widget<Material>(buttonMaterial);
|
|
expect(itemMaterial.color, themeData.colorScheme.onSurface.withOpacity(0.12)); // Menu 1 button is highlighted.
|
|
});
|
|
|
|
testWidgets('Highlight can move up/down from the searching result', (WidgetTester tester) async {
|
|
final ThemeData themeData = ThemeData();
|
|
await tester.pumpWidget(MaterialApp(
|
|
theme: themeData,
|
|
home: Scaffold(
|
|
body: DropdownMenu<TestMenu>(
|
|
dropdownMenuEntries: menuChildren,
|
|
),
|
|
),
|
|
));
|
|
|
|
// Open the menu
|
|
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
|
await tester.pump();
|
|
await tester.enterText(find.byType(TextField).first, 'Menu 1');
|
|
await tester.pumpAndSettle();
|
|
final Finder buttonMaterial = find.descendant(
|
|
of: find.widgetWithText(MenuItemButton, 'Menu 1').last,
|
|
matching: find.byType(Material),
|
|
);
|
|
final Material itemMaterial = tester.widget<Material>(buttonMaterial);
|
|
expect(itemMaterial.color, themeData.colorScheme.onSurface.withOpacity(0.12));
|
|
|
|
// Press up to the upper item (Item 0).
|
|
await simulateKeyDownEvent(LogicalKeyboardKey.arrowUp);
|
|
await tester.pumpAndSettle();
|
|
expect(find.widgetWithText(TextField, 'Item 0'), findsOneWidget);
|
|
final Finder button0Material = find.descendant(
|
|
of: find.widgetWithText(MenuItemButton, 'Item 0').last,
|
|
matching: find.byType(Material),
|
|
);
|
|
final Material item0Material = tester.widget<Material>(button0Material);
|
|
expect(item0Material.color, themeData.colorScheme.onSurface.withOpacity(0.12)); // Move up, the 'Item 0' is highlighted.
|
|
|
|
// Continue to move up to the last item (Item 5).
|
|
await simulateKeyDownEvent(LogicalKeyboardKey.arrowUp);
|
|
await tester.pumpAndSettle();
|
|
expect(find.widgetWithText(TextField, 'Item 5'), findsOneWidget);
|
|
final Finder button5Material = find.descendant(
|
|
of: find.widgetWithText(MenuItemButton, 'Item 5').last,
|
|
matching: find.byType(Material),
|
|
);
|
|
final Material item5Material = tester.widget<Material>(button5Material);
|
|
expect(item5Material.color, themeData.colorScheme.onSurface.withOpacity(0.12));
|
|
});
|
|
|
|
testWidgets('Filtering is disabled by default', (WidgetTester tester) async {
|
|
final ThemeData themeData = ThemeData();
|
|
await tester.pumpWidget(MaterialApp(
|
|
theme: themeData,
|
|
home: Scaffold(
|
|
body: DropdownMenu<TestMenu>(
|
|
dropdownMenuEntries: menuChildren,
|
|
),
|
|
),
|
|
));
|
|
|
|
// Open the menu
|
|
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
|
await tester.pump();
|
|
|
|
await tester.enterText(find.byType(TextField).first, 'Menu 1');
|
|
await tester.pumpAndSettle();
|
|
for (final TestMenu menu in TestMenu.values) {
|
|
// One is layout for the _DropdownMenuBody, the other one is the real button item in the menu.
|
|
expect(find.widgetWithText(MenuItemButton, menu.label), findsNWidgets(2));
|
|
}
|
|
});
|
|
|
|
testWidgets('Enable filtering', (WidgetTester tester) async {
|
|
final ThemeData themeData = ThemeData();
|
|
await tester.pumpWidget(MaterialApp(
|
|
theme: themeData,
|
|
home: Scaffold(
|
|
body: DropdownMenu<TestMenu>(
|
|
enableFilter: true,
|
|
dropdownMenuEntries: menuChildren,
|
|
),
|
|
),
|
|
));
|
|
|
|
// Open the menu
|
|
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
|
await tester.pump();
|
|
|
|
await tester.enterText(find
|
|
.byType(TextField)
|
|
.first, 'Menu 1');
|
|
await tester.pumpAndSettle();
|
|
for (final TestMenu menu in TestMenu.values) {
|
|
// 'Menu 1' should be 2, other items should only find one.
|
|
if (menu.label == TestMenu.mainMenu1.label) {
|
|
expect(find.widgetWithText(MenuItemButton, menu.label), findsNWidgets(2));
|
|
} else {
|
|
expect(find.widgetWithText(MenuItemButton, menu.label), findsOneWidget);
|
|
}
|
|
}
|
|
});
|
|
|
|
testWidgets('The controller can access the value in the input field', (WidgetTester tester) async {
|
|
final ThemeData themeData = ThemeData();
|
|
final TextEditingController controller = TextEditingController();
|
|
await tester.pumpWidget(MaterialApp(
|
|
theme: themeData,
|
|
home: StatefulBuilder(
|
|
builder: (BuildContext context, StateSetter setState) {
|
|
return Scaffold(
|
|
body: DropdownMenu<TestMenu>(
|
|
enableFilter: true,
|
|
dropdownMenuEntries: menuChildren,
|
|
controller: controller,
|
|
),
|
|
);
|
|
}
|
|
),
|
|
));
|
|
|
|
// Open the menu
|
|
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
|
await tester.pump();
|
|
final Finder item3 = find.widgetWithText(MenuItemButton, 'Item 3').last;
|
|
await tester.tap(item3);
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(controller.text, 'Item 3');
|
|
|
|
await tester.enterText(find.byType(TextField).first, 'New Item');
|
|
expect(controller.text, 'New Item');
|
|
});
|
|
|
|
testWidgets('The onSelected gets called only when a selection is made', (WidgetTester tester) async {
|
|
int selectionCount = 0;
|
|
|
|
final ThemeData themeData = ThemeData();
|
|
final List<DropdownMenuEntry<TestMenu>> menuWithDisabledItems = <DropdownMenuEntry<TestMenu>>[
|
|
const DropdownMenuEntry<TestMenu>(value: TestMenu.mainMenu0, label: 'Item 0'),
|
|
const DropdownMenuEntry<TestMenu>(value: TestMenu.mainMenu0, label: 'Item 1', enabled: false),
|
|
const DropdownMenuEntry<TestMenu>(value: TestMenu.mainMenu0, label: 'Item 2'),
|
|
const DropdownMenuEntry<TestMenu>(value: TestMenu.mainMenu0, label: 'Item 3'),
|
|
];
|
|
final TextEditingController controller = TextEditingController();
|
|
await tester.pumpWidget(MaterialApp(
|
|
theme: themeData,
|
|
home: StatefulBuilder(
|
|
builder: (BuildContext context, StateSetter setState) {
|
|
return Scaffold(
|
|
body: DropdownMenu<TestMenu>(
|
|
dropdownMenuEntries: menuWithDisabledItems,
|
|
controller: controller,
|
|
onSelected: (_) {
|
|
setState(() {
|
|
selectionCount++;
|
|
});
|
|
},
|
|
),
|
|
);
|
|
}
|
|
),
|
|
));
|
|
|
|
// Open the menu
|
|
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
|
await tester.pump();
|
|
|
|
// Test onSelected on key press
|
|
await simulateKeyDownEvent(LogicalKeyboardKey.arrowDown);
|
|
await tester.pumpAndSettle();
|
|
await tester.testTextInput.receiveAction(TextInputAction.done);
|
|
await tester.pumpAndSettle();
|
|
expect(selectionCount, 1);
|
|
|
|
// Disabled item doesn't trigger onSelected callback.
|
|
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
|
await tester.pump();
|
|
final Finder item1 = find.widgetWithText(MenuItemButton, 'Item 1').last;
|
|
await tester.tap(item1);
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(controller.text, 'Item 0');
|
|
expect(selectionCount, 1);
|
|
|
|
final Finder item2 = find.widgetWithText(MenuItemButton, 'Item 2').last;
|
|
await tester.tap(item2);
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(controller.text, 'Item 2');
|
|
expect(selectionCount, 2);
|
|
|
|
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
|
await tester.pump();
|
|
final Finder item3 = find.widgetWithText(MenuItemButton, 'Item 3').last;
|
|
await tester.tap(item3);
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(controller.text, 'Item 3');
|
|
expect(selectionCount, 3);
|
|
|
|
// When typing something in the text field without selecting any of the options,
|
|
// the onSelected should not be called.
|
|
await tester.enterText(find.byType(TextField).first, 'New Item');
|
|
expect(controller.text, 'New Item');
|
|
expect(selectionCount, 3);
|
|
expect(find.widgetWithText(TextField, 'New Item'), findsOneWidget);
|
|
await tester.enterText(find.byType(TextField).first, '');
|
|
expect(selectionCount, 3);
|
|
expect(controller.text.isEmpty, true);
|
|
});
|
|
|
|
|
|
testWidgets('The selectedValue gives an initial text and highlights the according item', (WidgetTester tester) async {
|
|
final ThemeData themeData = ThemeData();
|
|
final TextEditingController controller = TextEditingController();
|
|
await tester.pumpWidget(MaterialApp(
|
|
theme: themeData,
|
|
home: StatefulBuilder(
|
|
builder: (BuildContext context, StateSetter setState) {
|
|
return Scaffold(
|
|
body: DropdownMenu<TestMenu>(
|
|
initialSelection: TestMenu.mainMenu3,
|
|
dropdownMenuEntries: menuChildren,
|
|
controller: controller,
|
|
),
|
|
);
|
|
}
|
|
),
|
|
));
|
|
|
|
expect(find.widgetWithText(TextField, 'Item 3'), findsOneWidget);
|
|
|
|
// Open the menu
|
|
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
|
await tester.pump();
|
|
|
|
final Finder buttonMaterial = find.descendant(
|
|
of: find.widgetWithText(MenuItemButton, 'Item 3'),
|
|
matching: find.byType(Material),
|
|
).last;
|
|
|
|
// Validate the item 3 is highlighted.
|
|
final Material itemMaterial = tester.widget<Material>(buttonMaterial);
|
|
expect(itemMaterial.color, themeData.colorScheme.onSurface.withOpacity(0.12));
|
|
});
|
|
}
|
|
|
|
enum TestMenu {
|
|
mainMenu0('Item 0'),
|
|
mainMenu1('Menu 1'),
|
|
mainMenu2('Item 2'),
|
|
mainMenu3('Item 3'),
|
|
mainMenu4('Item 4'),
|
|
mainMenu5('Item 5');
|
|
|
|
const TestMenu(this.label);
|
|
final String label;
|
|
}
|
|
|
|
enum ShortMenu {
|
|
item0('I0'),
|
|
item1('I1'),
|
|
item2('I2');
|
|
|
|
const ShortMenu(this.label);
|
|
final String label;
|
|
}
|