mirror of
https://github.com/flutter/flutter
synced 2024-08-28 12:30:38 +00:00
Added controller
and onSelected
properties to DropdownMenu (#116259)
This commit is contained in:
parent
e5e21c9837
commit
6bb412e35e
|
@ -9,23 +9,31 @@ import 'package:flutter/material.dart';
|
|||
|
||||
void main() => runApp(const DropdownMenuExample());
|
||||
|
||||
class DropdownMenuExample extends StatelessWidget {
|
||||
class DropdownMenuExample extends StatefulWidget {
|
||||
const DropdownMenuExample({super.key});
|
||||
|
||||
List<DropdownMenuEntry> getEntryList() {
|
||||
final List<DropdownMenuEntry> entries = <DropdownMenuEntry>[];
|
||||
@override
|
||||
State<DropdownMenuExample> createState() => _DropdownMenuExampleState();
|
||||
}
|
||||
|
||||
for (int index = 0; index < EntryLabel.values.length; index++) {
|
||||
// Disabled item 1, 2 and 6.
|
||||
final bool enabled = index != 1 && index != 2 && index != 6;
|
||||
entries.add(DropdownMenuEntry(label: EntryLabel.values[index].label, enabled: enabled));
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
class _DropdownMenuExampleState extends State<DropdownMenuExample> {
|
||||
final TextEditingController colorController = TextEditingController();
|
||||
final TextEditingController iconController = TextEditingController();
|
||||
ColorLabel? selectedColor;
|
||||
IconLabel? selectedIcon;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final List<DropdownMenuEntry> dropdownMenuEntries = getEntryList();
|
||||
final List<DropdownMenuEntry<ColorLabel>> colorEntries = <DropdownMenuEntry<ColorLabel>>[];
|
||||
for (final ColorLabel color in ColorLabel.values) {
|
||||
colorEntries.add(
|
||||
DropdownMenuEntry<ColorLabel>(value: color, label: color.label, enabled: color.label != 'Grey'));
|
||||
}
|
||||
|
||||
final List<DropdownMenuEntry<IconLabel>> iconEntries = <DropdownMenuEntry<IconLabel>>[];
|
||||
for (final IconLabel icon in IconLabel.values) {
|
||||
iconEntries.add(DropdownMenuEntry<IconLabel>(value: icon, label: icon.label));
|
||||
}
|
||||
|
||||
return MaterialApp(
|
||||
theme: ThemeData(
|
||||
|
@ -34,25 +42,53 @@ class DropdownMenuExample extends StatelessWidget {
|
|||
),
|
||||
home: Scaffold(
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 20),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
DropdownMenu(
|
||||
label: const Text('Label'),
|
||||
dropdownMenuEntries: dropdownMenuEntries,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 20),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
DropdownMenu<ColorLabel>(
|
||||
initialSelection: ColorLabel.green,
|
||||
controller: colorController,
|
||||
label: const Text('Color'),
|
||||
dropdownMenuEntries: colorEntries,
|
||||
onSelected: (ColorLabel? color) {
|
||||
setState(() {
|
||||
selectedColor = color;
|
||||
});
|
||||
},
|
||||
),
|
||||
const SizedBox(width: 20),
|
||||
DropdownMenu<IconLabel>(
|
||||
controller: iconController,
|
||||
enableFilter: true,
|
||||
leadingIcon: const Icon(Icons.search),
|
||||
label: const Text('Icon'),
|
||||
dropdownMenuEntries: iconEntries,
|
||||
inputDecorationTheme: const InputDecorationTheme(filled: true),
|
||||
onSelected: (IconLabel? icon) {
|
||||
setState(() {
|
||||
selectedIcon = icon;
|
||||
});
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 20),
|
||||
DropdownMenu(
|
||||
enableFilter: true,
|
||||
leadingIcon: const Icon(Icons.search),
|
||||
label: const Text('Label'),
|
||||
dropdownMenuEntries: dropdownMenuEntries,
|
||||
inputDecorationTheme: const InputDecorationTheme(filled: true),
|
||||
),
|
||||
if (selectedColor != null && selectedIcon != null)
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text('You selected a ${selectedColor?.label} ${selectedIcon?.label}'),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 5),
|
||||
child: Icon(selectedIcon?.icon, color: selectedColor?.color,))
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
else const Text('Please select a color and an icon.')
|
||||
],
|
||||
)
|
||||
),
|
||||
),
|
||||
|
@ -60,15 +96,25 @@ class DropdownMenuExample extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
enum EntryLabel {
|
||||
item0('Item 0'),
|
||||
item1('Item 1'),
|
||||
item2('Item 2'),
|
||||
item3('Item 3'),
|
||||
item4('Item 4'),
|
||||
item5('Item 5'),
|
||||
item6('Item 6');
|
||||
enum ColorLabel {
|
||||
blue('Blue', Colors.blue),
|
||||
pink('Pink', Colors.pink),
|
||||
green('Green', Colors.green),
|
||||
yellow('Yellow', Colors.yellow),
|
||||
grey('Grey', Colors.grey);
|
||||
|
||||
const EntryLabel(this.label);
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -36,11 +36,12 @@ const double _kDefaultHorizontalPadding = 12.0;
|
|||
/// See also:
|
||||
///
|
||||
/// * [DropdownMenu]
|
||||
class DropdownMenuEntry {
|
||||
class DropdownMenuEntry<T> {
|
||||
/// Creates an entry that is used with [DropdownMenu.dropdownMenuEntries].
|
||||
///
|
||||
/// [label] must be non-null.
|
||||
const DropdownMenuEntry({
|
||||
required this.value,
|
||||
required this.label,
|
||||
this.leadingIcon,
|
||||
this.trailingIcon,
|
||||
|
@ -48,6 +49,11 @@ class DropdownMenuEntry {
|
|||
this.style,
|
||||
});
|
||||
|
||||
/// the value used to identify the entry.
|
||||
///
|
||||
/// This value must be unique across all entries in a [DropdownMenu].
|
||||
final T value;
|
||||
|
||||
/// The label displayed in the center of the menu item.
|
||||
final String label;
|
||||
|
||||
|
@ -101,7 +107,7 @@ class DropdownMenuEntry {
|
|||
/// The [DropdownMenu] uses a [TextField] as the "anchor".
|
||||
/// * [TextField], which is a text input widget that uses an [InputDecoration].
|
||||
/// * [DropdownMenuEntry], which is used to build the [MenuItemButton] in the [DropdownMenu] list.
|
||||
class DropdownMenu extends StatefulWidget {
|
||||
class DropdownMenu<T> extends StatefulWidget {
|
||||
/// Creates a const [DropdownMenu].
|
||||
///
|
||||
/// The leading and trailing icons in the text field can be customized by using
|
||||
|
@ -126,6 +132,9 @@ class DropdownMenu extends StatefulWidget {
|
|||
this.textStyle,
|
||||
this.inputDecorationTheme,
|
||||
this.menuStyle,
|
||||
this.controller,
|
||||
this.initialSelection,
|
||||
this.onSelected,
|
||||
required this.dropdownMenuEntries,
|
||||
});
|
||||
|
||||
|
@ -204,25 +213,40 @@ class DropdownMenu extends StatefulWidget {
|
|||
/// The default width of the menu is set to the width of the text field.
|
||||
final MenuStyle? menuStyle;
|
||||
|
||||
/// Controls the text being edited or selected in the menu.
|
||||
///
|
||||
/// If null, this widget will create its own [TextEditingController].
|
||||
final TextEditingController? controller;
|
||||
|
||||
/// The value used to for an initial selection.
|
||||
///
|
||||
/// Defaults to null.
|
||||
final T? initialSelection;
|
||||
|
||||
/// The callback is called when a selection is made.
|
||||
///
|
||||
/// Defaults to null. If null, only the text field is updated.
|
||||
final ValueChanged<T?>? onSelected;
|
||||
|
||||
/// Descriptions of the menu items in the [DropdownMenu].
|
||||
///
|
||||
/// This is a required parameter. It is recommended that at least one [DropdownMenuEntry]
|
||||
/// is provided. If this is an empty list, the menu will be empty and only
|
||||
/// contain space for padding.
|
||||
final List<DropdownMenuEntry> dropdownMenuEntries;
|
||||
final List<DropdownMenuEntry<T>> dropdownMenuEntries;
|
||||
|
||||
@override
|
||||
State<DropdownMenu> createState() => _DropdownMenuState();
|
||||
State<DropdownMenu<T>> createState() => _DropdownMenuState<T>();
|
||||
}
|
||||
|
||||
class _DropdownMenuState extends State<DropdownMenu> {
|
||||
final MenuController _controller = MenuController();
|
||||
class _DropdownMenuState<T> extends State<DropdownMenu<T>> {
|
||||
final GlobalKey _anchorKey = GlobalKey();
|
||||
final GlobalKey _leadingKey = GlobalKey();
|
||||
final FocusNode _textFocusNode = FocusNode();
|
||||
final TextEditingController _textEditingController = TextEditingController();
|
||||
final MenuController _controller = MenuController();
|
||||
late final TextEditingController _textEditingController;
|
||||
late bool _enableFilter;
|
||||
late List<DropdownMenuEntry> filteredEntries;
|
||||
late List<DropdownMenuEntry<T>> filteredEntries;
|
||||
List<Widget>? _initialMenu;
|
||||
int? currentHighlight;
|
||||
double? leadingPadding;
|
||||
|
@ -231,22 +255,37 @@ class _DropdownMenuState extends State<DropdownMenu> {
|
|||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_textEditingController = widget.controller ?? TextEditingController();
|
||||
_enableFilter = widget.enableFilter;
|
||||
filteredEntries = widget.dropdownMenuEntries;
|
||||
_menuHasEnabledItem = filteredEntries.any((DropdownMenuEntry entry) => entry.enabled);
|
||||
_menuHasEnabledItem = filteredEntries.any((DropdownMenuEntry<T> entry) => entry.enabled);
|
||||
|
||||
final int index = filteredEntries.indexWhere((DropdownMenuEntry<T> entry) => entry.value == widget.initialSelection);
|
||||
if (index != -1) {
|
||||
_textEditingController.text = filteredEntries[index].label;
|
||||
_textEditingController.selection =
|
||||
TextSelection.collapsed(offset: _textEditingController.text.length);
|
||||
}
|
||||
refreshLeadingPadding();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(DropdownMenu oldWidget) {
|
||||
void didUpdateWidget(DropdownMenu<T> oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.dropdownMenuEntries != widget.dropdownMenuEntries) {
|
||||
_menuHasEnabledItem = filteredEntries.any((DropdownMenuEntry entry) => entry.enabled);
|
||||
_menuHasEnabledItem = filteredEntries.any((DropdownMenuEntry<T> entry) => entry.enabled);
|
||||
}
|
||||
if (oldWidget.leadingIcon != widget.leadingIcon) {
|
||||
refreshLeadingPadding();
|
||||
}
|
||||
if (oldWidget.initialSelection != widget.initialSelection) {
|
||||
final int index = filteredEntries.indexWhere((DropdownMenuEntry<T> entry) => entry.value == widget.initialSelection);
|
||||
if (index != -1) {
|
||||
_textEditingController.text = filteredEntries[index].label;
|
||||
_textEditingController.selection =
|
||||
TextSelection.collapsed(offset: _textEditingController.text.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void refreshLeadingPadding() {
|
||||
|
@ -266,25 +305,25 @@ class _DropdownMenuState extends State<DropdownMenu> {
|
|||
return null;
|
||||
}
|
||||
|
||||
List<DropdownMenuEntry> filter(List<DropdownMenuEntry> entries, TextEditingController textEditingController) {
|
||||
List<DropdownMenuEntry<T>> filter(List<DropdownMenuEntry<T>> entries, TextEditingController textEditingController) {
|
||||
final String filterText = textEditingController.text.toLowerCase();
|
||||
return entries
|
||||
.where((DropdownMenuEntry entry) => entry.label.toLowerCase().contains(filterText))
|
||||
.where((DropdownMenuEntry<T> entry) => entry.label.toLowerCase().contains(filterText))
|
||||
.toList();
|
||||
}
|
||||
|
||||
int? search(List<DropdownMenuEntry> entries, TextEditingController textEditingController) {
|
||||
int? search(List<DropdownMenuEntry<T>> entries, TextEditingController textEditingController) {
|
||||
final String searchText = textEditingController.value.text.toLowerCase();
|
||||
if (searchText.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
final int index = entries.indexWhere((DropdownMenuEntry entry) => entry.label.toLowerCase().contains(searchText));
|
||||
final int index = entries.indexWhere((DropdownMenuEntry<T> entry) => entry.label.toLowerCase().contains(searchText));
|
||||
|
||||
return index != -1 ? index : null;
|
||||
}
|
||||
|
||||
List<Widget> _buildButtons(
|
||||
List<DropdownMenuEntry> filteredEntries,
|
||||
List<DropdownMenuEntry<T>> filteredEntries,
|
||||
TextEditingController textEditingController,
|
||||
TextDirection textDirection,
|
||||
{ int? focusedIndex }
|
||||
|
@ -306,7 +345,7 @@ class _DropdownMenuState extends State<DropdownMenu> {
|
|||
}
|
||||
|
||||
for (int i = 0; i < filteredEntries.length; i++) {
|
||||
final DropdownMenuEntry entry = filteredEntries[i];
|
||||
final DropdownMenuEntry<T> entry = filteredEntries[i];
|
||||
ButtonStyle effectiveStyle = entry.style ?? defaultStyle;
|
||||
final Color focusedBackgroundColor = effectiveStyle.foregroundColor?.resolve(<MaterialState>{MaterialState.focused})
|
||||
?? Theme.of(context).colorScheme.onSurface;
|
||||
|
@ -328,8 +367,9 @@ class _DropdownMenuState extends State<DropdownMenu> {
|
|||
? () {
|
||||
textEditingController.text = entry.label;
|
||||
textEditingController.selection =
|
||||
TextSelection.collapsed(offset: textEditingController.text.length);
|
||||
currentHighlight = widget.enableSearch ? i : -1;
|
||||
TextSelection.collapsed(offset: textEditingController.text.length);
|
||||
currentHighlight = widget.enableSearch ? i : null;
|
||||
widget.onSelected?.call(entry.value);
|
||||
}
|
||||
: null,
|
||||
requestFocusOnHover: false,
|
||||
|
@ -351,7 +391,8 @@ class _DropdownMenuState extends State<DropdownMenu> {
|
|||
while (!filteredEntries[currentHighlight!].enabled) {
|
||||
currentHighlight = (currentHighlight! - 1) % filteredEntries.length;
|
||||
}
|
||||
_textEditingController.text = filteredEntries[currentHighlight!].label;
|
||||
final String currentLabel = filteredEntries[currentHighlight!].label;
|
||||
_textEditingController.text = currentLabel;
|
||||
_textEditingController.selection =
|
||||
TextSelection.collapsed(offset: _textEditingController.text.length);
|
||||
});
|
||||
|
@ -366,14 +407,15 @@ class _DropdownMenuState extends State<DropdownMenu> {
|
|||
while (!filteredEntries[currentHighlight!].enabled) {
|
||||
currentHighlight = (currentHighlight! + 1) % filteredEntries.length;
|
||||
}
|
||||
_textEditingController.text = filteredEntries[currentHighlight!].label;
|
||||
final String currentLabel = filteredEntries[currentHighlight!].label;
|
||||
_textEditingController.text = currentLabel;
|
||||
_textEditingController.selection =
|
||||
TextSelection.collapsed(offset: _textEditingController.text.length);
|
||||
});
|
||||
|
||||
void handlePressed(MenuController controller) {
|
||||
if (controller.isOpen) {
|
||||
currentHighlight = -1;
|
||||
currentHighlight = null;
|
||||
controller.close();
|
||||
} else { // close to open
|
||||
if (_textEditingController.text.isNotEmpty) {
|
||||
|
@ -443,6 +485,7 @@ class _DropdownMenuState extends State<DropdownMenu> {
|
|||
controller: _controller,
|
||||
menuChildren: menu,
|
||||
crossAxisUnconstrained: false,
|
||||
onClose: () { setState(() {}); }, // To update the status of the IconButton
|
||||
builder: (BuildContext context, MenuController controller, Widget? child) {
|
||||
assert(_initialMenu != null);
|
||||
final Widget trailingButton = Padding(
|
||||
|
@ -473,16 +516,27 @@ class _DropdownMenuState extends State<DropdownMenu> {
|
|||
controller: _textEditingController,
|
||||
onEditingComplete: () {
|
||||
if (currentHighlight != null) {
|
||||
_textEditingController.text = filteredEntries[currentHighlight!].label;
|
||||
_textEditingController.selection =
|
||||
TextSelection.collapsed(offset: _textEditingController.text.length);
|
||||
final DropdownMenuEntry<T> entry = filteredEntries[currentHighlight!];
|
||||
if (entry.enabled) {
|
||||
_textEditingController.text = entry.label;
|
||||
_textEditingController.selection =
|
||||
TextSelection.collapsed(offset: _textEditingController.text.length);
|
||||
widget.onSelected?.call(entry.value);
|
||||
}
|
||||
} else {
|
||||
widget.onSelected?.call(null);
|
||||
}
|
||||
if (!widget.enableSearch) {
|
||||
currentHighlight = null;
|
||||
}
|
||||
if (_textEditingController.text.isNotEmpty) {
|
||||
controller.close();
|
||||
}
|
||||
controller.close();
|
||||
},
|
||||
onTap: () {
|
||||
handlePressed(controller);
|
||||
},
|
||||
onChanged: (_) {
|
||||
onChanged: (String text) {
|
||||
controller.open();
|
||||
setState(() {
|
||||
filteredEntries = widget.dropdownMenuEntries;
|
||||
|
|
|
@ -8,19 +8,19 @@ import 'package:flutter/services.dart';
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
final List<DropdownMenuEntry> menuChildren = <DropdownMenuEntry>[];
|
||||
final List<DropdownMenuEntry<TestMenu>> menuChildren = <DropdownMenuEntry<TestMenu>>[];
|
||||
|
||||
for (final TestMenu value in TestMenu.values) {
|
||||
final DropdownMenuEntry entry = DropdownMenuEntry(label: value.label);
|
||||
final DropdownMenuEntry<TestMenu> entry = DropdownMenuEntry<TestMenu>(value: value, label: value.label);
|
||||
menuChildren.add(entry);
|
||||
}
|
||||
|
||||
Widget buildTest(ThemeData themeData, List<DropdownMenuEntry> entries,
|
||||
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(
|
||||
body: DropdownMenu<T>(
|
||||
label: label,
|
||||
leadingIcon: leadingIcon,
|
||||
width: width,
|
||||
|
@ -80,7 +80,7 @@ void main() {
|
|||
theme: themeData,
|
||||
home: Scaffold(
|
||||
body: SafeArea(
|
||||
child: DropdownMenu(
|
||||
child: DropdownMenu<TestMenu>(
|
||||
enabled: false,
|
||||
dropdownMenuEntries: menuChildren,
|
||||
),
|
||||
|
@ -115,7 +115,7 @@ void main() {
|
|||
theme: themeData,
|
||||
home: Scaffold(
|
||||
body: SafeArea(
|
||||
child: DropdownMenu(
|
||||
child: DropdownMenu<TestMenu>(
|
||||
dropdownMenuEntries: menuChildren,
|
||||
),
|
||||
),
|
||||
|
@ -127,7 +127,7 @@ void main() {
|
|||
final Size anchorSize = tester.getSize(textField);
|
||||
expect(anchorSize, const Size(180.0, 54.0));
|
||||
|
||||
await tester.tap(find.byType(DropdownMenu));
|
||||
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final Finder menuMaterial = find.ancestor(
|
||||
|
@ -145,7 +145,7 @@ void main() {
|
|||
final Size size = tester.getSize(anchor);
|
||||
expect(size, const Size(200.0, 54.0));
|
||||
|
||||
await tester.tap(find.byType(DropdownMenu));
|
||||
await tester.tap(anchor);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final Finder updatedMenu = find.ancestor(
|
||||
|
@ -158,19 +158,19 @@ void main() {
|
|||
|
||||
testWidgets('The width property can customize the width of the dropdown menu', (WidgetTester tester) async {
|
||||
final ThemeData themeData = ThemeData();
|
||||
final List<DropdownMenuEntry> shortMenuItems = <DropdownMenuEntry>[];
|
||||
final List<DropdownMenuEntry<ShortMenu>> shortMenuItems = <DropdownMenuEntry<ShortMenu>>[];
|
||||
|
||||
for (final ShortMenu value in ShortMenu.values) {
|
||||
final DropdownMenuEntry entry = DropdownMenuEntry(label: value.label);
|
||||
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));
|
||||
RenderBox box = tester.firstRenderObject(find.byType(DropdownMenu<ShortMenu>));
|
||||
expect(box.size.width, customBigWidth);
|
||||
|
||||
await tester.tap(find.byType(DropdownMenu));
|
||||
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);
|
||||
|
@ -180,10 +180,10 @@ void main() {
|
|||
await tester.pumpWidget(Container());
|
||||
const double customSmallWidth = 100.0;
|
||||
await tester.pumpWidget(buildTest(themeData, shortMenuItems, width: customSmallWidth));
|
||||
box = tester.firstRenderObject(find.byType(DropdownMenu));
|
||||
box = tester.firstRenderObject(find.byType(DropdownMenu<ShortMenu>));
|
||||
expect(box.size.width, customSmallWidth);
|
||||
|
||||
await tester.tap(find.byType(DropdownMenu));
|
||||
await tester.tap(find.byType(DropdownMenu<ShortMenu>));
|
||||
await tester.pump();
|
||||
expect(find.byType(MenuItemButton), findsNWidgets(6));
|
||||
buttonSize = tester.getSize(find.widgetWithText(MenuItemButton, 'I0').last);
|
||||
|
@ -195,7 +195,7 @@ void main() {
|
|||
final ThemeData themeData = ThemeData();
|
||||
await tester.pumpWidget(buildTest(themeData, menuChildren));
|
||||
|
||||
await tester.tap(find.byType(DropdownMenu));
|
||||
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final Element firstItem = tester.element(find.widgetWithText(MenuItemButton, 'Item 0').last);
|
||||
|
@ -219,7 +219,7 @@ void main() {
|
|||
await tester.pumpWidget(buildTest(themeData, menuChildren, menuHeight: 100));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byType(DropdownMenu));
|
||||
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final Finder updatedMenu = find.ancestor(
|
||||
|
@ -240,7 +240,7 @@ void main() {
|
|||
final Finder label = find.text('label');
|
||||
final Offset labelTopLeft = tester.getTopLeft(label);
|
||||
|
||||
await tester.tap(find.byType(DropdownMenu));
|
||||
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
||||
await tester.pumpAndSettle();
|
||||
final Finder itemText = find.text('Item 0').last;
|
||||
final Offset itemTextTopLeft = tester.getTopLeft(itemText);
|
||||
|
@ -259,7 +259,7 @@ void main() {
|
|||
final Finder updatedLabel = find.text('label');
|
||||
final Offset updatedLabelTopLeft = tester.getTopLeft(updatedLabel);
|
||||
|
||||
await tester.tap(find.byType(DropdownMenu));
|
||||
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
||||
await tester.pumpAndSettle();
|
||||
final Finder updatedItemText = find.text('Item 0').last;
|
||||
final Offset updatedItemTextTopLeft = tester.getTopLeft(updatedItemText);
|
||||
|
@ -282,7 +282,7 @@ void main() {
|
|||
final Finder updatedLabel1 = find.text('label');
|
||||
final Offset updatedLabelTopLeft1 = tester.getTopLeft(updatedLabel1);
|
||||
|
||||
await tester.tap(find.byType(DropdownMenu));
|
||||
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
||||
await tester.pumpAndSettle();
|
||||
final Finder updatedItemText1 = find.text('Item 0').last;
|
||||
final Offset updatedItemTextTopLeft1 = tester.getTopLeft(updatedItemText1);
|
||||
|
@ -301,7 +301,7 @@ void main() {
|
|||
home: Scaffold(
|
||||
body: Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: DropdownMenu(
|
||||
child: DropdownMenu<TestMenu>(
|
||||
label: const Text('label'),
|
||||
dropdownMenuEntries: menuChildren,
|
||||
),
|
||||
|
@ -312,7 +312,7 @@ void main() {
|
|||
final Finder label = find.text('label');
|
||||
final Offset labelTopRight = tester.getTopRight(label);
|
||||
|
||||
await tester.tap(find.byType(DropdownMenu));
|
||||
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
||||
await tester.pumpAndSettle();
|
||||
final Finder itemText = find.text('Item 0').last;
|
||||
final Offset itemTextTopRight = tester.getTopRight(itemText);
|
||||
|
@ -326,7 +326,7 @@ void main() {
|
|||
home: Scaffold(
|
||||
body: Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: DropdownMenu(
|
||||
child: DropdownMenu<TestMenu>(
|
||||
leadingIcon: const Icon(Icons.search),
|
||||
label: const Text('label'),
|
||||
dropdownMenuEntries: menuChildren,
|
||||
|
@ -338,11 +338,11 @@ void main() {
|
|||
|
||||
final Finder leadingIcon = find.widgetWithIcon(Container, Icons.search);
|
||||
final double iconWidth = tester.getSize(leadingIcon).width;
|
||||
final Offset dropdownMenuTopRight = tester.getTopRight(find.byType(DropdownMenu));
|
||||
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));
|
||||
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
||||
await tester.pumpAndSettle();
|
||||
final Finder updatedItemText = find.text('Item 0').last;
|
||||
final Offset updatedItemTextTopRight = tester.getTopRight(updatedItemText);
|
||||
|
@ -358,7 +358,7 @@ void main() {
|
|||
home: Scaffold(
|
||||
body: Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: DropdownMenu(
|
||||
child: DropdownMenu<TestMenu>(
|
||||
leadingIcon: const SizedBox(width: 75.0, child: Icon(Icons.search)),
|
||||
label: const Text('label'),
|
||||
dropdownMenuEntries: menuChildren,
|
||||
|
@ -370,11 +370,11 @@ void main() {
|
|||
|
||||
final Finder largeLeadingIcon = find.widgetWithIcon(Container, Icons.search);
|
||||
final double largeIconWidth = tester.getSize(largeLeadingIcon).width;
|
||||
final Offset updatedDropdownMenuTopRight = tester.getTopRight(find.byType(DropdownMenu));
|
||||
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));
|
||||
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
||||
await tester.pumpAndSettle();
|
||||
final Finder updatedItemText1 = find.text('Item 0').last;
|
||||
final Offset updatedItemTextTopRight1 = tester.getTopRight(updatedItemText1);
|
||||
|
@ -407,7 +407,7 @@ void main() {
|
|||
await tester.pumpWidget(MaterialApp(
|
||||
theme: themeData,
|
||||
home: Scaffold(
|
||||
body: DropdownMenu(
|
||||
body: DropdownMenu<TestMenu>(
|
||||
trailingIcon: const Icon(Icons.ac_unit),
|
||||
dropdownMenuEntries: menuChildren,
|
||||
),
|
||||
|
@ -433,14 +433,14 @@ void main() {
|
|||
await tester.pumpWidget(MaterialApp(
|
||||
theme: themeData,
|
||||
home: Scaffold(
|
||||
body: DropdownMenu(
|
||||
body: DropdownMenu<TestMenu>(
|
||||
trailingIcon: const Icon(Icons.ac_unit),
|
||||
dropdownMenuEntries: menuChildren,
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
await tester.tap(find.byType(DropdownMenu));
|
||||
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
||||
await tester.pump();
|
||||
|
||||
await simulateKeyDownEvent(LogicalKeyboardKey.arrowDown);
|
||||
|
@ -475,13 +475,13 @@ void main() {
|
|||
await tester.pumpWidget(MaterialApp(
|
||||
theme: themeData,
|
||||
home: Scaffold(
|
||||
body: DropdownMenu(
|
||||
body: DropdownMenu<TestMenu>(
|
||||
dropdownMenuEntries: menuChildren,
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
await tester.tap(find.byType(DropdownMenu));
|
||||
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
||||
await tester.pump();
|
||||
|
||||
await simulateKeyDownEvent(LogicalKeyboardKey.arrowUp);
|
||||
|
@ -517,14 +517,14 @@ void main() {
|
|||
await tester.pumpWidget(MaterialApp(
|
||||
theme: themeData,
|
||||
home: Scaffold(
|
||||
body: DropdownMenu(
|
||||
body: DropdownMenu<TestMenu>(
|
||||
dropdownMenuEntries: menuChildren,
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
// Open the menu
|
||||
await tester.tap(find.byType(DropdownMenu));
|
||||
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
||||
await tester.pump();
|
||||
|
||||
await simulateKeyDownEvent(LogicalKeyboardKey.arrowDown);
|
||||
|
@ -547,14 +547,14 @@ void main() {
|
|||
await tester.pumpWidget(MaterialApp(
|
||||
theme: themeData,
|
||||
home: Scaffold(
|
||||
body: DropdownMenu(
|
||||
body: DropdownMenu<TestMenu>(
|
||||
dropdownMenuEntries: menuChildren,
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
// Open the menu
|
||||
await tester.tap(find.byType(DropdownMenu));
|
||||
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
||||
await tester.pump();
|
||||
|
||||
await simulateKeyDownEvent(LogicalKeyboardKey.arrowUp);
|
||||
|
@ -574,18 +574,18 @@ void main() {
|
|||
|
||||
testWidgets('Disabled button will be skipped while pressing up/down key', (WidgetTester tester) async {
|
||||
final ThemeData themeData = ThemeData();
|
||||
final List<DropdownMenuEntry> menuWithDisabledItems = <DropdownMenuEntry>[
|
||||
const DropdownMenuEntry(label: 'Item 0'),
|
||||
const DropdownMenuEntry(label: 'Item 1', enabled: false),
|
||||
const DropdownMenuEntry(label: 'Item 2', enabled: false),
|
||||
const DropdownMenuEntry(label: 'Item 3'),
|
||||
const DropdownMenuEntry(label: 'Item 4'),
|
||||
const DropdownMenuEntry(label: 'Item 5', enabled: false),
|
||||
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(
|
||||
body: DropdownMenu<TestMenu>(
|
||||
dropdownMenuEntries: menuWithDisabledItems,
|
||||
),
|
||||
),
|
||||
|
@ -593,7 +593,7 @@ void main() {
|
|||
await tester.pump();
|
||||
|
||||
// Open the menu
|
||||
await tester.tap(find.byType(DropdownMenu));
|
||||
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await simulateKeyDownEvent(LogicalKeyboardKey.arrowDown);
|
||||
|
@ -621,14 +621,14 @@ void main() {
|
|||
await tester.pumpWidget(MaterialApp(
|
||||
theme: themeData,
|
||||
home: Scaffold(
|
||||
body: DropdownMenu(
|
||||
body: DropdownMenu<TestMenu>(
|
||||
dropdownMenuEntries: menuChildren,
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
// Open the menu
|
||||
await tester.tap(find.byType(DropdownMenu));
|
||||
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
||||
await tester.pump();
|
||||
await tester.enterText(find.byType(TextField).first, 'Menu 1');
|
||||
await tester.pumpAndSettle();
|
||||
|
@ -645,14 +645,14 @@ void main() {
|
|||
await tester.pumpWidget(MaterialApp(
|
||||
theme: themeData,
|
||||
home: Scaffold(
|
||||
body: DropdownMenu(
|
||||
body: DropdownMenu<TestMenu>(
|
||||
dropdownMenuEntries: menuChildren,
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
// Open the menu
|
||||
await tester.tap(find.byType(DropdownMenu));
|
||||
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
||||
await tester.pump();
|
||||
await tester.enterText(find.byType(TextField).first, 'Menu 1');
|
||||
await tester.pumpAndSettle();
|
||||
|
@ -691,14 +691,14 @@ void main() {
|
|||
await tester.pumpWidget(MaterialApp(
|
||||
theme: themeData,
|
||||
home: Scaffold(
|
||||
body: DropdownMenu(
|
||||
body: DropdownMenu<TestMenu>(
|
||||
dropdownMenuEntries: menuChildren,
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
// Open the menu
|
||||
await tester.tap(find.byType(DropdownMenu));
|
||||
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
||||
await tester.pump();
|
||||
|
||||
await tester.enterText(find.byType(TextField).first, 'Menu 1');
|
||||
|
@ -714,7 +714,7 @@ void main() {
|
|||
await tester.pumpWidget(MaterialApp(
|
||||
theme: themeData,
|
||||
home: Scaffold(
|
||||
body: DropdownMenu(
|
||||
body: DropdownMenu<TestMenu>(
|
||||
enableFilter: true,
|
||||
dropdownMenuEntries: menuChildren,
|
||||
),
|
||||
|
@ -722,7 +722,7 @@ void main() {
|
|||
));
|
||||
|
||||
// Open the menu
|
||||
await tester.tap(find.byType(DropdownMenu));
|
||||
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
||||
await tester.pump();
|
||||
|
||||
await tester.enterText(find
|
||||
|
@ -738,6 +738,150 @@ void main() {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
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 {
|
||||
|
|
|
@ -43,11 +43,11 @@ void main() {
|
|||
theme: themeData,
|
||||
home: const Scaffold(
|
||||
body: Center(
|
||||
child: DropdownMenu(
|
||||
dropdownMenuEntries: <DropdownMenuEntry>[
|
||||
DropdownMenuEntry(label: 'Item 0'),
|
||||
DropdownMenuEntry(label: 'Item 1'),
|
||||
DropdownMenuEntry(label: 'Item 2'),
|
||||
child: DropdownMenu<int>(
|
||||
dropdownMenuEntries: <DropdownMenuEntry<int>>[
|
||||
DropdownMenuEntry<int>(value: 0, label: 'Item 0'),
|
||||
DropdownMenuEntry<int>(value: 1, label: 'Item 1'),
|
||||
DropdownMenuEntry<int>(value: 2, label: 'Item 2'),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -122,11 +122,11 @@ void main() {
|
|||
theme: theme,
|
||||
home: const Scaffold(
|
||||
body: Center(
|
||||
child: DropdownMenu(
|
||||
dropdownMenuEntries: <DropdownMenuEntry>[
|
||||
DropdownMenuEntry(label: 'Item 0'),
|
||||
DropdownMenuEntry(label: 'Item 1'),
|
||||
DropdownMenuEntry(label: 'Item 2'),
|
||||
child: DropdownMenu<int>(
|
||||
dropdownMenuEntries: <DropdownMenuEntry<int>>[
|
||||
DropdownMenuEntry<int>(value: 0, label: 'Item 0'),
|
||||
DropdownMenuEntry<int>(value: 1, label: 'Item 1'),
|
||||
DropdownMenuEntry<int>(value: 2, label: 'Item 2'),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -223,11 +223,11 @@ void main() {
|
|||
data: dropdownMenuTheme,
|
||||
child: const Scaffold(
|
||||
body: Center(
|
||||
child: DropdownMenu(
|
||||
dropdownMenuEntries: <DropdownMenuEntry>[
|
||||
DropdownMenuEntry(label: 'Item 0'),
|
||||
DropdownMenuEntry(label: 'Item 1'),
|
||||
DropdownMenuEntry(label: 'Item 2'),
|
||||
child: DropdownMenu<int>(
|
||||
dropdownMenuEntries: <DropdownMenuEntry<int>>[
|
||||
DropdownMenuEntry<int>(value: 0, label: 'Item 0'),
|
||||
DropdownMenuEntry<int>(value: 1, label: 'Item 1'),
|
||||
DropdownMenuEntry<int>(value: 2, label: 'Item 2'),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -326,7 +326,7 @@ void main() {
|
|||
data: dropdownMenuTheme,
|
||||
child: Scaffold(
|
||||
body: Center(
|
||||
child: DropdownMenu(
|
||||
child: DropdownMenu<int>(
|
||||
textStyle: TextStyle(
|
||||
color: Colors.pink,
|
||||
backgroundColor: Colors.cyan,
|
||||
|
@ -345,10 +345,10 @@ void main() {
|
|||
),
|
||||
),
|
||||
inputDecorationTheme: const InputDecorationTheme(filled: true, fillColor: Colors.deepPurple),
|
||||
dropdownMenuEntries: const <DropdownMenuEntry>[
|
||||
DropdownMenuEntry(label: 'Item 0'),
|
||||
DropdownMenuEntry(label: 'Item 1'),
|
||||
DropdownMenuEntry(label: 'Item 2'),
|
||||
dropdownMenuEntries: const <DropdownMenuEntry<int>>[
|
||||
DropdownMenuEntry<int>(value: 0, label: 'Item 0'),
|
||||
DropdownMenuEntry<int>(value: 1, label: 'Item 1'),
|
||||
DropdownMenuEntry<int>(value: 2, label: 'Item 2'),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
Loading…
Reference in a new issue