diff --git a/packages/flutter/lib/src/material/menu_anchor.dart b/packages/flutter/lib/src/material/menu_anchor.dart index 83076af810..524d8970ca 100644 --- a/packages/flutter/lib/src/material/menu_anchor.dart +++ b/packages/flutter/lib/src/material/menu_anchor.dart @@ -2156,7 +2156,7 @@ class _LocalizedShortcutLabeler { final LogicalKeyboardKey trigger = serialized.trigger!; final List modifiers = [ if (_usesSymbolicModifiers) ...[ - // MacOS/iOS platform convention uses this ordering, with ⌘ always last. + // macOS/iOS platform convention uses this ordering, with ⌘ always last. if (serialized.control!) _getModifierLabel(LogicalKeyboardKey.control, localizations), if (serialized.alt!) _getModifierLabel(LogicalKeyboardKey.alt, localizations), if (serialized.shift!) _getModifierLabel(LogicalKeyboardKey.shift, localizations), @@ -2190,7 +2190,24 @@ class _LocalizedShortcutLabeler { if (shortcutTrigger != null && shortcutTrigger.isNotEmpty) shortcutTrigger, ].join(keySeparator); } else if (serialized.character != null) { - return serialized.character!; + final List modifiers = [ + // Character based shortcuts cannot check shifted keys. + if (_usesSymbolicModifiers) ...[ + // macOS/iOS platform convention uses this ordering, with ⌘ always last. + if (serialized.control!) _getModifierLabel(LogicalKeyboardKey.control, localizations), + if (serialized.alt!) _getModifierLabel(LogicalKeyboardKey.alt, localizations), + if (serialized.meta!) _getModifierLabel(LogicalKeyboardKey.meta, localizations), + ] else ...[ + // This order matches the LogicalKeySet version. + if (serialized.alt!) _getModifierLabel(LogicalKeyboardKey.alt, localizations), + if (serialized.control!) _getModifierLabel(LogicalKeyboardKey.control, localizations), + if (serialized.meta!) _getModifierLabel(LogicalKeyboardKey.meta, localizations), + ], + ]; + return [ + ...modifiers, + serialized.character!, + ].join(keySeparator); } throw UnimplementedError('Shortcut labels for ShortcutActivators that do not implement ' 'MenuSerializableShortcut (e.g. ShortcutActivators other than SingleActivator or ' diff --git a/packages/flutter/test/material/menu_anchor_test.dart b/packages/flutter/test/material/menu_anchor_test.dart index 59277fa208..369b1ece4c 100644 --- a/packages/flutter/test/material/menu_anchor_test.dart +++ b/packages/flutter/test/material/menu_anchor_test.dart @@ -2201,6 +2201,58 @@ void main() { skip: kIsWeb && !isCanvasKit, // https://github.com/flutter/flutter/issues/145527 ); + // Regression test for https://github.com/flutter/flutter/issues/145040. + testWidgets('CharacterActivator shortcut mnemonics include modifiers', (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: Material( + child: MenuBar( + controller: controller, + children: createTestMenus( + shortcuts: { + TestMenu.subSubMenu110: const CharacterActivator('A', control: true), + TestMenu.subSubMenu111: const CharacterActivator('B', alt: true), + TestMenu.subSubMenu112: const CharacterActivator('C', meta: true), + }, + ), + ), + ), + ), + ); + + // Open a menu initially. + await tester.tap(find.text(TestMenu.mainMenu1.label)); + await tester.pump(); + + await tester.tap(find.text(TestMenu.subMenu11.label)); + await tester.pump(); + + final Text mnemonic0 = tester.widget(findMnemonic(TestMenu.subSubMenu110.label)); + final Text mnemonic1 = tester.widget(findMnemonic(TestMenu.subSubMenu111.label)); + final Text mnemonic2 = tester.widget(findMnemonic(TestMenu.subSubMenu112.label)); + + switch (defaultTargetPlatform) { + case TargetPlatform.android: + case TargetPlatform.fuchsia: + case TargetPlatform.linux: + expect(mnemonic0.data, equals('Ctrl+A')); + expect(mnemonic1.data, equals('Alt+B')); + expect(mnemonic2.data, equals('Meta+C')); + case TargetPlatform.windows: + expect(mnemonic0.data, equals('Ctrl+A')); + expect(mnemonic1.data, equals('Alt+B')); + expect(mnemonic2.data, equals('Win+C')); + case TargetPlatform.iOS: + case TargetPlatform.macOS: + expect(mnemonic0.data, equals('⌃ A')); + expect(mnemonic1.data, equals('⌥ B')); + expect(mnemonic2.data, equals('⌘ C')); + } + }, + variant: TargetPlatformVariant.all(), + skip: kIsWeb && !isCanvasKit, // https://github.com/flutter/flutter/issues/145527 + ); + testWidgets('leadingIcon is used when set', (WidgetTester tester) async { await tester.pumpWidget( MaterialApp(