Formix All

pub package License: MIT

The complete validation ecosystem for Dart & Flutter. One import gives you everything you need for type-safe, composable, and testable form validation.

What's Included

This umbrella package re-exports all Formix packages:

Package Description
formix_core Core validation engine with composable rules
formix_validators 50+ built-in validators for strings, numbers, dates, etc.
formix_flutter Flutter integration for TextFormField and forms
formix_test Testing utilities, matchers, and mocks

Installation

dependencies:
  formix_all: ^0.1.0
flutter pub get

Quick Start

import 'package:formix_all/formix_all.dart';

// Define validators
final emailValidator = Validate.all<String, String>([
  StringRules.required(error: 'Email is required'),
  StringRules.email(error: 'Invalid email format'),
]);

final passwordValidator = Validate.all<String, String>([
  StringRules.required(error: 'Password is required'),
  StringRules.minLength(8, error: 'At least 8 characters'),
  StringRules.hasUppercase(error: 'Need uppercase letter'),
  StringRules.hasDigit(error: 'Need a number'),
]);

// Use in Flutter forms
TextFormField(
  decoration: const InputDecoration(labelText: 'Email'),
  validator: emailValidator.toFieldValidator(),
)

// Or validate directly
final result = emailValidator.validate('test@example.com');
if (result.isValid) {
  print('Valid: ${result.value}');
} else {
  print('Errors: ${result.errors}');
}

Features

🎯 Type-Safe Validation

// Custom error types for i18n
enum EmailError { required, invalid, tooLong }

final validator = Validate.all<String, EmailError>([
  StringRules.required(error: EmailError.required),
  StringRules.email(error: EmailError.invalid),
]);

// Pattern match on results
switch (validator.validate(input)) {
  case Valid(:final value):
    saveEmail(value);
  case Invalid(:final error):
    showError(formatError(error));
}

🔗 Composable Rules

// Combine validators
final validator = Validate.all<String, String>([...]);  // AND logic
final validator = Validate.any<String, String>([...]);  // OR logic
final validator = rule1.andThen(rule2);                  // Sequential
final validator = rule.when((v) => v.isNotEmpty);       // Conditional
final validator = rule.optional();                       // Skip if null

⚡ Performance Optimized

// Cache validation results
final cached = validator.cached();
final lruCached = validator.lruCached(maxSize: 100);

// Use with real-time validation
TextFormField(
  validator: cached.toFieldValidator(),
  autovalidateMode: AutovalidateMode.onUserInteraction,
)

🧪 Testing Utilities

import 'package:formix/formix.dart';
import 'package:test/test.dart';

test('email validation', () {
  expect(emailValidator.validate('test@example.com'), isValid());
  expect(emailValidator.validate('invalid'), isInvalid());
  expect(emailValidator.validate(''), hasError('Email is required'));
});

Available Validators

StringRules

required, email, url, uuid, creditCard, minLength, maxLength, matches, contains, startsWith, endsWith, hasUppercase, hasLowercase, hasDigit, hasSpecialChar, alphanumeric, digitsOnly, and more.

NumberRules

min, max, range, positive, negative, integer, multipleOf, precision, and more.

DateRules

past, future, before, after, between, age, minimumAge, weekday, weekend, businessDay, and more.

CollectionRules

minLength, maxLength, notEmpty, unique, contains, every, any, and more.

PhoneRules

e164, usPhone, custom, and more.

FileRules

maxSize, extension, mimeType, imageOnly, documentOnly, and more.

Selective Imports

If you don't need everything, import only what you need:

// Core only (no Flutter, no validators)
import 'package:formix_core/formix_core.dart';

// Flutter integration only
import 'package:formix_flutter/formix_flutter.dart';

// Specific validator categories
import 'package:formix_validators/string.dart';
import 'package:formix_validators/number.dart';
import 'package:formix_validators/date.dart';

Complete Example

import 'package:flutter/material.dart';
import 'package:formix/formix.dart';

class RegistrationForm extends StatefulWidget {
  const RegistrationForm({super.key});

  @override
  State<RegistrationForm> createState() => _RegistrationFormState();
}

class _RegistrationFormState extends State<RegistrationForm> {
  final _formKey = GlobalKey<FormState>();

  static final _emailValidator = Validate.all<String, String>([
    StringRules.required(error: 'Email is required'),
    StringRules.email(error: 'Invalid email format'),
  ]);

  static final _passwordValidator = Validate.allCollect<String, String>([
    StringRules.required(error: 'Password is required'),
    StringRules.minLength(8, error: 'At least 8 characters'),
    StringRules.hasUppercase(error: 'Need uppercase letter'),
    StringRules.hasDigit(error: 'Need a number'),
  ]);

  @override
  Widget build(BuildContext context) {
    return Form(
      key: _formKey,
      child: Column(
        children: [
          TextFormField(
            decoration: const InputDecoration(labelText: 'Email'),
            validator: _emailValidator.toFieldValidator(),
          ),
          const SizedBox(height: 16),
          TextFormField(
            decoration: const InputDecoration(labelText: 'Password'),
            obscureText: true,
            validator: _passwordValidator.toFieldValidator(),
          ),
          const SizedBox(height: 24),
          ElevatedButton(
            onPressed: () {
              if (_formKey.currentState!.validate()) {
                // Form is valid!
              }
            },
            child: const Text('Register'),
          ),
        ],
      ),
    );
  }
}

Documentation

License

MIT License - see LICENSE file for details.


Made with ❤️ by Fady Fayez Younan

Libraries

formix_all