diff --git a/packages/flutter/lib/src/material/popup_menu.dart b/packages/flutter/lib/src/material/popup_menu.dart index 88a489b432..cf7feb6ba9 100644 --- a/packages/flutter/lib/src/material/popup_menu.dart +++ b/packages/flutter/lib/src/material/popup_menu.dart @@ -732,8 +732,6 @@ class _PopupMenuRoute extends PopupRoute { this.shape, this.color, required this.capturedThemes, - this.menuKey, - this.positionCallback, }) : itemSizes = List.filled(items.length, null); final RelativeRect position; @@ -745,8 +743,6 @@ class _PopupMenuRoute extends PopupRoute { final ShapeBorder? shape; final Color? color; final CapturedThemes capturedThemes; - final Key? menuKey; - final PopupMenuButtonPositionCallback? positionCallback; @override Animation createAnimation() { @@ -782,13 +778,12 @@ class _PopupMenuRoute extends PopupRoute { final Widget menu = _PopupMenu(route: this, semanticLabel: semanticLabel); - return StatefulBuilder( - key: menuKey, - builder: (BuildContext context, StateSetter setState) { + return Builder( + builder: (BuildContext context) { final MediaQueryData mediaQuery = MediaQuery.of(context); return CustomSingleChildLayout( delegate: _PopupMenuRouteLayout( - positionCallback == null ? position : positionCallback!(), + position, itemSizes, selectedItemIndex, Directionality.of(context), @@ -806,10 +801,6 @@ class _PopupMenuRoute extends PopupRoute { /// /// `items` should be non-null and not empty. /// -/// Prefer to use `positionCallback` to obtain position instead of 'position' -/// when `positionCallback` is non-null. In this way, the position of the menu -/// can be recalculated through this callback during the rebuild phase of the menu. -/// /// If `initialValue` is specified then the first item with a matching value /// will be highlighted and the value of `position` gives the rectangle whose /// vertical center will be aligned with the vertical center of the highlighted @@ -871,8 +862,6 @@ Future showMenu({ ShapeBorder? shape, Color? color, bool useRootNavigator = false, - Key? menuKey, - PopupMenuButtonPositionCallback? positionCallback, }) { assert(context != null); assert(position != null); @@ -902,8 +891,6 @@ Future showMenu({ shape: shape, color: color, capturedThemes: InheritedTheme.capture(from: context, to: navigator.context), - menuKey: menuKey, - positionCallback: positionCallback, )); } @@ -1103,44 +1090,11 @@ class PopupMenuButton extends StatefulWidget { PopupMenuButtonState createState() => PopupMenuButtonState(); } -/// Signature for the callback used by [showMenu] to obtain the position of the -/// [PopupMenuButton]. -/// -/// Used by [showMenu]. -typedef PopupMenuButtonPositionCallback = RelativeRect Function(); - /// The [State] for a [PopupMenuButton]. /// /// See [showButtonMenu] for a way to programmatically open the popup menu /// of your button state. class PopupMenuButtonState extends State> { - GlobalKey _menuGlobalKey = GlobalKey(); - RelativeRect? _buttonPosition; - - RelativeRect _getButtonPosition() => _buttonPosition!; - - RelativeRect _calculateButtonPosition() { - final RenderBox button = context.findRenderObject()! as RenderBox; - final RenderBox overlay = Navigator.of(context).overlay!.context.findRenderObject()! as RenderBox; - return RelativeRect.fromRect( - Rect.fromPoints( - button.localToGlobal(widget.offset, ancestor: overlay), - button.localToGlobal(button.size.bottomRight(Offset.zero) + widget.offset, ancestor: overlay), - ), - Offset.zero & overlay.size, - ); - } - - void _maybeUpdateMenuPosition() { - WidgetsBinding.instance!.addPostFrameCallback((Duration duration) { - final RelativeRect newPosition = _calculateButtonPosition(); - if (newPosition != _buttonPosition) { - _menuGlobalKey.currentState?.setState(() {}); - _buttonPosition = newPosition; - } - }); - } - /// A method to show a popup menu with the items supplied to /// [PopupMenuButton.itemBuilder] at the position of your [PopupMenuButton]. /// @@ -1151,12 +1105,16 @@ class PopupMenuButtonState extends State> { /// show the menu of the button with `globalKey.currentState.showButtonMenu`. void showButtonMenu() { final PopupMenuThemeData popupMenuTheme = PopupMenuTheme.of(context); + final RenderBox button = context.findRenderObject()! as RenderBox; + final RenderBox overlay = Navigator.of(context).overlay!.context.findRenderObject()! as RenderBox; + final RelativeRect position = RelativeRect.fromRect( + Rect.fromPoints( + button.localToGlobal(widget.offset, ancestor: overlay), + button.localToGlobal(button.size.bottomRight(Offset.zero) + widget.offset, ancestor: overlay), + ), + Offset.zero & overlay.size, + ); final List> items = widget.itemBuilder(context); - // It is possible that the fade-out animation of the menu has not finished - // yet, and the key needs to be regenerated at this time, otherwise there will - // be an exception of duplicate GlobalKey. - if (_menuGlobalKey.currentState != null) - _menuGlobalKey = GlobalKey(); // Only show the menu if there is something to show if (items.isNotEmpty) { showMenu( @@ -1164,11 +1122,9 @@ class PopupMenuButtonState extends State> { elevation: widget.elevation ?? popupMenuTheme.elevation, items: items, initialValue: widget.initialValue, - position: _buttonPosition!, + position: position, shape: widget.shape ?? popupMenuTheme.shape, color: widget.color ?? popupMenuTheme.color, - menuKey: _menuGlobalKey, - positionCallback: _getButtonPosition, ) .then((T? newValue) { if (!mounted) @@ -1192,18 +1148,6 @@ class PopupMenuButtonState extends State> { } } - @override - void didUpdateWidget(PopupMenuButton oldWidget) { - _maybeUpdateMenuPosition(); - super.didUpdateWidget(oldWidget); - } - - @override - void didChangeDependencies() { - _maybeUpdateMenuPosition(); - super.didChangeDependencies(); - } - @override Widget build(BuildContext context) { final bool enableFeedback = widget.enableFeedback diff --git a/packages/flutter/test/material/popup_menu_test.dart b/packages/flutter/test/material/popup_menu_test.dart index db6f864ebf..c7ebdd44b0 100644 --- a/packages/flutter/test/material/popup_menu_test.dart +++ b/packages/flutter/test/material/popup_menu_test.dart @@ -2209,87 +2209,6 @@ void main() { await tester.pumpAndSettle(); expect(find.text('foo'), findsOneWidget); }); - - testWidgets('The opened menu should follow if the button\'s position changed', (WidgetTester tester) async { - final GlobalKey buttonKey = GlobalKey(); - - Widget buildFrame(double width, double height) { - return MaterialApp( - home: Scaffold( - body: SizedBox( - height: height, - width: width, - child: Center( - child: PopupMenuButton( - child: SizedBox( - key: buttonKey, - height: 10.0, - width: 10.0, - child: const ColoredBox( - color: Colors.pink, - ), - ), - itemBuilder: (BuildContext context) => >[ - const PopupMenuItem(child: Text('-1-'), value: 1), - const PopupMenuItem(child: Text('-2-'), value: 2), - ], - ), - ), - ), - ), - ); - } - - await tester.pumpWidget(buildFrame(100.0, 100.0)); - - // Open the menu. - await tester.tap(find.byKey(buttonKey)); - await tester.pumpAndSettle(); - - // +--------+--------+ 100 - // | | | - // | | (50,50)| - // +--------+--------+ - // | | | - // | | | - // 100 +--------+--------+ - // - // The button is a rectangle of 10 * 10 size and is centered, - // so its top-left offset should be (45.0, 45.0). - Offset buttonOffset = tester.getTopLeft(find.byKey(buttonKey)); - expect(buttonOffset, const Offset(45.0, 45.0)); - - // The top-left corner of the menu and button should be aligned. - Offset popupMenuOffset = tester.getTopLeft(find.byType(SingleChildScrollView)); - expect(popupMenuOffset, buttonOffset); - - // Keep the menu opened and re-layout the screen. - await tester.pumpWidget(buildFrame(200.0, 300.0)); - - // +-----------+-----------+ 200 - // | | | - // | | | - // | | | - // | | | - // | | (100,150) | - // +-----------+-----------+ - // | | | - // | | | - // | | | - // | | | - // | | | - // 300 +-----------+-----------+ - // - // The button is a rectangle of 10 * 10 size and is centered, - // so its top-left offset should be (95.0, 145.0). - await tester.pump(); // Need a frame to update the menu. - buttonOffset = tester.getTopLeft(find.byKey(buttonKey)); - expect(buttonOffset, const Offset(95.0, 145.0)); - - // The popup menu should follow the button. - popupMenuOffset = tester.getTopLeft(find.byType(SingleChildScrollView)); - expect(popupMenuOffset, buttonOffset); - }); } class TestApp extends StatefulWidget {