Skip to content

Commit

Permalink
Fix and Test Conditional Validator Behavior in FormField (#132714)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
EdgarJan authored Sep 25, 2023
1 parent fffbbf2 commit 79caa83
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 0 deletions.
2 changes: 2 additions & 0 deletions packages/flutter/lib/src/widgets/form.dart
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,8 @@ class FormFieldState<T> extends State<FormField<T>> with RestorationMixin {
void _validate() {
if (widget.validator != null) {
_errorText.value = widget.validator!(_value);
} else {
_errorText.value = null;
}
}

Expand Down
67 changes: 67 additions & 0 deletions packages/flutter/test/widgets/form_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<FormState> formKey = GlobalKey<FormState>();
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);
});
}

0 comments on commit 79caa83

Please sign in to comment.