Generate a schema from a model
In this tutorial you turn the User model from Your first schema into an annotated class, generate its schema, and validate input with the generated function. It uses freezed and json_serializable. Models without freezed shows the other setups. The generated code on this page is the real output of luthor_generator for the model shown.
-
Add the packages.
Terminal window dart pub add luthor freezed_annotation json_annotationdart pub add dev:build_runner dev:luthor_generator dev:freezed dev:json_serializableOn Dart 3.11 use
freezed3.2.x.freezed4 needs Dart 3.13 or later. -
Write the model.
Annotate the class with
@luthornext to@freezed. Put Luthor’s annotations on the constructor parameters:@IsEmail()adds.email(),@HasMin(18)on anintadds.min(18). Non-nullable parameters without a default become required fields; nullable ones stay optional.lib/user.dart import 'package:freezed_annotation/freezed_annotation.dart';import 'package:luthor/luthor.dart';part 'user.freezed.dart';part 'user.g.dart';@luthor@freezedabstract class User with _$User {const factory User({@HasMin(2) required String name,@IsEmail() required String email,@HasMin(18) int? age,}) = _User;factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);} -
Run the generator.
Terminal window dart run build_runner builduser.g.dartnow holds five generated items forUser: the schema$UserSchema, the validate function$UserValidate, thevalidateSelfextension, the schema keysUserSchemaKeys, and the error keysUserErrorKeys. The schema is the one you would have written by hand:lib/user.g.dart (excerpt) final SchemaValidator $UserSchema = l.schema({UserSchemaKeys.name: l.string().min(2).required(),UserSchemaKeys.email: l.string().email().required(),UserSchemaKeys.age: l.int().min(18),}).withName("User");ValidationResult<User> $UserValidate(Object? json) =>$UserSchema.validateSchema(json, fromJson: User.fromJson); -
Validate with the generated function.
$UserValidateaccepts any input, validates it, and on success converts it withUser.fromJson. You get aValidationResult<User>.bin/main.dart import 'package:luthor/luthor.dart';import 'package:my_app/user.dart';void main() {final result = $UserValidate({'name': 'Ada','age': 36,});switch (result) {case ValidationSuccess(:final data):print('+ ${data.name} <${data.email}>');case ValidationFailure(:final errors):print('- $errors');}}dart run bin/main.dart, valid then invalid input + Ada <[email protected]>- {name: [name must be at least 2 characters long], email: [email must be a valid email address], age: [age must be greater than or equal to 18]}$UserValidate(input)+2-2- valid:
{'name': 'Ada', 'email': '[email protected]', 'age': 36} - valid:
{'name': 'Ada', 'email': '[email protected]'} - invalid:
{'name': 'A', 'email': 'ada', 'age': 12} - invalid:
'not a map'
- valid:
-
Read errors with the error keys.
UserErrorKeysis a record whose fields hold the error path of each model field. Use it withgetErrorinstead of a string, and a renamed field becomes a compile error instead of anull.print(result.getError(UserErrorKeys.email));print(result.getError(UserErrorKeys.age));email must be a valid email addressage must be greater than or equal to 18 -
Validate an existing instance.
The generated extension validates a model you already have, by serialising it with
toJson()and running the schema.final user = User(name: 'A', email: 'ada', age: 12);print(user.validateSelf().isValid);print(user.validateSelf().errors);false{name: [name must be at least 2 characters long], email: [email must be a valid email address], age: [age must be greater than or equal to 18]}
That is the whole loop: annotate, generate, validate. What gets generated describes each generated item, and Supported field types lists how every Dart type maps to a validator.