Fix: Announce only the first error message for better accessibility (#156399)
## Issue This pull request addresses an accessibility issue where all form error messages were concatenated and announced simultaneously, overwhelming users relying on screen readers like VoiceOver and TalkBack. This is the issue link: https://github.com/flutter/flutter/issues/156340 ## Update The change ensures that only the first error message is announced, providing a more manageable and user-friendly experience. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. --------- Co-authored-by: chunhtai <47866232+chunhtai@users.noreply.github.com>
This commit is contained in:
parent
fdc666158e
commit
d541354936
@ -357,7 +357,10 @@ class FormState extends State<Form> {
|
||||
if (!validateOnFocusChange || !field._focusNode.hasFocus) {
|
||||
final bool isFieldValid = field.validate();
|
||||
hasError = !isFieldValid || hasError;
|
||||
errorMessage += field.errorText ?? '';
|
||||
// Ensure that only the first error message gets announced to the user.
|
||||
if (errorMessage.isEmpty) {
|
||||
errorMessage = field.errorText ?? '';
|
||||
}
|
||||
if (invalidFields != null && !isFieldValid) {
|
||||
invalidFields.add(field);
|
||||
}
|
||||
|
@ -139,44 +139,52 @@ void main() {
|
||||
await checkErrorText('');
|
||||
});
|
||||
|
||||
testWidgets('Should announce error text when validate returns error', (WidgetTester tester) async {
|
||||
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: MediaQuery(
|
||||
data: const MediaQueryData(),
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Center(
|
||||
child: Material(
|
||||
child: Form(
|
||||
key: formKey,
|
||||
child: TextFormField(
|
||||
validator: (_)=> 'error',
|
||||
),
|
||||
testWidgets('Should announce only the first error message when validate returns errors', (WidgetTester tester) async {
|
||||
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: MediaQuery(
|
||||
data: const MediaQueryData(),
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Center(
|
||||
child: Material(
|
||||
child: Form(
|
||||
key: formKey,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
TextFormField(
|
||||
validator: (_) => 'First error message',
|
||||
),
|
||||
TextFormField(
|
||||
validator: (_) => 'Second error message',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
formKey.currentState!.reset();
|
||||
await tester.enterText(find.byType(TextFormField), '');
|
||||
await tester.pump();
|
||||
),
|
||||
);
|
||||
formKey.currentState!.reset();
|
||||
await tester.enterText(find.byType(TextFormField).first, '');
|
||||
await tester.pump();
|
||||
|
||||
// Manually validate.
|
||||
expect(find.text('error'), findsNothing);
|
||||
formKey.currentState!.validate();
|
||||
await tester.pump();
|
||||
expect(find.text('error'), findsOneWidget);
|
||||
// Manually validate.
|
||||
expect(find.text('First error message'), findsNothing);
|
||||
expect(find.text('Second error message'), findsNothing);
|
||||
formKey.currentState!.validate();
|
||||
await tester.pump();
|
||||
expect(find.text('First error message'), findsOneWidget);
|
||||
expect(find.text('Second error message'), findsOneWidget);
|
||||
|
||||
final CapturedAccessibilityAnnouncement announcement = tester.takeAnnouncements().single;
|
||||
expect(announcement.message, 'error');
|
||||
expect(announcement.textDirection, TextDirection.ltr);
|
||||
expect(announcement.assertiveness, Assertiveness.assertive);
|
||||
|
||||
});
|
||||
final CapturedAccessibilityAnnouncement announcement = tester.takeAnnouncements().single;
|
||||
expect(announcement.message, 'First error message');
|
||||
expect(announcement.textDirection, TextDirection.ltr);
|
||||
expect(announcement.assertiveness, Assertiveness.assertive);
|
||||
});
|
||||
|
||||
testWidgets('isValid returns true when a field is valid', (WidgetTester tester) async {
|
||||
final GlobalKey<FormFieldState<String>> fieldKey1 = GlobalKey<FormFieldState<String>>();
|
||||
|
Loading…
x
Reference in New Issue
Block a user