Boilerplate-free form validation library.
- Minimalistic.
- Perfectly fits flutter's form handling system.
- Your teammates just need to explore a couple of simple concepts.
Use case: We want to validate that email doesn't exist in our database. If it does, display error.
class SignUpFormState extends ChangeNotifier with ValidationMixin {
FieldBoolTrigger _email;
SignUpFormState([
this._emailAlreadyExistsTg = const FieldBoolTrigger.disabled()
]);
String? validateEmail(String? email) {
return validate(email)
.isValidatedByBool(_emailAlreadyExistsTg)
.isEmail()();
}
Future<void> submit() {
await Future.delayed(Duration(seconds: 1)); // access the database
// suppose, that we checked email presence, and there is user with this email already
_emailAlreadyExistsTg = FieldBoolTrigger();
notifyListeners();
}
}
Somewhere in Flutter code:
final signUpFormState = SignUpFormState();
void initState() {
super.initState();
signUpFormState.addListener(() => setState(() {}));
}
Widget build(BuildContext context) => Column(
children: [
TextFormField(
validator: signUpFormState.validateEmail,
),
OutlinedButton(onPressed: signUpFormState.submit),
]
);
Trigger ships several different types of triggers. The most used ones - FieldTrigger and FieldBoolTrigger
When trigger.isDisabled == true
, it always validates value it receives.
Example:
class State with ValidationMixin {
FieldBoolTrigger emailAlreadyExists = const FieldBoolTrigger.disabled();
String? validateEmail(String? email) {
return validate(email) // comes from ValidationMixin
.isValidatedByBool(emailAlreadyExists, error: "Already exists")()
}
}
final state = State();
state.validateEmail("example@mail.com")
>>> null // always, because emailAlreadyExists is disabled
If we reassign emailAlreadyExists somewhere like this
emailAlreadyExists = FieldBoolTrigger();
then:
final state = State();
state.validateEmail("example@mail.com")
>>> "Already exists" // Because we 'triggered' emailAlreadyExists and transitioned it to 'enabled' state.
If we validate another value within same state, it will be considered valid
state.validateEmail("anotherexample@mail.com")
>>> null // Trigger only invalidates the first value it receives after becoming enabled
*BoolTrigger
is a special case of Trigger
.
Let's compare FieldBoolTrigger
and FieldTrigger
.
FieldBoolTrigger
:
FieldBoolTrigger enabledTrigger = FieldBoolTrigger();
FieldBoolTrigger disabledTrigger = FieldBoolTrigger.disabled();
FieldTrigger
:
FieldTrigger<String> enabledTrigger = FieldTrigger("API error from server");
FieldTrigger<String> disabledTrigger = FieldTrigger.disabled();
FieldBoolTrigger
:
class State with ValidationMixin {
FieldBoolTrigger enabledTrigger = FieldBoolTrigger();
String? validateEmail(String? email) {
return validate(email).isValidatedByBool(
enabledTrigger,
error: "Not validated",
)();
}
}
FieldTrigger
:
class State with ValidationMixin {
FieldTrigger<String> enabledTrigger = FieldTrigger("API error from server");
String? validateEmail(String? email) {
return validate(email).isValidatedBy(
enabledTrigger,
errorBuilder: (content) => "Not validated, error: $content",
)();
}
}
Actually the FieldBoolTrigger
behavior can be achieved with FieldTrigger<bool>
, but in this case you have to always provide true
to enabled constructor
FieldTrigger<bool> trigger = FieldTrigger(true);
The whole work was inspired by event concept in async_redux, created by Marcelo Glasberg.