Closed erayerdin closed 1 year ago
In this cases the better way to avoid external errors, is build minimal example to reproduce the issue.
With this example, I can't reproduce the error because doesn't has the Bloc dependencies, like FormCubit
,
Another thing that I migth need for test, is the password or some similar string
Here's the simplest app that fails.
It's a web app.
https://user-images.githubusercontent.com/2399084/204495594-03346b09-4884-4701-85f8-c3e8e4e28fca.mp4
As you can see, even though I put foo@bar.b
and it is valid since there are no error texts, the validity text does not change.
The validity text, however, changes when I put foo@bar.ba
. It seems like FormBuilderState.isValid
is coming from a step behind.
The strange thing is, a plain function validator works as I expected, see this:
FormBuilderTextField(
name: 'email',
decoration: const InputDecoration(
labelText: 'your@email.here',
border: OutlineInputBorder(),
),
// instead of FormBuilderValidators.email, a plain function is here
validator: (value) {
// if the input is empty
if (value == null) {
return 'Field is required';
}
// if the input is not empty
// store these conditions in a list of bools
final conditions = [
value.contains('@'), // input has `@`
value.contains('.'), // input has a dot
!value.startsWith('@'), // input does not start with `@`
!value.startsWith('.'), // input does not start with a dot
!value.endsWith('@'), // input does not end with `@`
!value.endsWith('.'), // input does not end with a dot
];
// if any one of these conditions are false
if (!conditions.every((element) => element)) {
return 'Email is invalid.';
}
// if all conditions are true
return null;
},
),
So, this might be a bug in form_builder_validators
?
Not only FormBuilderValidators.email
, but FormBuilderValidators.minLength
fails to this as well.
I confirmed this email regex pattern has no problem. Also considering FormBuilderValidators.minLength
fails, I think this is not a problem originating from form_builder_validators
.
Also, FormBuilder.onChanged
has no problem because it works with plain function validators. So, it also has no probsies with it.
This rabbit hole goes deeper baby.
For now, I will stick to plain function validators in my app until we can figure this out.
Should try run minLength validators tests with your password. I'm not sure where is your problem, but try apply your validator on example app, only with minimal widgets, no bloc or other things
Sure thing, I can even provide the code directly. This uses StatefulWidget
instead of Bloc.
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:form_builder_validators/form_builder_validators.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: FormPage(),
);
}
}
class FormPage extends StatefulWidget {
final _formKey = GlobalKey<FormBuilderState>();
FormPage({super.key});
@override
State<FormPage> createState() => _FormPageState();
}
class _FormPageState extends State<FormPage> {
bool? isValid;
@override
Widget build(BuildContext context) {
return FormBuilder(
key: widget._formKey,
autovalidateMode: AutovalidateMode.always,
onChanged: () {
// rebuild when form changes
isValid = widget._formKey.currentState?.isValid;
setState(() {});
},
child: Scaffold(
body: Padding(
padding: const EdgeInsets.all(16),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
// UI BELOW //
children: [
FormBuilderTextField(
name: 'email',
decoration: const InputDecoration(
border: OutlineInputBorder(),
),
validator: FormBuilderValidators.compose([
FormBuilderValidators.required(),
FormBuilderValidators.email(),
]),
),
const SizedBox(height: 16),
Text(
isValid == null
? 'initial state'
: isValid!
? 'valid'
: 'invalid',
),
// UI ABOVE //
],
),
),
),
),
);
}
}
If you input foo@bar.b
as input, the value for _FormPageState.isValid
will be false even though there's clearly no error or error text in the field.
However, the second you put a new character, let's say foo@bar.ba
, _FormPageState.isValid
will be true.
The value of _FormPageState.isValid
comes from FormBuilder.onChanged
, which gets the value from FormBuilderState.isValid
.
We need solve if this issue is from validator or from logic of isValid
property. After that, can investigate what is the issue with this internal code
Hello, @erayerdin I encountered the same issue in my example and the only workaround I found for this issue is that you should wrap _formKey.currentState?.isValid
call within WidgetsBinding.instance.addPostFrameCallback((_) {})
. Then everything works as expected and you get isValid
true immediately after the field becomes valid.
So the onChanged callback in your FormBuilder should look like this:
onChanged: () {
// rebuild when form changes
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
isValid = widget._formKey.currentState?.isValid;
setState(() {});
});
},
You're a lifesaver @icelija. Unfortunately, I'm not dealing with this thing right now, let's wait for someone else to validate it works for them.
Other than that, the main problem I think is flutter_form_builder
using the GlobalKey
. It initializes currentState
when the widget is mounted. So currentState
is not initialized when GlobalKey
initialized.
I can get why it is the way it is though. When you develop a third-party package, it's better to depend on the standard library rather than another third-party one. The way it works also has caused me headaches in #1162.
I'm working on this bug on branch improve-autovalidate-mode
Can test this on pre-release 9.0.0-dev.1
Environment
Package version: 7.7.0
Flutter doctor
``` [✓] Flutter (Channel stable, 3.3.9, on Fedora Linux 36 (KDE Plasma) 6.0.9-200.fc36.x86_64, locale en_US.UTF-8) • Flutter version 3.3.9 on channel stable at /home/erayerdin/.local/lib/flutter • Upstream repository https://github.com/flutter/flutter.git • Framework revision b8f7f1f986 (6 days ago), 2022-11-23 06:43:51 +0900 • Engine revision 8f2221fbef • Dart version 2.18.5 • DevTools version 2.15.0 [✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0) • Android SDK at /home/erayerdin/.sdks/android/ • Platform android-33, build-tools 33.0.0 • ANDROID_SDK_ROOT = /home/erayerdin/.sdks/android • Java binary at: /var/lib/snapd/snap/android-studio/125/android-studio/jre/bin/java • Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866) • All Android licenses accepted. [✓] Chrome - develop for the web • Chrome at google-chrome [✓] Linux toolchain - develop for Linux desktop • clang version 14.0.5 (Fedora 14.0.5-1.fc36) • cmake version 3.24.2 • ninja version 1.10.2 • pkg-config version 1.8.0 [✓] Android Studio (version 2021.3) • Android Studio at /var/lib/snapd/snap/android-studio/125/android-studio • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart • Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866) [✓] VS Code • VS Code at /snap/code/current • Flutter extension version 3.52.0 [✓] Connected device (2 available) • Linux (desktop) • linux • linux-x64 • Fedora Linux 36 (KDE Plasma) 6.0.9-200.fc36.x86_64 • Chrome (web) • chrome • web-javascript • Google Chrome 107.0.5304.121 [✓] HTTP Host Availability • All required HTTP hosts are available • No issues found! ```Code sample
```dart import 'package:flutter/material.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: FormPage(), ); } } class FormPage extends StatefulWidget { final _formKey = GlobalKeyDescription
Expected behavior:
_formKey.currentState.isValid
should return true when all the fields in the form are valid.Current behavior: It does not return true.
Steps to reproduce
_formKey.currentState.isValid
is false.Images
Stacktrace/Logcat