Validation Patterns
Real-world validation patterns from an LMS domain, covering required fields, format validation, range checks, temporal constraints, and soft warnings.
Required Fields
dart
.text('student_name', title: 'Student Name')
.required('Student name is required')
// For boolean fields: must be explicitly true
.boolean('accept_terms', title: 'Accept Terms')
.required('You must accept the terms and conditions')Required validation checks for null, empty strings, empty lists, and empty maps.
Email and URL Validation
dart
.text('email', title: 'Email')
.required()
.email('Please enter a valid email address')
.text('portfolio', title: 'Portfolio URL')
.pattern(
r'^https?://',
message: 'URL must start with http:// or https://',
)Length Constraints
dart
// Minimum length
.text('bio', title: 'Biography')
.minLength(20, message: 'Bio must be at least 20 characters')
// Maximum length
.text('summary', title: 'Summary')
.maxLength(500, message: 'Summary cannot exceed 500 characters')
// Exact length
// Use TextLengthValidation directly for exact match
TextField(
name: 'zip',
title: 'ZIP Code',
validations: [
TextLengthValidation(
operator: OperatorType.equal,
length: 5,
errorMessage: 'ZIP code must be exactly 5 digits',
),
],
)Number Range Validation
dart
// Bounded range
.number('credit_hours', title: 'Credit Hours')
.range(min: 0.5, max: 12, message: 'Must be between 0.5 and 12')
// Minimum only
.number('max_students', title: 'Max Students')
.min(1, message: 'Must have at least 1 student')
// Maximum only
.number('absences', title: 'Allowed Absences')
.max(5, message: 'Cannot exceed 5 absences')Date Validation
Future Date Constraint
dart
DateTimeField(
name: 'start_date',
title: 'Start Date',
selectionType: SelectionType.date,
validations: [
RequiredValidation(errorMessage: 'Required'),
FutureDateValidation(
maxFutureDays: 180,
errorMessage: 'Cannot schedule more than 180 days ahead',
),
],
)Past Date Constraint
dart
DateTimeField(
name: 'birth_date',
title: 'Date of Birth',
selectionType: SelectionType.date,
validations: [
RequiredValidation(errorMessage: 'Required'),
PastDateValidation(
maxPastDays: 36500, // ~100 years
errorMessage: 'Please enter a valid date of birth',
),
],
)Business Hours Validation
dart
DateTimeField(
name: 'exam_date',
title: 'Exam Date',
selectionType: SelectionType.date,
validations: [
BusinessHoursValidation(
allowedWeekdays: [1, 2, 3, 4, 5], // Monday through Friday
timeRanges: [
TimeRange(startTime: '09:00', endTime: '17:00'),
],
excludedDates: ['2026-12-25', '2026-01-01'], // Holidays
dateOnly: true,
errorMessage: 'Exams can only be scheduled on business days',
),
],
)Cross-Field Date Validation
dart
// End date must be after start date
DateTimeField(
name: 'end_date',
title: 'End Date',
selectionType: SelectionType.date,
validations: [
RequiredValidation(errorMessage: 'Required'),
DateDependencyValidation(
dependentFieldName: 'start_date',
operator: DateComparisonOperator.afterOrEqual,
offsetDays: 7,
errorMessage: 'End date must be at least 7 days after start date',
),
],
)The form automatically subscribes to start_date changes and re-validates end_date in real time.
Pattern Validation
dart
// Student ID format
.text('student_id', title: 'Student ID')
.pattern(r'^\d{4}-\d{4}$', message: 'Format: 1234-5678')
.required()
// No special characters
.text('username', title: 'Username')
.identifier('Only lowercase letters, digits, and underscores')
.required()Soft Validation (Warnings)
Soft validations warn but do not block submission. Useful for recommended ranges:
dart
NumberField(
name: 'class_size',
title: 'Class Size',
validations: [
// Hard limit: blocks submission
NumberRangeValidation(
min: 1,
max: 200,
errorMessage: 'Class size must be between 1 and 200',
),
// Soft limit: warns but allows
SoftRangeValidation(
softMin: 10,
softMax: 40,
warningMessage: 'Recommended class size is 10-40 students',
triggersException: true,
exceptionSeverity: ExceptionSeverity.medium,
),
],
)Collect soft breaches on submit:
dart
if (form.validate()) {
final breaches = form.collectSoftBreaches();
if (breaches.isNotEmpty) {
// Show confirmation dialog listing warnings
final proceed = await showSoftBreachDialog(breaches);
if (!proceed) return;
}
// Submit
}Async Validation
For server-side checks like uniqueness:
dart
.text('email', title: 'Email')
.required()
.email()
.asyncValidation(CustomAsyncValidation(
errorMessage: 'This email is already registered',
))On submit, validate all async validators:
dart
if (form.validate()) {
final asyncValid = await form.validateAllAsync();
if (asyncValid) {
// All validations passed, submit
}
}Combining Multiple Validations
Validations run in order. The first failure stops and displays its error:
dart
.text('password', title: 'Password')
.required('Password is required')
.minLength(8, message: 'Must be at least 8 characters')
.pattern(r'[A-Z]', message: 'Must contain an uppercase letter')
.pattern(r'\d', message: 'Must contain a digit')Next Steps
- Dynamic Behavior -- Conditional rules
- Cross-Field Validation -- Advanced cross-field patterns
- Building Forms -- Step-by-step form construction