Defining validation

Validation is defined through part methods on ValidatorFactory such as notNull(), satisfy() and other parts.

Each validation rule defines

  • value validation, e.g notNull() defines that value can not be null. satisfy() defines a predicate which has to be true to be valid etc.
  • devMessage - a message which will be displayed if no translation is provided.
  • key - Validation error's identification. Usable for translation.
  • severity - Severity of validation. By default it is ValidationSeverity.error.

This example defines validation that int value can not be null and has to be greater or equal to 18.

ageInput = GladeIntInput(
  validator: (v) => (v
        ..notNull()
        ..satisfy(
          (value) => value >= 18,
          devMessage: (_, __) => 'Value must be greater or equal to 18',
          key: _ErrorKeys.ageRestriction,
        ))
      .build(),
  value: 0,
);

The order of each validation part matters. By default, the first failing part with severity error stops validation. You can control this behavior using the following options on .build():

  • stopOnFirstError (default: true): Stops validation on the first error. Set to false to validate all parts, collecting all errors and warnings.
  • stopOnFirstErrorOrWarning (default: false): If set to true, validation stops on the first error or warning encountered.

Example:

ageInput = GladeIntInput(
  validator: (v) => (v
        ..notNull(
          devMessage: (_, __) => 'Value is required',
          severity: ValidationSeverity.error,
        )
        ..satisfy(
          (value) => value >= 18,
          devMessage: (_, __) => 'Recommended age is 18 or above',
          severity: ValidationSeverity.warning,
        ))
      .build(stopOnFirstError: false, stopOnFirstErrorOrWarning: false),
  value: 0,
);

In this example, all validation parts are checked, and both errors and warnings are collected. Adjust these options to fit your validation flow.

Fields connected with textFormFieldInputValidator will automatically call validator and validation error (if any) is passed down to fields. By default devMessage is used unless translation is specified.

Validation severity

Each validation rule can specify a severity using the ValidationSeverity enum. This allows you to distinguish between errors (which typically block form submission) and warnings (which inform the user but do not block submission).

The available severities are:

  • ValidationSeverity.error – Indicates a validation error. This is the default and usually prevents form submission.
  • ValidationSeverity.warning – Indicates a warning. Warnings are shown to the user but do not block form submission.

You can set the severity for each validation part:

ageInput = GladeIntInput(
  validator: (v) => (v
        ..notNull(
          devMessage: (_, __) => 'Value is required',
          severity: ValidationSeverity.error,
        )
        ..satisfy(
          (value) => value >= 18,
          devMessage: (_, __) => 'Recommended age is 18 or above',
          severity: ValidationSeverity.warning,
        ))
      .build(),
  value: 0,
);

In this example, if the value is null, an error is shown and form submission is blocked. If the value is less than 18, a warning is shown, but the user can still submit the form.

Skipping Specific Validation

To conditionally skip specific validations, use the shouldValidate callback. If an input should be entirely excluded from validation, consider using conditional logic in the inputs getter.

(v
  ..minLength(length: 2, shouldValidate: (_) => false)
  ..maxLength(length: 6))
  .build();

// Omit input from validation if the condition is not met
get inputs => [if (condition) input];

This ensures that the minLength validation is always skipped, while the maxLength validation is applied.

If the condition is met, the input is included in the validation process.

Creating custom reusable validators

You have several options to create reusable validators.

Most straightforward way is to extend GladeValidator (or specialised variant such as StringValidator) and add your custom validation logic.

Creating custom part

Better way is to create a custom validation part by extending InputValidatorPart<T> and provide that part through customPart(part) method.

class AgeValidatorPart extends InputValidatorPart<int> {
  AgeValidatorPart() : super();

  @override
  bool validate(int? value) {
    return value != null && value >= 18;
  }
}

// Usage
ageInput = GladeIntInput(
  validator: (v) => (v..customPart(AgeValidatorPart())).build(),
  value: 0,
);

Using validators without GladeInput

It is possible to use GladeValidator without associated GladeInput.

Just create instance of GladeValidator (or specialised variant such as StringValidator) and use it.

final validator = (StringValidator()..notEmpty()).build();
final result = validator.validate(null);