Skip to content

Commit 668da74

Browse files
feat: WIP improve autovalidate mode
1 parent 346520b commit 668da74

File tree

5 files changed

+103
-34
lines changed

5 files changed

+103
-34
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import 'package:flutter/material.dart';
2+
3+
extension AutovalidateModeExtension on AutovalidateMode {
4+
/// Is always or is onUserInteraction
5+
bool get isEnable => isAlways || isOnUserInteraction;
6+
bool get isAlways => this == AutovalidateMode.always;
7+
bool get isOnUserInteraction => this == AutovalidateMode.onUserInteraction;
8+
}

lib/src/form_builder.dart

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'package:flutter/material.dart';
2+
import 'package:flutter_form_builder/src/extensions/autovalidatemode_extension.dart';
23
import 'package:flutter_form_builder/flutter_form_builder.dart';
34

45
/// A container for form fields.
@@ -151,23 +152,16 @@ class FormBuilderState extends State<FormBuilder> {
151152
initialValue[name];
152153
}
153154

154-
void setInternalFieldValue<T>(String name, T? value,
155-
{required bool isSetState}) {
155+
void setInternalFieldValue<T>(String name, T? value) {
156156
_instantValue[name] = value;
157-
if (isSetState) {
158-
setState(() {});
157+
if (widget.autovalidateMode?.isEnable ?? false) {
158+
validate();
159159
}
160160
widget.onChanged?.call();
161161
}
162162

163-
void removeInternalFieldValue(
164-
String name, {
165-
required bool isSetState,
166-
}) {
163+
void removeInternalFieldValue(String name) {
167164
_instantValue.remove(name);
168-
if (isSetState) {
169-
setState(() {});
170-
}
171165
}
172166

173167
void registerField(String name, FormBuilderFieldState field) {

lib/src/form_builder_field.dart

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'package:flutter/material.dart';
22
import 'package:flutter_form_builder/flutter_form_builder.dart';
3+
import 'package:flutter_form_builder/src/extensions/autovalidatemode_extension.dart';
34

45
enum OptionsOrientation { horizontal, vertical, wrap }
56

@@ -51,7 +52,7 @@ class FormBuilderField<T> extends FormField<T> {
5152
super.key,
5253
super.onSaved,
5354
super.initialValue,
54-
super.autovalidateMode = AutovalidateMode.onUserInteraction,
55+
super.autovalidateMode,
5556
super.enabled = true,
5657
super.validator,
5758
super.restorationId,
@@ -106,6 +107,12 @@ class FormBuilderFieldState<F extends FormBuilderField<T>, T>
106107

107108
bool get enabled => widget.enabled && (_formBuilderState?.enabled ?? true);
108109
bool get _readOnly => !(_formBuilderState?.widget.skipDisabled ?? false);
110+
bool get _isEnableValidate =>
111+
widget.autovalidateMode.isEnable ||
112+
(_formBuilderState?.widget.autovalidateMode?.isEnable ?? false);
113+
bool get _isAlwaysValidate =>
114+
widget.autovalidateMode.isAlways ||
115+
(_formBuilderState?.widget.autovalidateMode?.isAlways ?? false);
109116

110117
/// Will be true if the field is dirty
111118
///
@@ -145,10 +152,7 @@ class FormBuilderFieldState<F extends FormBuilderField<T>, T>
145152
focusAttachment = effectiveFocusNode.attach(context);
146153

147154
// Verify if need auto validate form
148-
if ((enabled || _readOnly) &&
149-
(widget.autovalidateMode == AutovalidateMode.always ||
150-
_formBuilderState?.widget.autovalidateMode ==
151-
AutovalidateMode.always)) {
155+
if ((enabled || _readOnly) && _isAlwaysValidate) {
152156
WidgetsBinding.instance.addPostFrameCallback((_) {
153157
validate();
154158
});
@@ -186,17 +190,11 @@ class FormBuilderFieldState<F extends FormBuilderField<T>, T>
186190
if (_formBuilderState != null) {
187191
_dirty = true;
188192
if (enabled || _readOnly) {
189-
_formBuilderState!.setInternalFieldValue<T>(
190-
widget.name,
191-
value,
192-
isSetState: false,
193-
);
194-
} else {
195-
_formBuilderState!.removeInternalFieldValue(
196-
widget.name,
197-
isSetState: false,
198-
);
193+
_formBuilderState!.setInternalFieldValue<T>(widget.name, value);
194+
if (_isEnableValidate) validate();
195+
return;
199196
}
197+
_formBuilderState!.removeInternalFieldValue(widget.name);
200198
}
201199
}
202200

test/src/form_builder_field_test.dart

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,58 @@ void main() {
2424
expect(find.text(errorTextField), findsOneWidget);
2525
});
2626
});
27+
group('isValid -', () {
28+
testWidgets('Should invalid when set custom error', (tester) async {
29+
final textFieldKey = GlobalKey<FormBuilderFieldState>();
30+
const textFieldName = 'text';
31+
const errorTextField = 'error text field';
32+
final testWidget = FormBuilderTextField(
33+
name: textFieldName,
34+
key: textFieldKey,
35+
);
36+
await tester.pumpWidget(buildTestableFieldWidget(testWidget));
37+
38+
// Set custom error
39+
textFieldKey.currentState?.invalidate(errorTextField);
40+
await tester.pumpAndSettle();
41+
42+
expect(textFieldKey.currentState?.isValid, isFalse);
43+
});
44+
});
45+
group('hasErrors -', () {
46+
testWidgets('Should has errors when set custom error', (tester) async {
47+
final textFieldKey = GlobalKey<FormBuilderFieldState>();
48+
const textFieldName = 'text';
49+
const errorTextField = 'error text field';
50+
final testWidget = FormBuilderTextField(
51+
name: textFieldName,
52+
key: textFieldKey,
53+
);
54+
await tester.pumpWidget(buildTestableFieldWidget(testWidget));
55+
56+
// Set custom error
57+
textFieldKey.currentState?.invalidate(errorTextField);
58+
await tester.pumpAndSettle();
59+
60+
expect(textFieldKey.currentState?.hasError, isTrue);
61+
});
62+
testWidgets('Should no has errors when is empty and no has validators',
63+
(tester) async {
64+
final textFieldKey = GlobalKey<FormBuilderFieldState>();
65+
const textFieldName = 'text';
66+
final testWidget = FormBuilderTextField(
67+
name: textFieldName,
68+
key: textFieldKey,
69+
);
70+
await tester.pumpWidget(buildTestableFieldWidget(testWidget));
71+
72+
// Set custom error
73+
textFieldKey.currentState?.validate();
74+
await tester.pumpAndSettle();
75+
76+
expect(textFieldKey.currentState?.hasError, isFalse);
77+
});
78+
});
2779

2880
group('autovalidateMode -', () {
2981
testWidgets(
@@ -33,14 +85,29 @@ void main() {
3385
const errorTextField = 'error text field';
3486
final testWidget = FormBuilderTextField(
3587
name: textFieldName,
36-
validator: (value) => errorTextField,
88+
validator: (value) =>
89+
value == null || value.isEmpty ? errorTextField : null,
90+
autovalidateMode: AutovalidateMode.always,
3791
);
38-
await tester.pumpWidget(
39-
buildTestableFieldWidget(
40-
testWidget,
41-
autovalidateMode: AutovalidateMode.always,
42-
),
92+
await tester.pumpWidget(buildTestableFieldWidget(testWidget));
93+
await tester.pumpAndSettle();
94+
95+
expect(find.text(errorTextField), findsOneWidget);
96+
});
97+
testWidgets(
98+
'Should show error when AutovalidateMode is onUserInteraction and change field',
99+
(tester) async {
100+
const textFieldName = 'text4';
101+
const errorTextField = 'error text field';
102+
final testWidget = FormBuilderTextField(
103+
name: textFieldName,
104+
autovalidateMode: AutovalidateMode.onUserInteraction,
105+
validator: (value) => errorTextField,
43106
);
107+
await tester.pumpWidget(buildTestableFieldWidget(testWidget));
108+
expect(find.text(errorTextField), findsNothing);
109+
110+
await tester.enterText(find.byWidget(testWidget), 'hola');
44111
await tester.pumpAndSettle();
45112

46113
expect(find.text(errorTextField), findsOneWidget);

test/src/form_builder_test.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,9 +177,11 @@ void main() {
177177
final testWidget = FormBuilderTextField(
178178
name: textFieldName,
179179
validator: (value) => errorTextField,
180-
autovalidateMode: AutovalidateMode.always,
181180
);
182-
await tester.pumpWidget(buildTestableFieldWidget(testWidget));
181+
await tester.pumpWidget(buildTestableFieldWidget(
182+
testWidget,
183+
autovalidateMode: AutovalidateMode.always,
184+
));
183185
await tester.pumpAndSettle();
184186

185187
expect(find.text(errorTextField), findsOneWidget);

0 commit comments

Comments
 (0)