Skip to content

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