Results and issues
Luthor never throws for invalid input. validate and validateSchema return a ValidationResult<T> with two cases: ValidationSuccess<T> holding the output, and ValidationFailure<T> holding a list of issues. Everything else you can read from a result is derived from those issues. The reference lists the members.
Matching on the result
Section titled “Matching on the result”The result is sealed, so a switch has to handle both cases and the compiler tells you when one is missing. Object patterns pull the fields out.
final result = l.string().email().required().validate(input);
switch (result) { case ValidationSuccess(:final data): sendTo(data); case ValidationFailure(:final issues): for (final issue in issues) { log(issue.code, issue.errorPath, issue.message); }}T is the validator’s output type, String here. With validateSchema and a fromJson, T is your model.
Issues are the source
Section titled “Issues are the source”A ValidationIssue records one problem: a code such as IssueCode.tooSmall, the path to the value inside the input, the message, the params the check used, and the fieldName that went into the message.
final issue = l.int().min(18).withName('age').validate(12).issues.single;print(issue);ValidationIssue(code: tooSmall, path: , message: age must be greater than or equal to 18)Switch on the code when the UI needs to react to the kind of problem rather than show the text. The set of codes is open, so keep a default case. ValidationIssue and IssueCode lists every code and its params.
Four views of the same issues
Section titled “Four views of the same issues”Every result, success or failure, exposes the issues in four shapes. On a success all four are empty, so you can read them without matching first.
| View | Type | Use it for |
|---|---|---|
issues |
List<ValidationIssue> |
Logging, telemetry, switching on codes |
messages |
List<String> |
A flat list of text, for a toast or a log line |
errors |
Map<String, List<String>> |
Form fields: messages grouped by error path |
getError(path), getErrors(path) |
String?, List<String> |
One field’s first message, or all of them |
final user = l.schema({ 'email': l.string().email().required(), 'age': l.int().min(18), 'profile': l.schema({ 'displayName': l.string().min(2).required(), }).required(),});
final result = user.validate({ 'email': 'bad', 'age': 12, 'profile': {'displayName': 'x'},});{ "email": [ "email must be a valid email address" ], "age": [ "age must be greater than or equal to 18" ], "profile.displayName": [ "displayName must be at least 2 characters long" ]}email must be a valid email addressdisplayName must be at least 2 characters longnullage must be greater than or equal to 18getError('profile') is null because the issue sits at profile.displayName, not at profile. Errors and error paths explains the paths.
Success data is the output, not the input
Section titled “Success data is the output, not the input”A schema or list returns a new collection of the validated values. For a schema, that means unknown keys are gone unless the schema has .passthrough():
ValidationSuccess(data: {email: [email protected], profile: {displayName: Ada}})Converting to a model
Section titled “Converting to a model”validateSchema takes a fromJson and runs it on the output map after every field passed. The success case then holds the model.
final result = user.validateSchema(json, fromJson: User.fromJson);ValidationSuccess(data: User(email: [email protected], age: null))Two things that used to throw are now failures: a fromJson that throws becomes a fromJsonFailed issue, and null input becomes a required issue.
ValidationFailure(errors: {(root): [value could not be converted]})ValidationFailure(errors: {(root): [value is required]})Printing a result
Section titled “Printing a result”Both cases override toString. The failure form prints the error map, with object-level errors under (root) instead of the empty key. Only the printed form changes: errors and getError('') still use ''.
ValidationSuccess(data: [email protected])ValidationFailure(errors: {(root): [value must be a valid email address]})ValidationFailure(errors: {email: [email must be a valid email address]})