diff --git a/packages/flutter/lib/src/widgets/form.dart b/packages/flutter/lib/src/widgets/form.dart index dac0bf9c8d..0fa2ed741b 100644 --- a/packages/flutter/lib/src/widgets/form.dart +++ b/packages/flutter/lib/src/widgets/form.dart @@ -354,9 +354,11 @@ class FormState extends State
{ final bool validateOnFocusChange = widget.autovalidateMode == AutovalidateMode.onUnfocus; for (final FormFieldState field in _fields) { - if (!validateOnFocusChange || !field._focusNode.hasFocus) { + final bool hasFocus = field._focusNode.hasFocus; + + if (!validateOnFocusChange || !hasFocus || (validateOnFocusChange && hasFocus)) { final bool isFieldValid = field.validate(); - hasError = !isFieldValid || hasError; + hasError |= !isFieldValid; // Ensure that only the first error message gets announced to the user. if (errorMessage.isEmpty) { errorMessage = field.errorText ?? ''; diff --git a/packages/flutter/test/widgets/form_test.dart b/packages/flutter/test/widgets/form_test.dart index 1fc97db56f..e460217213 100644 --- a/packages/flutter/test/widgets/form_test.dart +++ b/packages/flutter/test/widgets/form_test.dart @@ -1505,4 +1505,53 @@ void main() { expect(find.text(errorText('foo')), findsOneWidget); expect(find.text(errorText('bar')), findsOneWidget); }); + + testWidgets('AutovalidateMode.onUnfocus should validate all fields manually with FormState', (WidgetTester tester) async { + final GlobalKey formKey = GlobalKey(); + const Key fieldKey = Key('form field'); + String errorText(String? value) => '$value/error'; + + await tester.pumpWidget( + MaterialApp( + home: Center( + child: Form( + key: formKey, + autovalidateMode: AutovalidateMode.onUnfocus, + child: Material( + child: Column( + children: [ + TextFormField( + key: fieldKey, + initialValue: 'foo', + validator: errorText, + ), + TextFormField( + initialValue: 'bar', + validator: errorText, + ), + ], + ), + ), + ), + ), + ), + ); + + + // Focus on the first field. + await tester.tap(find.byKey(fieldKey)); + await tester.pump(); + + // Check no error messages are displayed initially. + expect(find.text('foo/error'), findsNothing); + expect(find.text('bar/error'), findsNothing); + + // Validate all fields manually using FormState. + expect(formKey.currentState!.validate(), isFalse); + await tester.pump(); + + // Check error messages are displayed. + expect(find.text('foo/error'), findsOneWidget); + expect(find.text('bar/error'), findsOneWidget); + }); }