From d68e05bf36af64469fd4af7929524ddd387febea Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Wed, 12 Jun 2024 13:05:10 -0700 Subject: [PATCH] Reland: Request focus if accessibility focus is given to a Focus widget (#142942) (#149840) ## Description This attempts to re-land #142942 after being reverted in https://github.com/flutter/flutter/pull/149741 because it broke the iOS [platform view UI integration test](https://github.com/flutter/flutter/blob/master/dev/integration_tests/ios_platform_view_tests/ios/PlatformViewUITests/PlatformViewUITests.m?rgh-link-date=2024-06-06T19%3A47%3A27Z). The changes here from the original are that in the Focus widget we no longer set the `onFocus` for the `Semantics` if the platform is iOS. It was not intended to do anything on iOS anyhow. Also, I updated the matchers to not actually do anything yet with the SemanticsAction.focus matching, so that this can be landed without breaking customer tests, and once they have been updated to correctly look for the focus action, we can land a PR that will turn it on. ## Related Issues - https://github.com/flutter/flutter/issues/149838 - https://github.com/flutter/flutter/issues/83809 - https://github.com/flutter/flutter/issues/149842 ## Tests - Updated framework tests to look for the appropriate things using the matchers, even though it doesn't actually test for them yet. --- .../test/demo/material/chip_demo_test.dart | 2 + .../flutter/lib/src/widgets/focus_scope.dart | 9 ++++ .../flutter/test/cupertino/checkbox_test.dart | 10 ++-- .../flutter/test/cupertino/radio_test.dart | 4 ++ .../flutter/test/cupertino/route_test.dart | 2 +- .../test/cupertino/text_field_test.dart | 2 +- .../test/material/back_button_test.dart | 2 + .../material/bottom_navigation_bar_test.dart | 14 ++++- .../material/calendar_date_picker_test.dart | 33 ++++++++++++ packages/flutter/test/material/card_test.dart | 1 + .../material/checkbox_list_tile_test.dart | 1 + .../flutter/test/material/checkbox_test.dart | 11 ++-- packages/flutter/test/material/chip_test.dart | 10 ++-- .../test/material/date_picker_test.dart | 7 +++ .../test/material/date_range_picker_test.dart | 1 + .../test/material/drawer_button_test.dart | 2 + .../flutter/test/material/drawer_test.dart | 2 +- .../flutter/test/material/dropdown_test.dart | 10 ++-- .../test/material/elevated_button_test.dart | 1 + .../test/material/expand_icon_test.dart | 4 ++ .../test/material/expansion_panel_test.dart | 8 +++ .../test/material/expansion_tile_test.dart | 2 + .../test/material/filled_button_test.dart | 1 + .../material/floating_action_button_test.dart | 3 ++ .../test/material/icon_button_test.dart | 3 ++ .../flutter/test/material/ink_well_test.dart | 6 ++- .../flutter/test/material/list_tile_test.dart | 2 +- .../test/material/material_button_test.dart | 2 + .../test/material/menu_anchor_test.dart | 7 +-- .../test/material/navigation_bar_test.dart | 8 +++ .../test/material/navigation_drawer_test.dart | 4 ++ .../test/material/navigation_rail_test.dart | 8 +-- .../test/material/outlined_button_test.dart | 1 + .../test/material/popup_menu_test.dart | 20 +++---- .../test/material/radio_list_tile_test.dart | 5 +- .../flutter/test/material/radio_test.dart | 4 ++ .../material/raw_material_button_test.dart | 1 + .../test/material/reorderable_list_test.dart | 1 + .../flutter/test/material/search_test.dart | 8 +-- .../test/material/segmented_button_test.dart | 4 ++ .../flutter/test/material/slider_test.dart | 11 ++-- .../test/material/switch_list_tile_test.dart | 6 +-- packages/flutter/test/material/tabs_test.dart | 12 ++--- .../test/material/text_button_test.dart | 1 + .../test/material/text_field_test.dart | 4 +- .../test/material/time_picker_test.dart | 4 +- .../test/material/toggle_buttons_test.dart | 5 ++ .../user_accounts_drawer_header_test.dart | 1 + .../test/widgets/absorb_pointer_test.dart | 1 + .../flutter/test/widgets/actions_test.dart | 5 ++ packages/flutter/test/widgets/basic_test.dart | 1 + .../flutter/test/widgets/drawer_test.dart | 2 +- .../test/widgets/focus_scope_test.dart | 41 +++++++++++++++ .../test/widgets/focus_traversal_test.dart | 3 ++ .../gesture_detector_semantics_test.dart | 2 +- .../test/widgets/selectable_region_test.dart | 2 +- .../test/widgets/semantics_tester.dart | 28 +++++++--- packages/flutter_test/lib/src/matchers.dart | 52 +++++++++++++++---- packages/flutter_test/test/matchers_test.dart | 3 ++ 59 files changed, 329 insertions(+), 81 deletions(-) diff --git a/dev/integration_tests/flutter_gallery/test/demo/material/chip_demo_test.dart b/dev/integration_tests/flutter_gallery/test/demo/material/chip_demo_test.dart index 9d769f7337..1b11c4d833 100644 --- a/dev/integration_tests/flutter_gallery/test/demo/material/chip_demo_test.dart +++ b/dev/integration_tests/flutter_gallery/test/demo/material/chip_demo_test.dart @@ -20,6 +20,7 @@ void main() { isEnabled: true, isFocusable: true, hasTapAction: true, + hasFocusAction: true, label: 'Update border shape', )); @@ -29,6 +30,7 @@ void main() { isEnabled: true, isFocusable: true, hasTapAction: true, + hasFocusAction: true, label: 'Reset chips', )); diff --git a/packages/flutter/lib/src/widgets/focus_scope.dart b/packages/flutter/lib/src/widgets/focus_scope.dart index acd9b04f1b..c59fb5f305 100644 --- a/packages/flutter/lib/src/widgets/focus_scope.dart +++ b/packages/flutter/lib/src/widgets/focus_scope.dart @@ -671,6 +671,15 @@ class _FocusState extends State { Widget child = widget.child; if (widget.includeSemantics) { child = Semantics( + // Automatically request the focus for a focusable widget when it + // receives an input focus action from the semantics. Nothing is needed + // for losing the focus because if focus is lost, that means another + // node will gain focus and take focus from this widget. + // TODO(gspencergoog): Allow this to be set on iOS once the issue is + // addressed: https://github.com/flutter/flutter/issues/150030 + onFocus: defaultTargetPlatform != TargetPlatform.iOS && _couldRequestFocus + ? focusNode.requestFocus + : null, focusable: _couldRequestFocus, focused: _hadPrimaryFocus, child: widget.child, diff --git a/packages/flutter/test/cupertino/checkbox_test.dart b/packages/flutter/test/cupertino/checkbox_test.dart index 601aef991d..b6bdaf6baf 100644 --- a/packages/flutter/test/cupertino/checkbox_test.dart +++ b/packages/flutter/test/cupertino/checkbox_test.dart @@ -34,6 +34,7 @@ void main() { hasEnabledState: true, isEnabled: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); @@ -54,6 +55,7 @@ void main() { isChecked: true, isEnabled: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); @@ -73,6 +75,7 @@ void main() { hasEnabledState: true, // isFocusable is delayed by 1 frame. isFocusable: true, + hasFocusAction: true, )); await tester.pump(); @@ -178,6 +181,7 @@ void main() { hasEnabledState: true, isEnabled: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); handle.dispose(); @@ -247,7 +251,7 @@ void main() { SemanticsFlag.isFocusable, SemanticsFlag.isCheckStateMixed, ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], ), hasLength(1)); await tester.pumpWidget( @@ -268,7 +272,7 @@ void main() { SemanticsFlag.isChecked, SemanticsFlag.isFocusable, ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], ), hasLength(1)); await tester.pumpWidget( @@ -288,7 +292,7 @@ void main() { SemanticsFlag.isEnabled, SemanticsFlag.isFocusable, ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], ), hasLength(1)); semantics.dispose(); diff --git a/packages/flutter/test/cupertino/radio_test.dart b/packages/flutter/test/cupertino/radio_test.dart index cd30734d41..6035c20cee 100644 --- a/packages/flutter/test/cupertino/radio_test.dart +++ b/packages/flutter/test/cupertino/radio_test.dart @@ -148,6 +148,7 @@ void main() { ], actions: [ SemanticsAction.tap, + SemanticsAction.focus, ], ), ); @@ -172,6 +173,7 @@ void main() { hasEnabledState: true, isEnabled: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, isInMutuallyExclusiveGroup: true, )); @@ -191,6 +193,7 @@ void main() { hasEnabledState: true, isEnabled: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, isInMutuallyExclusiveGroup: true, isChecked: true, @@ -211,6 +214,7 @@ void main() { hasEnabledState: true, isFocusable: true, isInMutuallyExclusiveGroup: true, + hasFocusAction: true, )); await tester.pump(); diff --git a/packages/flutter/test/cupertino/route_test.dart b/packages/flutter/test/cupertino/route_test.dart index 2ca816975e..649efb25a5 100644 --- a/packages/flutter/test/cupertino/route_test.dart +++ b/packages/flutter/test/cupertino/route_test.dart @@ -1945,7 +1945,7 @@ void main() { await tester.pumpAndSettle(); expect(semantics, isNot(includesNodeWith( - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], label: 'Dismiss', ))); debugDefaultTargetPlatformOverride = null; diff --git a/packages/flutter/test/cupertino/text_field_test.dart b/packages/flutter/test/cupertino/text_field_test.dart index c46651ab15..7407eccea1 100644 --- a/packages/flutter/test/cupertino/text_field_test.dart +++ b/packages/flutter/test/cupertino/text_field_test.dart @@ -2621,7 +2621,7 @@ void main() { ), ); - expect(semantics, isNot(includesNodeWith(actions: [SemanticsAction.tap]))); + expect(semantics, isNot(includesNodeWith(actions: [SemanticsAction.tap, SemanticsAction.focus]))); semantics.dispose(); }); diff --git a/packages/flutter/test/material/back_button_test.dart b/packages/flutter/test/material/back_button_test.dart index afdba078a1..682dc4b232 100644 --- a/packages/flutter/test/material/back_button_test.dart +++ b/packages/flutter/test/material/back_button_test.dart @@ -215,6 +215,7 @@ void main() { hasEnabledState: true, isEnabled: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); handle.dispose(); @@ -258,6 +259,7 @@ void main() { hasEnabledState: true, isEnabled: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); handle.dispose(); diff --git a/packages/flutter/test/material/bottom_navigation_bar_test.dart b/packages/flutter/test/material/bottom_navigation_bar_test.dart index c74f893e6e..f0e65837bb 100644 --- a/packages/flutter/test/material/bottom_navigation_bar_test.dart +++ b/packages/flutter/test/material/bottom_navigation_bar_test.dart @@ -2111,6 +2111,7 @@ void main() { isFocusable: true, isSelected: true, hasTapAction: true, + hasFocusAction: true, ), ); expect( @@ -2120,6 +2121,7 @@ void main() { textDirection: TextDirection.ltr, isFocusable: true, hasTapAction: true, + hasFocusAction: true, ), ); expect( @@ -2129,6 +2131,7 @@ void main() { textDirection: TextDirection.ltr, isFocusable: true, hasTapAction: true, + hasFocusAction: true, ), ); }); @@ -2165,6 +2168,7 @@ void main() { isFocusable: true, isSelected: true, hasTapAction: true, + hasFocusAction: true, ), ); expect( @@ -2174,6 +2178,7 @@ void main() { textDirection: TextDirection.ltr, isFocusable: true, hasTapAction: true, + hasFocusAction: true, ), ); expect( @@ -2183,6 +2188,7 @@ void main() { textDirection: TextDirection.ltr, isFocusable: true, hasTapAction: true, + hasFocusAction: true, ), ); }); @@ -2515,6 +2521,7 @@ void main() { isFocusable: true, isSelected: true, hasTapAction: true, + hasFocusAction: true, ), ); expect( @@ -2524,6 +2531,7 @@ void main() { textDirection: TextDirection.ltr, isFocusable: true, hasTapAction: true, + hasFocusAction: true, ), ); }); @@ -2558,6 +2566,7 @@ void main() { isFocusable: true, isSelected: true, hasTapAction: true, + hasFocusAction: true, ), ); expect( @@ -2567,6 +2576,7 @@ void main() { textDirection: TextDirection.ltr, isFocusable: true, hasTapAction: true, + hasFocusAction: true, ), ); }); @@ -2747,13 +2757,13 @@ void main() { SemanticsFlag.isSelected, SemanticsFlag.isFocusable, ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], label: 'A\nTab 1 of 2', textDirection: TextDirection.ltr, ), TestSemantics( flags: [SemanticsFlag.isFocusable], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], label: 'B\nTab 2 of 2', textDirection: TextDirection.ltr, ), diff --git a/packages/flutter/test/material/calendar_date_picker_test.dart b/packages/flutter/test/material/calendar_date_picker_test.dart index ed7c1aa205..b6eb7c577a 100644 --- a/packages/flutter/test/material/calendar_date_picker_test.dart +++ b/packages/flutter/test/material/calendar_date_picker_test.dart @@ -864,6 +864,7 @@ void main() { tooltip: 'Previous month', isButton: true, hasTapAction: true, + hasFocusAction: true, isEnabled: true, hasEnabledState: true, isFocusable: true, @@ -872,6 +873,7 @@ void main() { tooltip: 'Next month', isButton: true, hasTapAction: true, + hasFocusAction: true, isEnabled: true, hasEnabledState: true, isFocusable: true, @@ -882,90 +884,105 @@ void main() { label: '1, Friday, January 1, 2016', isButton: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); expect(tester.getSemantics(find.text('2')), matchesSemantics( label: '2, Saturday, January 2, 2016', isButton: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); expect(tester.getSemantics(find.text('3')), matchesSemantics( label: '3, Sunday, January 3, 2016, Today', isButton: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); expect(tester.getSemantics(find.text('4')), matchesSemantics( label: '4, Monday, January 4, 2016', isButton: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); expect(tester.getSemantics(find.text('5')), matchesSemantics( label: '5, Tuesday, January 5, 2016', isButton: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); expect(tester.getSemantics(find.text('6')), matchesSemantics( label: '6, Wednesday, January 6, 2016', isButton: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); expect(tester.getSemantics(find.text('7')), matchesSemantics( label: '7, Thursday, January 7, 2016', isButton: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); expect(tester.getSemantics(find.text('8')), matchesSemantics( label: '8, Friday, January 8, 2016', isButton: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); expect(tester.getSemantics(find.text('9')), matchesSemantics( label: '9, Saturday, January 9, 2016', isButton: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); expect(tester.getSemantics(find.text('10')), matchesSemantics( label: '10, Sunday, January 10, 2016', isButton: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); expect(tester.getSemantics(find.text('11')), matchesSemantics( label: '11, Monday, January 11, 2016', isButton: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); expect(tester.getSemantics(find.text('12')), matchesSemantics( label: '12, Tuesday, January 12, 2016', isButton: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); expect(tester.getSemantics(find.text('13')), matchesSemantics( label: '13, Wednesday, January 13, 2016', isButton: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); expect(tester.getSemantics(find.text('14')), matchesSemantics( label: '14, Thursday, January 14, 2016', isButton: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); expect(tester.getSemantics(find.text('15')), matchesSemantics( label: '15, Friday, January 15, 2016', isButton: true, hasTapAction: true, + hasFocusAction: true, isSelected: true, isFocusable: true, )); @@ -973,90 +990,105 @@ void main() { label: '16, Saturday, January 16, 2016', isButton: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); expect(tester.getSemantics(find.text('17')), matchesSemantics( label: '17, Sunday, January 17, 2016', isButton: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); expect(tester.getSemantics(find.text('18')), matchesSemantics( label: '18, Monday, January 18, 2016', isButton: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); expect(tester.getSemantics(find.text('19')), matchesSemantics( label: '19, Tuesday, January 19, 2016', isButton: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); expect(tester.getSemantics(find.text('20')), matchesSemantics( label: '20, Wednesday, January 20, 2016', isButton: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); expect(tester.getSemantics(find.text('21')), matchesSemantics( label: '21, Thursday, January 21, 2016', isButton: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); expect(tester.getSemantics(find.text('22')), matchesSemantics( label: '22, Friday, January 22, 2016', isButton: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); expect(tester.getSemantics(find.text('23')), matchesSemantics( label: '23, Saturday, January 23, 2016', isButton: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); expect(tester.getSemantics(find.text('24')), matchesSemantics( label: '24, Sunday, January 24, 2016', isButton: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); expect(tester.getSemantics(find.text('25')), matchesSemantics( label: '25, Monday, January 25, 2016', isButton: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); expect(tester.getSemantics(find.text('26')), matchesSemantics( label: '26, Tuesday, January 26, 2016', isButton: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); expect(tester.getSemantics(find.text('27')), matchesSemantics( label: '27, Wednesday, January 27, 2016', isButton: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); expect(tester.getSemantics(find.text('28')), matchesSemantics( label: '28, Thursday, January 28, 2016', isButton: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); expect(tester.getSemantics(find.text('29')), matchesSemantics( label: '29, Friday, January 29, 2016', isButton: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); expect(tester.getSemantics(find.text('30')), matchesSemantics( label: '30, Saturday, January 30, 2016', isButton: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); semantics.dispose(); @@ -1081,6 +1113,7 @@ void main() { expect(tester.getSemantics(find.text('$year')), matchesSemantics( label: '$year', hasTapAction: true, + hasFocusAction: true, isSelected: year == 2016, isFocusable: true, isButton: true, diff --git a/packages/flutter/test/material/card_test.dart b/packages/flutter/test/material/card_test.dart index c8a5815e44..5e9003a1bf 100644 --- a/packages/flutter/test/material/card_test.dart +++ b/packages/flutter/test/material/card_test.dart @@ -130,6 +130,7 @@ void main() { textDirection: TextDirection.ltr, actions: [ SemanticsAction.tap, + SemanticsAction.focus, ], flags: [ SemanticsFlag.hasEnabledState, diff --git a/packages/flutter/test/material/checkbox_list_tile_test.dart b/packages/flutter/test/material/checkbox_list_tile_test.dart index b91628a16c..81acbc9a60 100644 --- a/packages/flutter/test/material/checkbox_list_tile_test.dart +++ b/packages/flutter/test/material/checkbox_list_tile_test.dart @@ -1189,6 +1189,7 @@ void main() { hasEnabledState: true, isEnabled: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, label: 'Hello\nthere', )); diff --git a/packages/flutter/test/material/checkbox_test.dart b/packages/flutter/test/material/checkbox_test.dart index 636456d962..56cbf37d97 100644 --- a/packages/flutter/test/material/checkbox_test.dart +++ b/packages/flutter/test/material/checkbox_test.dart @@ -78,6 +78,7 @@ void main() { hasEnabledState: true, isEnabled: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); @@ -97,6 +98,7 @@ void main() { isChecked: true, isEnabled: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); @@ -115,6 +117,7 @@ void main() { hasEnabledState: true, // isFocusable is delayed by 1 frame. isFocusable: true, + hasFocusAction: true, )); await tester.pump(); @@ -213,6 +216,7 @@ void main() { isChecked: true, isEnabled: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); handle.dispose(); @@ -242,6 +246,7 @@ void main() { hasEnabledState: true, isEnabled: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); handle.dispose(); @@ -317,7 +322,7 @@ void main() { SemanticsFlag.isFocusable, SemanticsFlag.isCheckStateMixed, ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], ), hasLength(1)); await tester.pumpWidget( @@ -341,7 +346,7 @@ void main() { SemanticsFlag.isChecked, SemanticsFlag.isFocusable, ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], ), hasLength(1)); await tester.pumpWidget( @@ -364,7 +369,7 @@ void main() { SemanticsFlag.isEnabled, SemanticsFlag.isFocusable, ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], ), hasLength(1)); semantics.dispose(); diff --git a/packages/flutter/test/material/chip_test.dart b/packages/flutter/test/material/chip_test.dart index 0a398a1dad..d86147d099 100644 --- a/packages/flutter/test/material/chip_test.dart +++ b/packages/flutter/test/material/chip_test.dart @@ -2971,7 +2971,7 @@ void main() { children: [ TestSemantics( tooltip: 'Delete', - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], textDirection: TextDirection.ltr, flags: [ SemanticsFlag.isButton, @@ -3030,7 +3030,7 @@ void main() { SemanticsFlag.isEnabled, SemanticsFlag.isFocusable, ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], ), ], ), @@ -3088,7 +3088,7 @@ void main() { SemanticsFlag.isEnabled, SemanticsFlag.isFocusable, ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], ), ], ), @@ -3141,7 +3141,7 @@ void main() { SemanticsFlag.isFocusable, SemanticsFlag.isSelected, ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], ), ], ), @@ -3295,7 +3295,7 @@ void main() { SemanticsFlag.isEnabled, SemanticsFlag.isFocusable, ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], ), ], ), diff --git a/packages/flutter/test/material/date_picker_test.dart b/packages/flutter/test/material/date_picker_test.dart index df83154d4d..8ba368cbea 100644 --- a/packages/flutter/test/material/date_picker_test.dart +++ b/packages/flutter/test/material/date_picker_test.dart @@ -1545,6 +1545,7 @@ void main() { label: '3, Sunday, January 3, 2016, Today', isButton: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); @@ -1553,6 +1554,7 @@ void main() { tooltip: 'Switch to input', isButton: true, hasTapAction: true, + hasFocusAction: true, isEnabled: true, hasEnabledState: true, isFocusable: true, @@ -1565,6 +1567,7 @@ void main() { label: 'OK', isButton: true, hasTapAction: true, + hasFocusAction: true, isEnabled: true, hasEnabledState: true, isFocusable: true, @@ -1573,6 +1576,7 @@ void main() { label: 'CANCEL', isButton: true, hasTapAction: true, + hasFocusAction: true, isEnabled: true, hasEnabledState: true, isFocusable: true, @@ -1603,6 +1607,7 @@ void main() { tooltip: 'Switch to calendar', isButton: true, hasTapAction: true, + hasFocusAction: true, isEnabled: true, hasEnabledState: true, isFocusable: true, @@ -1630,6 +1635,7 @@ void main() { label: 'OK', isButton: true, hasTapAction: true, + hasFocusAction: true, isEnabled: true, hasEnabledState: true, isFocusable: true, @@ -1638,6 +1644,7 @@ void main() { label: 'CANCEL', isButton: true, hasTapAction: true, + hasFocusAction: true, isEnabled: true, hasEnabledState: true, isFocusable: true, diff --git a/packages/flutter/test/material/date_range_picker_test.dart b/packages/flutter/test/material/date_range_picker_test.dart index 353e35d47a..dbe3cdd4ee 100644 --- a/packages/flutter/test/material/date_range_picker_test.dart +++ b/packages/flutter/test/material/date_range_picker_test.dart @@ -1319,6 +1319,7 @@ void main() { matchesSemantics( label: '30, Saturday, January 30, 2016, Today', hasTapAction: true, + hasFocusAction: true, isFocusable: true, ), ); diff --git a/packages/flutter/test/material/drawer_button_test.dart b/packages/flutter/test/material/drawer_button_test.dart index bb565c8c75..e1fe735323 100644 --- a/packages/flutter/test/material/drawer_button_test.dart +++ b/packages/flutter/test/material/drawer_button_test.dart @@ -182,6 +182,7 @@ void main() { hasEnabledState: true, isEnabled: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); handle.dispose(); @@ -239,6 +240,7 @@ void main() { hasEnabledState: true, isEnabled: true, hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); handle.dispose(); diff --git a/packages/flutter/test/material/drawer_test.dart b/packages/flutter/test/material/drawer_test.dart index 36b9ae2ff5..1562be4b65 100644 --- a/packages/flutter/test/material/drawer_test.dart +++ b/packages/flutter/test/material/drawer_test.dart @@ -147,7 +147,7 @@ void main() { expect(semantics, isNot(includesNodeWith( label: const DefaultMaterialLocalizations().modalBarrierDismissLabel, - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], ))); semantics.dispose(); diff --git a/packages/flutter/test/material/dropdown_test.dart b/packages/flutter/test/material/dropdown_test.dart index 5ec084aa05..c04ae2b1aa 100644 --- a/packages/flutter/test/material/dropdown_test.dart +++ b/packages/flutter/test/material/dropdown_test.dart @@ -1300,6 +1300,7 @@ void main() { isButton: true, label: 'test', hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); @@ -1315,6 +1316,7 @@ void main() { isButton: true, label: 'three', hasTapAction: true, + hasFocusAction: true, isFocusable: true, )); handle.dispose(); @@ -1360,28 +1362,28 @@ void main() { SemanticsFlag.isFocusable, ], tags: [const SemanticsTag('RenderViewport.twoPane')], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], ), TestSemantics( label: 'two', textDirection: TextDirection.ltr, flags: [SemanticsFlag.isFocusable], tags: [const SemanticsTag('RenderViewport.twoPane')], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], ), TestSemantics( label: 'three', textDirection: TextDirection.ltr, flags: [SemanticsFlag.isFocusable], tags: [const SemanticsTag('RenderViewport.twoPane')], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], ), TestSemantics( label: 'four', textDirection: TextDirection.ltr, flags: [SemanticsFlag.isFocusable], tags: [const SemanticsTag('RenderViewport.twoPane')], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], ), ], ), diff --git a/packages/flutter/test/material/elevated_button_test.dart b/packages/flutter/test/material/elevated_button_test.dart index 2e9d958beb..d6627b5d94 100644 --- a/packages/flutter/test/material/elevated_button_test.dart +++ b/packages/flutter/test/material/elevated_button_test.dart @@ -819,6 +819,7 @@ void main() { TestSemantics.rootChild( actions: [ SemanticsAction.tap, + SemanticsAction.focus, ], label: 'ABC', rect: const Rect.fromLTRB(0.0, 0.0, 88.0, 48.0), diff --git a/packages/flutter/test/material/expand_icon_test.dart b/packages/flutter/test/material/expand_icon_test.dart index 7ec1338fa4..7ff43ab84f 100644 --- a/packages/flutter/test/material/expand_icon_test.dart +++ b/packages/flutter/test/material/expand_icon_test.dart @@ -217,6 +217,7 @@ void main() { expect(tester.getSemantics(find.byType(ExpandIcon)), matchesSemantics( hasTapAction: true, + hasFocusAction: true, hasEnabledState: true, isEnabled: true, isFocusable: true, @@ -233,6 +234,7 @@ void main() { expect(tester.getSemantics(find.byType(ExpandIcon)), matchesSemantics( hasTapAction: true, + hasFocusAction: true, hasEnabledState: true, isEnabled: true, isFocusable: true, @@ -258,6 +260,7 @@ void main() { children: [ matchesSemantics( hasTapAction: true, + hasFocusAction: true, hasEnabledState: true, isEnabled: true, isFocusable: true, @@ -277,6 +280,7 @@ void main() { children: [ matchesSemantics( hasTapAction: true, + hasFocusAction: true, hasEnabledState: true, isEnabled: true, isFocusable: true, diff --git a/packages/flutter/test/material/expansion_panel_test.dart b/packages/flutter/test/material/expansion_panel_test.dart index feab74d92d..8df161448f 100644 --- a/packages/flutter/test/material/expansion_panel_test.dart +++ b/packages/flutter/test/material/expansion_panel_test.dart @@ -211,6 +211,7 @@ void main() { isEnabled: true, isFocusable: true, hasTapAction: true, + hasFocusAction: true, )); // Check custom header widget semantics is preserved. @@ -261,6 +262,7 @@ void main() { isEnabled: true, isFocusable: true, hasTapAction: true, + hasFocusAction: true, ), ], )); @@ -1099,6 +1101,7 @@ void main() { isEnabled: true, isFocusable: true, hasTapAction: true, + hasFocusAction: true, onTapHint: localizations.expandedIconTapHint, )); @@ -1123,6 +1126,7 @@ void main() { isEnabled: true, isFocusable: true, hasTapAction: true, + hasFocusAction: true, onTapHint: localizations.collapsedIconTapHint, )); @@ -1187,6 +1191,7 @@ void main() { isEnabled: true, isFocusable: true, hasTapAction: true, + hasFocusAction: true, ), ], )); @@ -1215,6 +1220,7 @@ void main() { isEnabled: true, isFocusable: true, hasTapAction: true, + hasFocusAction: true, ), ], )); @@ -1268,6 +1274,7 @@ void main() { isFocusable: true, hasEnabledState: true, hasTapAction: true, + hasFocusAction: true, )); expect(tester.getSemantics(find.byKey(collapsedKey)), matchesSemantics( @@ -1276,6 +1283,7 @@ void main() { isFocusable: true, hasEnabledState: true, hasTapAction: true, + hasFocusAction: true, )); handle.dispose(); diff --git a/packages/flutter/test/material/expansion_tile_test.dart b/packages/flutter/test/material/expansion_tile_test.dart index 70daebbb82..c6de9c673e 100644 --- a/packages/flutter/test/material/expansion_tile_test.dart +++ b/packages/flutter/test/material/expansion_tile_test.dart @@ -728,6 +728,7 @@ void main() { tester.getSemantics(find.byType(ListTile).first), matchesSemantics( hasTapAction: true, + hasFocusAction: true, hasEnabledState: true, isEnabled: true, isFocused: true, @@ -742,6 +743,7 @@ void main() { tester.getSemantics(find.byType(ListTile).last), matchesSemantics( hasTapAction: true, + hasFocusAction: true, hasEnabledState: true, isEnabled: true, isFocusable: true, diff --git a/packages/flutter/test/material/filled_button_test.dart b/packages/flutter/test/material/filled_button_test.dart index e0dcc9f26b..ad7f766e19 100644 --- a/packages/flutter/test/material/filled_button_test.dart +++ b/packages/flutter/test/material/filled_button_test.dart @@ -1004,6 +1004,7 @@ void main() { TestSemantics.rootChild( actions: [ SemanticsAction.tap, + SemanticsAction.focus, ], label: 'ABC', rect: const Rect.fromLTRB(0.0, 0.0, 88.0, 48.0), diff --git a/packages/flutter/test/material/floating_action_button_test.dart b/packages/flutter/test/material/floating_action_button_test.dart index e5cfc6b3f5..5a9f0148ce 100644 --- a/packages/flutter/test/material/floating_action_button_test.dart +++ b/packages/flutter/test/material/floating_action_button_test.dart @@ -631,6 +631,7 @@ void main() { ], actions: [ SemanticsAction.tap, + SemanticsAction.focus, ], ), ], @@ -699,6 +700,7 @@ void main() { tooltip: 'Add Photo', actions: [ SemanticsAction.tap, + SemanticsAction.focus, ], flags: [ SemanticsFlag.hasEnabledState, @@ -922,6 +924,7 @@ void main() { tester.getSemantics(find.byType(FloatingActionButton)), matchesSemantics( hasTapAction: true, + hasFocusAction: true, hasEnabledState: true, isButton: true, isEnabled: true, diff --git a/packages/flutter/test/material/icon_button_test.dart b/packages/flutter/test/material/icon_button_test.dart index 7814043b77..cdae2a9431 100644 --- a/packages/flutter/test/material/icon_button_test.dart +++ b/packages/flutter/test/material/icon_button_test.dart @@ -616,6 +616,7 @@ void main() { rect: const Rect.fromLTRB(0.0, 0.0, 48.0, 48.0), actions: [ SemanticsAction.tap, + SemanticsAction.focus, ], flags: [ SemanticsFlag.hasEnabledState, @@ -690,6 +691,7 @@ void main() { TestSemantics( actions: [ SemanticsAction.tap, + SemanticsAction.focus, ], flags: [ SemanticsFlag.hasEnabledState, @@ -2177,6 +2179,7 @@ void main() { TestSemantics.rootChild( actions: [ SemanticsAction.tap, + SemanticsAction.focus, ], rect: const Rect.fromLTRB(0.0, 0.0, 88.0, 48.0), transform: Matrix4.translationValues(356.0, 276.0, 0.0), diff --git a/packages/flutter/test/material/ink_well_test.dart b/packages/flutter/test/material/ink_well_test.dart index d90aded12e..3cc961ccc1 100644 --- a/packages/flutter/test/material/ink_well_test.dart +++ b/packages/flutter/test/material/ink_well_test.dart @@ -1178,7 +1178,7 @@ testWidgets('InkResponse radius can be updated', (WidgetTester tester) async { ), ), )); - expect(semantics, includesNodeWith(label: 'Button', actions: [SemanticsAction.tap])); + expect(semantics, includesNodeWith(label: 'Button', actions: [SemanticsAction.tap, SemanticsAction.focus])); await tester.pumpWidget(Directionality( textDirection: TextDirection.ltr, @@ -1190,7 +1190,7 @@ testWidgets('InkResponse radius can be updated', (WidgetTester tester) async { ), ), )); - expect(semantics, isNot(includesNodeWith(label: 'Button', actions: [SemanticsAction.tap]))); + expect(semantics, isNot(includesNodeWith(label: 'Button', actions: [SemanticsAction.tap, SemanticsAction.focus]))); semantics.dispose(); }); @@ -1983,6 +1983,7 @@ testWidgets('InkResponse radius can be updated', (WidgetTester tester) async { label: 'Foo', hasLongPressAction: true, isFocusable: true, + hasFocusAction: true, textDirection: TextDirection.ltr, )); @@ -2003,6 +2004,7 @@ testWidgets('InkResponse radius can be updated', (WidgetTester tester) async { expect(tester.getSemantics(find.bySemanticsLabel('Foo')), matchesSemantics( label: 'Foo', hasTapAction: true, + hasFocusAction: true, hasLongPressAction: true, isFocusable: true, textDirection: TextDirection.ltr, diff --git a/packages/flutter/test/material/list_tile_test.dart b/packages/flutter/test/material/list_tile_test.dart index 4860309ce3..97d8ea6abe 100644 --- a/packages/flutter/test/material/list_tile_test.dart +++ b/packages/flutter/test/material/list_tile_test.dart @@ -308,7 +308,7 @@ void main() { SemanticsFlag.isEnabled, SemanticsFlag.isFocusable, ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], label: 'two', ), TestSemantics.rootChild( diff --git a/packages/flutter/test/material/material_button_test.dart b/packages/flutter/test/material/material_button_test.dart index 482812fdd8..4f1612bfa1 100644 --- a/packages/flutter/test/material/material_button_test.dart +++ b/packages/flutter/test/material/material_button_test.dart @@ -620,6 +620,7 @@ void main() { label: 'Button', actions: [ SemanticsAction.tap, + SemanticsAction.focus, ], flags: [ SemanticsFlag.hasEnabledState, @@ -661,6 +662,7 @@ void main() { SemanticsFlag.isButton, SemanticsFlag.isFocusable, ], + actions: [SemanticsAction.focus], ), ], ), diff --git a/packages/flutter/test/material/menu_anchor_test.dart b/packages/flutter/test/material/menu_anchor_test.dart index 3759ccf191..85676da183 100644 --- a/packages/flutter/test/material/menu_anchor_test.dart +++ b/packages/flutter/test/material/menu_anchor_test.dart @@ -3421,6 +3421,7 @@ void main() { TestSemantics.rootChild( actions: [ SemanticsAction.tap, + SemanticsAction.focus, ], label: 'ABC', rect: const Rect.fromLTRB(0.0, 0.0, 88.0, 48.0), @@ -3551,7 +3552,7 @@ void main() { SemanticsFlag.hasExpandedState, SemanticsFlag.isExpanded, ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], label: 'ABC', rect: const Rect.fromLTRB(0.0, 0.0, 88.0, 48.0), ), @@ -3573,7 +3574,7 @@ void main() { SemanticsFlag.isEnabled, SemanticsFlag.isFocusable, ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], ), ], ), @@ -3621,7 +3622,7 @@ void main() { SemanticsFlag.isEnabled, SemanticsFlag.isFocusable, ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], label: 'ABC', rect: const Rect.fromLTRB(0.0, 0.0, 88.0, 48.0), ), diff --git a/packages/flutter/test/material/navigation_bar_test.dart b/packages/flutter/test/material/navigation_bar_test.dart index f4676d597f..01ff1cb19f 100644 --- a/packages/flutter/test/material/navigation_bar_test.dart +++ b/packages/flutter/test/material/navigation_bar_test.dart @@ -559,6 +559,7 @@ void main() { isFocusable: true, isSelected: true, hasTapAction: true, + hasFocusAction: true, ), ); expect( @@ -568,6 +569,7 @@ void main() { textDirection: TextDirection.ltr, isFocusable: true, hasTapAction: true, + hasFocusAction: true, ), ); @@ -580,6 +582,7 @@ void main() { textDirection: TextDirection.ltr, isFocusable: true, hasTapAction: true, + hasFocusAction: true, ), ); expect( @@ -590,6 +593,7 @@ void main() { isFocusable: true, isSelected: true, hasTapAction: true, + hasFocusAction: true, ), ); }); @@ -624,6 +628,7 @@ void main() { isFocusable: true, isSelected: true, hasTapAction: true, + hasFocusAction: true, ), ); expect( @@ -633,6 +638,7 @@ void main() { textDirection: TextDirection.ltr, isFocusable: true, hasTapAction: true, + hasFocusAction: true, ), ); @@ -645,6 +651,7 @@ void main() { textDirection: TextDirection.ltr, isFocusable: true, hasTapAction: true, + hasFocusAction: true, ), ); expect( @@ -655,6 +662,7 @@ void main() { isFocusable: true, isSelected: true, hasTapAction: true, + hasFocusAction: true, ), ); }); diff --git a/packages/flutter/test/material/navigation_drawer_test.dart b/packages/flutter/test/material/navigation_drawer_test.dart index 35c39f918d..d38a064469 100644 --- a/packages/flutter/test/material/navigation_drawer_test.dart +++ b/packages/flutter/test/material/navigation_drawer_test.dart @@ -324,6 +324,7 @@ void main() { isFocusable: true, isSelected: true, hasTapAction: true, + hasFocusAction: true, ), ); expect( @@ -333,6 +334,7 @@ void main() { textDirection: TextDirection.ltr, isFocusable: true, hasTapAction: true, + hasFocusAction: true, ), ); @@ -345,6 +347,7 @@ void main() { textDirection: TextDirection.ltr, isFocusable: true, hasTapAction: true, + hasFocusAction: true, ), ); expect( @@ -355,6 +358,7 @@ void main() { isFocusable: true, isSelected: true, hasTapAction: true, + hasFocusAction: true, ), ); }); diff --git a/packages/flutter/test/material/navigation_rail_test.dart b/packages/flutter/test/material/navigation_rail_test.dart index de70ed5f7a..8e70e6cd21 100644 --- a/packages/flutter/test/material/navigation_rail_test.dart +++ b/packages/flutter/test/material/navigation_rail_test.dart @@ -5513,25 +5513,25 @@ TestSemantics _expectedSemantics() { SemanticsFlag.isSelected, SemanticsFlag.isFocusable, ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], label: 'Abc\nTab 1 of 4', textDirection: TextDirection.ltr, ), TestSemantics( flags: [SemanticsFlag.isFocusable], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], label: 'Def\nTab 2 of 4', textDirection: TextDirection.ltr, ), TestSemantics( flags: [SemanticsFlag.isFocusable], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], label: 'Ghi\nTab 3 of 4', textDirection: TextDirection.ltr, ), TestSemantics( flags: [SemanticsFlag.isFocusable], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], label: 'Jkl\nTab 4 of 4', textDirection: TextDirection.ltr, ), diff --git a/packages/flutter/test/material/outlined_button_test.dart b/packages/flutter/test/material/outlined_button_test.dart index 6b9241b7bc..e2a9235e87 100644 --- a/packages/flutter/test/material/outlined_button_test.dart +++ b/packages/flutter/test/material/outlined_button_test.dart @@ -1073,6 +1073,7 @@ void main() { TestSemantics.rootChild( actions: [ SemanticsAction.tap, + SemanticsAction.focus, ], label: 'ABC', rect: const Rect.fromLTRB(0.0, 0.0, 88.0, 48.0), diff --git a/packages/flutter/test/material/popup_menu_test.dart b/packages/flutter/test/material/popup_menu_test.dart index 95cd7f0eba..7c53019452 100644 --- a/packages/flutter/test/material/popup_menu_test.dart +++ b/packages/flutter/test/material/popup_menu_test.dart @@ -1219,7 +1219,7 @@ void main() { SemanticsFlag.isEnabled, SemanticsFlag.isFocusable, ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], label: '1', textDirection: TextDirection.ltr, ), @@ -1230,7 +1230,7 @@ void main() { SemanticsFlag.isEnabled, SemanticsFlag.isFocusable, ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], label: '2', textDirection: TextDirection.ltr, ), @@ -1241,7 +1241,7 @@ void main() { SemanticsFlag.isEnabled, SemanticsFlag.isFocusable, ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], label: '3', textDirection: TextDirection.ltr, ), @@ -1252,7 +1252,7 @@ void main() { SemanticsFlag.isEnabled, SemanticsFlag.isFocusable, ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], label: '4', textDirection: TextDirection.ltr, ), @@ -1263,7 +1263,7 @@ void main() { SemanticsFlag.isEnabled, SemanticsFlag.isFocusable, ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], label: '5', textDirection: TextDirection.ltr, ), @@ -1351,7 +1351,7 @@ void main() { SemanticsFlag.isEnabled, SemanticsFlag.isFocusable, ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], label: 'test1\ntest2', textDirection: TextDirection.ltr, ), @@ -1432,7 +1432,7 @@ void main() { SemanticsFlag.isEnabled, SemanticsFlag.isFocusable, ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], label: '1', textDirection: TextDirection.ltr, ), @@ -1452,7 +1452,7 @@ void main() { SemanticsFlag.isEnabled, SemanticsFlag.isFocusable, ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], label: '3', textDirection: TextDirection.ltr, ), @@ -1463,7 +1463,7 @@ void main() { SemanticsFlag.isEnabled, SemanticsFlag.isFocusable, ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], label: '4', textDirection: TextDirection.ltr, ), @@ -1474,7 +1474,7 @@ void main() { SemanticsFlag.isEnabled, SemanticsFlag.isFocusable, ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], label: '5', textDirection: TextDirection.ltr, ), diff --git a/packages/flutter/test/material/radio_list_tile_test.dart b/packages/flutter/test/material/radio_list_tile_test.dart index 43355b2ce8..c1bb21ae16 100644 --- a/packages/flutter/test/material/radio_list_tile_test.dart +++ b/packages/flutter/test/material/radio_list_tile_test.dart @@ -411,7 +411,7 @@ void main() { SemanticsFlag.isInMutuallyExclusiveGroup, SemanticsFlag.isFocusable, ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], label: 'Title', textDirection: TextDirection.ltr, ), @@ -448,7 +448,7 @@ void main() { SemanticsFlag.isInMutuallyExclusiveGroup, SemanticsFlag.isFocusable, ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], label: 'Title', textDirection: TextDirection.ltr, ), @@ -483,6 +483,7 @@ void main() { SemanticsFlag.isInMutuallyExclusiveGroup, SemanticsFlag.isFocusable, ], + actions: [SemanticsAction.focus], label: 'Title', textDirection: TextDirection.ltr, ), diff --git a/packages/flutter/test/material/radio_test.dart b/packages/flutter/test/material/radio_test.dart index 4aabeed1a8..8b0456b7fc 100644 --- a/packages/flutter/test/material/radio_test.dart +++ b/packages/flutter/test/material/radio_test.dart @@ -221,6 +221,7 @@ void main() { ], actions: [ SemanticsAction.tap, + SemanticsAction.focus, ], ), ); @@ -254,6 +255,7 @@ void main() { ], actions: [ SemanticsAction.tap, + SemanticsAction.focus, ], ), ], @@ -284,6 +286,7 @@ void main() { ], actions: [ SemanticsAction.tap, + SemanticsAction.focus, ], ), ], @@ -310,6 +313,7 @@ void main() { SemanticsFlag.isInMutuallyExclusiveGroup, SemanticsFlag.isFocusable, // This flag is delayed by 1 frame. ], + actions: [SemanticsAction.focus], ), ], ), ignoreRect: true, ignoreTransform: true)); diff --git a/packages/flutter/test/material/raw_material_button_test.dart b/packages/flutter/test/material/raw_material_button_test.dart index d58db5f785..5f17df39f7 100644 --- a/packages/flutter/test/material/raw_material_button_test.dart +++ b/packages/flutter/test/material/raw_material_button_test.dart @@ -160,6 +160,7 @@ void main() { ], actions: [ SemanticsAction.tap, + SemanticsAction.focus, ], label: '+', textDirection: TextDirection.ltr, diff --git a/packages/flutter/test/material/reorderable_list_test.dart b/packages/flutter/test/material/reorderable_list_test.dart index 5595fe6448..fcd36d7d5c 100644 --- a/packages/flutter/test/material/reorderable_list_test.dart +++ b/packages/flutter/test/material/reorderable_list_test.dart @@ -751,6 +751,7 @@ void main() { hasEnabledState: true, label: 'Switch tile', hasTapAction: true, + hasFocusAction: true, )); handle.dispose(); }); diff --git a/packages/flutter/test/material/search_test.dart b/packages/flutter/test/material/search_test.dart index 79e856c6cb..61963b37ec 100644 --- a/packages/flutter/test/material/search_test.dart +++ b/packages/flutter/test/material/search_test.dart @@ -662,7 +662,7 @@ void main() { SemanticsFlag.isEnabled, SemanticsFlag.isFocusable, ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], tooltip: 'Back', textDirection: TextDirection.ltr, ), @@ -718,7 +718,7 @@ void main() { SemanticsFlag.isEnabled, SemanticsFlag.isFocusable, ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], label: 'Suggestions', textDirection: TextDirection.ltr, ), @@ -812,7 +812,7 @@ void main() { SemanticsFlag.isEnabled, SemanticsFlag.isFocusable, ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], tooltip: 'Back', textDirection: TextDirection.ltr, ), @@ -856,7 +856,7 @@ void main() { SemanticsFlag.isEnabled, SemanticsFlag.isFocusable, ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], label: 'Suggestions', textDirection: TextDirection.ltr, ), diff --git a/packages/flutter/test/material/segmented_button_test.dart b/packages/flutter/test/material/segmented_button_test.dart index c05986a3d1..06424c7cf2 100644 --- a/packages/flutter/test/material/segmented_button_test.dart +++ b/packages/flutter/test/material/segmented_button_test.dart @@ -449,6 +449,7 @@ void main() { label: '1', actions: [ SemanticsAction.tap, + SemanticsAction.focus, ], ), @@ -466,6 +467,7 @@ void main() { label: '2', actions: [ SemanticsAction.tap, + SemanticsAction.focus, ], ), @@ -529,6 +531,7 @@ void main() { label: '1', actions: [ SemanticsAction.tap, + SemanticsAction.focus, ], ), @@ -544,6 +547,7 @@ void main() { label: '2', actions: [ SemanticsAction.tap, + SemanticsAction.focus, ], ), diff --git a/packages/flutter/test/material/slider_test.dart b/packages/flutter/test/material/slider_test.dart index cb26860d91..9f2786db2e 100644 --- a/packages/flutter/test/material/slider_test.dart +++ b/packages/flutter/test/material/slider_test.dart @@ -1292,6 +1292,7 @@ void main() { SemanticsFlag.isSlider, ], actions: [ + SemanticsAction.focus, SemanticsAction.increase, SemanticsAction.decrease, ], @@ -1350,6 +1351,7 @@ void main() { SemanticsFlag.isFocusable, SemanticsFlag.isSlider, ], + actions: [SemanticsAction.focus], value: '50%', increasedValue: '55%', decreasedValue: '45%', @@ -1452,7 +1454,7 @@ void main() { TestSemantics( id: 4, flags: [SemanticsFlag.hasEnabledState, SemanticsFlag.isEnabled, SemanticsFlag.isFocusable, SemanticsFlag.isSlider], - actions: [SemanticsAction.increase, SemanticsAction.decrease], + actions: [SemanticsAction.focus, SemanticsAction.increase, SemanticsAction.decrease], value: '50%', increasedValue: '60%', decreasedValue: '40%', @@ -1565,6 +1567,7 @@ void main() { SemanticsFlag.isSlider, ], actions: [ + SemanticsAction.focus, SemanticsAction.increase, SemanticsAction.decrease, SemanticsAction.didGainAccessibilityFocus, @@ -1625,6 +1628,7 @@ void main() { SemanticsFlag.isSlider, ], actions: [ + SemanticsAction.focus, SemanticsAction.didGainAccessibilityFocus, ], value: '50%', @@ -1729,7 +1733,7 @@ void main() { TestSemantics( id: 4, flags: [SemanticsFlag.hasEnabledState, SemanticsFlag.isEnabled, SemanticsFlag.isFocusable, SemanticsFlag.isSlider], - actions: [SemanticsAction.increase, SemanticsAction.decrease], + actions: [SemanticsAction.focus, SemanticsAction.increase, SemanticsAction.decrease], value: '40', increasedValue: '60', decreasedValue: '20', @@ -1789,7 +1793,7 @@ void main() { TestSemantics( id: 4, flags: [SemanticsFlag.hasEnabledState, SemanticsFlag.isEnabled, SemanticsFlag.isFocusable, SemanticsFlag.isSlider], - actions: [SemanticsAction.increase, SemanticsAction.decrease], + actions: [SemanticsAction.focus, SemanticsAction.increase, SemanticsAction.decrease], value: '40', increasedValue: '60', decreasedValue: '20', @@ -2633,6 +2637,7 @@ void main() { SemanticsFlag.isSlider, ], actions: [ + SemanticsAction.focus, SemanticsAction.increase, SemanticsAction.decrease, SemanticsAction.didGainAccessibilityFocus, diff --git a/packages/flutter/test/material/switch_list_tile_test.dart b/packages/flutter/test/material/switch_list_tile_test.dart index bfdf3ba9e5..c42b4e0ac6 100644 --- a/packages/flutter/test/material/switch_list_tile_test.dart +++ b/packages/flutter/test/material/switch_list_tile_test.dart @@ -78,7 +78,7 @@ void main() { SemanticsFlag.isFocusable, SemanticsFlag.isToggled, ], - actions: SemanticsAction.tap.index, + actions: SemanticsAction.tap.index | SemanticsAction.focus.index, label: 'aaa\nAAA', ), TestSemantics.rootChild( @@ -92,7 +92,7 @@ void main() { SemanticsFlag.isEnabled, SemanticsFlag.isFocusable, ], - actions: SemanticsAction.tap.index, + actions: SemanticsAction.tap.index | SemanticsAction.focus.index, label: 'bbb\nBBB', ), TestSemantics.rootChild( @@ -106,7 +106,7 @@ void main() { SemanticsFlag.isFocusable, SemanticsFlag.isInMutuallyExclusiveGroup, ], - actions: SemanticsAction.tap.index, + actions: SemanticsAction.tap.index | SemanticsAction.focus.index, label: 'CCC\nccc', ), ], diff --git a/packages/flutter/test/material/tabs_test.dart b/packages/flutter/test/material/tabs_test.dart index aa9091fb6b..d0b66dce90 100644 --- a/packages/flutter/test/material/tabs_test.dart +++ b/packages/flutter/test/material/tabs_test.dart @@ -3592,7 +3592,7 @@ void main() { children: [ TestSemantics( id: 4, - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], flags: [ SemanticsFlag.isSelected, SemanticsFlag.isFocusable, @@ -3604,7 +3604,7 @@ void main() { TestSemantics( id: 5, flags: [SemanticsFlag.isFocusable], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], label: 'TAB #1\nTab 2 of 2', rect: const Rect.fromLTRB(0.0, 0.0, 116.0, kTextTabBarHeight), transform: Matrix4.translationValues(116.0, 276.0, 0.0), @@ -3863,7 +3863,7 @@ void main() { SemanticsFlag.isSelected, SemanticsFlag.isFocusable, ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], label: 'Semantics override 0\nTab 1 of 2', rect: const Rect.fromLTRB(0.0, 0.0, 116.0, kTextTabBarHeight), transform: Matrix4.translationValues(0.0, 276.0, 0.0), @@ -3871,7 +3871,7 @@ void main() { TestSemantics( id: 5, flags: [SemanticsFlag.isFocusable], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], label: 'Semantics override 1\nTab 2 of 2', rect: const Rect.fromLTRB(0.0, 0.0, 116.0, kTextTabBarHeight), transform: Matrix4.translationValues(116.0, 276.0, 0.0), @@ -5652,14 +5652,14 @@ void main() { flags: [SemanticsFlag.isFocusable, SemanticsFlag.isSelected], id: 2, rect: TestSemantics.fullScreen, - actions: 1, + actions: 1 | SemanticsAction.focus.index, ), TestSemantics( label: 'TAB2\nTab 2 of 2', flags: [SemanticsFlag.isFocusable], id: 3, rect: TestSemantics.fullScreen, - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], ), TestSemantics( id: 4, diff --git a/packages/flutter/test/material/text_button_test.dart b/packages/flutter/test/material/text_button_test.dart index f213635cb7..640d9649f8 100644 --- a/packages/flutter/test/material/text_button_test.dart +++ b/packages/flutter/test/material/text_button_test.dart @@ -607,6 +607,7 @@ void main() { TestSemantics.rootChild( actions: [ SemanticsAction.tap, + SemanticsAction.focus, ], label: 'ABC', rect: const Rect.fromLTRB(0.0, 0.0, 88.0, 48.0), diff --git a/packages/flutter/test/material/text_field_test.dart b/packages/flutter/test/material/text_field_test.dart index 1f667d152e..fd1f69e791 100644 --- a/packages/flutter/test/material/text_field_test.dart +++ b/packages/flutter/test/material/text_field_test.dart @@ -6964,7 +6964,7 @@ void main() { ), ); - expect(semantics, isNot(includesNodeWith(actions: [SemanticsAction.tap]))); + expect(semantics, isNot(includesNodeWith(actions: [SemanticsAction.tap, SemanticsAction.focus]))); semantics.dispose(); }); @@ -7005,7 +7005,7 @@ void main() { ), ); - expect(semantics, isNot(includesNodeWith(actions: [SemanticsAction.tap]))); + expect(semantics, isNot(includesNodeWith(actions: [SemanticsAction.tap, SemanticsAction.focus]))); semantics.dispose(); }); diff --git a/packages/flutter/test/material/time_picker_test.dart b/packages/flutter/test/material/time_picker_test.dart index 60b44a4bc5..1236c63fc1 100644 --- a/packages/flutter/test/material/time_picker_test.dart +++ b/packages/flutter/test/material/time_picker_test.dart @@ -1250,7 +1250,7 @@ void main() { semantics, includesNodeWith( label: amString, - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], flags: [ SemanticsFlag.isButton, SemanticsFlag.isChecked, @@ -1264,7 +1264,7 @@ void main() { semantics, includesNodeWith( label: pmString, - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], flags: [ SemanticsFlag.isButton, SemanticsFlag.isInMutuallyExclusiveGroup, diff --git a/packages/flutter/test/material/toggle_buttons_test.dart b/packages/flutter/test/material/toggle_buttons_test.dart index 1681ca3a80..9da18f8f8d 100644 --- a/packages/flutter/test/material/toggle_buttons_test.dart +++ b/packages/flutter/test/material/toggle_buttons_test.dart @@ -2117,6 +2117,7 @@ void main() { ], actions: [ SemanticsAction.tap, + SemanticsAction.focus, ], rect: const Rect.fromLTRB(0.0, 0.0, 87.0, 48.0), ), @@ -2130,6 +2131,7 @@ void main() { ], actions: [ SemanticsAction.tap, + SemanticsAction.focus, ], rect: const Rect.fromLTRB(0.0, 0.0, 88.0, 48.0) ), @@ -2143,6 +2145,7 @@ void main() { ], actions: [ SemanticsAction.tap, + SemanticsAction.focus, ], rect: const Rect.fromLTRB(0.0, 0.0, 88.0, 48.0), ), @@ -2188,6 +2191,7 @@ void main() { ], actions: [ SemanticsAction.tap, + SemanticsAction.focus, ], ), TestSemantics( @@ -2201,6 +2205,7 @@ void main() { ], actions: [ SemanticsAction.tap, + SemanticsAction.focus, ], ), ], diff --git a/packages/flutter/test/material/user_accounts_drawer_header_test.dart b/packages/flutter/test/material/user_accounts_drawer_header_test.dart index a106eaabf8..1da4ab880b 100644 --- a/packages/flutter/test/material/user_accounts_drawer_header_test.dart +++ b/packages/flutter/test/material/user_accounts_drawer_header_test.dart @@ -531,6 +531,7 @@ void main() { flags: [SemanticsFlag.isFocusable], label: 'Signed in\nname\nemail', textDirection: TextDirection.ltr, + actions: [SemanticsAction.focus], children: [ TestSemantics( label: r'B', diff --git a/packages/flutter/test/widgets/absorb_pointer_test.dart b/packages/flutter/test/widgets/absorb_pointer_test.dart index 6c2a88f58c..5952cc69b2 100644 --- a/packages/flutter/test/widgets/absorb_pointer_test.dart +++ b/packages/flutter/test/widgets/absorb_pointer_test.dart @@ -48,6 +48,7 @@ void main() { matchesSemantics( label: 'button', hasTapAction: true, + hasFocusAction: true, isButton: true, isFocusable: true, hasEnabledState: true, diff --git a/packages/flutter/test/widgets/actions_test.dart b/packages/flutter/test/widgets/actions_test.dart index 87d2f2b0e1..3b87e8c44f 100644 --- a/packages/flutter/test/widgets/actions_test.dart +++ b/packages/flutter/test/widgets/actions_test.dart @@ -1013,9 +1013,11 @@ void main() { // This semantic is from `Focus` widget under `FocusableActionDetector`. matchesSemantics( isFocusable: true, + hasFocusAction: true, children: [ matchesSemantics( hasTapAction: true, + hasFocusAction: true, isButton: true, hasEnabledState: true, isEnabled: true, @@ -1025,6 +1027,7 @@ void main() { ), matchesSemantics( hasTapAction: true, + hasFocusAction: true, isButton: true, hasEnabledState: true, isEnabled: true, @@ -1068,6 +1071,7 @@ void main() { children: [ matchesSemantics( hasTapAction: true, + hasFocusAction: true, isButton: true, hasEnabledState: true, isEnabled: true, @@ -1077,6 +1081,7 @@ void main() { ), matchesSemantics( hasTapAction: true, + hasFocusAction: true, isButton: true, hasEnabledState: true, isEnabled: true, diff --git a/packages/flutter/test/widgets/basic_test.dart b/packages/flutter/test/widgets/basic_test.dart index 8ad224376c..ee472a9085 100644 --- a/packages/flutter/test/widgets/basic_test.dart +++ b/packages/flutter/test/widgets/basic_test.dart @@ -858,6 +858,7 @@ void main() { matchesSemantics( label: 'button', hasTapAction: true, + hasFocusAction: true, isButton: true, isFocusable: true, hasEnabledState: true, diff --git a/packages/flutter/test/widgets/drawer_test.dart b/packages/flutter/test/widgets/drawer_test.dart index cb3aba7fd8..e6741fb2f2 100644 --- a/packages/flutter/test/widgets/drawer_test.dart +++ b/packages/flutter/test/widgets/drawer_test.dart @@ -348,7 +348,7 @@ void main() { scaffoldKey.currentState!.openDrawer(); await tester.pump(const Duration(milliseconds: 100)); - expect(semantics, isNot(includesNodeWith(actions: [SemanticsAction.tap]))); + expect(semantics, isNot(includesNodeWith(actions: [SemanticsAction.tap, SemanticsAction.focus]))); expect(semantics, isNot(includesNodeWith(label: 'Dismiss'))); semantics.dispose(); diff --git a/packages/flutter/test/widgets/focus_scope_test.dart b/packages/flutter/test/widgets/focus_scope_test.dart index 13b25fe370..ad5514c909 100644 --- a/packages/flutter/test/widgets/focus_scope_test.dart +++ b/packages/flutter/test/widgets/focus_scope_test.dart @@ -1999,6 +1999,47 @@ void main() { ), ); }); + + testWidgets('Focus widget gains input focus when it gains accessibility focus', (WidgetTester tester) async { + final SemanticsTester semantics = SemanticsTester(tester); + final SemanticsOwner semanticsOwner = tester.binding.pipelineOwner.semanticsOwner!; + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.rtl, + child: Focus( + focusNode: focusNode, + child: const Text('Test'), + ), + ), + ); + + expect( + semantics, + hasSemantics( + TestSemantics.root( + children: [ + TestSemantics( + id: 1, + flags: [SemanticsFlag.isFocusable], + actions: [SemanticsAction.focus], + label: 'Test', + textDirection: TextDirection.rtl, + ), + ], + ), + ignoreRect: true, + ignoreTransform: true, + ), + ); + + expect(focusNode.hasFocus, isFalse); + semanticsOwner.performAction(1, SemanticsAction.focus); + await tester.pumpAndSettle(); + expect(focusNode.hasFocus, isTrue); + semantics.dispose(); + }); }); group('ExcludeFocus', () { diff --git a/packages/flutter/test/widgets/focus_traversal_test.dart b/packages/flutter/test/widgets/focus_traversal_test.dart index 9d9f215730..27ef9d5b3c 100644 --- a/packages/flutter/test/widgets/focus_traversal_test.dart +++ b/packages/flutter/test/widgets/focus_traversal_test.dart @@ -3182,6 +3182,9 @@ void main() { flags: [ SemanticsFlag.isFocusable, ], + actions: [ + SemanticsAction.focus, + ], ), ], ); diff --git a/packages/flutter/test/widgets/gesture_detector_semantics_test.dart b/packages/flutter/test/widgets/gesture_detector_semantics_test.dart index 4632cf53bc..d2de056630 100644 --- a/packages/flutter/test/widgets/gesture_detector_semantics_test.dart +++ b/packages/flutter/test/widgets/gesture_detector_semantics_test.dart @@ -333,7 +333,7 @@ void main() { ); expect(semantics, isNot(includesNodeWith( - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], ))); semantics.dispose(); diff --git a/packages/flutter/test/widgets/selectable_region_test.dart b/packages/flutter/test/widgets/selectable_region_test.dart index 6877d6f9b7..c1710f8dd3 100644 --- a/packages/flutter/test/widgets/selectable_region_test.dart +++ b/packages/flutter/test/widgets/selectable_region_test.dart @@ -260,7 +260,7 @@ void main() { SemanticsFlag.isEnabled, SemanticsFlag.isFocusable ], - actions: [SemanticsAction.tap], + actions: [SemanticsAction.tap, SemanticsAction.focus], label: 'Button', textDirection: TextDirection.ltr, ), diff --git a/packages/flutter/test/widgets/semantics_tester.dart b/packages/flutter/test/widgets/semantics_tester.dart index 014137f658..e71bb142d6 100644 --- a/packages/flutter/test/widgets/semantics_tester.dart +++ b/packages/flutter/test/widgets/semantics_tester.dart @@ -269,7 +269,10 @@ class TestSemantics { final int actionsBitmask = actions is int ? actions as int : (actions as List).fold(0, (int bitmask, SemanticsAction action) => bitmask | action.index); - if (actionsBitmask != nodeData.actions) { + // TODO(gspencergoog): Remove focus filter once customer tests have been + // updated with the proper actions information for focus. + // https://github.com/flutter/flutter/issues/149842 + if ((actionsBitmask & ~SemanticsAction.focus.index) != (nodeData.actions & ~SemanticsAction.focus.index)) { return fail('expected node id $id to have actions $actions but found actions ${nodeData.actions}.'); } @@ -523,9 +526,16 @@ class SemanticsTester { if (textDirection != null && node.textDirection != textDirection) { return false; } + if (actions != null) { - final int expectedActions = actions.fold(0, (int value, SemanticsAction action) => value | action.index); - final int actualActions = node.getSemanticsData().actions; + // TODO(gspencergoog): Remove focus filter once customer tests have been + // updated with the proper actions information for focus. + // https://github.com/flutter/flutter/issues/149842 + final List nonFocusActions = actions.where( + (SemanticsAction action) => action != SemanticsAction.focus + ).toList(); + final int expectedActions = nonFocusActions.fold(0, (int value, SemanticsAction action) => value | action.index); + final int actualActions = node.getSemanticsData().actions & ~SemanticsAction.focus.index; if (expectedActions != actualActions) { return false; } @@ -647,11 +657,14 @@ class SemanticsTester { static String _actionsToSemanticsActionExpression(dynamic actions) { Iterable list; + // TODO(gspencergoog): Remove focus filter once customer tests have been + // updated with the proper actions information for focus. + // https://github.com/flutter/flutter/issues/149842 if (actions is int) { list = SemanticsAction.values - .where((SemanticsAction action) => (action.index & actions) != 0); + .where((SemanticsAction action) => action != SemanticsAction.focus && (action.index & actions) != 0); } else { - list = actions as List; + list = (actions as List).where((SemanticsAction action) => action != SemanticsAction.focus); } return '[${list.join(', ')}]'; } @@ -874,7 +887,10 @@ class _IncludesNodeWith extends Matcher { if (value != null) 'value "$value"', if (hint != null) 'hint "$hint"', if (textDirection != null) ' (${textDirection!.name})', - if (actions != null) 'actions "${actions!.join(', ')}"', + // TODO(gspencergoog): Remove focus filter once customer tests have been + // updated with the proper actions information for focus. + // https://github.com/flutter/flutter/issues/149842 + if (actions != null) 'actions "${actions!.where((SemanticsAction action) => action != SemanticsAction.focus).join(', ')}"', if (flags != null) 'flags "${flags!.join(', ')}"', if (tags != null) 'tags "${tags!.join(', ')}"', if (scrollPosition != null) 'scrollPosition "$scrollPosition"', diff --git a/packages/flutter_test/lib/src/matchers.dart b/packages/flutter_test/lib/src/matchers.dart index 6e955a4fdb..dfe8f2ea0d 100644 --- a/packages/flutter_test/lib/src/matchers.dart +++ b/packages/flutter_test/lib/src/matchers.dart @@ -674,6 +674,7 @@ Matcher matchesSemantics({ bool isExpanded = false, // Actions // bool hasTapAction = false, + bool hasFocusAction = false, bool hasLongPressAction = false, bool hasScrollLeftAction = false, bool hasScrollRightAction = false, @@ -753,6 +754,7 @@ Matcher matchesSemantics({ isExpanded: isExpanded, // Actions hasTapAction: hasTapAction, + hasFocusAction: hasFocusAction, hasLongPressAction: hasLongPressAction, hasScrollLeftAction: hasScrollLeftAction, hasScrollRightAction: hasScrollRightAction, @@ -860,6 +862,7 @@ Matcher containsSemantics({ bool? isExpanded, // Actions bool? hasTapAction, + bool? hasFocusAction, bool? hasLongPressAction, bool? hasScrollLeftAction, bool? hasScrollRightAction, @@ -939,6 +942,7 @@ Matcher containsSemantics({ isExpanded: isExpanded, // Actions hasTapAction: hasTapAction, + hasFocusAction: hasFocusAction, hasLongPressAction: hasLongPressAction, hasScrollLeftAction: hasScrollLeftAction, hasScrollRightAction: hasScrollRightAction, @@ -2259,6 +2263,11 @@ class _MatchesSemanticsData extends Matcher { required bool? isExpanded, // Actions required bool? hasTapAction, + // TODO(gspencergoog): Once this has landed, and customer tests have been + // updated, remove the ignore below. + // https://github.com/flutter/flutter/issues/149842 + // ignore: avoid_unused_constructor_parameters + required bool? hasFocusAction, required bool? hasLongPressAction, required bool? hasScrollLeftAction, required bool? hasScrollRightAction, @@ -2317,6 +2326,9 @@ class _MatchesSemanticsData extends Matcher { }, actions = { if (hasTapAction != null) SemanticsAction.tap: hasTapAction, + // TODO(gspencergoog): Once this has landed, and customer tests have + // been updated, add a line here that adds handling for + // hasFocusAction. https://github.com/flutter/flutter/issues/149842 if (hasLongPressAction != null) SemanticsAction.longPress: hasLongPressAction, if (hasScrollLeftAction != null) SemanticsAction.scrollLeft: hasScrollLeftAction, if (hasScrollRightAction != null) SemanticsAction.scrollRight: hasScrollRightAction, @@ -2379,8 +2391,8 @@ class _MatchesSemanticsData extends Matcher { final Map flags; @override - Description describe(Description description) { - description.add('has semantics'); + Description describe(Description description, [String? index]) { + description.add('${index == null ? '' : 'Child $index '}has semantics'); if (label != null) { description.add(' with label: $label'); } @@ -2414,12 +2426,19 @@ class _MatchesSemanticsData extends Matcher { if (tooltip != null) { description.add(' with tooltip: $tooltip'); } - if (actions.isNotEmpty) { - final List expectedActions = actions.entries + // TODO(gspencergoog): Remove filter once customer tests have been updated + // with the proper actions information for focus. + // https://github.com/flutter/flutter/issues/149842 + final Map nonFocusActions = + Map.fromEntries(actions.entries.where( + (MapEntry e) => e.key != SemanticsAction.focus + )); + if (nonFocusActions.isNotEmpty) { + final List expectedActions = nonFocusActions.entries .where((MapEntry e) => e.value) .map((MapEntry e) => e.key) .toList(); - final List notExpectedActions = actions.entries + final List notExpectedActions = nonFocusActions.entries .where((MapEntry e) => !e.value) .map((MapEntry e) => e.key) .toList(); @@ -2479,9 +2498,15 @@ class _MatchesSemanticsData extends Matcher { description.add(' with custom hints: $hintOverrides'); } if (children != null) { - description.add(' with children:\n'); - for (final _MatchesSemanticsData child in children!.cast<_MatchesSemanticsData>()) { - child.describe(description); + description.add(' with children:\n '); + final List<_MatchesSemanticsData> childMatches = children!.cast<_MatchesSemanticsData>(); + int childIndex = 1; + for (final _MatchesSemanticsData child in childMatches) { + child.describe(description, index != null ? '$index:$childIndex': '$childIndex'); + if (child != childMatches.last) { + description.add('\n '); + } + childIndex += 1; } } return description; @@ -2592,10 +2617,17 @@ class _MatchesSemanticsData extends Matcher { if (maxValueLength != null && maxValueLength != data.maxValueLength) { return failWithDescription(matchState, 'maxValueLength was: ${data.maxValueLength}'); } - if (actions.isNotEmpty) { + // TODO(gspencergoog): Remove filter once customer tests have been updated + // with the proper actions information for focus. + // https://github.com/flutter/flutter/issues/149842 + final Map nonFocusActions = + Map.fromEntries(actions.entries.where( + (MapEntry e) => e.key != SemanticsAction.focus + )); + if (nonFocusActions.isNotEmpty) { final List unexpectedActions = []; final List missingActions = []; - for (final MapEntry actionEntry in actions.entries) { + for (final MapEntry actionEntry in nonFocusActions.entries) { final ui.SemanticsAction action = actionEntry.key; final bool actionExpected = actionEntry.value; final bool actionPresent = (action.index & data.actions) == action.index; diff --git a/packages/flutter_test/test/matchers_test.dart b/packages/flutter_test/test/matchers_test.dart index 1825755da8..d1fb147243 100644 --- a/packages/flutter_test/test/matchers_test.dart +++ b/packages/flutter_test/test/matchers_test.dart @@ -747,6 +747,7 @@ void main() { hasDidGainAccessibilityFocusAction: true, hasDidLoseAccessibilityFocusAction: true, hasDismissAction: true, + hasFocusAction: true, customActions: [action], )); }); @@ -1035,6 +1036,7 @@ void main() { hasDidGainAccessibilityFocusAction: true, hasDidLoseAccessibilityFocusAction: true, hasDismissAction: true, + hasFocusAction: true, customActions: [action], ), ); @@ -1128,6 +1130,7 @@ void main() { hasDidGainAccessibilityFocusAction: false, hasDidLoseAccessibilityFocusAction: false, hasDismissAction: false, + hasFocusAction: false, ), ); });