From b227df3089b28b8a37fa4e19576950b071ac4638 Mon Sep 17 00:00:00 2001 From: hangyu Date: Mon, 30 Jan 2023 14:45:26 -0800 Subject: [PATCH] Hint text semantics to be excluded in a11y read out if hintText is not visible. (#119198) * Update input_decorator.dart * Update text_field_test.dart * Update time_picker.dart Update time_picker.dart --- .../lib/src/material/input_decorator.dart | 1 - .../flutter/lib/src/material/time_picker.dart | 50 +++++++++---------- .../test/material/text_field_test.dart | 18 +++++-- 3 files changed, 38 insertions(+), 31 deletions(-) diff --git a/packages/flutter/lib/src/material/input_decorator.dart b/packages/flutter/lib/src/material/input_decorator.dart index 5d536eea288..74cf19e131b 100644 --- a/packages/flutter/lib/src/material/input_decorator.dart +++ b/packages/flutter/lib/src/material/input_decorator.dart @@ -2193,7 +2193,6 @@ class _InputDecoratorState extends State with TickerProviderStat opacity: (isEmpty && !_hasInlineLabel) ? 1.0 : 0.0, duration: _kTransitionDuration, curve: _kTransitionCurve, - alwaysIncludeSemantics: isEmpty || (decoration.labelText == null && decoration.label == null), child: Text( hintText, style: hintStyle, diff --git a/packages/flutter/lib/src/material/time_picker.dart b/packages/flutter/lib/src/material/time_picker.dart index 4482ab1379d..d3cdbdfe298 100644 --- a/packages/flutter/lib/src/material/time_picker.dart +++ b/packages/flutter/lib/src/material/time_picker.dart @@ -2050,12 +2050,9 @@ class _HourMinuteTextFieldState extends State<_HourMinuteTextField> with Restora final InputDecorationTheme inputDecorationTheme = timePickerTheme.inputDecorationTheme ?? defaultTheme.inputDecorationTheme; InputDecoration inputDecoration = const InputDecoration().applyDefaults(inputDecorationTheme); - // If screen reader is in use, make the hint text say hours/minutes. - // Otherwise, remove the hint text when focused because the centered cursor + // Remove the hint text when focused because the centered cursor // appears odd above the hint text. - final String? hintText = MediaQuery.accessibleNavigationOf(context) || View.of(context).platformDispatcher.semanticsEnabled - ? widget.semanticHintText - : (focusNode.hasFocus ? null : _formattedValue); + final String? hintText = focusNode.hasFocus ? null : _formattedValue; // Because the fill color is specified in both the inputDecorationTheme and // the TimePickerTheme, if there's one in the user's input decoration theme, @@ -2102,26 +2099,29 @@ class _HourMinuteTextFieldState extends State<_HourMinuteTextField> with Restora data: MediaQuery.of(context).copyWith(textScaleFactor: 1), child: UnmanagedRestorationScope( bucket: bucket, - child: TextFormField( - restorationId: 'hour_minute_text_form_field', - autofocus: widget.autofocus ?? false, - expands: true, - maxLines: null, - inputFormatters: [ - LengthLimitingTextInputFormatter(2), - ], - focusNode: focusNode, - textAlign: TextAlign.center, - textInputAction: widget.inputAction, - keyboardType: TextInputType.number, - style: effectiveStyle, - controller: controller.value, - decoration: inputDecoration, - validator: widget.validator, - onEditingComplete: () => widget.onSavedSubmitted(controller.value.text), - onSaved: widget.onSavedSubmitted, - onFieldSubmitted: widget.onSavedSubmitted, - onChanged: widget.onChanged, + child: Semantics( + label: widget.semanticHintText, + child: TextFormField( + restorationId: 'hour_minute_text_form_field', + autofocus: widget.autofocus ?? false, + expands: true, + maxLines: null, + inputFormatters: [ + LengthLimitingTextInputFormatter(2), + ], + focusNode: focusNode, + textAlign: TextAlign.center, + textInputAction: widget.inputAction, + keyboardType: TextInputType.number, + style: effectiveStyle, + controller: controller.value, + decoration: inputDecoration, + validator: widget.validator, + onEditingComplete: () => widget.onSavedSubmitted(controller.value.text), + onSaved: widget.onSavedSubmitted, + onFieldSubmitted: widget.onSavedSubmitted, + onChanged: widget.onChanged, + ), ), ), ), diff --git a/packages/flutter/test/material/text_field_test.dart b/packages/flutter/test/material/text_field_test.dart index 07d876269f6..1110d2058c9 100644 --- a/packages/flutter/test/material/text_field_test.dart +++ b/packages/flutter/test/material/text_field_test.dart @@ -6862,7 +6862,7 @@ void main() { await tester.tap(find.byKey(key)); await tester.pump(); - expect(node.label, 'label\nhint'); + expect(node.label, 'label'); expect(node.value, ''); semantics.dispose(); }); @@ -6930,7 +6930,7 @@ void main() { semantics.dispose(); }); - testWidgets('TextField semantics always include hint when no label is given', (WidgetTester tester) async { + testWidgets('TextField semantics only include hint when it is visible', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); final TextEditingController controller = TextEditingController(text: 'value'); final Key key = UniqueKey(); @@ -6949,15 +6949,23 @@ void main() { final SemanticsNode node = tester.getSemantics(find.byKey(key)); - expect(node.label, 'hint'); + expect(node.label, ''); expect(node.value, 'value'); // Focus text field. await tester.tap(find.byKey(key)); await tester.pump(); - expect(node.label, 'hint'); + expect(node.label, ''); expect(node.value, 'value'); + + // Clear the Text. + await tester.enterText(find.byType(TextField), ''); + await tester.pumpAndSettle(); + + expect(node.value, ''); + expect(node.label, 'hint'); + semantics.dispose(); }); @@ -7698,7 +7706,7 @@ void main() { expect(semantics, hasSemantics(TestSemantics.root( children: [ TestSemantics.rootChild( - label: 'label\nhint', + label: 'label', id: 1, textDirection: TextDirection.ltr, textSelection: const TextSelection(baseOffset: 0, extentOffset: 0),