Skip to content

Commit 38a24f6

Browse files
Merge pull request #1506 from majumdersubhanu/issue-1484
fix: #1484 autoValidateMode breaks invalidate method
2 parents bff073a + d223f21 commit 38a24f6

10 files changed

Lines changed: 144 additions & 11 deletions

example/pubspec.lock

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ packages:
6060
path: ".."
6161
relative: true
6262
source: path
63-
version: "10.3.0+1"
63+
version: "10.3.0+2"
6464
flutter_lints:
6565
dependency: "direct dev"
6666
description:
@@ -131,10 +131,10 @@ packages:
131131
dependency: transitive
132132
description:
133133
name: matcher
134-
sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6"
134+
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
135135
url: "https://pub.dev"
136136
source: hosted
137-
version: "0.12.18"
137+
version: "0.12.19"
138138
material_color_utilities:
139139
dependency: transitive
140140
description:
@@ -208,10 +208,10 @@ packages:
208208
dependency: transitive
209209
description:
210210
name: test_api
211-
sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636"
211+
sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
212212
url: "https://pub.dev"
213213
source: hosted
214-
version: "0.7.9"
214+
version: "0.7.10"
215215
vector_math:
216216
dependency: transitive
217217
description:

lib/src/fields/form_builder_checkbox.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ class FormBuilderCheckbox extends FormBuilderFieldDecoration<bool> {
107107
super.focusNode,
108108
super.restorationId,
109109
super.errorBuilder,
110+
super.forceErrorText,
110111
required this.title,
111112
this.activeColor,
112113
this.autofocus = false,

lib/src/fields/form_builder_checkbox_group.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class FormBuilderCheckboxGroup<T> extends FormBuilderFieldDecoration<List<T>> {
4646
super.focusNode,
4747
super.restorationId,
4848
super.errorBuilder,
49+
super.forceErrorText,
4950
required this.options,
5051
this.activeColor,
5152
this.checkColor,

lib/src/fields/form_builder_choice_chips.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,7 @@ class FormBuilderChoiceChips<T> extends FormBuilderFieldDecoration<T> {
353353
super.decoration,
354354
super.key,
355355
required super.name,
356+
super.forceErrorText,
356357
required this.options,
357358
super.initialValue,
358359
super.restorationId,

lib/src/fields/form_builder_text_field.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,7 @@ class FormBuilderTextField extends FormBuilderFieldDecoration<String> {
378378
super.onReset,
379379
super.focusNode,
380380
super.restorationId,
381+
super.forceErrorText,
381382
String? initialValue,
382383
super.errorBuilder,
383384
this.readOnly = false,

lib/src/form_builder_field.dart

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ class FormBuilderField<T> extends FormField<T> {
5252
required super.builder,
5353
super.errorBuilder,
5454
super.onReset,
55+
super.forceErrorText,
5556
required this.name,
5657
this.valueTransformer,
5758
this.onChanged,
@@ -210,6 +211,9 @@ class FormBuilderFieldState<F extends FormBuilderField<T>, T>
210211
@override
211212
void didChange(T? value) {
212213
super.didChange(value);
214+
if (_customErrorText != null) {
215+
setState(() => _customErrorText = null);
216+
}
213217
_informFormForFieldChange();
214218
widget.onChanged?.call(value);
215219
}
@@ -229,8 +233,13 @@ class FormBuilderFieldState<F extends FormBuilderField<T>, T>
229233

230234
/// Validate field
231235
///
236+
/// **BREAKING CHANGE**:
237+
/// In previous versions, calling `validate()` would automatically clear any custom errors set via `invalidate()`.
238+
/// Now, `validate()` does not clear custom errors by default.
239+
/// If you want to clear the custom error when validating, you must explicitly pass `clearCustomError: true`.
240+
///
232241
/// Clear custom error if [clearCustomError] is `true`.
233-
/// By default `true`
242+
/// By default `false`
234243
///
235244
/// Focus when field is invalid if [focusOnInvalid] is `true`.
236245
/// By default `true`
@@ -244,7 +253,7 @@ class FormBuilderFieldState<F extends FormBuilderField<T>, T>
244253
/// not because [autoScrollWhenFocusOnInvalid] is `true`.
245254
@override
246255
bool validate({
247-
bool clearCustomError = true,
256+
bool clearCustomError = false,
248257
bool focusOnInvalid = true,
249258
bool autoScrollWhenFocusOnInvalid = false,
250259
}) {

lib/src/form_builder_field_decoration.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class FormBuilderFieldDecoration<T> extends FormBuilderField<T> {
2020
super.onReset,
2121
super.focusNode,
2222
super.errorBuilder,
23+
super.forceErrorText,
2324
required super.builder,
2425
this.decoration = const InputDecoration(),
2526
}) : assert(

pubspec.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,10 @@ packages:
111111
dependency: transitive
112112
description:
113113
name: matcher
114-
sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6"
114+
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
115115
url: "https://pub.dev"
116116
source: hosted
117-
version: "0.12.18"
117+
version: "0.12.19"
118118
material_color_utilities:
119119
dependency: transitive
120120
description:
@@ -188,10 +188,10 @@ packages:
188188
dependency: transitive
189189
description:
190190
name: test_api
191-
sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636"
191+
sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
192192
url: "https://pub.dev"
193193
source: hosted
194-
version: "0.7.9"
194+
version: "0.7.10"
195195
vector_math:
196196
dependency: transitive
197197
description:

test/src/fields/form_builder_text_field_test.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,20 @@ void main() {
6666
expect(Focus.of(tester.element(widgetFinder)).hasFocus, true);
6767
expect(focusNode?.hasFocus, true);
6868
});
69+
testWidgets('forceErrorText should show error', (
70+
WidgetTester tester,
71+
) async {
72+
const errorText = 'Force error message';
73+
const textFieldName = 'text1';
74+
final testWidget = FormBuilderTextField(
75+
name: textFieldName,
76+
forceErrorText: errorText,
77+
);
78+
await tester.pumpWidget(buildTestableFieldWidget(testWidget));
79+
await tester.pumpAndSettle();
80+
81+
expect(find.text(errorText), findsOneWidget);
82+
});
6983
});
7084
}
7185

test/src/form_builder_field_test.dart

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,77 @@ void main() {
2525
await tester.pumpAndSettle();
2626
expect(find.text(errorTextField), findsOneWidget);
2727
});
28+
testWidgets(
29+
'Should persist custom error when autovalidateMode is onUserInteraction',
30+
(tester) async {
31+
final textFieldKey = GlobalKey<FormBuilderFieldState>();
32+
const textFieldName = 'text';
33+
const errorTextField = 'custom error';
34+
final testWidget = FormBuilderTextField(
35+
name: textFieldName,
36+
key: textFieldKey,
37+
autovalidateMode: AutovalidateMode.onUserInteraction,
38+
);
39+
await tester.pumpWidget(buildTestableFieldWidget(testWidget));
40+
41+
// Set custom error
42+
textFieldKey.currentState?.invalidate(errorTextField);
43+
await tester.pumpAndSettle();
44+
expect(find.text(errorTextField), findsOneWidget);
45+
46+
// Simulate framework call (e.g. build again)
47+
tester.binding.scheduleFrame();
48+
await tester.pumpAndSettle();
49+
50+
// Should still be there
51+
expect(find.text(errorTextField), findsOneWidget);
52+
53+
// Should clear when value changes
54+
await tester.enterText(find.byType(TextField), 'test');
55+
await tester.pumpAndSettle();
56+
expect(find.text(errorTextField), findsNothing);
57+
},
58+
);
59+
});
60+
61+
group('transformedValue -', () {
62+
testWidgets('Should return raw value when valueTransformer is null', (
63+
tester,
64+
) async {
65+
final textFieldKey = GlobalKey<FormBuilderFieldState>();
66+
const textFieldName = 'text';
67+
final testWidget = FormBuilderTextField(
68+
name: textFieldName,
69+
key: textFieldKey,
70+
);
71+
await tester.pumpWidget(buildTestableFieldWidget(testWidget));
72+
73+
final widgetFinder = find.byWidget(testWidget);
74+
await tester.enterText(widgetFinder, '123');
75+
await tester.pumpAndSettle();
76+
77+
expect(textFieldKey.currentState?.transformedValue, '123');
78+
});
79+
80+
testWidgets(
81+
'Should return transformed value when valueTransformer is provided',
82+
(tester) async {
83+
final textFieldKey = GlobalKey<FormBuilderFieldState>();
84+
const textFieldName = 'text';
85+
final testWidget = FormBuilderTextField(
86+
name: textFieldName,
87+
key: textFieldKey,
88+
valueTransformer: (text) => int.tryParse(text ?? ''),
89+
);
90+
await tester.pumpWidget(buildTestableFieldWidget(testWidget));
91+
92+
final widgetFinder = find.byWidget(testWidget);
93+
await tester.enterText(widgetFinder, '123');
94+
await tester.pumpAndSettle();
95+
96+
expect(textFieldKey.currentState?.transformedValue, 123);
97+
},
98+
);
2899
});
29100

30101
group('isValid -', () {
@@ -516,5 +587,39 @@ void main() {
516587
},
517588
);
518589
});
590+
group('forceErrorText -', () {
591+
testWidgets('Should show error when forceErrorText is set', (
592+
tester,
593+
) async {
594+
const errorText = 'Force error message';
595+
final testWidget = FormBuilderTextField(
596+
name: 'text',
597+
forceErrorText: errorText,
598+
);
599+
await tester.pumpWidget(buildTestableFieldWidget(testWidget));
600+
await tester.pumpAndSettle();
601+
602+
expect(find.text(errorText), findsOneWidget);
603+
});
604+
605+
testWidgets(
606+
'Should override validator error when forceErrorText is set',
607+
(tester) async {
608+
const forceError = 'Force error';
609+
const validatorError = 'Validator error';
610+
final testWidget = FormBuilderTextField(
611+
name: 'text',
612+
forceErrorText: forceError,
613+
validator: (value) => validatorError,
614+
autovalidateMode: AutovalidateMode.always,
615+
);
616+
await tester.pumpWidget(buildTestableFieldWidget(testWidget));
617+
await tester.pumpAndSettle();
618+
619+
expect(find.text(forceError), findsOneWidget);
620+
expect(find.text(validatorError), findsNothing);
621+
},
622+
);
623+
});
519624
});
520625
}

0 commit comments

Comments
 (0)