diff --git a/dev/integration_tests/android_semantics_testing/test_driver/main_test.dart b/dev/integration_tests/android_semantics_testing/test_driver/main_test.dart index 7c9adf790d59e..bdf0245b352ad 100644 --- a/dev/integration_tests/android_semantics_testing/test_driver/main_test.dart +++ b/dev/integration_tests/android_semantics_testing/test_driver/main_test.dart @@ -435,7 +435,7 @@ void main() { expect( await getSemantics(find.byValueKey('$popupKeyValue.$item')), hasAndroidSemantics( - className: AndroidClassName.view, + className: AndroidClassName.button, isChecked: false, isCheckable: false, isEnabled: true, @@ -459,7 +459,7 @@ void main() { expect( await getSemantics(find.byValueKey('$popupKeyValue.$item')), hasAndroidSemantics( - className: AndroidClassName.view, + className: AndroidClassName.button, isChecked: false, isCheckable: false, isEnabled: true, diff --git a/packages/flutter/lib/src/material/popup_menu.dart b/packages/flutter/lib/src/material/popup_menu.dart index 2e40407312423..3b2ecfbde7a6b 100644 --- a/packages/flutter/lib/src/material/popup_menu.dart +++ b/packages/flutter/lib/src/material/popup_menu.dart @@ -342,11 +342,15 @@ class PopupMenuItemState> extends State { }, ); - return InkWell( - onTap: widget.enabled ? handleTap : null, - canRequestFocus: widget.enabled, - mouseCursor: effectiveMouseCursor, - child: item, + return Semantics( + enabled: widget.enabled, + button: true, + child: InkWell( + onTap: widget.enabled ? handleTap : null, + canRequestFocus: widget.enabled, + mouseCursor: effectiveMouseCursor, + child: item, + ), ); } } diff --git a/packages/flutter/test/material/popup_menu_test.dart b/packages/flutter/test/material/popup_menu_test.dart index db67c1a755a93..a17845c1cf9f8 100644 --- a/packages/flutter/test/material/popup_menu_test.dart +++ b/packages/flutter/test/material/popup_menu_test.dart @@ -765,31 +765,175 @@ void main() { ], children: [ TestSemantics( - flags: [SemanticsFlag.isFocusable], + flags: [ + SemanticsFlag.isButton, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, + SemanticsFlag.isFocusable, + ], actions: [SemanticsAction.tap], label: '1', textDirection: TextDirection.ltr, ), TestSemantics( - flags: [SemanticsFlag.isFocusable], + flags: [ + SemanticsFlag.isButton, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, + SemanticsFlag.isFocusable, + ], actions: [SemanticsAction.tap], label: '2', textDirection: TextDirection.ltr, ), TestSemantics( - flags: [SemanticsFlag.isFocusable], + flags: [ + SemanticsFlag.isButton, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, + SemanticsFlag.isFocusable, + ], actions: [SemanticsAction.tap], label: '3', textDirection: TextDirection.ltr, ), TestSemantics( - flags: [SemanticsFlag.isFocusable], + flags: [ + SemanticsFlag.isButton, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, + SemanticsFlag.isFocusable, + ], actions: [SemanticsAction.tap], label: '4', textDirection: TextDirection.ltr, ), TestSemantics( - flags: [SemanticsFlag.isFocusable], + flags: [ + SemanticsFlag.isButton, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, + SemanticsFlag.isFocusable, + ], + actions: [SemanticsAction.tap], + label: '5', + textDirection: TextDirection.ltr, + ), + ], + ), + ], + ), + ], + ), + TestSemantics(), + ], + ), + ], + ), + ignoreId: true, ignoreTransform: true, ignoreRect: true, + )); + + semantics.dispose(); + }); + + testWidgets('disabled PopupMenuItem has correct semantics', (WidgetTester tester) async { + // Regression test for https://github.com/flutter/flutter/issues/45044. + final SemanticsTester semantics = SemanticsTester(tester); + await tester.pumpWidget( + MaterialApp( + home: Material( + child: PopupMenuButton( + itemBuilder: (BuildContext context) { + return >[ + const PopupMenuItem(value: 1, child: Text('1')), + const PopupMenuItem(value: 2, enabled: false ,child: Text('2')), + const PopupMenuItem(value: 3, child: Text('3')), + const PopupMenuItem(value: 4, child: Text('4')), + const PopupMenuItem(value: 5, child: Text('5')), + ]; + }, + child: const SizedBox( + height: 100.0, + width: 100.0, + child: Text('XXX'), + ), + ), + ), + ), + ); + await tester.tap(find.text('XXX')); + await tester.pumpAndSettle(); + + expect(semantics, hasSemantics( + TestSemantics.root( + children: [ + TestSemantics( + textDirection: TextDirection.ltr, + children: [ + TestSemantics( + children: [ + TestSemantics( + flags: [ + SemanticsFlag.scopesRoute, + SemanticsFlag.namesRoute, + ], + label: 'Popup menu', + textDirection: TextDirection.ltr, + children: [ + TestSemantics( + flags: [ + SemanticsFlag.hasImplicitScrolling, + ], + children: [ + TestSemantics( + flags: [ + SemanticsFlag.isButton, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, + SemanticsFlag.isFocusable, + ], + actions: [SemanticsAction.tap], + label: '1', + textDirection: TextDirection.ltr, + ), + TestSemantics( + flags: [ + SemanticsFlag.isButton, + SemanticsFlag.hasEnabledState, + ], + actions: [], + label: '2', + textDirection: TextDirection.ltr, + ), + TestSemantics( + flags: [ + SemanticsFlag.isButton, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, + SemanticsFlag.isFocusable, + ], + actions: [SemanticsAction.tap], + label: '3', + textDirection: TextDirection.ltr, + ), + TestSemantics( + flags: [ + SemanticsFlag.isButton, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, + SemanticsFlag.isFocusable, + ], + actions: [SemanticsAction.tap], + label: '4', + textDirection: TextDirection.ltr, + ), + TestSemantics( + flags: [ + SemanticsFlag.isButton, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, + SemanticsFlag.isFocusable, + ], actions: [SemanticsAction.tap], label: '5', textDirection: TextDirection.ltr,