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 9d769f7337f..1b11c4d833c 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 acd9b04f1bf..c59fb5f3050 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 601aef991d8..b6bdaf6baf2 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 cd30734d418..6035c20ceeb 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 2ca816975ef..649efb25a55 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 c46651ab159..7407eccea10 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 afdba078a12..682dc4b2325 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 c74f893e6e7..f0e65837bb7 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 ed7c1aa2054..b6eb7c577ac 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 c8a5815e441..5e9003a1bf5 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 b91628a16c1..81acbc9a605 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 636456d9620..56cbf37d977 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 0a398a1dad7..d86147d0991 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 df83154d4df..8ba368cbea1 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 353e35d47aa..dbe3cdd4ee9 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 bb565c8c753..e1fe7353238 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 36b9ae2ff50..1562be4b655 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 5ec084aa05e..c04ae2b1aa4 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 2e9d958bebf..d6627b5d944 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 7ec1338fa44..7ff43ab84fd 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 feab74d92d2..8df161448fe 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 70daebbb82f..c6de9c673ec 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 e0dcc9f26b5..ad7f766e191 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 e5cfc6b3f5b..5a9f0148ced 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 7814043b776..cdae2a94318 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 d90aded12e3..3cc961ccc10 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 4860309ce34..97d8ea6abe7 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 482812fdd89..4f1612bfa1c 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 3759ccf1915..85676da1834 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 f4676d597f6..01ff1cb19f4 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 35c39f918d3..d38a064469d 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 de70ed5f7ad..8e70e6cd213 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 6b9241b7bc5..e2a9235e878 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 95cd7f0eba1..7c530194526 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 43355b2ce87..c1bb21ae161 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 4aabeed1a87..8b0456b7fc8 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 d58db5f7851..5f17df39f71 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 5595fe6448f..fcd36d7d5c6 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 79e856c6cb7..61963b37ecf 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 c05986a3d1c..06424c7cf29 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 cb26860d915..9f2786db2e3 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 bfdf3ba9e50..c42b4e0ac61 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 aa9091fb6bf..d0b66dce901 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 f213635cb78..640d9649f89 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 1f667d152ee..fd1f69e7914 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 60b44a4bc53..1236c63fc11 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 1681ca3a801..9da18f8f8d9 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 a106eaabf8f..1da4ab880b2 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 6c2a88f58ca..5952cc69b28 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 87d2f2b0e11..3b87e8c44f3 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 8ad224376c0..ee472a90854 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 cb3aba7fd82..e6741fb2f28 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 13b25fe370b..ad5514c9091 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 9d9f2157305..27ef9d5b3c1 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 4632cf53bc5..d2de0566303 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 6877d6f9b7a..c1710f8dd36 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 014137f6584..e71bb142d66 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 6e955a4fdb2..dfe8f2ea0d1 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 1825755da86..d1fb1472433 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, ), ); });