validate_kit 1.0.0
validate_kit: ^1.0.0 copied to clipboard
A zero-dependency, fluent form validation toolkit for Dart and Flutter. Chain readable rules for email, password, length, range, matching fields, and custom logic with clean, localization-ready error messages.
validate_kit #
A zero-dependency, fluent form validation toolkit for Dart and Flutter.
Every Flutter developer writes the same validation code over and over: emails,
passwords, matching fields, numeric ranges, custom regex. validate_kit turns
that repetitive, nested if soup into clean, readable chains that return the
first error — or every error when you want them all.
final error = FieldValidator()
.required('Field cannot be blank')
.email('Enter a valid email address')
.custom((val) =>
val!.contains('admin') ? 'Admin emails not allowed' : null)
.validate(controller.text);
Demo #
The example app in action:

The example/ directory contains a full registration form and a live
validation playground.
Highlights #
- Fluent chaining. Compose rules in the order they should be evaluated.
- Zero dependencies. Pure Dart. Works in Flutter, Dart CLI, and on the server. No transitive packages to audit.
- Short-circuits by default. The chain stops at the first failure, with an
opt-in
aggregate()mode to collect every error instead. - Sensible optional fields. Every format rule skips empty values; only
requiredforces a value, so optional fields just work. - Localization-ready. Override any default message per call or globally via
ValidationMessages. - Drop-in for
TextFormField. AFieldValidatoris callable and exposes anasFunctiontear-off, so it plugs straight intovalidator:. - Rich results.
validateAll()andvalidateResult()give you every error and a typed result object. - Handy extensions.
'a@b.com'.isValidEmail,input.isBlank, and more. - Fully documented and tested. Public API docs on every member and a broad unit-test suite covering null safety, boundaries, and edge cases.
Installation #
Add the dependency to your pubspec.yaml:
dependencies:
validate_kit: ^1.0.0
Then run:
flutter pub get # or: dart pub get
Quick start #
import 'package:validate_kit/validate_kit.dart';
final emailValidator = FieldValidator()
.required('Email is required')
.email('Enter a valid email address');
// In a form:
TextFormField(
controller: emailController,
validator: emailValidator.asFunction,
);
// Anywhere else:
final error = emailValidator.validate(emailController.text);
if (error != null) {
// Show `error` to the user.
}
A FieldValidator is callable, so you can also write
validator: FieldValidator().required().email().asFunction.
Built-in rules #
| Rule | Description |
|---|---|
required([message]) |
Value must not be null, empty, or whitespace. |
email([message]) |
Value must be a valid email address. |
url([message]) |
Value must be a valid URL (scheme optional). |
numeric([message]) |
Value must be an integer or decimal number. |
numericRange(min, max, {message}) |
Number must fall within an inclusive range; either bound may be null. |
integer([message]) |
Value must be a whole number. |
minLength(min, [message]) |
Trimmed value must have at least min characters. |
maxLength(max, [message]) |
Trimmed value must have at most max characters. |
lengthBetween(min, max, [message]) |
Trimmed length must be within [min, max]. |
exactLength(length, [message]) |
Trimmed value must be exactly length characters. |
strongPassword({...}) |
Password must satisfy configurable character requirements. |
matches(() => other, [message]) |
Value must equal a lazily-evaluated other value. |
matchesValue(other, [message]) |
Value must equal a fixed value. |
pattern(RegExp, [message]) |
Value must match a regular expression. |
contains(substring, [message]) |
Value must contain a substring. |
equals(expected, {caseSensitive, message}) |
Value must equal an expected string. |
oneOf(values, {caseSensitive, message}) |
Value must be one of a set of options. |
alphabetic([message]) |
Value must contain only letters. |
alphanumeric([message]) |
Value must contain only letters and digits. |
custom((value) => ..., {skipOnEmpty}) |
Run any function returning null or an error message. |
Rules that validate a format or length skip empty values, so they are safe to chain on optional fields. Add
required()when the field is mandatory.
Common recipes #
Confirm password #
final confirmValidator = FieldValidator()
.required('Please confirm your password')
.matches(() => passwordController.text, 'Passwords do not match');
The matches callback is evaluated at validation time, so it always reads the
current password.
Strong password #
final passwordValidator = FieldValidator().strongPassword(
minLength: 10,
requireSpecialCharacter: false,
);
By default it requires at least 8 characters plus an uppercase letter, a lowercase letter, a number, and a special character. Each requirement can be toggled.
Age / numeric range #
final ageValidator = FieldValidator()
.required('Age is required')
.integer('Whole numbers only')
.numericRange(18, 120, message: 'You must be between 18 and 120');
Custom rule #
final noAdminValidator = FieldValidator().custom(
(value) =>
value!.contains('admin') ? 'Admin emails are not allowed' : null,
);
Collecting every error #
By default validate() returns only the first failing message. Enable
aggregation to return them all joined by a separator:
final validator = FieldValidator(
aggregate: true,
separator: '\n',
)
.required('Field cannot be blank')
.email('Enter a valid email address');
// Or configure it fluently:
final same = FieldValidator()
.required('Field cannot be blank')
.email('Enter a valid email address')
.aggregate(separator: ' • ');
For structured access, validateAll() always returns the full list and
validateResult() returns a ValidationResult:
final result = validator.validateResult(input);
if (result.isInvalid) {
for (final message in result.errors) {
debugPrint(message);
}
}
Localization #
Every default message lives on ValidationMessages. Override the ones you need
once at start-up, then continue to pass per-call overrides where it makes sense:
void main() {
ValidationMessages.required = 'Este campo es obligatorio.';
ValidationMessages.email = 'Introduce un correo válido.';
ValidationMessages.mismatch = 'Los valores no coinciden.';
runApp(const MyApp());
}
String extensions #
For quick checks outside a form, import the library and use the extensions:
'a@b.com'.isValidEmail; // true
'https://a.com'.isValidUrl; // true
'3.14'.isNumeric; // true
'42'.isInteger; // true
'Abcd123!'.isStrongPassword; // true
' '.isBlank; // true (null-aware)
'ab1'.isAlphanumeric; // true
// Normalize optional input:
final value = input.trimmedOrNull; // null when blank
You can also delegate to a validator with validateWith:
final error = input.validateWith(
FieldValidator().required().email(),
);
Single-rule helpers #
When one rule is all you need, Validators returns a plain
String? Function(String?):
TextFormField(validator: Validators.email());
TextFormField(validator: Validators.minLength(3, 'Too short'));
Example #
The example/ directory contains a runnable app with a full registration form
(every built-in rule in context) and a live playground that validates as you
type:
cd example
flutter run
API overview #
FieldValidator(aliasMultiValidator) — the fluent builder.ValidationRule— a single immutable rule; extend the package withaddRule.ValidationResult—isValid,errors,firstError.ValidationMessages— overridable default messages.Validators— single-rule factory helpers.- String extensions on
StringandString?.
Author and Contact #
Developed and maintained by Ebrahim Joy.
- Email: eebrahimjoy@gmail.com
- Website: eebrahimjoy.com
- GitHub: @eebrahimjoy
Contributing #
Please report bugs and request features through the issue tracker. Pull requests are welcome.
License #
Released under the MIT License.