From fdb17ab069cf6318e582f07cc46f12a4f8643bf7 Mon Sep 17 00:00:00 2001 From: hangyu Date: Tue, 27 Feb 2024 14:49:13 -0800 Subject: [PATCH] Reland [a11y] Add isEnabled semantics flag to text field (#143601) Reland #143334 --- .../flutter/lib/src/material/text_field.dart | 1 + .../test/cupertino/text_field_test.dart | 7 +- .../input_date_picker_form_field_test.dart | 2 + .../flutter/test/material/search_test.dart | 4 + .../test/material/text_field_test.dart | 73 +++++++++++++++++-- .../test/material/time_picker_test.dart | 14 +++- .../flutter_test/test/controller_test.dart | 9 ++- 7 files changed, 94 insertions(+), 16 deletions(-) diff --git a/packages/flutter/lib/src/material/text_field.dart b/packages/flutter/lib/src/material/text_field.dart index 7bc78fdb379dae..99822ef0fac789 100644 --- a/packages/flutter/lib/src/material/text_field.dart +++ b/packages/flutter/lib/src/material/text_field.dart @@ -1574,6 +1574,7 @@ class _TextFieldState extends State with RestorationMixin implements animation: controller, // changes the _currentLength builder: (BuildContext context, Widget? child) { return Semantics( + enabled: _isEnabled, maxValueLength: semanticsMaxValueLength, currentValueLength: _currentLength, onTap: widget.readOnly ? null : () { diff --git a/packages/flutter/test/cupertino/text_field_test.dart b/packages/flutter/test/cupertino/text_field_test.dart index 3f2b163fba9a67..6955117509d623 100644 --- a/packages/flutter/test/cupertino/text_field_test.dart +++ b/packages/flutter/test/cupertino/text_field_test.dart @@ -529,8 +529,11 @@ void main() { children: [ TestSemantics( id: 4, - flags: [SemanticsFlag.isTextField, - SemanticsFlag.hasEnabledState, SemanticsFlag.isEnabled,], + flags: [ + SemanticsFlag.isTextField, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, + ], actions: [SemanticsAction.tap, SemanticsAction.didGainAccessibilityFocus,], textDirection: TextDirection.ltr, diff --git a/packages/flutter/test/material/input_date_picker_form_field_test.dart b/packages/flutter/test/material/input_date_picker_form_field_test.dart index 0588bdfc03ed80..14773781409f15 100644 --- a/packages/flutter/test/material/input_date_picker_form_field_test.dart +++ b/packages/flutter/test/material/input_date_picker_form_field_test.dart @@ -279,6 +279,8 @@ void main() { expect(tester.getSemantics(find.byType(EditableText)), matchesSemantics( label: 'Enter Date', isTextField: true, + hasEnabledState: true, + isEnabled: true, isFocused: true, value: '01/15/2016', hasTapAction: true, diff --git a/packages/flutter/test/material/search_test.dart b/packages/flutter/test/material/search_test.dart index 4b4359b769620e..79e856c6cb746a 100644 --- a/packages/flutter/test/material/search_test.dart +++ b/packages/flutter/test/material/search_test.dart @@ -670,6 +670,8 @@ void main() { id: 9, flags: [ SemanticsFlag.isTextField, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, SemanticsFlag.isFocused, SemanticsFlag.isHeader, if (debugDefaultTargetPlatformOverride != TargetPlatform.iOS && @@ -818,6 +820,8 @@ void main() { id: 11, flags: [ SemanticsFlag.isTextField, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, SemanticsFlag.isFocused, SemanticsFlag.isHeader, if (debugDefaultTargetPlatformOverride != TargetPlatform.iOS && diff --git a/packages/flutter/test/material/text_field_test.dart b/packages/flutter/test/material/text_field_test.dart index 5ca5573d3ae297..87c56e649545fd 100644 --- a/packages/flutter/test/material/text_field_test.dart +++ b/packages/flutter/test/material/text_field_test.dart @@ -746,7 +746,7 @@ void main() { children: [ TestSemantics( id: 4, - flags: [SemanticsFlag.isTextField], + flags: [SemanticsFlag.isTextField, SemanticsFlag.hasEnabledState, SemanticsFlag.isEnabled], actions: [ SemanticsAction.tap, SemanticsAction.didGainAccessibilityFocus, @@ -1856,7 +1856,12 @@ void main() { children: [ TestSemantics( id: 1, - flags: [SemanticsFlag.isTextField, SemanticsFlag.isFocused], + flags: [ + SemanticsFlag.isTextField, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, + SemanticsFlag.isFocused, + ], actions: [ SemanticsAction.tap, SemanticsAction.moveCursorBackwardByCharacter, @@ -5189,6 +5194,8 @@ void main() { ], flags: [ SemanticsFlag.isTextField, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, ], ), TestSemantics.rootChild( @@ -6530,7 +6537,7 @@ void main() { ), ); - expect(semantics, includesNodeWith(flags: [SemanticsFlag.isTextField])); + expect(semantics, includesNodeWith(flags: [SemanticsFlag.isTextField, SemanticsFlag.hasEnabledState, SemanticsFlag.isEnabled])); semantics.dispose(); }); @@ -6944,7 +6951,7 @@ void main() { ); expect(semantics, includesNodeWith( - flags: [SemanticsFlag.isTextField], + flags: [SemanticsFlag.isTextField, SemanticsFlag.hasEnabledState, SemanticsFlag.isEnabled], maxValueLength: 10, currentValueLength: 0, )); @@ -6959,7 +6966,12 @@ void main() { await tester.pump(); expect(semantics, includesNodeWith( - flags: [SemanticsFlag.isTextField, SemanticsFlag.isFocused], + flags: [ + SemanticsFlag.isTextField, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, + SemanticsFlag.isFocused, + ], maxValueLength: 10, currentValueLength: 3, )); @@ -6985,7 +6997,12 @@ void main() { expect( semantics, - includesNodeWith(flags: [SemanticsFlag.isTextField, SemanticsFlag.isReadOnly]), + includesNodeWith(flags: [ + SemanticsFlag.isTextField, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, + SemanticsFlag.isReadOnly, + ]), ); semantics.dispose(); @@ -8011,6 +8028,8 @@ void main() { ], flags: [ SemanticsFlag.isTextField, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, ], ), ], @@ -8030,6 +8049,8 @@ void main() { ], flags: [ SemanticsFlag.isTextField, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, ], ), ], @@ -8055,6 +8076,8 @@ void main() { ], flags: [ SemanticsFlag.isTextField, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, SemanticsFlag.isFocused, ], ), @@ -8083,6 +8106,8 @@ void main() { ], flags: [ SemanticsFlag.isTextField, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, SemanticsFlag.isFocused, ], ), @@ -8110,6 +8135,8 @@ void main() { ], flags: [ SemanticsFlag.isTextField, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, SemanticsFlag.isFocused, ], ), @@ -8135,6 +8162,8 @@ void main() { textDirection: TextDirection.ltr, flags: [ SemanticsFlag.isTextField, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, ], value: 'Hello', ) @@ -8150,6 +8179,8 @@ void main() { textDirection: TextDirection.ltr, flags: [ SemanticsFlag.isTextField, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, SemanticsFlag.isObscured, ], ) @@ -8165,6 +8196,8 @@ void main() { textDirection: TextDirection.ltr, flags: [ SemanticsFlag.isTextField, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, ], value: 'Hello', ) @@ -8207,6 +8240,8 @@ void main() { ], flags: [ SemanticsFlag.isTextField, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, SemanticsFlag.isFocused, ], ), @@ -8242,6 +8277,8 @@ void main() { ], flags: [ SemanticsFlag.isTextField, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, ], ), ], @@ -8268,6 +8305,8 @@ void main() { ], flags: [ SemanticsFlag.isTextField, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, SemanticsFlag.isFocused, ], ), @@ -8298,6 +8337,8 @@ void main() { ], flags: [ SemanticsFlag.isTextField, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, SemanticsFlag.isFocused, ], ), @@ -8347,6 +8388,8 @@ void main() { ], flags: [ SemanticsFlag.isTextField, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, SemanticsFlag.isFocused, ], ), @@ -8395,6 +8438,8 @@ void main() { ], flags: [ SemanticsFlag.isTextField, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, SemanticsFlag.isFocused, ], ), @@ -8431,7 +8476,7 @@ void main() { children: [ TestSemantics( id: inputFieldId, - flags: [SemanticsFlag.isTextField], + flags: [SemanticsFlag.isTextField, SemanticsFlag.hasEnabledState, SemanticsFlag.isEnabled], actions: [SemanticsAction.tap], value: textInTextField, textDirection: TextDirection.ltr, @@ -8451,6 +8496,8 @@ void main() { id: inputFieldId, flags: [ SemanticsFlag.isTextField, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, SemanticsFlag.isFocused, ], actions: [ @@ -8504,7 +8551,7 @@ void main() { children: [ TestSemantics( id: inputFieldId, - flags: [SemanticsFlag.isTextField], + flags: [SemanticsFlag.isTextField, SemanticsFlag.hasEnabledState, SemanticsFlag.isEnabled], actions: [SemanticsAction.tap], value: textInTextField, textDirection: TextDirection.ltr, @@ -8524,6 +8571,8 @@ void main() { id: inputFieldId, flags: [ SemanticsFlag.isTextField, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, SemanticsFlag.isFocused, ], actions: [ @@ -8704,6 +8753,8 @@ void main() { ], flags: [ SemanticsFlag.isTextField, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, ], children: [ TestSemantics( @@ -8739,6 +8790,8 @@ void main() { ], flags: [ SemanticsFlag.isTextField, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, SemanticsFlag.isFocused, ], children: [ @@ -8796,6 +8849,8 @@ void main() { ], flags: [ SemanticsFlag.isTextField, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, ], children: [ TestSemantics( @@ -8843,6 +8898,8 @@ void main() { ], flags: [ SemanticsFlag.isTextField, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, ], children: [ TestSemantics( diff --git a/packages/flutter/test/material/time_picker_test.dart b/packages/flutter/test/material/time_picker_test.dart index 295e3a8d529230..52f2c19ae2e280 100644 --- a/packages/flutter/test/material/time_picker_test.dart +++ b/packages/flutter/test/material/time_picker_test.dart @@ -1343,7 +1343,12 @@ void main() { label: 'Hour', value: '07', actions: [SemanticsAction.tap], - flags: [SemanticsFlag.isTextField, SemanticsFlag.isMultiline], + flags: [ + SemanticsFlag.isTextField, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, + SemanticsFlag.isMultiline, + ], ), ); expect( @@ -1352,7 +1357,12 @@ void main() { label: 'Minute', value: '00', actions: [SemanticsAction.tap], - flags: [SemanticsFlag.isTextField, SemanticsFlag.isMultiline], + flags: [ + SemanticsFlag.isTextField, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, + SemanticsFlag.isMultiline, + ], ), ); diff --git a/packages/flutter_test/test/controller_test.dart b/packages/flutter_test/test/controller_test.dart index afc10dd576f017..8d8b93bb8f50bf 100644 --- a/packages/flutter_test/test/controller_test.dart +++ b/packages/flutter_test/test/controller_test.dart @@ -883,6 +883,7 @@ void main() { group('simulatedTraversal', () { final List fullTraversalMatchers = [ containsSemantics(isHeader: true, label: 'Semantics Test'), + containsSemantics(label: 'Text Field'), containsSemantics(isTextField: true), containsSemantics(label: 'Off Switch'), containsSemantics(hasToggledState: true), @@ -913,7 +914,7 @@ void main() { await tester.pumpWidget(const MaterialApp(home: _SemanticsTestWidget())); // We're expecting the traversal to start where the slider is. - final List expectedMatchers = [...fullTraversalMatchers]..removeRange(0, 8); + final List expectedMatchers = [...fullTraversalMatchers]..removeRange(0, 9); expect( tester.semantics.simulatedAccessibilityTraversal(start: find.byType(Slider)), @@ -988,7 +989,7 @@ void main() { await tester.pumpWidget(const MaterialApp(home: _SemanticsTestWidget())); // We're expecting the traversal to end where the slider is, inclusive. - final Iterable expectedMatchers = [...fullTraversalMatchers].getRange(0, 9); + final Iterable expectedMatchers = [...fullTraversalMatchers].getRange(0, 10); expect( tester.semantics.simulatedAccessibilityTraversal(end: find.byType(Slider)), @@ -1049,7 +1050,7 @@ void main() { await tester.pumpWidget(const MaterialApp(home: _SemanticsTestWidget())); // We're expecting the traversal to start at the text field and end at the slider. - final Iterable expectedMatchers = [...fullTraversalMatchers].getRange(1, 9); + final Iterable expectedMatchers = [...fullTraversalMatchers].getRange(1, 10); expect( tester.semantics.simulatedAccessibilityTraversal( @@ -1777,7 +1778,7 @@ class _SemanticsTestWidget extends StatelessWidget { child: Column( children: [ const _SemanticsTestCard( - label: 'TextField', + label: 'Text Field', widget: TextField(), ), _SemanticsTestCard(