Migrating from v0
Validasi v1 is a rewrite: schemas are typed, rules are explicit, and validation returns a ValidasiResult instead of the old validator objects. This guide maps the v0 API to v1. The old documentation stays available at v0 documentation.
Dependency
validasi v1 is the default version on pub.dev:
dart pub add validasi
# or
flutter pub add validasiYour v0 code keeps working on the 0.0.x line if you pin it explicitly:
dependencies:
validasi: ^0.0.10Imports
v0 exposed everything from package:validasi/validasi.dart. v1 splits the surface:
import 'package:validasi/validasi.dart'; // Validasi.* builders, ValidasiResult, ValidationError
import 'package:validasi/rules.dart'; // Rules.*, Rule, AsyncRule, ValidationState, FieldRulesValidasiEngine and the field/schema abstractions are available from package:validasi/engine.dart and package:validasi/fields.dart when you need them directly.
Schema construction
v0 used a fluent validator per type. v1 takes a list of rules:
final schema = Validasi.string().minLength(3).maxLength(20).email();
final result = schema.tryParse('user@example.com');final schema = Validasi.string([
Rules.string.minLength(3),
Rules.string.maxLength(20),
Rules.string.email(),
]);
final result = schema.validate('user@example.com');Required and nullable
v0 made every field required by default and used .nullable() to opt out. v1 has no implicit required check: add Rules.required<T>() when null must fail, and Rules.nullable<T>() when it must pass.
final schema = Validasi.string().nullable();
print(schema.tryParse(null).isValid); // truefinal schema = Validasi.string([
Rules.nullable<String>(),
Rules.string.minLength(3),
]);
print(schema.validate(null).isValid); // trueRules.nullable<T>() stops the rule pipeline for null, so later rules are skipped. Put it first.
Result access
| v0 | v1 |
|---|---|
schema.tryParse(value) | schema.validate(value) |
schema.tryParseAsync(value) | schema.validateAsync(value) |
result.value | result.data |
result.isValid | result.isValid (unchanged) |
result.errors (FieldError) | result.errors (ValidationError) |
error.path (single path) | error.path (List<String>?) |
schema.parse(value) (throws) | result.requireValue() or throw manually |
final result = schema.validate(input);
if (!result.isValid) {
for (final error in result.errors) {
print('${error.path?.join('.') ?? 'root'}: ${error.message}');
}
throw FormatException(
result.errors.map((error) => error.message).join(', '),
);
}
final value = result.requireValue();Type-specific builders
| v0 | v1 |
|---|---|
Validasi.string() | Validasi.string([...]) |
Validasi.number() | Validasi.number<num>([...]) or number<int> / number<double> |
Validasi.object({...}) | Validasi.map<dynamic>([Rules.map.hasFields({...})]) |
Validasi.array(itemSchema) | Validasi.list<T>([Rules.iterable.forEach([...])]) |
Validasi.generic<T>() | Validasi.any<T>([...]) |
Validasi.date() | No built-in date schema; use Validasi.any<DateTime>([...]) with an inline/custom rule |
Object / map
final schema = Validasi.object({
'name': Validasi.string().minLength(1),
'age': Validasi.number().gte(0),
});final schema = Validasi.map<dynamic>([
Rules.map.hasFields({
'name': FieldRules<String>([
Rules.string.minLength(1),
]),
'age': FieldRules<int>([
Rules.number.moreThanEqual(0),
]),
}),
]);Array / list
final schema = Validasi.array(Validasi.string().minLength(1));final schema = Validasi.list<String>([
Rules.iterable.forEach<String>([
Rules.string.minLength(1),
]),
]);Generic
final schema = Validasi.generic<String>().oneOf(['a', 'b']);final schema = Validasi.any<String>([
Rules.having<String>(['a', 'b']),
]);Rules mapping
| v0 method | v1 rule |
|---|---|
minLength(n) | Rules.string.minLength(n) / Rules.iterable.minLength(n) |
maxLength(n) | Rules.string.maxLength(n) / Rules.iterable.maxLength(n) |
email() | Rules.string.email() |
startsWith(s) / endsWith(s) / contains(s) | Rules.string.startsWith/endsWith/contains(s) |
gt(n) / gte(n) / lt(n) / lte(n) | Rules.number.moreThan/moreThanEqual/lessThan/lessThanEqual(n) |
positive() / negative() / nonPositive() / nonNegative() | Rules.number.positive/negative/nonPositive/nonNegative() |
nonDecimal() | Rules.number.integer() |
decimal() | Rules.number.decimal() |
finite() | Rules.number.finite() |
equalTo(v) / notEqualTo(v) | Rules.equals(v) / Rules.notEquals(v) |
oneOf(values) / notOneOf(values) | Rules.having(values) / Rules.notEquals(v) per value, or Rules.anyOf(...) |
min(n) / max(n) on arrays | Rules.iterable.minLength(n) / Rules.iterable.maxLength(n) |
contains(v) / notContains(v) on arrays | Rules.iterable.contains(v) / Rules.iterable.notContains(v) |
unique() | Rules.iterable.unique() |
.extend({...}) / .without([...]) | Compose Rules.map.hasFields / Rules.map.forbiddenKeys or build the field map in Dart |
Transformers and preprocessing
v0's transformer: constructor parameter is split in v1:
- Use
.withPreprocess(...)when the input type differs from the schema type. - Use
Rules.transform<T>(...)when the value is already the right type but needs normalization.
final schema = Validasi.number(transformer: NumberTransformer());
final result = schema.tryParse('10');final schema = Validasi.number<int>([
Rules.number.moreThan(0),
]).withPreprocess((String input) => int.parse(input));
final result = schema.validate('10');Custom rules
v0's .custom((value, fail) { ... }) becomes either an inline rule or a reusable Rule<T>.
final schema = Validasi.string().custom((value, fail) {
return value.startsWith('SKU-') || fail('Invalid SKU');
});final schema = Validasi.string([
Rules.inline<String>(
(value) => value != null && value.startsWith('SKU-'),
message: 'Invalid SKU',
),
]);For reusable rules, extend Rule<T> (sync) or AsyncRule<T> (async) and implement apply / applyAsync; see Custom Rules.
Async validation
v0's tryParseAsync / parseAsync become validateAsync. Async rules are explicit: Rules.inlineAsync, Rules.transformAsync, AsyncConditionalField, or a custom AsyncRule<T>.
final result = await schema.tryParseAsync(value);final result = await schema.validateAsync(value);Calling sync validate() on a pipeline containing an async rule throws a StateError.
Flutter form helpers
The v0 FieldValidator / GroupValidator helpers are gone. Use the companion packages:
validasi_uiprovidesValidasiForm,ValidasiFormField,ValidasiTextField, andValidasiFormControllerfor Flutter forms.validasi_annotation+validasi_gengenerate typedXFieldsand schemas so the form and model stay in sync.
See Form Management and Code Generation for the v1 patterns.
Removed APIs
parse/parseAsyncthrowing variants: usevalidate/validateAsyncand inspect the result.tryParse/tryParseAsync: replaced byvalidate/validateAsync.ValidasiResult.value: replaced byValidasiResult.data.Validasi.object,Validasi.array,Validasi.generic,Validasi.date: replaced byValidasi.map,Validasi.list,Validasi.any, and rules for the latter.- Validator chaining (
.minLength(...).email()): replaced by an explicit rule list. FieldError: replaced byValidationError, with a list-basedpath.
Checklist
- Replace fluent validator chains with
Validasi.*([Rules...]). - Add
Rules.required<T>()where null must be rejected;Rules.nullable<T>()where it must pass. - Replace
tryParsewithvalidate,valuewithdata, and update error-path handling. - Move object/array schemas to
Validasi.map/Validasi.listwithFieldRulesand iterable rules. - Move input conversion to
.withPreprocess(...); keep normalization inRules.transform. - Replace custom callbacks with
Rules.inlineor aRule<T>subclass. - For Flutter forms, move from
FieldValidator/GroupValidatortovalidasi_ui.
