diff --git a/packages/flutter/lib/src/material/dropdown_menu.dart b/packages/flutter/lib/src/material/dropdown_menu.dart index def4ea08a2..ab16bc12bf 100644 --- a/packages/flutter/lib/src/material/dropdown_menu.dart +++ b/packages/flutter/lib/src/material/dropdown_menu.dart @@ -1022,6 +1022,17 @@ class _DropdownMenuState extends State> { ); } + // 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: >{ _ArrowUpIntent: CallbackAction<_ArrowUpIntent>( diff --git a/packages/flutter/test/material/dropdown_menu_test.dart b/packages/flutter/test/material/dropdown_menu_test.dart index d78f5f1488..33c0a6db7d 100644 --- a/packages/flutter/test/material/dropdown_menu_test.dart +++ b/packages/flutter/test/material/dropdown_menu_test.dart @@ -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( + 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( + 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( + 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( + 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 {