diff --git a/packages/flutter/lib/src/material/dropdown_menu.dart b/packages/flutter/lib/src/material/dropdown_menu.dart index 45f3a38e3f8..0fcb87f0a04 100644 --- a/packages/flutter/lib/src/material/dropdown_menu.dart +++ b/packages/flutter/lib/src/material/dropdown_menu.dart @@ -1110,6 +1110,7 @@ class _DropdownMenuState extends State> { Widget menuAnchor = MenuAnchor( style: effectiveMenuStyle, alignmentOffset: widget.alignmentOffset, + reservedPadding: EdgeInsets.zero, controller: _controller, menuChildren: menu, crossAxisUnconstrained: false, diff --git a/packages/flutter/lib/src/material/menu_anchor.dart b/packages/flutter/lib/src/material/menu_anchor.dart index bb7ee3597b8..72793e83210 100644 --- a/packages/flutter/lib/src/material/menu_anchor.dart +++ b/packages/flutter/lib/src/material/menu_anchor.dart @@ -161,6 +161,7 @@ class MenuAnchor extends StatefulWidget { this.childFocusNode, this.style, this.alignmentOffset = Offset.zero, + this.reservedPadding, this.layerLink, this.clipBehavior = Clip.hardEdge, @Deprecated( @@ -318,6 +319,11 @@ class MenuAnchor extends StatefulWidget { /// to rebuild this child when those change. final Widget? child; + /// The padding between the edge of the safe area and the menu panel. + /// + /// Defaults to EdgeInsets.all(8). + final EdgeInsetsGeometry? reservedPadding; + @override State createState() => _MenuAnchorState(); @@ -406,6 +412,7 @@ class _MenuAnchorState extends State { menuPosition: position, anchor: this, alignmentOffset: widget.alignmentOffset ?? Offset.zero, + reservedPadding: widget.reservedPadding ?? const EdgeInsets.all(_kMenuViewPadding), ); } @@ -2920,6 +2927,7 @@ class _MenuLayout extends SingleChildLayoutDelegate { required this.avoidBounds, required this.orientation, required this.parentOrientation, + required this.reservedPadding, }); // Rectangle of underlying button, relative to the overlay's dimensions. @@ -2951,13 +2959,14 @@ class _MenuLayout extends SingleChildLayoutDelegate { // The orientation of this menu's parent. final Axis parentOrientation; + // How close to the edge of the safe area the menu will be placed. + final EdgeInsetsGeometry reservedPadding; + @override BoxConstraints getConstraintsForChild(BoxConstraints constraints) { - // The menu can be at most the size of the overlay minus _kMenuViewPadding - // pixels in each direction. - return BoxConstraints.loose( - constraints.biggest, - ).deflate(const EdgeInsets.all(_kMenuViewPadding)); + // The menu can be at most the size of the overlay minus the view padding + // in each direction. + return BoxConstraints.loose(constraints.biggest).deflate(reservedPadding); } @override @@ -3289,6 +3298,7 @@ class _Submenu extends StatelessWidget { this.crossAxisUnconstrained = true, required this.menuChildren, required this.menuScopeNode, + required this.reservedPadding, }); final FocusScopeNode menuScopeNode; @@ -3301,6 +3311,7 @@ class _Submenu extends StatelessWidget { final Clip clipBehavior; final bool crossAxisUnconstrained; final List menuChildren; + final EdgeInsetsGeometry reservedPadding; @override Widget build(BuildContext context) { @@ -3399,6 +3410,7 @@ class _Submenu extends StatelessWidget { menuPosition: menuPosition.position, orientation: anchor._orientation, parentOrientation: anchor._parent?._orientation ?? Axis.horizontal, + reservedPadding: reservedPadding, ), child: menuPanel, ); diff --git a/packages/flutter/test/material/dropdown_menu_test.dart b/packages/flutter/test/material/dropdown_menu_test.dart index c2d4a54ecef..79f34540e8b 100644 --- a/packages/flutter/test/material/dropdown_menu_test.dart +++ b/packages/flutter/test/material/dropdown_menu_test.dart @@ -927,6 +927,31 @@ void main() { expect(buttonSize.width, parentWidth - 35.0 - 20.0); }); + // Regression test for https://github.com/flutter/flutter/issues/172680. + testWidgets('Menu panel width can expand to full-screen width', (WidgetTester tester) async { + await tester.pumpWidget( + const MaterialApp( + home: Scaffold( + body: DropdownMenu( + expandedInsets: EdgeInsets.zero, + dropdownMenuEntries: >[ + DropdownMenuEntry(value: 0, label: 'Flutter'), + ], + ), + ), + ), + ); + + final double dropdownWidth = tester.getSize(find.byType(DropdownMenu)).width; + expect(dropdownWidth, 800); + + await tester.tap(find.byType(DropdownMenu)); + await tester.pump(); + + final double menuWidth = tester.getSize(findMenuItemButton('Flutter')).width; + expect(dropdownWidth, menuWidth); + }); + testWidgets( 'Material2 - The menuHeight property can be used to show a shorter scrollable menu list instead of the complete list', (WidgetTester tester) async { diff --git a/packages/flutter/test/material/menu_anchor_test.dart b/packages/flutter/test/material/menu_anchor_test.dart index 9a7b950c7d0..7ef98382433 100644 --- a/packages/flutter/test/material/menu_anchor_test.dart +++ b/packages/flutter/test/material/menu_anchor_test.dart @@ -5047,6 +5047,55 @@ void main() { expect(find.byIcon(disabledIcon), findsOneWidget); }); }); + + testWidgets('Menu panel default reserved padding', (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: Material( + child: Center( + child: MenuAnchor( + controller: controller, + menuChildren: const [SizedBox(width: 800, height: 24)], + builder: (BuildContext context, MenuController controller, Widget? child) { + return const SizedBox(width: 800, height: 24); + }, + ), + ), + ), + ), + ); + + controller.open(); + await tester.pump(); + + const double defaultReservedPadding = 8.0; // See _kMenuViewPadding. + expect(tester.getRect(findMenuPanels()).width, 800.0 - defaultReservedPadding * 2); + }); + + testWidgets('Menu panel accepts custom reserved padding', (WidgetTester tester) async { + const EdgeInsetsGeometry reservedPadding = EdgeInsets.symmetric(horizontal: 13.0); + await tester.pumpWidget( + MaterialApp( + home: Material( + child: Center( + child: MenuAnchor( + controller: controller, + reservedPadding: reservedPadding, + menuChildren: const [SizedBox(width: 800, height: 24)], + builder: (BuildContext context, MenuController controller, Widget? child) { + return const SizedBox(width: 800, height: 24); + }, + ), + ), + ), + ), + ); + + controller.open(); + await tester.pump(); + + expect(tester.getRect(findMenuPanels()).width, 800.0 - reservedPadding.horizontal); + }); } List createTestMenus({