Fix DropdownMenu default width does not take label into account (#161219)
## Description This PR fixes `DropdownMenu` default width when a long label is provided. Before: The width is based on the longest menu item (which can be smaller than the label):  After: The width also depends on the label width when it is longer than the menu items.  ## Related Issue Fixes [DropdownMenu default width does not take label into account](https://github.com/flutter/flutter/issues/136678) ## Tests Adds 2 tests.
This commit is contained in:
parent
60d0bfcca4
commit
b61335bd3b
@ -1079,11 +1079,24 @@ class _DropdownMenuState<T> extends State<DropdownMenu<T>> {
|
|||||||
? textField
|
? textField
|
||||||
: _DropdownMenuBody(
|
: _DropdownMenuBody(
|
||||||
width: widget.width,
|
width: widget.width,
|
||||||
|
// The children, except the text field, are used to compute the preferred width,
|
||||||
|
// which is the width of the longest children, plus the width of trailingButton
|
||||||
|
// and leadingButton.
|
||||||
|
//
|
||||||
|
// See _RenderDropdownMenuBody layout logic.
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
textField,
|
textField,
|
||||||
..._initialMenu!.map(
|
..._initialMenu!.map(
|
||||||
(Widget item) => ExcludeFocus(excluding: !controller.isOpen, child: item),
|
(Widget item) => ExcludeFocus(excluding: !controller.isOpen, child: item),
|
||||||
),
|
),
|
||||||
|
if (widget.label != null)
|
||||||
|
ExcludeSemantics(
|
||||||
|
child: Padding(
|
||||||
|
// See RenderEditable.floatingCursorAddedMargin for the default horizontal padding.
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||||
|
child: DefaultTextStyle(style: effectiveTextStyle!, child: widget.label!),
|
||||||
|
),
|
||||||
|
),
|
||||||
trailingButton,
|
trailingButton,
|
||||||
leadingButton,
|
leadingButton,
|
||||||
],
|
],
|
||||||
@ -1317,9 +1330,11 @@ class _RenderDropdownMenuBody extends RenderBox
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
final double maxIntrinsicWidth = child.getMinIntrinsicWidth(height);
|
final double maxIntrinsicWidth = child.getMinIntrinsicWidth(height);
|
||||||
|
// Add the width of leading icon.
|
||||||
if (child == lastChild) {
|
if (child == lastChild) {
|
||||||
width += maxIntrinsicWidth;
|
width += maxIntrinsicWidth;
|
||||||
}
|
}
|
||||||
|
// Add the width of trailing icon.
|
||||||
if (child == childBefore(lastChild!)) {
|
if (child == childBefore(lastChild!)) {
|
||||||
width += maxIntrinsicWidth;
|
width += maxIntrinsicWidth;
|
||||||
}
|
}
|
||||||
@ -1344,11 +1359,11 @@ class _RenderDropdownMenuBody extends RenderBox
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
final double maxIntrinsicWidth = child.getMaxIntrinsicWidth(height);
|
final double maxIntrinsicWidth = child.getMaxIntrinsicWidth(height);
|
||||||
// Add the width of leading Icon.
|
// Add the width of leading icon.
|
||||||
if (child == lastChild) {
|
if (child == lastChild) {
|
||||||
width += maxIntrinsicWidth;
|
width += maxIntrinsicWidth;
|
||||||
}
|
}
|
||||||
// Add the width of trailing Icon.
|
// Add the width of trailing icon.
|
||||||
if (child == childBefore(lastChild!)) {
|
if (child == childBefore(lastChild!)) {
|
||||||
width += maxIntrinsicWidth;
|
width += maxIntrinsicWidth;
|
||||||
}
|
}
|
||||||
|
@ -718,6 +718,64 @@ void main() {
|
|||||||
expect(box.size.width, customWidth);
|
expect(box.size.width, customWidth);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('The width is determined by the menu entries', (WidgetTester tester) async {
|
||||||
|
const double entryLabelWidth = 100;
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
const MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
body: DropdownMenu<int>(
|
||||||
|
dropdownMenuEntries: <DropdownMenuEntry<int>>[
|
||||||
|
DropdownMenuEntry<int>(
|
||||||
|
value: 0,
|
||||||
|
label: 'Flutter',
|
||||||
|
labelWidget: SizedBox(width: entryLabelWidth),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final double width = tester.getSize(find.byType(DropdownMenu<int>)).width;
|
||||||
|
const double menuEntryPadding = 24.0; // See _kDefaultHorizontalPadding.
|
||||||
|
const double leadingWidth = 16.0;
|
||||||
|
const double trailingWidth = 56.0;
|
||||||
|
|
||||||
|
expect(width, entryLabelWidth + leadingWidth + trailingWidth + menuEntryPadding);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('The width is determined by the label when it is longer than menu entries', (
|
||||||
|
WidgetTester tester,
|
||||||
|
) async {
|
||||||
|
const double labelWidth = 120;
|
||||||
|
const double entryLabelWidth = 100;
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
const MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
body: DropdownMenu<int>(
|
||||||
|
label: SizedBox(width: labelWidth),
|
||||||
|
dropdownMenuEntries: <DropdownMenuEntry<int>>[
|
||||||
|
DropdownMenuEntry<int>(
|
||||||
|
value: 0,
|
||||||
|
label: 'Flutter',
|
||||||
|
labelWidget: SizedBox(width: entryLabelWidth),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final double width = tester.getSize(find.byType(DropdownMenu<int>)).width;
|
||||||
|
const double leadingWidth = 16.0;
|
||||||
|
const double trailingWidth = 56.0;
|
||||||
|
const double labelPadding = 8.0; // See RenderEditable.floatingCursorAddedMargin.
|
||||||
|
|
||||||
|
expect(width, labelWidth + labelPadding + leadingWidth + trailingWidth);
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('The width of MenuAnchor respects MenuAnchor.expandedInsets', (
|
testWidgets('The width of MenuAnchor respects MenuAnchor.expandedInsets', (
|
||||||
WidgetTester tester,
|
WidgetTester tester,
|
||||||
) async {
|
) async {
|
||||||
@ -962,7 +1020,7 @@ void main() {
|
|||||||
// Default text field (without leading icon).
|
// Default text field (without leading icon).
|
||||||
await tester.pumpWidget(buildTest(themeData, menuChildren, label: const Text('label')));
|
await tester.pumpWidget(buildTest(themeData, menuChildren, label: const Text('label')));
|
||||||
|
|
||||||
final Finder label = find.text('label');
|
final Finder label = find.text('label').first;
|
||||||
final Offset labelTopLeft = tester.getTopLeft(label);
|
final Offset labelTopLeft = tester.getTopLeft(label);
|
||||||
|
|
||||||
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
||||||
@ -985,7 +1043,7 @@ void main() {
|
|||||||
|
|
||||||
final Finder leadingIcon = find.widgetWithIcon(SizedBox, Icons.search).last;
|
final Finder leadingIcon = find.widgetWithIcon(SizedBox, Icons.search).last;
|
||||||
final double iconWidth = tester.getSize(leadingIcon).width;
|
final double iconWidth = tester.getSize(leadingIcon).width;
|
||||||
final Finder updatedLabel = find.text('label');
|
final Finder updatedLabel = find.text('label').first;
|
||||||
final Offset updatedLabelTopLeft = tester.getTopLeft(updatedLabel);
|
final Offset updatedLabelTopLeft = tester.getTopLeft(updatedLabel);
|
||||||
|
|
||||||
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
||||||
@ -1009,7 +1067,7 @@ void main() {
|
|||||||
|
|
||||||
final Finder largeLeadingIcon = find.widgetWithIcon(SizedBox, Icons.search).last;
|
final Finder largeLeadingIcon = find.widgetWithIcon(SizedBox, Icons.search).last;
|
||||||
final double largeIconWidth = tester.getSize(largeLeadingIcon).width;
|
final double largeIconWidth = tester.getSize(largeLeadingIcon).width;
|
||||||
final Finder updatedLabel1 = find.text('label');
|
final Finder updatedLabel1 = find.text('label').first;
|
||||||
final Offset updatedLabelTopLeft1 = tester.getTopLeft(updatedLabel1);
|
final Offset updatedLabelTopLeft1 = tester.getTopLeft(updatedLabel1);
|
||||||
|
|
||||||
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
||||||
@ -1040,7 +1098,7 @@ void main() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final Finder label = find.text('label');
|
final Finder label = find.text('label').first;
|
||||||
final Offset labelTopRight = tester.getTopRight(label);
|
final Offset labelTopRight = tester.getTopRight(label);
|
||||||
|
|
||||||
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
||||||
@ -1072,7 +1130,7 @@ void main() {
|
|||||||
final Finder leadingIcon = find.widgetWithIcon(SizedBox, Icons.search).last;
|
final Finder leadingIcon = find.widgetWithIcon(SizedBox, Icons.search).last;
|
||||||
final double iconWidth = tester.getSize(leadingIcon).width;
|
final double iconWidth = tester.getSize(leadingIcon).width;
|
||||||
final Offset dropdownMenuTopRight = tester.getTopRight(find.byType(DropdownMenu<TestMenu>));
|
final Offset dropdownMenuTopRight = tester.getTopRight(find.byType(DropdownMenu<TestMenu>));
|
||||||
final Finder updatedLabel = find.text('label');
|
final Finder updatedLabel = find.text('label').first;
|
||||||
final Offset updatedLabelTopRight = tester.getTopRight(updatedLabel);
|
final Offset updatedLabelTopRight = tester.getTopRight(updatedLabel);
|
||||||
|
|
||||||
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
||||||
@ -1110,7 +1168,7 @@ void main() {
|
|||||||
final Offset updatedDropdownMenuTopRight = tester.getTopRight(
|
final Offset updatedDropdownMenuTopRight = tester.getTopRight(
|
||||||
find.byType(DropdownMenu<TestMenu>),
|
find.byType(DropdownMenu<TestMenu>),
|
||||||
);
|
);
|
||||||
final Finder updatedLabel1 = find.text('label');
|
final Finder updatedLabel1 = find.text('label').first;
|
||||||
final Offset updatedLabelTopRight1 = tester.getTopRight(updatedLabel1);
|
final Offset updatedLabelTopRight1 = tester.getTopRight(updatedLabel1);
|
||||||
|
|
||||||
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
await tester.tap(find.byType(DropdownMenu<TestMenu>));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user