mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Closes https://github.com/flutter/flutter/issues/180031 ### Description - Adds `tooltip` support to `PlatformMenuItem` and `PlatformMenu` - Updates `platform_menu_bar_test.dart` to cover `tooltip` related changes and makes test-specific code private - Updates `FlutterMenuPlugin.mm` to support `tooltip` - Updates `FlutterMenuPluginTest.mm` to cover `tooltip` related changes https://github.com/user-attachments/assets/abafc1ec-dc2f-45ae-a3b5-1e88759dac37 <details closed><summary>Code sample</summary> ```dart // THIS SAMPLE ONLY WORKS ON MACOS. import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; /// Flutter code sample for [PlatformMenuBar]. void main() => runApp(const ExampleApp()); enum MenuSelection { about, showMessage } class ExampleApp extends StatelessWidget { const ExampleApp({super.key}); @override Widget build(BuildContext context) { return const MaterialApp(home: Scaffold(body: PlatformMenuBarExample())); } } class PlatformMenuBarExample extends StatefulWidget { const PlatformMenuBarExample({super.key}); @override State<PlatformMenuBarExample> createState() => _PlatformMenuBarExampleState(); } class _PlatformMenuBarExampleState extends State<PlatformMenuBarExample> { String _message = 'Hello'; bool _showMessage = false; void _handleMenuSelection(MenuSelection value) { switch (value) { case MenuSelection.about: showAboutDialog( context: context, applicationName: 'MenuBar Sample', applicationVersion: '1.0.0', ); case MenuSelection.showMessage: setState(() { _showMessage = !_showMessage; }); } } @override Widget build(BuildContext context) { //////////////////////////////////// // THIS SAMPLE ONLY WORKS ON MACOS. //////////////////////////////////// // This builds a menu hierarchy that looks like this: // Flutter API Sample // ├ About // ├ ──────── (group divider) // ├ Hide/Show Message // ├ Messages // │ ├ I am not throwing away my shot. // │ └ There's a million things I haven't done, but just you wait. // └ Quit return PlatformMenuBar( menus: <PlatformMenuItem>[ PlatformMenu( label: 'Flutter API Sample', menus: <PlatformMenuItem>[ PlatformMenuItemGroup( members: <PlatformMenuItem>[ PlatformMenuItem( label: 'About', tooltip: 'Show information about APP_NAME', onSelected: () { _handleMenuSelection(MenuSelection.about); }, ), ], ), PlatformMenuItemGroup( members: <PlatformMenuItem>[ PlatformMenuItem( onSelected: () { _handleMenuSelection(MenuSelection.showMessage); }, shortcut: const CharacterActivator('m'), label: _showMessage ? 'Hide Message' : 'Show Message', tooltip: _showMessage ? 'The message will be hidden.' : 'The message will be shown.', ), PlatformMenu( label: 'Messages', menus: <PlatformMenuItem>[ PlatformMenuItem( label: 'I am not throwing away my shot.', shortcut: const SingleActivator( LogicalKeyboardKey.digit1, meta: true, ), onSelected: () { setState(() { _message = 'I am not throwing away my shot.'; }); }, ), PlatformMenuItem( label: "There's a million things I haven't done, but just you wait.", shortcut: const SingleActivator( LogicalKeyboardKey.digit2, meta: true, ), onSelected: () { setState(() { _message = "There's a million things I haven't done, but just you wait."; }); }, ), ], ), ], ), if (PlatformProvidedMenuItem.hasMenu( PlatformProvidedMenuItemType.quit, )) const PlatformProvidedMenuItem( type: PlatformProvidedMenuItemType.quit, ), ], ), ], child: Center( child: Text( _showMessage ? _message : 'This space intentionally left blank.\n' 'Show a message here using the menu.', ), ), ); } } ``` </details> ## Pre-launch Checklist - [X] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [X] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [X] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [X] I signed the [CLA]. - [X] I listed at least one issue that this PR fixes in the description above. - [X] I updated/added relevant documentation (doc comments with `///`). - [X] I added new tests to check the change I am making, or this PR is [test-exempt]. - [X] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [X] All existing and new tests are passing. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md