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.
This commit is contained in:
Greg Spencer 2024-06-12 13:05:10 -07:00 committed by GitHub
parent 0c692b86ba
commit d68e05bf36
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
59 changed files with 329 additions and 81 deletions

View File

@ -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',
));

View File

@ -671,6 +671,15 @@ class _FocusState extends State<Focus> {
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,

View File

@ -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>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
), hasLength(1));
await tester.pumpWidget(
@ -268,7 +272,7 @@ void main() {
SemanticsFlag.isChecked,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
), hasLength(1));
await tester.pumpWidget(
@ -288,7 +292,7 @@ void main() {
SemanticsFlag.isEnabled,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
), hasLength(1));
semantics.dispose();

View File

@ -148,6 +148,7 @@ void main() {
],
actions: <SemanticsAction>[
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();

View File

@ -1945,7 +1945,7 @@ void main() {
await tester.pumpAndSettle();
expect(semantics, isNot(includesNodeWith(
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
label: 'Dismiss',
)));
debugDefaultTargetPlatformOverride = null;

View File

@ -2621,7 +2621,7 @@ void main() {
),
);
expect(semantics, isNot(includesNodeWith(actions: <SemanticsAction>[SemanticsAction.tap])));
expect(semantics, isNot(includesNodeWith(actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus])));
semantics.dispose();
});

View File

@ -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();

View File

@ -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>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
label: 'A\nTab 1 of 2',
textDirection: TextDirection.ltr,
),
TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.isFocusable],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
label: 'B\nTab 2 of 2',
textDirection: TextDirection.ltr,
),

View File

@ -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,

View File

@ -130,6 +130,7 @@ void main() {
textDirection: TextDirection.ltr,
actions: <SemanticsAction>[
SemanticsAction.tap,
SemanticsAction.focus,
],
flags: <SemanticsFlag>[
SemanticsFlag.hasEnabledState,

View File

@ -1189,6 +1189,7 @@ void main() {
hasEnabledState: true,
isEnabled: true,
hasTapAction: true,
hasFocusAction: true,
isFocusable: true,
label: 'Hello\nthere',
));

View File

@ -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>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
), hasLength(1));
await tester.pumpWidget(
@ -341,7 +346,7 @@ void main() {
SemanticsFlag.isChecked,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
), hasLength(1));
await tester.pumpWidget(
@ -364,7 +369,7 @@ void main() {
SemanticsFlag.isEnabled,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
), hasLength(1));
semantics.dispose();

View File

@ -2971,7 +2971,7 @@ void main() {
children: <TestSemantics>[
TestSemantics(
tooltip: 'Delete',
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
textDirection: TextDirection.ltr,
flags: <SemanticsFlag>[
SemanticsFlag.isButton,
@ -3030,7 +3030,7 @@ void main() {
SemanticsFlag.isEnabled,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
),
],
),
@ -3088,7 +3088,7 @@ void main() {
SemanticsFlag.isEnabled,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
),
],
),
@ -3141,7 +3141,7 @@ void main() {
SemanticsFlag.isFocusable,
SemanticsFlag.isSelected,
],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
),
],
),
@ -3295,7 +3295,7 @@ void main() {
SemanticsFlag.isEnabled,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
),
],
),

View File

@ -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,

View File

@ -1319,6 +1319,7 @@ void main() {
matchesSemantics(
label: '30, Saturday, January 30, 2016, Today',
hasTapAction: true,
hasFocusAction: true,
isFocusable: true,
),
);

View File

@ -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();

View File

@ -147,7 +147,7 @@ void main() {
expect(semantics, isNot(includesNodeWith(
label: const DefaultMaterialLocalizations().modalBarrierDismissLabel,
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
)));
semantics.dispose();

View File

@ -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: <SemanticsTag>[const SemanticsTag('RenderViewport.twoPane')],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
),
TestSemantics(
label: 'two',
textDirection: TextDirection.ltr,
flags: <SemanticsFlag>[SemanticsFlag.isFocusable],
tags: <SemanticsTag>[const SemanticsTag('RenderViewport.twoPane')],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
),
TestSemantics(
label: 'three',
textDirection: TextDirection.ltr,
flags: <SemanticsFlag>[SemanticsFlag.isFocusable],
tags: <SemanticsTag>[const SemanticsTag('RenderViewport.twoPane')],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
),
TestSemantics(
label: 'four',
textDirection: TextDirection.ltr,
flags: <SemanticsFlag>[SemanticsFlag.isFocusable],
tags: <SemanticsTag>[const SemanticsTag('RenderViewport.twoPane')],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
),
],
),

View File

@ -819,6 +819,7 @@ void main() {
TestSemantics.rootChild(
actions: <SemanticsAction>[
SemanticsAction.tap,
SemanticsAction.focus,
],
label: 'ABC',
rect: const Rect.fromLTRB(0.0, 0.0, 88.0, 48.0),

View File

@ -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: <Matcher>[
matchesSemantics(
hasTapAction: true,
hasFocusAction: true,
hasEnabledState: true,
isEnabled: true,
isFocusable: true,
@ -277,6 +280,7 @@ void main() {
children: <Matcher>[
matchesSemantics(
hasTapAction: true,
hasFocusAction: true,
hasEnabledState: true,
isEnabled: true,
isFocusable: true,

View File

@ -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();

View File

@ -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,

View File

@ -1004,6 +1004,7 @@ void main() {
TestSemantics.rootChild(
actions: <SemanticsAction>[
SemanticsAction.tap,
SemanticsAction.focus,
],
label: 'ABC',
rect: const Rect.fromLTRB(0.0, 0.0, 88.0, 48.0),

View File

@ -631,6 +631,7 @@ void main() {
],
actions: <SemanticsAction>[
SemanticsAction.tap,
SemanticsAction.focus,
],
),
],
@ -699,6 +700,7 @@ void main() {
tooltip: 'Add Photo',
actions: <SemanticsAction>[
SemanticsAction.tap,
SemanticsAction.focus,
],
flags: <SemanticsFlag>[
SemanticsFlag.hasEnabledState,
@ -922,6 +924,7 @@ void main() {
tester.getSemantics(find.byType(FloatingActionButton)),
matchesSemantics(
hasTapAction: true,
hasFocusAction: true,
hasEnabledState: true,
isButton: true,
isEnabled: true,

View File

@ -616,6 +616,7 @@ void main() {
rect: const Rect.fromLTRB(0.0, 0.0, 48.0, 48.0),
actions: <SemanticsAction>[
SemanticsAction.tap,
SemanticsAction.focus,
],
flags: <SemanticsFlag>[
SemanticsFlag.hasEnabledState,
@ -690,6 +691,7 @@ void main() {
TestSemantics(
actions: <SemanticsAction>[
SemanticsAction.tap,
SemanticsAction.focus,
],
flags: <SemanticsFlag>[
SemanticsFlag.hasEnabledState,
@ -2177,6 +2179,7 @@ void main() {
TestSemantics.rootChild(
actions: <SemanticsAction>[
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),

View File

@ -1178,7 +1178,7 @@ testWidgets('InkResponse radius can be updated', (WidgetTester tester) async {
),
),
));
expect(semantics, includesNodeWith(label: 'Button', actions: <SemanticsAction>[SemanticsAction.tap]));
expect(semantics, includesNodeWith(label: 'Button', actions: <SemanticsAction>[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>[SemanticsAction.tap])));
expect(semantics, isNot(includesNodeWith(label: 'Button', actions: <SemanticsAction>[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,

View File

@ -308,7 +308,7 @@ void main() {
SemanticsFlag.isEnabled,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
label: 'two',
),
TestSemantics.rootChild(

View File

@ -620,6 +620,7 @@ void main() {
label: 'Button',
actions: <SemanticsAction>[
SemanticsAction.tap,
SemanticsAction.focus,
],
flags: <SemanticsFlag>[
SemanticsFlag.hasEnabledState,
@ -661,6 +662,7 @@ void main() {
SemanticsFlag.isButton,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[SemanticsAction.focus],
),
],
),

View File

@ -3421,6 +3421,7 @@ void main() {
TestSemantics.rootChild(
actions: <SemanticsAction>[
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>[SemanticsAction.tap],
actions: <SemanticsAction>[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>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
),
],
),
@ -3621,7 +3622,7 @@ void main() {
SemanticsFlag.isEnabled,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
label: 'ABC',
rect: const Rect.fromLTRB(0.0, 0.0, 88.0, 48.0),
),

View File

@ -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,
),
);
});

View File

@ -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,
),
);
});

View File

@ -5513,25 +5513,25 @@ TestSemantics _expectedSemantics() {
SemanticsFlag.isSelected,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
label: 'Abc\nTab 1 of 4',
textDirection: TextDirection.ltr,
),
TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.isFocusable],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
label: 'Def\nTab 2 of 4',
textDirection: TextDirection.ltr,
),
TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.isFocusable],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
label: 'Ghi\nTab 3 of 4',
textDirection: TextDirection.ltr,
),
TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.isFocusable],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
label: 'Jkl\nTab 4 of 4',
textDirection: TextDirection.ltr,
),

View File

@ -1073,6 +1073,7 @@ void main() {
TestSemantics.rootChild(
actions: <SemanticsAction>[
SemanticsAction.tap,
SemanticsAction.focus,
],
label: 'ABC',
rect: const Rect.fromLTRB(0.0, 0.0, 88.0, 48.0),

View File

@ -1219,7 +1219,7 @@ void main() {
SemanticsFlag.isEnabled,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
label: '1',
textDirection: TextDirection.ltr,
),
@ -1230,7 +1230,7 @@ void main() {
SemanticsFlag.isEnabled,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
label: '2',
textDirection: TextDirection.ltr,
),
@ -1241,7 +1241,7 @@ void main() {
SemanticsFlag.isEnabled,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
label: '3',
textDirection: TextDirection.ltr,
),
@ -1252,7 +1252,7 @@ void main() {
SemanticsFlag.isEnabled,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
label: '4',
textDirection: TextDirection.ltr,
),
@ -1263,7 +1263,7 @@ void main() {
SemanticsFlag.isEnabled,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
label: '5',
textDirection: TextDirection.ltr,
),
@ -1351,7 +1351,7 @@ void main() {
SemanticsFlag.isEnabled,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
label: 'test1\ntest2',
textDirection: TextDirection.ltr,
),
@ -1432,7 +1432,7 @@ void main() {
SemanticsFlag.isEnabled,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
label: '1',
textDirection: TextDirection.ltr,
),
@ -1452,7 +1452,7 @@ void main() {
SemanticsFlag.isEnabled,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
label: '3',
textDirection: TextDirection.ltr,
),
@ -1463,7 +1463,7 @@ void main() {
SemanticsFlag.isEnabled,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
label: '4',
textDirection: TextDirection.ltr,
),
@ -1474,7 +1474,7 @@ void main() {
SemanticsFlag.isEnabled,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
label: '5',
textDirection: TextDirection.ltr,
),

View File

@ -411,7 +411,7 @@ void main() {
SemanticsFlag.isInMutuallyExclusiveGroup,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
label: 'Title',
textDirection: TextDirection.ltr,
),
@ -448,7 +448,7 @@ void main() {
SemanticsFlag.isInMutuallyExclusiveGroup,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
label: 'Title',
textDirection: TextDirection.ltr,
),
@ -483,6 +483,7 @@ void main() {
SemanticsFlag.isInMutuallyExclusiveGroup,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[SemanticsAction.focus],
label: 'Title',
textDirection: TextDirection.ltr,
),

View File

@ -221,6 +221,7 @@ void main() {
],
actions: <SemanticsAction>[
SemanticsAction.tap,
SemanticsAction.focus,
],
),
);
@ -254,6 +255,7 @@ void main() {
],
actions: <SemanticsAction>[
SemanticsAction.tap,
SemanticsAction.focus,
],
),
],
@ -284,6 +286,7 @@ void main() {
],
actions: <SemanticsAction>[
SemanticsAction.tap,
SemanticsAction.focus,
],
),
],
@ -310,6 +313,7 @@ void main() {
SemanticsFlag.isInMutuallyExclusiveGroup,
SemanticsFlag.isFocusable, // This flag is delayed by 1 frame.
],
actions: <SemanticsAction>[SemanticsAction.focus],
),
],
), ignoreRect: true, ignoreTransform: true));

View File

@ -160,6 +160,7 @@ void main() {
],
actions: <SemanticsAction>[
SemanticsAction.tap,
SemanticsAction.focus,
],
label: '+',
textDirection: TextDirection.ltr,

View File

@ -751,6 +751,7 @@ void main() {
hasEnabledState: true,
label: 'Switch tile',
hasTapAction: true,
hasFocusAction: true,
));
handle.dispose();
});

View File

@ -662,7 +662,7 @@ void main() {
SemanticsFlag.isEnabled,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
tooltip: 'Back',
textDirection: TextDirection.ltr,
),
@ -718,7 +718,7 @@ void main() {
SemanticsFlag.isEnabled,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
label: 'Suggestions',
textDirection: TextDirection.ltr,
),
@ -812,7 +812,7 @@ void main() {
SemanticsFlag.isEnabled,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
tooltip: 'Back',
textDirection: TextDirection.ltr,
),
@ -856,7 +856,7 @@ void main() {
SemanticsFlag.isEnabled,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
label: 'Suggestions',
textDirection: TextDirection.ltr,
),

View File

@ -449,6 +449,7 @@ void main() {
label: '1',
actions: <SemanticsAction>[
SemanticsAction.tap,
SemanticsAction.focus,
],
),
@ -466,6 +467,7 @@ void main() {
label: '2',
actions: <SemanticsAction>[
SemanticsAction.tap,
SemanticsAction.focus,
],
),
@ -529,6 +531,7 @@ void main() {
label: '1',
actions: <SemanticsAction>[
SemanticsAction.tap,
SemanticsAction.focus,
],
),
@ -544,6 +547,7 @@ void main() {
label: '2',
actions: <SemanticsAction>[
SemanticsAction.tap,
SemanticsAction.focus,
],
),

View File

@ -1292,6 +1292,7 @@ void main() {
SemanticsFlag.isSlider,
],
actions: <SemanticsAction>[
SemanticsAction.focus,
SemanticsAction.increase,
SemanticsAction.decrease,
],
@ -1350,6 +1351,7 @@ void main() {
SemanticsFlag.isFocusable,
SemanticsFlag.isSlider,
],
actions: <SemanticsAction>[SemanticsAction.focus],
value: '50%',
increasedValue: '55%',
decreasedValue: '45%',
@ -1452,7 +1454,7 @@ void main() {
TestSemantics(
id: 4,
flags: <SemanticsFlag>[SemanticsFlag.hasEnabledState, SemanticsFlag.isEnabled, SemanticsFlag.isFocusable, SemanticsFlag.isSlider],
actions: <SemanticsAction>[SemanticsAction.increase, SemanticsAction.decrease],
actions: <SemanticsAction>[SemanticsAction.focus, SemanticsAction.increase, SemanticsAction.decrease],
value: '50%',
increasedValue: '60%',
decreasedValue: '40%',
@ -1565,6 +1567,7 @@ void main() {
SemanticsFlag.isSlider,
],
actions: <SemanticsAction>[
SemanticsAction.focus,
SemanticsAction.increase,
SemanticsAction.decrease,
SemanticsAction.didGainAccessibilityFocus,
@ -1625,6 +1628,7 @@ void main() {
SemanticsFlag.isSlider,
],
actions: <SemanticsAction>[
SemanticsAction.focus,
SemanticsAction.didGainAccessibilityFocus,
],
value: '50%',
@ -1729,7 +1733,7 @@ void main() {
TestSemantics(
id: 4,
flags: <SemanticsFlag>[SemanticsFlag.hasEnabledState, SemanticsFlag.isEnabled, SemanticsFlag.isFocusable, SemanticsFlag.isSlider],
actions: <SemanticsAction>[SemanticsAction.increase, SemanticsAction.decrease],
actions: <SemanticsAction>[SemanticsAction.focus, SemanticsAction.increase, SemanticsAction.decrease],
value: '40',
increasedValue: '60',
decreasedValue: '20',
@ -1789,7 +1793,7 @@ void main() {
TestSemantics(
id: 4,
flags: <SemanticsFlag>[SemanticsFlag.hasEnabledState, SemanticsFlag.isEnabled, SemanticsFlag.isFocusable, SemanticsFlag.isSlider],
actions: <SemanticsAction>[SemanticsAction.increase, SemanticsAction.decrease],
actions: <SemanticsAction>[SemanticsAction.focus, SemanticsAction.increase, SemanticsAction.decrease],
value: '40',
increasedValue: '60',
decreasedValue: '20',
@ -2633,6 +2637,7 @@ void main() {
SemanticsFlag.isSlider,
],
actions: <SemanticsAction>[
SemanticsAction.focus,
SemanticsAction.increase,
SemanticsAction.decrease,
SemanticsAction.didGainAccessibilityFocus,

View File

@ -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',
),
],

View File

@ -3592,7 +3592,7 @@ void main() {
children: <TestSemantics>[
TestSemantics(
id: 4,
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
flags: <SemanticsFlag>[
SemanticsFlag.isSelected,
SemanticsFlag.isFocusable,
@ -3604,7 +3604,7 @@ void main() {
TestSemantics(
id: 5,
flags: <SemanticsFlag>[SemanticsFlag.isFocusable],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[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>[SemanticsAction.tap],
actions: <SemanticsAction>[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>[SemanticsFlag.isFocusable],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[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>[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>[SemanticsFlag.isFocusable],
id: 3,
rect: TestSemantics.fullScreen,
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
),
TestSemantics(
id: 4,

View File

@ -607,6 +607,7 @@ void main() {
TestSemantics.rootChild(
actions: <SemanticsAction>[
SemanticsAction.tap,
SemanticsAction.focus,
],
label: 'ABC',
rect: const Rect.fromLTRB(0.0, 0.0, 88.0, 48.0),

View File

@ -6964,7 +6964,7 @@ void main() {
),
);
expect(semantics, isNot(includesNodeWith(actions: <SemanticsAction>[SemanticsAction.tap])));
expect(semantics, isNot(includesNodeWith(actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus])));
semantics.dispose();
});
@ -7005,7 +7005,7 @@ void main() {
),
);
expect(semantics, isNot(includesNodeWith(actions: <SemanticsAction>[SemanticsAction.tap])));
expect(semantics, isNot(includesNodeWith(actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus])));
semantics.dispose();
});

View File

@ -1250,7 +1250,7 @@ void main() {
semantics,
includesNodeWith(
label: amString,
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
flags: <SemanticsFlag>[
SemanticsFlag.isButton,
SemanticsFlag.isChecked,
@ -1264,7 +1264,7 @@ void main() {
semantics,
includesNodeWith(
label: pmString,
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
flags: <SemanticsFlag>[
SemanticsFlag.isButton,
SemanticsFlag.isInMutuallyExclusiveGroup,

View File

@ -2117,6 +2117,7 @@ void main() {
],
actions: <SemanticsAction>[
SemanticsAction.tap,
SemanticsAction.focus,
],
rect: const Rect.fromLTRB(0.0, 0.0, 87.0, 48.0),
),
@ -2130,6 +2131,7 @@ void main() {
],
actions: <SemanticsAction>[
SemanticsAction.tap,
SemanticsAction.focus,
],
rect: const Rect.fromLTRB(0.0, 0.0, 88.0, 48.0)
),
@ -2143,6 +2145,7 @@ void main() {
],
actions: <SemanticsAction>[
SemanticsAction.tap,
SemanticsAction.focus,
],
rect: const Rect.fromLTRB(0.0, 0.0, 88.0, 48.0),
),
@ -2188,6 +2191,7 @@ void main() {
],
actions: <SemanticsAction>[
SemanticsAction.tap,
SemanticsAction.focus,
],
),
TestSemantics(
@ -2201,6 +2205,7 @@ void main() {
],
actions: <SemanticsAction>[
SemanticsAction.tap,
SemanticsAction.focus,
],
),
],

View File

@ -531,6 +531,7 @@ void main() {
flags: <SemanticsFlag>[SemanticsFlag.isFocusable],
label: 'Signed in\nname\nemail',
textDirection: TextDirection.ltr,
actions: <SemanticsAction>[SemanticsAction.focus],
children: <TestSemantics>[
TestSemantics(
label: r'B',

View File

@ -48,6 +48,7 @@ void main() {
matchesSemantics(
label: 'button',
hasTapAction: true,
hasFocusAction: true,
isButton: true,
isFocusable: true,
hasEnabledState: true,

View File

@ -1013,9 +1013,11 @@ void main() {
// This semantic is from `Focus` widget under `FocusableActionDetector`.
matchesSemantics(
isFocusable: true,
hasFocusAction: true,
children: <Matcher>[
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: <Matcher>[
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,

View File

@ -858,6 +858,7 @@ void main() {
matchesSemantics(
label: 'button',
hasTapAction: true,
hasFocusAction: true,
isButton: true,
isFocusable: true,
hasEnabledState: true,

View File

@ -348,7 +348,7 @@ void main() {
scaffoldKey.currentState!.openDrawer();
await tester.pump(const Duration(milliseconds: 100));
expect(semantics, isNot(includesNodeWith(actions: <SemanticsAction>[SemanticsAction.tap])));
expect(semantics, isNot(includesNodeWith(actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus])));
expect(semantics, isNot(includesNodeWith(label: 'Dismiss')));
semantics.dispose();

View File

@ -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>[
TestSemantics(
id: 1,
flags: <SemanticsFlag>[SemanticsFlag.isFocusable],
actions: <SemanticsAction>[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', () {

View File

@ -3182,6 +3182,9 @@ void main() {
flags: <SemanticsFlag>[
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[
SemanticsAction.focus,
],
),
],
);

View File

@ -333,7 +333,7 @@ void main() {
);
expect(semantics, isNot(includesNodeWith(
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
)));
semantics.dispose();

View File

@ -260,7 +260,7 @@ void main() {
SemanticsFlag.isEnabled,
SemanticsFlag.isFocusable
],
actions: <SemanticsAction>[SemanticsAction.tap],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
label: 'Button',
textDirection: TextDirection.ltr,
),

View File

@ -269,7 +269,10 @@ class TestSemantics {
final int actionsBitmask = actions is int
? actions as int
: (actions as List<SemanticsAction>).fold<int>(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<int>(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<SemanticsAction> nonFocusActions = actions.where(
(SemanticsAction action) => action != SemanticsAction.focus
).toList();
final int expectedActions = nonFocusActions.fold<int>(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<SemanticsAction> 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<SemanticsAction>;
list = (actions as List<SemanticsAction>).where((SemanticsAction action) => action != SemanticsAction.focus);
}
return '<SemanticsAction>[${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"',

View File

@ -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 = <SemanticsAction, bool>{
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<SemanticsFlag, bool> 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<SemanticsAction> 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<ui.SemanticsAction, bool> nonFocusActions =
Map<ui.SemanticsAction, bool>.fromEntries(actions.entries.where(
(MapEntry<ui.SemanticsAction, bool> e) => e.key != SemanticsAction.focus
));
if (nonFocusActions.isNotEmpty) {
final List<SemanticsAction> expectedActions = nonFocusActions.entries
.where((MapEntry<ui.SemanticsAction, bool> e) => e.value)
.map((MapEntry<ui.SemanticsAction, bool> e) => e.key)
.toList();
final List<SemanticsAction> notExpectedActions = actions.entries
final List<SemanticsAction> notExpectedActions = nonFocusActions.entries
.where((MapEntry<ui.SemanticsAction, bool> e) => !e.value)
.map((MapEntry<ui.SemanticsAction, bool> 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<ui.SemanticsAction, bool> nonFocusActions =
Map<ui.SemanticsAction, bool>.fromEntries(actions.entries.where(
(MapEntry<ui.SemanticsAction, bool> e) => e.key != SemanticsAction.focus
));
if (nonFocusActions.isNotEmpty) {
final List<SemanticsAction> unexpectedActions = <SemanticsAction>[];
final List<SemanticsAction> missingActions = <SemanticsAction>[];
for (final MapEntry<ui.SemanticsAction, bool> actionEntry in actions.entries) {
for (final MapEntry<ui.SemanticsAction, bool> actionEntry in nonFocusActions.entries) {
final ui.SemanticsAction action = actionEntry.key;
final bool actionExpected = actionEntry.value;
final bool actionPresent = (action.index & data.actions) == action.index;

View File

@ -747,6 +747,7 @@ void main() {
hasDidGainAccessibilityFocusAction: true,
hasDidLoseAccessibilityFocusAction: true,
hasDismissAction: true,
hasFocusAction: true,
customActions: <CustomSemanticsAction>[action],
));
});
@ -1035,6 +1036,7 @@ void main() {
hasDidGainAccessibilityFocusAction: true,
hasDidLoseAccessibilityFocusAction: true,
hasDismissAction: true,
hasFocusAction: true,
customActions: <CustomSemanticsAction>[action],
),
);
@ -1128,6 +1130,7 @@ void main() {
hasDidGainAccessibilityFocusAction: false,
hasDidLoseAccessibilityFocusAction: false,
hasDismissAction: false,
hasFocusAction: false,
),
);
});