philiprehberger_form_validator
Declarative form validation with composable rules and JSON schemas
Requirements
- Dart >= 3.5
Installation
Add to your pubspec.yaml:
dependencies:
philiprehberger_form_validator: ^0.3.0
Then run:
dart pub get
Usage
import 'package:philiprehberger_form_validator/form_validator.dart';
final schema = FormSchema({
'name': [Rules.required(), Rules.minLength(2)],
'email': [Rules.required(), Rules.email()],
});
final result = schema.validate({
'name': 'Alice',
'email': 'alice@example.com',
});
print(result.isValid); // true
Built-in Rules
Rules.required()
Rules.email()
Rules.url()
Rules.minLength(3)
Rules.maxLength(100)
Rules.pattern(RegExp(r'^\d+$'))
Rules.numeric()
Rules.between(1, 100)
Rules.equals('password') // cross-field comparison
Rules.oneOf(['a', 'b', 'c'])
Rules.inRange(1, 100)
Rules.custom((v) => v != null, message: 'Required')
JSON Schema Definition
final schema = FormSchema.fromJson({
'email': ['required', 'email'],
'name': ['required', 'minLength:3', 'maxLength:100'],
'age': ['numeric', 'between:18,120'],
});
Cross-field Validation
final schema = FormSchema({
'password': [Rules.required(), Rules.minLength(8)],
'confirm': [Rules.required(), Rules.equals('password')],
});
final result = schema.validate({
'password': 'secret123',
'confirm': 'secret123',
});
print(result.isValid); // true
Conditional Validation
final schema = FormSchema({
'country': [Rules.required()],
'state': [Rules.when((data) => data['country'] == 'US', Rules.required())],
});
final result = schema.validate({'country': 'US'});
print(result.hasError('state')); // true — required only when country is US
Combining Validators
// All must pass
final strict = Rules.all([Rules.required(), Rules.minLength(8)]);
// Any can pass
final flexible = Rules.any([Rules.email(), Rules.url()]);
Async Validation
final schema = FormSchema({'username': [Rules.required()]});
final result = await schema.validateAsync(
{'username': 'taken'},
asyncValidators: [
MapEntry('username', AsyncFieldValidator(
'Username already taken',
(value) async => value != 'taken', // e.g. check server
)),
],
);
print(result.isValid); // false
Nested Object Validation
final schema = FormSchema.nested(
{
'name': [Rules.required()],
},
nestedSchemas: {
'address': FormSchema({
'city': [Rules.required()],
'zip': [Rules.required(), Rules.pattern(RegExp(r'^\d{5}$'))],
}),
},
);
final result = schema.validateNested({
'name': 'Alice',
'address': {'city': '', 'zip': 'bad'},
});
print(result.hasError('address.city')); // true
print(result.hasError('address.zip')); // true
// Extract nested errors
final addressErrors = result.nested('address');
print(addressErrors.errorsFor('city')); // [This field is required]
Localization
class SpanishMessages extends MessageProvider {
@override
String message(String ruleKey, Map<String, dynamic> params) {
switch (ruleKey) {
case 'required':
return 'Campo obligatorio';
case 'email':
return 'Correo electronico invalido';
default:
return 'Error de validacion';
}
}
}
// Set globally before creating rules
MessageProvider.setProvider(SpanishMessages());
final rule = Rules.required();
print(rule.validate(null)); // Campo obligatorio
// Reset to English defaults
MessageProvider.resetProvider();
Inspecting Errors
final result = schema.validate(data);
result.isValid; // true if no errors
result.hasError('email'); // check specific field
result.errorsFor('email'); // list of error messages
result.allErrors; // flat list of all errors
result.errorCount; // total error count
API
| Class | Description |
|---|---|
FieldValidator |
Single validation rule with message and test function |
Rules |
Static factory methods for built-in validators |
FormSchema |
Schema defining validators per field, validates form data maps |
FormSchema.fromJson() |
Create schema from JSON-like rule descriptor map |
ValidationResult |
Result object with errors, field queries, and counts |
CrossFieldValidator |
Validator that compares against another field's value |
AsyncFieldValidator |
Async validation rule (e.g. server-side checks) |
Rules.when() |
Conditional validator based on form data |
Rules.inRange() |
Inclusive numeric range validation |
Rules.all() |
Composite validator requiring all rules to pass |
Rules.any() |
Composite validator requiring any rule to pass |
MessageProvider |
Abstract class for localizable error messages |
DefaultMessageProvider |
Built-in English message provider |
FormSchema.nested() |
Schema with support for nested object validation |
FormSchema.validateNested() |
Validates data including nested objects with dot-path keys |
ValidationResult.nested() |
Extracts errors for a nested prefix |
Development
dart pub get
dart analyze --fatal-infos
dart test
Support
If you find this project useful:
License
Libraries
- form_validator
- Declarative form validation with composable rules and JSON schemas
- philiprehberger_form_validator