一键导入
lucid-validation-assistant
Instructions and guidelines for AI models writing, debugging, and extending validators in the LucidValidation codebase.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Instructions and guidelines for AI models writing, debugging, and extending validators in the LucidValidation codebase.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | lucid-validation-assistant |
| description | Instructions and guidelines for AI models writing, debugging, and extending validators in the LucidValidation codebase. |
This guide contains everything an AI assistant or human developer needs to know to work with the LucidValidation package. Use it as a reference for syntax, API surface, architectural patterns, and extension methodologies.
LucidValidation is inspired by FluentValidation (C#). It evaluates validation rules against models s s.
LucidValidator<E>: The base abstract class. Subclasses extend this with their target entity type E (e.g. UserValidator extends LucidValidator<User>).
validate or validateAsync.LucidValidationBuilder<TProp, Entity>: Extends the validator to define rules for a specific property of type TProp extracted from the parent Entity.SimpleValidationBuilder<T>: Alias of LucidValidationBuilder<T, T>, used when validating primitive values or when writing extensions that don't depend on the parent entity.ValidationException: Holds validation details (message, code, key, entity, index).ValidationResult: Wraps the execution result (isValid, exceptions, errorsByKey, firstErrorFor).[!IMPORTANT]
- Sync validations: Registered using
must,use, oruseValidation. Executed viavalidate(entity).- Async validations: Registered using
mustAsync,useAsync, oruseValidationAsync. Executed viavalidateAsync(entity).- Critical Pitfall: If a validator contains any async rules, calling
validate()will throw anAsyncValidationException. You must usevalidateAsync()in this case.
All built-in validation rules are implemented as extensions on SimpleValidationBuilder<T> or LucidValidationBuilder<T, E>.
must(bool Function(T value) predicate, String message, String code): Custom check.mustWith(bool Function(T value, E entity) predicate, String message, String code): Custom check with entity context.mustAsync(Future<bool> Function(T value) predicate, ...): Async custom check.mustWithAsync(Future<bool> Function(T value, E entity) predicate, ...): Async custom check with entity context.isNull(), isNotNull(): Check for nullability.equalTo(T Function(E) comparison): Check equality to another field.notEqualTo(T Function(E) comparison): Check inequality to another field.isInEnum(List<T> values): Check value is in allowed list (e.g. MyEnum.values).isEmpty(), notEmpty(): Checks if string is empty / not empty.minLength(int), maxLength(int), length(min, max): Length constraints.isAlphanumeric(), isNumeric(): Character set validations.isUppercase(), isLowercase(): Casing constraints.contains(String), startsWith(String), endsWith(String): Substring checks.matchesPattern(Pattern): Regular expression validation.mustHaveLowercase(), mustHaveUppercase(): Character type presence.mustHaveNumber(), mustHaveSpecialCharacter(): Character type presence (note: mustHaveNumber is singular).hasNoSequentialRepeatedCharacters(int length): Rejects sequences like "aaa".hasNoSequentialCharacters(int length): Rejects sequences like "123" or "abc".min(num), max(num): Value bounds.range(min, max), inclusiveBetween(min, max), exclusiveBetween(min, max): Range bounds.isPositive(), isNegative(), isNonNegative(), isNonZero(): Sign checks.isEven(), isOdd(): Modulo checks.multipleOf(num factor): Divisibility check.precisionScale(int precision, int scale): Digits and decimal bounds.isTrue(), isFalse(): Strictly boolean checks.greaterThan(DateTime), greaterThanOrEqualTo(DateTime): Min date checks.lessThan(DateTime), lessThanOrEqualTo(DateTime): Max date checks.inclusiveBetween(DateTime, DateTime), exclusiveBetween(DateTime, DateTime): Date range checks.inPast(), inFuture(): Time-relative checks.afterField(DateTime Function(E) other): Asserts date is after another property.beforeField(DateTime Function(E) other): Asserts date is before another property.minItems(int), maxItems(int): Iterable length bounds.listContains(T item): Item presence in list.validEmail(): Valid email format.validUrl(), httpUrl(): Valid URL / HTTP(S) URL.validUuid(): Valid UUID format (note: validUuid is camelCased).validIpv4(), validIpv6(): IPv4 / IPv6 addresses (note: lowercase pv).validCreditCard(): Luhn algorithm check.validCPF(), validCNPJ(), validCPFOrCNPJ(), validCEP(): Brazilian national identifiers.validPhoneBR(), validPhoneWithCountryCodeBR(): Brazilian telephone numbers.include)To combine multiple validators for the same entity class:
class FullUserValidator extends LucidValidator<UserModel> {
FullUserValidator() {
include(ContactValidator());
include(BillingValidator());
}
}
setValidator)Use setValidator to delegate validation of a sub-property to a dedicated validator:
class CustomerValidator extends LucidValidator<Customer> {
CustomerValidator() {
ruleFor((c) => c.address, key: 'address').setValidator(AddressValidator());
}
}
setEach)Use setEach to apply a validator to every item inside a collection, which automatically tracks item index:
ruleFor((c) => c.students, key: 'students').setEach(StudentValidator());
ruleForEach)Use ruleForEach when validating lists of primitive values or inline rules:
ruleForEach((order) => order.tags, key: 'tags').notEmpty().maxLength(10);
Global language setting is managed via the Culture object:
LucidValidation.global.culture = Culture('en', 'US');
Override individual translations s s dynamically using LanguageManager:
class CustomLanguageManager extends LanguageManager {
CustomLanguageManager() {
// Override a specific translation code
addTranslation(Culture('pt', 'BR'), Language.code.equalTo, 'O valor deve ser idêntico a {ComparisonValue}.');
}
}
// Assign it globally
LucidValidation.global.languageManager = CustomLanguageManager();
To add a new language, subclass Language from lib/src/localization/language.dart and pass a map of s s translation keys in the call to super():
import 'package:lucid_validation/src/localization/language.dart';
class SpanishLanguage extends Language {
SpanishLanguage()
: super({
Language.code.notEmpty: "'{PropertyName}' no puede estar vacío.",
Language.code.validEmail: "'{PropertyName}' no es un correo electrónico válido.",
// Add translations for all Language.code.* keys
});
}
Custom rules are declared as Dart extensions on SimpleValidationBuilder<T> (for entity-agnostic rules) or LucidValidationBuilder<T, E> (for entity-aware rules).
extension StrongPasswordValidation on SimpleValidationBuilder<String> {
SimpleValidationBuilder<String> strongPassword() {
return notEmpty()
.minLength(8)
.mustHaveLowercase()
.mustHaveUppercase()
.mustHaveNumber()
.mustHaveSpecialCharacter();
}
}
useValidation (Simple Predicate)Use this when you want the validation engine to handle formatting and ValidationException creation:
extension CustomMinWords on SimpleValidationBuilder<String> {
SimpleValidationBuilder<String> minWords(int min, {String? message, String? code}) {
return useValidation(
(value, entity) => value.trim().split(RegExp(r'\s+')).length >= min,
code: code ?? 'minWords',
message: message ?? "'{PropertyName}' must have at least {Min} words.",
parameters: (value, entity) => {'Min': '$min'},
);
}
}
useValidationAsyncFor database lookups or HTTP calls, s:
extension UniqueUsername on SimpleValidationBuilder<String> {
SimpleValidationBuilder<String> uniqueUsername(
Future<bool> Function(String username) isUnique, {
String? message,
String? code,
}) {
return useValidationAsync(
(value, entity) => isUnique(value),
code: code ?? 'usernameTaken',
message: message ?? "'{PropertyName}' is already taken.",
);
}
}
use / useAsync)Use use (sync) or useAsync (async) when you want to construct the ValidationException manually (e.g. dynamic keys, nested properties, customized entity names):
extension CustomPrefixCheck on SimpleValidationBuilder<String> {
SimpleValidationBuilder<String> checkPrefix(String prefix) {
return use((value, entity) {
if (!value.startsWith(prefix)) {
return ValidationException(
key: key,
message: "'{PropertyName}' must start with '$prefix'.",
code: 'invalid_prefix',
entity: 'CustomEntity',
);
}
return null; // Return null if validation passes
});
}
}
You can integrate validations in your Flutter forms by defining a dedicated validator class (recommended for clean architecture / Domain Layer validations) or inline directly within your Widget (recommended for rapid prototyping or purely UI-focused validation).
First, create a dedicated validation class:
import 'package:lucid_validation/lucid_validation.dart';
class UserRegisterForm {
String email = '';
String username = '';
}
class UserRegisterValidator extends LucidValidator<UserRegisterForm> {
UserRegisterValidator() {
ruleFor((c) => c.email, key: 'email')
.notEmpty()
.validEmail();
ruleFor((c) => c.username, key: 'username')
.notEmpty()
.minLength(3);
}
}
Then, use it in your Widget:
import 'package:flutter/material.dart';
import 'package:lucid_validation/lucid_validation.dart';
class RegisterFormWidget extends StatelessWidget {
final credentials = UserRegisterForm();
final validator = UserRegisterValidator();
RegisterFormWidget({super.key});
@override
Widget build(BuildContext context) {
return Form(
child: Column(
children: [
TextFormField(
decoration: const InputDecoration(hintText: 'Email'),
onChanged: (val) => credentials.email = val,
validator: validator.byField(credentials, 'email'),
),
TextFormField(
decoration: const InputDecoration(hintText: 'Username'),
onChanged: (val) => credentials.username = val,
validator: validator.byField(credentials, 'username'),
),
],
),
);
}
}
LucidValidator.inline)Define the validator directly within your Widget to keep validations local to the UI file:
import 'package:flutter/material.dart';
import 'package:lucid_validation/lucid_validation.dart';
class RegisterFormWidgetInline extends StatelessWidget {
final credentials = UserRegisterForm();
// Define the validator inline on the class
final validator = LucidValidator<UserRegisterForm>.inline((v) {
v.ruleFor((c) => c.email, key: 'email')
.notEmpty()
.validEmail();
v.ruleFor((c) => c.username, key: 'username')
.notEmpty()
.minLength(3);
});
RegisterFormWidgetInline({super.key});
@override
Widget build(BuildContext context) {
return Form(
child: Column(
children: [
TextFormField(
decoration: const InputDecoration(hintText: 'Email'),
onChanged: (val) => credentials.email = val,
validator: validator.byField(credentials, 'email'),
),
TextFormField(
decoration: const InputDecoration(hintText: 'Username'),
onChanged: (val) => credentials.username = val,
validator: validator.byField(credentials, 'username'),
),
],
),
);
}
}
This example demonstrates how to use rulesForField to drive a checklist UI indicating to the user in real-time which password criteria are met as they type.
import 'package:flutter/material.dart';
import 'package:lucid_validation/lucid_validation.dart';
class PasswordChecklistWidget extends StatefulWidget {
const PasswordChecklistWidget({super.key});
@override
State<PasswordChecklistWidget> createState() => _PasswordChecklistWidgetState();
}
class _PasswordChecklistWidgetState extends State<PasswordChecklistWidget> {
String _password = '';
final validator = LucidValidator<String>.inline((v) {
v.ruleFor((p) => p, key: 'password')
.notEmpty().withErrorCode('required')
.minLength(8).withErrorCode('length')
.mustHaveUppercase().withErrorCode('uppercase')
.mustHaveNumber().withErrorCode('number')
.mustHaveSpecialCharacter().withErrorCode('special');
});
@override
Widget build(BuildContext context) {
// Evaluate all rules for the password field against the current string state
final rules = validator.rulesForField(_password, 'password');
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextField(
onChanged: (value) => setState(() => _password = value),
obscureText: true,
decoration: const InputDecoration(
labelText: 'Password',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 12),
...rules.map((rule) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: Row(
children: [
Icon(
rule.isValid ? Icons.check_circle : Icons.cancel,
color: rule.isValid ? Colors.green : Colors.red,
size: 20,
),
const SizedBox(width: 8),
Text(
rule.message,
style: TextStyle(
color: rule.isValid ? Colors.green[800] : Colors.red[800],
),
),
],
),
);
}),
],
);
}
}
This example showcases a full order-placement schema with:
setValidator).setEach and minItems).switchOn).import 'package:lucid_validation/lucid_validation.dart';
// Domain Models
class Order {
final Customer customer;
final List<OrderItem> items;
final PaymentDetails payment;
final String status;
Order({
required this.customer,
required this.items,
required this.payment,
required this.status,
});
}
class Customer {
final String name;
final String email;
Customer({required this.name, required this.email});
}
class OrderItem {
final String sku;
final int quantity;
final double price;
OrderItem({required this.sku, required this.quantity, required this.price});
}
class PaymentDetails {
final String type; // 'credit', 'pix', 'bank_transfer'
final String? cardNumber;
final String? pixKey;
PaymentDetails({required this.type, this.cardNumber, this.pixKey});
}
// Validator
class OrderValidator extends LucidValidator<Order> {
OrderValidator() {
// Flat field check
ruleFor((o) => o.status, key: 'status').notEmpty();
// Nested object validator
ruleFor((o) => o.customer, key: 'customer').setValidator(
LucidValidator<Customer>.inline((v) {
v.ruleFor((c) => c.name, key: 'name').notEmpty();
v.ruleFor((c) => c.email, key: 'email').notEmpty().validEmail();
}),
);
// List validation with item index tracking
ruleFor((o) => o.items, key: 'items')
.minItems(1, message: 'Your order must contain at least one item.')
.setEach(
LucidValidator<OrderItem>.inline((v) {
v.ruleFor((i) => i.sku, key: 'sku').notEmpty().maxLength(10);
v.ruleFor((i) => i.quantity, key: 'quantity').min(1);
v.ruleFor((i) => i.price, key: 'price').isPositive();
}),
);
// Nested object with polymorphic validation using switchOn
ruleFor((o) => o.payment, key: 'payment').setValidator(
LucidValidator<PaymentDetails>.inline((v) {
v.ruleFor((p) => p.type, key: 'type').notEmpty();
v.switchOn((p) => p.type, cases: {
'credit': (inner) {
inner.ruleFor((p) => p.cardNumber!, key: 'cardNumber').notEmpty().validCreditCard();
},
'pix': (inner) {
inner.ruleFor((p) => p.pixKey!, key: 'pixKey').notEmpty();
},
});
}),
);
}
}
// Executing Validation
void main() {
final order = Order(
status: 'pending',
customer: Customer(name: 'Alice', email: 'invalid-email'),
items: [
OrderItem(sku: 'SKU123', quantity: 0, price: -5.0), // invalid quantity/price
],
payment: PaymentDetails(type: 'credit', cardNumber: ''), // missing credit card number
);
final result = OrderValidator().validate(order);
print(result.isValid); // false
print(result.errorsByKey);
// Output maps errors to nested paths & indices:
// {
// "customer.email": ["'email' is not a valid email address."],
// "items.quantity": ["'quantity' must be greater than or equal to 1. You entered 0."],
// "items.price": ["'price' must be a positive number."],
// "payment.cardNumber": ["'cardNumber' must not be empty."]
// }
}