diff --git a/packages/flutter/lib/src/material/popup_menu.dart b/packages/flutter/lib/src/material/popup_menu.dart index 4e092c3200..0255a54e55 100644 --- a/packages/flutter/lib/src/material/popup_menu.dart +++ b/packages/flutter/lib/src/material/popup_menu.dart @@ -1037,10 +1037,22 @@ class PopupMenuButton extends StatefulWidget { final bool captureInheritedThemes; @override - _PopupMenuButtonState createState() => _PopupMenuButtonState(); + PopupMenuButtonState createState() => PopupMenuButtonState(); } -class _PopupMenuButtonState extends State> { +/// The [State] for a [PopupMenuButton]. +/// +/// See [showButtonMenu] for a way to programmatically open the popup menu +/// of your button state. +class PopupMenuButtonState extends State> { + /// A method to show a popup menu with the items supplied to + /// [PopupMenuButton.itemBuilder] at the position of your [PopupMenuButton]. + /// + /// By default, it is called when the user taps the button and [PopupMenuButton.enabled] + /// is set to `true`. Moreover, you can open the button by calling the method manually. + /// + /// You would access your [PopupMenuButtonState] using a [GlobalKey] and + /// 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; diff --git a/packages/flutter/test/material/popup_menu_test.dart b/packages/flutter/test/material/popup_menu_test.dart index 86cf63dd7f..096f6e34f5 100644 --- a/packages/flutter/test/material/popup_menu_test.dart +++ b/packages/flutter/test/material/popup_menu_test.dart @@ -1197,6 +1197,41 @@ void main() { expect(rootObserver.menuCount, 1); expect(nestedObserver.menuCount, 0); }); + + testWidgets('PopupMenuButton calling showButtonMenu manually', (WidgetTester tester) async { + final GlobalKey> globalKey = GlobalKey(); + + await tester.pumpWidget( + MaterialApp( + home: Material( + child: Column( + children: [ + PopupMenuButton( + key: globalKey, + itemBuilder: (BuildContext context) { + return >[ + const PopupMenuItem( + value: 1, + child: Text('Tap me please!'), + ), + ]; + }, + ), + ], + ), + ), + ) + ); + + expect(find.text('Tap me please!'), findsNothing); + + globalKey.currentState.showButtonMenu(); + // The PopupMenuItem will appear after an animation, hence, + // we have to first wait for the tester to settle. + await tester.pumpAndSettle(); + + expect(find.text('Tap me please!'), findsOneWidget); + }); } class TestApp extends StatefulWidget {