philiprehberger_form_validator

Tests pub package Last updated

Declarative form validation with composable rules and JSON schemas

Requirements

  • Dart >= 3.5

Installation

Add to your pubspec.yaml:

dependencies:
  philiprehberger_form_validator: ^0.3.0

Then run:

dart pub get

Usage

import 'package:philiprehberger_form_validator/form_validator.dart';

final schema = FormSchema({
  'name': [Rules.required(), Rules.minLength(2)],
  'email': [Rules.required(), Rules.email()],
});

final result = schema.validate({
  'name': 'Alice',
  'email': 'alice@example.com',
});

print(result.isValid); // true

Built-in Rules

Rules.required()
Rules.email()
Rules.url()
Rules.minLength(3)
Rules.maxLength(100)
Rules.pattern(RegExp(r'^\d+$'))
Rules.numeric()
Rules.between(1, 100)
Rules.equals('password')    // cross-field comparison
Rules.oneOf(['a', 'b', 'c'])
Rules.inRange(1, 100)
Rules.custom((v) => v != null, message: 'Required')

JSON Schema Definition

final schema = FormSchema.fromJson({
  'email': ['required', 'email'],
  'name': ['required', 'minLength:3', 'maxLength:100'],
  'age': ['numeric', 'between:18,120'],
});

Cross-field Validation

final schema = FormSchema({
  'password': [Rules.required(), Rules.minLength(8)],
  'confirm': [Rules.required(), Rules.equals('password')],
});

final result = schema.validate({
  'password': 'secret123',
  'confirm': 'secret123',
});
print(result.isValid); // true

Conditional Validation

final schema = FormSchema({
  'country': [Rules.required()],
  'state': [Rules.when((data) => data['country'] == 'US', Rules.required())],
});

final result = schema.validate({'country': 'US'});
print(result.hasError('state')); // true — required only when country is US

Combining Validators

// All must pass
final strict = Rules.all([Rules.required(), Rules.minLength(8)]);

// Any can pass
final flexible = Rules.any([Rules.email(), Rules.url()]);

Async Validation

final schema = FormSchema({'username': [Rules.required()]});

final result = await schema.validateAsync(
  {'username': 'taken'},
  asyncValidators: [
    MapEntry('username', AsyncFieldValidator(
      'Username already taken',
      (value) async => value != 'taken', // e.g. check server
    )),
  ],
);
print(result.isValid); // false

Nested Object Validation

final schema = FormSchema.nested(
  {
    'name': [Rules.required()],
  },
  nestedSchemas: {
    'address': FormSchema({
      'city': [Rules.required()],
      'zip': [Rules.required(), Rules.pattern(RegExp(r'^\d{5}$'))],
    }),
  },
);

final result = schema.validateNested({
  'name': 'Alice',
  'address': {'city': '', 'zip': 'bad'},
});

print(result.hasError('address.city')); // true
print(result.hasError('address.zip'));  // true

// Extract nested errors
final addressErrors = result.nested('address');
print(addressErrors.errorsFor('city')); // [This field is required]

Localization

class SpanishMessages extends MessageProvider {
  @override
  String message(String ruleKey, Map<String, dynamic> params) {
    switch (ruleKey) {
      case 'required':
        return 'Campo obligatorio';
      case 'email':
        return 'Correo electronico invalido';
      default:
        return 'Error de validacion';
    }
  }
}

// Set globally before creating rules
MessageProvider.setProvider(SpanishMessages());
final rule = Rules.required();
print(rule.validate(null)); // Campo obligatorio

// Reset to English defaults
MessageProvider.resetProvider();

Inspecting Errors

final result = schema.validate(data);

result.isValid;              // true if no errors
result.hasError('email');    // check specific field
result.errorsFor('email');   // list of error messages
result.allErrors;            // flat list of all errors
result.errorCount;           // total error count

API

Class Description
FieldValidator Single validation rule with message and test function
Rules Static factory methods for built-in validators
FormSchema Schema defining validators per field, validates form data maps
FormSchema.fromJson() Create schema from JSON-like rule descriptor map
ValidationResult Result object with errors, field queries, and counts
CrossFieldValidator Validator that compares against another field's value
AsyncFieldValidator Async validation rule (e.g. server-side checks)
Rules.when() Conditional validator based on form data
Rules.inRange() Inclusive numeric range validation
Rules.all() Composite validator requiring all rules to pass
Rules.any() Composite validator requiring any rule to pass
MessageProvider Abstract class for localizable error messages
DefaultMessageProvider Built-in English message provider
FormSchema.nested() Schema with support for nested object validation
FormSchema.validateNested() Validates data including nested objects with dot-path keys
ValidationResult.nested() Extracts errors for a nested prefix

Development

dart pub get
dart analyze --fatal-infos
dart test

Support

If you find this project useful:

Star the repo

🐛 Report issues

💡 Suggest features

❤️ Sponsor development

🌐 All Open Source Projects

💻 GitHub Profile

🔗 LinkedIn Profile

License

MIT

Libraries

form_validator
Declarative form validation with composable rules and JSON schemas
philiprehberger_form_validator