From 79caa8373c42383b7ee3f2bd1b2b6d2070ffafc2 Mon Sep 17 00:00:00 2001 From: Edgar Jan Date: Mon, 25 Sep 2023 22:17:07 +0300 Subject: [PATCH] Fix and Test Conditional Validator Behavior in FormField (#132714) In the FormField widget, if a validator is initially set (and validation fails), then subsequently the validator is set to null, the form incorrectly retains its error state. This is not expected behavior as removing the validator should clear any validation errors. --- packages/flutter/lib/src/widgets/form.dart | 2 + packages/flutter/test/widgets/form_test.dart | 67 ++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/packages/flutter/lib/src/widgets/form.dart b/packages/flutter/lib/src/widgets/form.dart index 3989da3ea429..cb8b5d9c6e8b 100644 --- a/packages/flutter/lib/src/widgets/form.dart +++ b/packages/flutter/lib/src/widgets/form.dart @@ -524,6 +524,8 @@ class FormFieldState extends State> with RestorationMixin { void _validate() { if (widget.validator != null) { _errorText.value = widget.validator!(_value); + } else { + _errorText.value = null; } } diff --git a/packages/flutter/test/widgets/form_test.dart b/packages/flutter/test/widgets/form_test.dart index 90f5fe727bf3..6d2996c72364 100644 --- a/packages/flutter/test/widgets/form_test.dart +++ b/packages/flutter/test/widgets/form_test.dart @@ -945,4 +945,71 @@ void main() { fieldKey.currentState!.reset(); expect(fieldKey.currentState!.hasInteractedByUser, isFalse); }); + + testWidgets('Validator is nullified and error text behaves accordingly', + (WidgetTester tester) async { + final GlobalKey formKey = GlobalKey(); + bool useValidator = false; + late StateSetter setState; + + String? validator(String? value) { + if (value == null || value.isEmpty) { + return 'test_error'; + } + return null; + } + + Widget builder() { + return StatefulBuilder( + builder: (BuildContext context, StateSetter setter) { + setState = setter; + return MaterialApp( + home: MediaQuery( + data: const MediaQueryData(), + child: Directionality( + textDirection: TextDirection.ltr, + child: Center( + child: Material( + child: Form( + key: formKey, + child: TextFormField( + validator: useValidator ? validator : null, + ), + ), + ), + ), + ), + ), + ); + }, + ); + } + + await tester.pumpWidget(builder()); + + // Start with no validator. + await tester.enterText(find.byType(TextFormField), ''); + await tester.pump(); + formKey.currentState!.validate(); + await tester.pump(); + expect(find.text('test_error'), findsNothing); + + // Now use the validator. + setState(() { + useValidator = true; + }); + await tester.pump(); + formKey.currentState!.validate(); + await tester.pump(); + expect(find.text('test_error'), findsOneWidget); + + // Remove the validator again and expect the error to disappear. + setState(() { + useValidator = false; + }); + await tester.pump(); + formKey.currentState!.validate(); + await tester.pump(); + expect(find.text('test_error'), findsNothing); + }); }