From 336d60d29c89912ef294c825c8fe1634ba81fe71 Mon Sep 17 00:00:00 2001 From: Hans Muller Date: Wed, 30 Aug 2023 14:33:54 -0700 Subject: [PATCH] Updated DropdownMenu example and added a test (#133592) --- dev/bots/check_code_samples.dart | 1 - .../dropdown_menu/dropdown_menu.0.dart | 105 ++++++++++-------- .../dropdown_menu/dropdown_menu.0_test.dart | 55 +++++++++ 3 files changed, 114 insertions(+), 47 deletions(-) create mode 100644 examples/api/test/material/dropdown_menu/dropdown_menu.0_test.dart diff --git a/dev/bots/check_code_samples.dart b/dev/bots/check_code_samples.dart index 792fe54224d6..78de4d43de00 100644 --- a/dev/bots/check_code_samples.dart +++ b/dev/bots/check_code_samples.dart @@ -321,7 +321,6 @@ final Set _knownMissingTests = { 'examples/api/test/material/scrollbar/scrollbar.1_test.dart', 'examples/api/test/material/scrollbar/scrollbar.0_test.dart', 'examples/api/test/material/dropdown_menu/dropdown_menu.1_test.dart', - 'examples/api/test/material/dropdown_menu/dropdown_menu.0_test.dart', 'examples/api/test/material/radio/radio.toggleable.0_test.dart', 'examples/api/test/material/radio/radio.0_test.dart', 'examples/api/test/material/search_anchor/search_anchor.0_test.dart', diff --git a/examples/api/lib/material/dropdown_menu/dropdown_menu.0.dart b/examples/api/lib/material/dropdown_menu/dropdown_menu.0.dart index 8a8e51b66c98..0e1814b020cc 100644 --- a/examples/api/lib/material/dropdown_menu/dropdown_menu.0.dart +++ b/examples/api/lib/material/dropdown_menu/dropdown_menu.0.dart @@ -2,13 +2,45 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// which is the default configuration, and the second one has a filled input decoration. - import 'package:flutter/material.dart'; -/// Flutter code sample for [DropdownMenu]s. The first dropdown menu has an outlined border. +// Flutter code sample for [DropdownMenu]s. The first dropdown menu +// has the default outlined border and demos using the +// [DropdownMenuEntry] style parameter to customize its appearance. +// The second dropdown menu customizes the appearance of the dropdown +// menu's text field with its [InputDecorationTheme] parameter. + +void main() { + runApp(const DropdownMenuExample()); +} + +// DropdownMenuEntry labels and values for the first dropdown menu. +enum ColorLabel { + blue('Blue', Colors.blue), + pink('Pink', Colors.pink), + green('Green', Colors.green), + yellow('Orange', Colors.orange), + grey('Grey', Colors.grey); + + const ColorLabel(this.label, this.color); + final String label; + final Color color; +} + +// DropdownMenuEntry labels and values for the second dropdown menu. +enum IconLabel { + smile('Smile', Icons.sentiment_satisfied_outlined), + cloud( + 'Cloud', + Icons.cloud_outlined, + ), + brush('Brush', Icons.brush_outlined), + heart('Heart', Icons.favorite); -void main() => runApp(const DropdownMenuExample()); + const IconLabel(this.label, this.icon); + final String label; + final IconData icon; +} class DropdownMenuExample extends StatefulWidget { const DropdownMenuExample({super.key}); @@ -25,18 +57,6 @@ class _DropdownMenuExampleState extends State { @override Widget build(BuildContext context) { - final List> colorEntries = >[]; - for (final ColorLabel color in ColorLabel.values) { - colorEntries.add( - DropdownMenuEntry(value: color, label: color.label, enabled: color.label != 'Grey'), - ); - } - - final List> iconEntries = >[]; - for (final IconLabel icon in IconLabel.values) { - iconEntries.add(DropdownMenuEntry(value: icon, label: icon.label)); - } - return MaterialApp( theme: ThemeData( useMaterial3: true, @@ -55,20 +75,30 @@ class _DropdownMenuExampleState extends State { initialSelection: ColorLabel.green, controller: colorController, label: const Text('Color'), - dropdownMenuEntries: colorEntries, onSelected: (ColorLabel? color) { setState(() { selectedColor = color; }); }, + dropdownMenuEntries: ColorLabel.values.map>( + (ColorLabel color) { + return DropdownMenuEntry( + value: color, + label: color.label, + enabled: color.label != 'Grey', + style: MenuItemButton.styleFrom( + foregroundColor: color.color, + ), + ); + } + ).toList(), ), - const SizedBox(width: 20), + const SizedBox(width: 24), DropdownMenu( controller: iconController, enableFilter: true, leadingIcon: const Icon(Icons.search), label: const Text('Icon'), - dropdownMenuEntries: iconEntries, inputDecorationTheme: const InputDecorationTheme( filled: true, contentPadding: EdgeInsets.symmetric(vertical: 5.0), @@ -78,7 +108,16 @@ class _DropdownMenuExampleState extends State { selectedIcon = icon; }); }, - ) + dropdownMenuEntries: IconLabel.values.map>( + (IconLabel icon) { + return DropdownMenuEntry( + value: icon, + label: icon.label, + leadingIcon: Icon(icon.icon), + ); + }, + ).toList(), + ), ], ), ), @@ -105,29 +144,3 @@ class _DropdownMenuExampleState extends State { ); } } - -enum ColorLabel { - blue('Blue', Colors.blue), - pink('Pink', Colors.pink), - green('Green', Colors.green), - yellow('Yellow', Colors.yellow), - grey('Grey', Colors.grey); - - const ColorLabel(this.label, this.color); - final String label; - final Color color; -} - -enum IconLabel { - smile('Smile', Icons.sentiment_satisfied_outlined), - cloud( - 'Cloud', - Icons.cloud_outlined, - ), - brush('Brush', Icons.brush_outlined), - heart('Heart', Icons.favorite); - - const IconLabel(this.label, this.icon); - final String label; - final IconData icon; -} diff --git a/examples/api/test/material/dropdown_menu/dropdown_menu.0_test.dart b/examples/api/test/material/dropdown_menu/dropdown_menu.0_test.dart new file mode 100644 index 000000000000..62e0c6e195d3 --- /dev/null +++ b/examples/api/test/material/dropdown_menu/dropdown_menu.0_test.dart @@ -0,0 +1,55 @@ +// 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_api_samples/material/dropdown_menu/dropdown_menu.0.dart' as example; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('DropdownMenu', (WidgetTester tester) async { + await tester.pumpWidget( + const example.DropdownMenuExample(), + ); + + expect(find.text('You selected a Blue Smile'), findsNothing); + + final Finder colorMenu = find.byType(DropdownMenu); + final Finder iconMenu = find.byType(DropdownMenu); + expect(colorMenu, findsOneWidget); + expect(iconMenu, findsOneWidget); + + Finder findMenuItem(String label) { + return find.widgetWithText(MenuItemButton, label).last; + } + + await tester.tap(colorMenu); + await tester.pumpAndSettle(); + expect(findMenuItem('Blue'), findsOneWidget); + expect(findMenuItem('Pink'), findsOneWidget); + expect(findMenuItem('Green'), findsOneWidget); + expect(findMenuItem('Orange'), findsOneWidget); + expect(findMenuItem('Grey'), findsOneWidget); + + await tester.tap(findMenuItem('Blue')); + + // The DropdownMenu's onSelected callback is delayed + // with SchedulerBinding.instance.addPostFrameCallback + // to give the focus a chance to return to where it was + // before the menu appeared. The pumpAndSettle() + // give the callback a chance to run. + await tester.pumpAndSettle(); + + await tester.tap(iconMenu); + await tester.pumpAndSettle(); + expect(findMenuItem('Smile'), findsOneWidget); + expect(findMenuItem('Cloud'), findsOneWidget); + expect(findMenuItem('Brush'), findsOneWidget); + expect(findMenuItem('Heart'), findsOneWidget); + + await tester.tap(findMenuItem('Smile')); + await tester.pumpAndSettle(); + + expect(find.text('You selected a Blue Smile'), findsOneWidget); + }); +}