Skip to content

Commit 336d60d

Browse files
authored
Updated DropdownMenu example and added a test (flutter#133592)
1 parent 1e770c3 commit 336d60d

File tree

3 files changed

+114
-47
lines changed

3 files changed

+114
-47
lines changed

dev/bots/check_code_samples.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,6 @@ final Set<String> _knownMissingTests = <String>{
321321
'examples/api/test/material/scrollbar/scrollbar.1_test.dart',
322322
'examples/api/test/material/scrollbar/scrollbar.0_test.dart',
323323
'examples/api/test/material/dropdown_menu/dropdown_menu.1_test.dart',
324-
'examples/api/test/material/dropdown_menu/dropdown_menu.0_test.dart',
325324
'examples/api/test/material/radio/radio.toggleable.0_test.dart',
326325
'examples/api/test/material/radio/radio.0_test.dart',
327326
'examples/api/test/material/search_anchor/search_anchor.0_test.dart',

examples/api/lib/material/dropdown_menu/dropdown_menu.0.dart

Lines changed: 59 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,45 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
// which is the default configuration, and the second one has a filled input decoration.
6-
75
import 'package:flutter/material.dart';
86

9-
/// Flutter code sample for [DropdownMenu]s. The first dropdown menu has an outlined border.
7+
// Flutter code sample for [DropdownMenu]s. The first dropdown menu
8+
// has the default outlined border and demos using the
9+
// [DropdownMenuEntry] style parameter to customize its appearance.
10+
// The second dropdown menu customizes the appearance of the dropdown
11+
// menu's text field with its [InputDecorationTheme] parameter.
12+
13+
void main() {
14+
runApp(const DropdownMenuExample());
15+
}
16+
17+
// DropdownMenuEntry labels and values for the first dropdown menu.
18+
enum ColorLabel {
19+
blue('Blue', Colors.blue),
20+
pink('Pink', Colors.pink),
21+
green('Green', Colors.green),
22+
yellow('Orange', Colors.orange),
23+
grey('Grey', Colors.grey);
24+
25+
const ColorLabel(this.label, this.color);
26+
final String label;
27+
final Color color;
28+
}
29+
30+
// DropdownMenuEntry labels and values for the second dropdown menu.
31+
enum IconLabel {
32+
smile('Smile', Icons.sentiment_satisfied_outlined),
33+
cloud(
34+
'Cloud',
35+
Icons.cloud_outlined,
36+
),
37+
brush('Brush', Icons.brush_outlined),
38+
heart('Heart', Icons.favorite);
1039

11-
void main() => runApp(const DropdownMenuExample());
40+
const IconLabel(this.label, this.icon);
41+
final String label;
42+
final IconData icon;
43+
}
1244

1345
class DropdownMenuExample extends StatefulWidget {
1446
const DropdownMenuExample({super.key});
@@ -25,18 +57,6 @@ class _DropdownMenuExampleState extends State<DropdownMenuExample> {
2557

2658
@override
2759
Widget build(BuildContext context) {
28-
final List<DropdownMenuEntry<ColorLabel>> colorEntries = <DropdownMenuEntry<ColorLabel>>[];
29-
for (final ColorLabel color in ColorLabel.values) {
30-
colorEntries.add(
31-
DropdownMenuEntry<ColorLabel>(value: color, label: color.label, enabled: color.label != 'Grey'),
32-
);
33-
}
34-
35-
final List<DropdownMenuEntry<IconLabel>> iconEntries = <DropdownMenuEntry<IconLabel>>[];
36-
for (final IconLabel icon in IconLabel.values) {
37-
iconEntries.add(DropdownMenuEntry<IconLabel>(value: icon, label: icon.label));
38-
}
39-
4060
return MaterialApp(
4161
theme: ThemeData(
4262
useMaterial3: true,
@@ -55,20 +75,30 @@ class _DropdownMenuExampleState extends State<DropdownMenuExample> {
5575
initialSelection: ColorLabel.green,
5676
controller: colorController,
5777
label: const Text('Color'),
58-
dropdownMenuEntries: colorEntries,
5978
onSelected: (ColorLabel? color) {
6079
setState(() {
6180
selectedColor = color;
6281
});
6382
},
83+
dropdownMenuEntries: ColorLabel.values.map<DropdownMenuEntry<ColorLabel>>(
84+
(ColorLabel color) {
85+
return DropdownMenuEntry<ColorLabel>(
86+
value: color,
87+
label: color.label,
88+
enabled: color.label != 'Grey',
89+
style: MenuItemButton.styleFrom(
90+
foregroundColor: color.color,
91+
),
92+
);
93+
}
94+
).toList(),
6495
),
65-
const SizedBox(width: 20),
96+
const SizedBox(width: 24),
6697
DropdownMenu<IconLabel>(
6798
controller: iconController,
6899
enableFilter: true,
69100
leadingIcon: const Icon(Icons.search),
70101
label: const Text('Icon'),
71-
dropdownMenuEntries: iconEntries,
72102
inputDecorationTheme: const InputDecorationTheme(
73103
filled: true,
74104
contentPadding: EdgeInsets.symmetric(vertical: 5.0),
@@ -78,7 +108,16 @@ class _DropdownMenuExampleState extends State<DropdownMenuExample> {
78108
selectedIcon = icon;
79109
});
80110
},
81-
)
111+
dropdownMenuEntries: IconLabel.values.map<DropdownMenuEntry<IconLabel>>(
112+
(IconLabel icon) {
113+
return DropdownMenuEntry<IconLabel>(
114+
value: icon,
115+
label: icon.label,
116+
leadingIcon: Icon(icon.icon),
117+
);
118+
},
119+
).toList(),
120+
),
82121
],
83122
),
84123
),
@@ -105,29 +144,3 @@ class _DropdownMenuExampleState extends State<DropdownMenuExample> {
105144
);
106145
}
107146
}
108-
109-
enum ColorLabel {
110-
blue('Blue', Colors.blue),
111-
pink('Pink', Colors.pink),
112-
green('Green', Colors.green),
113-
yellow('Yellow', Colors.yellow),
114-
grey('Grey', Colors.grey);
115-
116-
const ColorLabel(this.label, this.color);
117-
final String label;
118-
final Color color;
119-
}
120-
121-
enum IconLabel {
122-
smile('Smile', Icons.sentiment_satisfied_outlined),
123-
cloud(
124-
'Cloud',
125-
Icons.cloud_outlined,
126-
),
127-
brush('Brush', Icons.brush_outlined),
128-
heart('Heart', Icons.favorite);
129-
130-
const IconLabel(this.label, this.icon);
131-
final String label;
132-
final IconData icon;
133-
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:flutter/material.dart';
6+
import 'package:flutter_api_samples/material/dropdown_menu/dropdown_menu.0.dart' as example;
7+
import 'package:flutter_test/flutter_test.dart';
8+
9+
void main() {
10+
testWidgets('DropdownMenu', (WidgetTester tester) async {
11+
await tester.pumpWidget(
12+
const example.DropdownMenuExample(),
13+
);
14+
15+
expect(find.text('You selected a Blue Smile'), findsNothing);
16+
17+
final Finder colorMenu = find.byType(DropdownMenu<example.ColorLabel>);
18+
final Finder iconMenu = find.byType(DropdownMenu<example.IconLabel>);
19+
expect(colorMenu, findsOneWidget);
20+
expect(iconMenu, findsOneWidget);
21+
22+
Finder findMenuItem(String label) {
23+
return find.widgetWithText(MenuItemButton, label).last;
24+
}
25+
26+
await tester.tap(colorMenu);
27+
await tester.pumpAndSettle();
28+
expect(findMenuItem('Blue'), findsOneWidget);
29+
expect(findMenuItem('Pink'), findsOneWidget);
30+
expect(findMenuItem('Green'), findsOneWidget);
31+
expect(findMenuItem('Orange'), findsOneWidget);
32+
expect(findMenuItem('Grey'), findsOneWidget);
33+
34+
await tester.tap(findMenuItem('Blue'));
35+
36+
// The DropdownMenu's onSelected callback is delayed
37+
// with SchedulerBinding.instance.addPostFrameCallback
38+
// to give the focus a chance to return to where it was
39+
// before the menu appeared. The pumpAndSettle()
40+
// give the callback a chance to run.
41+
await tester.pumpAndSettle();
42+
43+
await tester.tap(iconMenu);
44+
await tester.pumpAndSettle();
45+
expect(findMenuItem('Smile'), findsOneWidget);
46+
expect(findMenuItem('Cloud'), findsOneWidget);
47+
expect(findMenuItem('Brush'), findsOneWidget);
48+
expect(findMenuItem('Heart'), findsOneWidget);
49+
50+
await tester.tap(findMenuItem('Smile'));
51+
await tester.pumpAndSettle();
52+
53+
expect(find.text('You selected a Blue Smile'), findsOneWidget);
54+
});
55+
}

0 commit comments

Comments
 (0)