Fix DropdownMenu does not rematch initialSelection when entries have changed (#155757)

## Description

This PR makes DropdownMenu rematching the initialSelection when the entries are updated.
If the new entries contains one entry whose value matches `initialSelection` this entry's label is used to initialize the inner text field, if no entries matches `initialSelection` the text field is emptied.

## Related Issue

Fixes [DropdownMenu.didUpdateWidget should re-match initialSelection when dropdownMenuEntries have changed](https://github.com/flutter/flutter/issues/155660).

## Tests

Adds 3 tests.
This commit is contained in:
Bruno Leroux
2024-10-02 14:03:20 +02:00
committed by GitHub
parent 91c621ee4e
commit 21381d843f
5 changed files with 198 additions and 61 deletions

View File

@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
// Flutter code sample for [DropdownMenu]s. The first dropdown menu
@@ -14,6 +15,8 @@ void main() {
runApp(const DropdownMenuExample());
}
typedef ColorEntry = DropdownMenuEntry<ColorLabel>;
// DropdownMenuEntry labels and values for the first dropdown menu.
enum ColorLabel {
blue('Blue', Colors.blue),
@@ -25,21 +28,43 @@ enum ColorLabel {
const ColorLabel(this.label, this.color);
final String label;
final Color color;
static final List<ColorEntry> entries = UnmodifiableListView<ColorEntry>(
values.map<ColorEntry>(
(ColorLabel color) => ColorEntry(
value: color,
label: color.label,
enabled: color.label != 'Grey',
style: MenuItemButton.styleFrom(
foregroundColor: color.color,
),
),
),
);
}
typedef IconEntry = DropdownMenuEntry<IconLabel>;
// DropdownMenuEntry labels and values for the second dropdown menu.
enum IconLabel {
smile('Smile', Icons.sentiment_satisfied_outlined),
cloud(
'Cloud',
Icons.cloud_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;
static final List<IconEntry> entries = UnmodifiableListView<IconEntry>(
values.map<IconEntry>(
(IconLabel icon) => IconEntry(
value: icon,
label: icon.label,
leadingIcon: Icon(icon.icon),
),
),
);
}
class DropdownMenuExample extends StatefulWidget {
@@ -85,18 +110,7 @@ class _DropdownMenuExampleState extends State<DropdownMenuExample> {
selectedColor = color;
});
},
dropdownMenuEntries: ColorLabel.values.map<DropdownMenuEntry<ColorLabel>>(
(ColorLabel color) {
return DropdownMenuEntry<ColorLabel>(
value: color,
label: color.label,
enabled: color.label != 'Grey',
style: MenuItemButton.styleFrom(
foregroundColor: color.color,
),
);
}
).toList(),
dropdownMenuEntries: ColorLabel.entries,
),
const SizedBox(width: 24),
DropdownMenu<IconLabel>(
@@ -114,15 +128,7 @@ class _DropdownMenuExampleState extends State<DropdownMenuExample> {
selectedIcon = icon;
});
},
dropdownMenuEntries: IconLabel.values.map<DropdownMenuEntry<IconLabel>>(
(IconLabel icon) {
return DropdownMenuEntry<IconLabel>(
value: icon,
label: icon.label,
leadingIcon: Icon(icon.icon),
);
},
).toList(),
dropdownMenuEntries: IconLabel.entries,
),
],
),

View File

@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
/// Flutter code sample for [DropdownMenu].
@@ -34,7 +35,12 @@ class DropdownMenuExample extends StatefulWidget {
State<DropdownMenuExample> createState() => _DropdownMenuExampleState();
}
typedef MenuEntry = DropdownMenuEntry<String>;
class _DropdownMenuExampleState extends State<DropdownMenuExample> {
static final List<MenuEntry> menuEntries = UnmodifiableListView<MenuEntry>(
list.map<MenuEntry>((String name) => MenuEntry(value: name, label: name)),
);
String dropdownValue = list.first;
@override
@@ -47,12 +53,7 @@ class _DropdownMenuExampleState extends State<DropdownMenuExample> {
dropdownValue = value!;
});
},
dropdownMenuEntries: list.map<DropdownMenuEntry<String>>((String value) {
return DropdownMenuEntry<String>(
value: value,
label: value
);
}).toList(),
dropdownMenuEntries: menuEntries,
);
}
}

View File

@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
/// Flutter code sample for [DropdownMenu].
@@ -33,7 +34,12 @@ class DropdownMenuExample extends StatefulWidget {
State<DropdownMenuExample> createState() => _DropdownMenuExampleState();
}
typedef MenuEntry = DropdownMenuEntry<String>;
class _DropdownMenuExampleState extends State<DropdownMenuExample> {
static final List<MenuEntry> menuEntries = UnmodifiableListView<MenuEntry>(
list.map<MenuEntry>((String name) => MenuEntry(value: name, label: name)),
);
String dropdownValue = list.first;
@override
@@ -62,10 +68,7 @@ class _DropdownMenuExampleState extends State<DropdownMenuExample> {
dropdownValue = value!;
});
},
dropdownMenuEntries:
list.map<DropdownMenuEntry<String>>((String value) {
return DropdownMenuEntry<String>(value: value, label: value);
}).toList(),
dropdownMenuEntries: menuEntries,
),
const Text('Text cursor is shown when hovering over the DropdownMenu.'),
],
@@ -92,10 +95,7 @@ class _DropdownMenuExampleState extends State<DropdownMenuExample> {
dropdownValue = value!;
});
},
dropdownMenuEntries:
list.map<DropdownMenuEntry<String>>((String value) {
return DropdownMenuEntry<String>(value: value, label: value);
}).toList(),
dropdownMenuEntries: menuEntries,
),
const Text('Clickable cursor is shown when hovering over the DropdownMenu.'),
],
@@ -123,10 +123,7 @@ class _DropdownMenuExampleState extends State<DropdownMenuExample> {
dropdownValue = value!;
});
},
dropdownMenuEntries:
list.map<DropdownMenuEntry<String>>((String value) {
return DropdownMenuEntry<String>(value: value, label: value);
}).toList(),
dropdownMenuEntries: menuEntries,
),
const Text('Default cursor is shown when hovering over the DropdownMenu.'),
],
@@ -154,10 +151,7 @@ class _DropdownMenuExampleState extends State<DropdownMenuExample> {
dropdownValue = value!;
});
},
dropdownMenuEntries:
list.map<DropdownMenuEntry<String>>((String value) {
return DropdownMenuEntry<String>(value: value, label: value);
}).toList(),
dropdownMenuEntries: menuEntries,
),
const Text('Default cursor is shown when hovering over the DropdownMenu.'),
],