philiprehberger_form_validator 0.3.0 copy "philiprehberger_form_validator: ^0.3.0" to clipboard
philiprehberger_form_validator: ^0.3.0 copied to clipboard

Declarative form validation with composable rules and JSON schemas

example/example.dart

import 'package:philiprehberger_form_validator/form_validator.dart';

void main() {
  // --- Schema with composable rules ---
  final schema = FormSchema({
    'name': [Rules.required(), Rules.minLength(2), Rules.maxLength(50)],
    'email': [Rules.required(), Rules.email()],
    'age': [Rules.required(), Rules.numeric(), Rules.between(18, 120)],
    'website': [Rules.url()],
    'role': [Rules.oneOf(['admin', 'user', 'guest'])],
    'password': [Rules.required(), Rules.minLength(8)],
    'confirm': [Rules.required(), Rules.equals('password')],
  });

  // Valid data
  final validData = {
    'name': 'Alice',
    'email': 'alice@example.com',
    'age': '30',
    'website': 'https://alice.dev',
    'role': 'admin',
    'password': 'secret123',
    'confirm': 'secret123',
  };

  final validResult = schema.validate(validData);
  print('Valid: ${validResult.isValid}'); // true

  // Invalid data
  final invalidData = {
    'name': '',
    'email': 'not-an-email',
    'age': '200',
    'website': 'bad-url',
    'role': 'superadmin',
    'password': 'short',
    'confirm': 'mismatch',
  };

  final invalidResult = schema.validate(invalidData);
  print('Valid: ${invalidResult.isValid}'); // false
  print('Error count: ${invalidResult.errorCount}');

  for (final field in schema.fields) {
    if (invalidResult.hasError(field)) {
      print('$field: ${invalidResult.errorsFor(field)}');
    }
  }

  // --- JSON schema definition ---
  final jsonSchema = FormSchema.fromJson({
    'email': ['required', 'email'],
    'name': ['required', 'minLength:3', 'maxLength:100'],
    'age': ['numeric', 'between:18,120'],
    'code': [r'pattern:^\d{4}$'],
  });

  final jsonResult = jsonSchema.validate({
    'email': 'bob@example.com',
    'name': 'Bob',
    'age': '25',
    'code': '1234',
  });
  print('\nJSON schema valid: ${jsonResult.isValid}'); // true

  // --- Standalone rule usage ---
  final emailRule = Rules.email();
  print('\nEmail check: ${emailRule.validate('test@example.com')}'); // null
  print('Email check: ${emailRule.validate('invalid')}'); // error message

  // --- Custom rule ---
  final noSpaces = Rules.custom(
    (value) => value is String && !value.contains(' '),
    message: 'Must not contain spaces',
  );
  print('\nCustom rule: ${noSpaces.validate('hello')}'); // null
  print('Custom rule: ${noSpaces.validate('hello world')}'); // error

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

  final nestedResult = nestedSchema.validateNested({
    'name': 'Alice',
    'address': {'city': '', 'zip': 'bad'},
  });
  print('\nNested valid: ${nestedResult.isValid}'); // false
  print('address.city errors: ${nestedResult.errorsFor('address.city')}');
  print('address.zip errors: ${nestedResult.errorsFor('address.zip')}');

  // Extract nested sub-result
  final addressErrors = nestedResult.nested('address');
  print('Address error count: ${addressErrors.errorCount}');
}