Message builders
Every validation has a default message, listed on Error messages. Three things can replace it: message on the validation, messageBuilder on the validation, and l.messageBuilder for the whole isolate. Every generator annotation takes the first two.
A fixed message
Section titled “A fixed message”message replaces the default with the string you pass. The field name is not inserted; the string is used as is.
final email = l .string() .email(message: 'Enter an email address') .required(message: 'Email is required');email.validate(input)+1-2- valid:
'[email protected]' - invalid:
'bad' - invalid:
null
A message builder
Section titled “A message builder”messageBuilder is a String Function(ValidationIssue issue). Luthor calls it when the validation fails, with the issue it is about to report. The issue carries the default message, the code, the path, the params and the field name, so the builder can use any of them.
String short(ValidationIssue issue) => 'at least ${issue.params['min']} characters';
l.string().min(8, messageBuilder: short).validate('ab').messages;l.string().min(8, messageBuilder: short).withName('password').validate('ab').messages;l.string().min(8, messageBuilder: (issue) => '${issue.fieldName}: ${issue.message}') .withName('password') .validate('ab') .messages;[at least 8 characters][at least 8 characters][password: password must be at least 8 characters long]One builder for every message
Section titled “One builder for every message”Set l.messageBuilder to translate or restyle every message in one place. It runs for every issue that has no message or messageBuilder of its own. Switch on issue.code and return issue.message for the codes you do not handle.
l.messageBuilder = (issue) => switch (issue.code) { IssueCode.required => '${issue.fieldName ?? 'Wert'} fehlt', IssueCode.invalidEmail => 'keine gültige E-Mail-Adresse', _ => issue.message,};
l.schema({'email': l.string().email().required(), 'age': l.int().min(18)}) .validate({'email': 'bad', 'age': 3}) .errors;l.string().required().validate(null).messages;l.string().required(message: 'Pflichtfeld').validate(null).messages;{email: [keine gültige E-Mail-Adresse], age: [age must be greater than or equal to 18]}[Wert fehlt][Pflichtfeld]Set it back to null to restore the defaults.
Precedence
Section titled “Precedence”For each issue: message on the validation, then messageBuilder on the validation, then l.messageBuilder, then the default.
[fixed]With the generator
Section titled “With the generator”Annotations accept message and messageBuilder. A messageBuilder has to be a top-level function or a static method, because annotation arguments are constants and the generated code refers to the function by name.
bool isEven(int value) => value.isEven;
String seatMessage(ValidationIssue issue) => 'Seat ${issue.params['min']} or higher';
final ticket = l.schema({ 'seat': l.int().custom(isEven, message: 'must be even').required(), 'row': l.int().min(1, messageBuilder: seatMessage).required(),});bool isEven(int value) => value.isEven;
String seatMessage(ValidationIssue issue) => 'Seat ${issue.params['min']} or higher';
@luthor@freezedabstract class Ticket with _$Ticket { const factory Ticket({ @WithCustomValidator(isEven, message: 'must be even') required int seat, @HasMin(1, messageBuilder: seatMessage) required int row, }) = _Ticket;
factory Ticket.fromJson(Map<String, dynamic> json) => _$TicketFromJson(json);}$TicketValidate(input)+1-1- valid:
{'seat': 4, 'row': 2} - invalid:
{'seat': 3, 'row': 0}
Field names
Section titled “Field names”Default messages start with the field name: the schema key, or the name given to withName, or value when there is neither. A custom message does not get the name added, so include it yourself if the message will be shown away from its field. A builder gets it as issue.fieldName.