Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion packages/material_ui/lib/src/menu_anchor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3466,7 +3466,32 @@ class _MenuLayout extends SingleChildLayoutDelegate {
BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
// 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);
final BoxConstraints result = BoxConstraints.loose(
constraints.biggest,
).deflate(reservedPadding);
if (menuPosition != null || orientation == Axis.horizontal) {
return result;
}
// A vertical menu is placed either above or below the anchor, so it can
// only ever occupy the space on one of those two sides. Capping its height
// to the larger of the two keeps a tall menu from growing past the anchor
// and covering it; the menu scrolls instead.
//
// This uses the same bounds as the positioning logic below, so that the cap
// matches the space the menu is actually allowed to be placed in.
final Rect overlayRect = mediaQueryData.padding.deflateRect(
mediaQueryData.viewInsets.deflateRect(Offset.zero & constraints.biggest),
);
final double availableHeight = math.max(
anchorRect.top - overlayRect.top,
overlayRect.bottom - anchorRect.bottom,
);
if (availableHeight <= 0.0) {
// The anchor leaves no room on either side, so there is nothing to cap
// the menu to. Leave it to the positioning logic to fit what it can.
return result;
}
return result.copyWith(maxHeight: math.min(availableHeight, result.maxHeight));
}

@override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
changelog: |
- Fixes `MenuAnchor` covering its anchor when the menu is too tall to fit above it.
version: patch
49 changes: 49 additions & 0 deletions packages/material_ui/test/menu_anchor_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4114,6 +4114,55 @@ void main() {
);
});

// Regression test for https://github.com/flutter/flutter/issues/161474
testWidgets('Menu does not cover the anchor when it does not fit above it', (
WidgetTester tester,
) async {
tester.view.physicalSize = const Size(400, 200);
tester.view.devicePixelRatio = 1.0;
addTearDown(tester.view.reset);

final controller = MenuController();
final anchorKey = UniqueKey();

await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
MenuAnchor(
controller: controller,
menuChildren: List<MenuItemButton>.generate(
3,
(int index) =>
MenuItemButton(onPressed: () {}, child: Text('Item ${index + 1}')),
),
builder: (BuildContext context, MenuController controller, Widget? child) {
return SizedBox(key: anchorKey, width: 56, height: 56);
},
),
],
),
),
),
);

controller.open();
await tester.pumpAndSettle();

final Rect anchorRect = tester.getRect(find.byKey(anchorKey));
final Rect menuRect = tester.getRect(findMenuPanels());
// There is not enough room above the anchor to show the whole menu, so
// the menu is shortened rather than being pushed down over the anchor.
expect(
menuRect.bottom,
lessThanOrEqualTo(anchorRect.top),
reason:
'Menu bottom (${menuRect.bottom}) should not cover the anchor top (${anchorRect.top})',
);
});

testWidgets(
'Menu is correctly offset when a LayerLink is provided and alignmentOffset is set',
(WidgetTester tester) async {
Expand Down