Fix DropdownMenu menu is detached from the text field (#159665)

## Description

This PR fixes `DropdownMenu` menu vertical position when tight
constraints are given to the `DropdownMenu`.

Before:


![image](https://github.com/user-attachments/assets/d8d9b857-07ed-4d3c-afe2-44518d979e66)

After:


![image](https://github.com/user-attachments/assets/a71ea3c8-a408-43a1-8c99-c2612ad4b24b)



## Related Issue

Fixes [DropdownMenu does not correctly handle incoming maxWidth and
minHeight
constraints](https://github.com/flutter/flutter/issues/147076).
(It fixes the minHeight part, the maxWidth part was already fixed in
https://github.com/flutter/flutter/pull/147233).

## Tests

Adds 4 tests.
This commit is contained in:
Bruno Leroux 2024-12-03 08:51:37 +01:00 committed by GitHub
parent f66f6a7cb9
commit 11fa41e699
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 99 additions and 0 deletions

View File

@ -1022,6 +1022,17 @@ class _DropdownMenuState<T> extends State<DropdownMenu<T>> {
);
}
// Wrap the menu anchor with an Align to narrow down the constraints.
// Without this Align, when tight constraints are applied to DropdownMenu,
// the menu will appear below these constraints instead of below the
// text field.
menuAnchor = Align(
alignment: AlignmentDirectional.topStart,
widthFactor: 1.0,
heightFactor: 1.0,
child: menuAnchor,
);
return Actions(
actions: <Type, Action<Intent>>{
_ArrowUpIntent: CallbackAction<_ArrowUpIntent>(

View File

@ -3488,6 +3488,94 @@ void main() {
// None of the menus should be closed.
expect(dropdownMenuAnchor.controller!.isOpen, true);
});
group('The menu is attached at the bottom of the TextField', () {
// Define the expected text field bottom instead of querying it using
// tester.getRect because when tight constraints are applied to the
// Dropdown the TextField bounds are expanded while the visible size
// remains 56 pixels.
const double textFieldBottom = 56.0;
testWidgets('when given loose constraints and expandedInsets is set', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: DropdownMenu<TestMenu>(
expandedInsets: EdgeInsets.zero,
initialSelection: TestMenu.mainMenu3,
dropdownMenuEntries: menuChildrenWithIcons,
),
),
));
// Open the menu.
await tester.tap(find.byType(TextField));
await tester.pump();
expect(tester.getRect(findMenuMaterial()).top, textFieldBottom);
});
testWidgets('when given tight constraints and expandedInsets is set', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: SizedBox(
width: 200,
height: 300,
child: DropdownMenu<TestMenu>(
expandedInsets: EdgeInsets.zero,
initialSelection: TestMenu.mainMenu3,
dropdownMenuEntries: menuChildrenWithIcons,
),
),
),
));
// Open the menu.
await tester.tap(find.byType(TextField));
await tester.pump();
expect(tester.getRect(findMenuMaterial()).top, textFieldBottom);
});
// Regression test for https://github.com/flutter/flutter/issues/147076.
testWidgets('when given loose constraints and expandedInsets is not set', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: DropdownMenu<TestMenu>(
initialSelection: TestMenu.mainMenu3,
dropdownMenuEntries: menuChildrenWithIcons,
),
),
));
// Open the menu.
await tester.tap(find.byType(TextField));
await tester.pump();
expect(tester.getRect(findMenuMaterial()).top, textFieldBottom);
});
// Regression test for https://github.com/flutter/flutter/issues/147076.
testWidgets('when given tight constraints and expandedInsets is not set', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: SizedBox(
width: 200,
height: 300,
child: DropdownMenu<TestMenu>(
initialSelection: TestMenu.mainMenu3,
dropdownMenuEntries: menuChildrenWithIcons,
),
),
),
));
// Open the menu.
await tester.tap(find.byType(TextField));
await tester.pump();
expect(tester.getRect(findMenuMaterial()).top, textFieldBottom);
});
});
}
enum TestMenu {