VooForms

A comprehensive, type-safe forms package for Flutter with clean architecture, atomic design pattern, and excellent developer experience.

pub package License: MIT

โœจ Features

  • ๐ŸŽฏ Type-Safe Fields - Full type safety with generics throughout
  • ๐Ÿ›๏ธ Clean Architecture - Follows SOLID principles with clear separation of concerns
  • ๐ŸŽจ Atomic Design - Components organized as atoms, molecules, and organisms
  • ๐Ÿ“ฑ Cross-Platform - Works seamlessly on iOS, Android, Web, Desktop
  • ๐ŸŽจ Material 3 - Built with the latest Material Design guidelines
  • โœ… Rich Validators - 13+ built-in validators with composable validation
  • ๐Ÿ”ง Smart Formatters - 12+ formatters for phone, credit card, currency, etc.
  • ๐ŸŽฎ Powerful Controller - Advanced form state management
  • ๐Ÿ“ Multiple Layouts - Vertical, grid, stepped, tabbed layouts
  • ๐Ÿš€ Excellent DX - Intuitive API with great IDE support

๐Ÿ“ฆ Installation

Add voo_forms to your pubspec.yaml:

dependencies:
  voo_forms: ^0.3.2

๐Ÿš€ Quick Start

Simple Form Example

import 'package:voo_forms/voo_forms.dart';

// Create a form using direct widget instantiation
final form = VooForm(
  fields: [
    VooTextField(
      name: 'username',
      label: 'Username',
      validators: [
        RequiredValidation(),
        MinLengthValidation(minLength: 3),
      ],
    ),
    VooEmailField(
      name: 'email',
      label: 'Email Address',
      validators: [
        RequiredValidation(),
        EmailValidation(),
      ],
    ),
    VooPasswordField(
      name: 'password',
      label: 'Password',
      validators: [
        RequiredValidation(),
        MinLengthValidation(minLength: 8),
        PatternValidation(
          pattern: r'^(?=.*[A-Z])(?=.*[0-9])',
          errorMessage: 'Must contain uppercase and number',
        ),
      ],
    ),
  ],
);

Organizing Fields with Sections

// Group related fields in collapsible sections
final form = VooForm(
  fields: [
    VooFormSection(
      title: 'Personal Information',
      description: 'Enter your basic details',
      isCollapsible: true,
      children: [
        VooTextField(name: 'firstName', placeholder: 'First Name'),
        VooTextField(name: 'lastName', placeholder: 'Last Name'),
        VooDateField(name: 'birthDate', label: 'Date of Birth'),
      ],
    ),
    VooFormSection(
      title: 'Contact Details',
      children: [
        VooEmailField(name: 'email', label: 'Email'),
        VooPhoneField(name: 'phone', label: 'Phone'),
      ],
    ),
  ],
);

๐ŸŽจ Field Types

VooForms provides direct widget classes for all field types:

// Text Fields
VooTextField(name: 'username', label: 'Username')
VooEmailField(name: 'email', label: 'Email')
VooPasswordField(name: 'password', label: 'Password')
VooPhoneField(name: 'phone', label: 'Phone')
VooMultilineField(name: 'bio', label: 'Bio')
VooNumberField(name: 'age', label: 'Age')

// Selection Fields
VooDropdownField<String>(
  name: 'country',
  label: 'Country',
  options: ['United States', 'United Kingdom', 'Canada'],
)
VooCheckboxField(name: 'terms', label: 'Accept Terms')
VooBooleanField(name: 'newsletter', label: 'Subscribe')

// Date & Time
VooDateField(name: 'birthday', label: 'Birthday')
VooDateFieldButton(name: 'appointment', label: 'Appointment Date')

// Numeric Fields
VooIntegerField(name: 'age', label: 'Age')
VooDecimalField(name: 'price', label: 'Price')
VooCurrencyField(name: 'salary', label: 'Salary')
VooPercentageField(name: 'discount', label: 'Discount')

// Other Types
VooFileField(name: 'avatar', label: 'Avatar')
VooListField<String>(
  name: 'tags',
  label: 'Tags',
  items: tags,
  itemBuilder: (tag) => Chip(label: Text(tag)),
)

โœ… Validators

Built-in Validators

Each validator is in its own file for better organization:

// Basic Validators
RequiredValidation()
EmailValidation()
PhoneValidation()
UrlValidation()

// String Validators
MinLengthValidation(minLength: 3)
MaxLengthValidation(maxLength: 50)
PatternValidation(pattern: r'^[A-Z]', errorMessage: 'Must start with capital')

// Numeric Validators
MinValueValidation(minValue: 0)
MaxValueValidation(maxValue: 100)
RangeValidation(minValue: 0, maxValue: 100)

// Date Validators
DateRangeValidation(
  minDate: DateTime(2020),
  maxDate: DateTime(2030),
)

// Custom Validation
CustomValidation(
  validator: (value) {
    if (value == 'admin') return 'Username not available';
    return null;
  },
)

// Combine Multiple Validators
CompoundValidation(rules: [
  RequiredValidation(),
  EmailValidation(),
])

Creating Custom Validators

class PasswordMatchValidation extends VooValidationRule<String> {
  final String Function() getPassword;
  
  PasswordMatchValidation({required this.getPassword})
    : super(errorMessage: 'Passwords do not match');
  
  @override
  String? validate(String? value) {
    if (value != getPassword()) {
      return errorMessage;
    }
    return null;
  }
}

๐Ÿ”ง Formatters

Smart input formatting for better UX:

// Phone Formatters
VooFormatters.phoneUS()              // (123) 456-7890
VooFormatters.phoneInternational()   // +1 234 567 8900

// Card & Financial
VooFormatters.creditCard()           // 1234 5678 9012 3456
VooFormatters.currency(symbol: '\$') // $1,234.56

// Date Formatters
VooFormatters.dateUS()               // MM/DD/YYYY
VooFormatters.dateEU()               // DD/MM/YYYY
VooFormatters.dateISO()              // YYYY-MM-DD

// Text Formatters
VooFormatters.uppercase()
VooFormatters.lowercase()
VooFormatters.alphanumeric()
VooFormatters.numbersOnly()

// US Specific
VooFormatters.ssn()                  // 123-45-6789
VooFormatters.zipCode()              // 12345 or 12345-6789

// Custom Patterns
VooFormatters.mask('###-##-####')    // Custom mask
VooFormatters.pattern(
  pattern: 'AA-####',
  patternMapping: {
    'A': RegExp(r'[A-Z]'),
    '#': RegExp(r'[0-9]'),
  },
)

๐ŸŽญ Field Options

Customize field appearance and behavior:

VooField.text(
  name: 'username',
  options: VooFieldOptions(
    // Label positioning
    labelPosition: LabelPosition.floating,  // or above, left, placeholder, hidden
    
    // Field styling
    fieldVariant: FieldVariant.filled,     // or outlined, underlined, ghost, rounded, sharp
    
    // Validation behavior
    validateOnChange: true,
    validateOnFocusLost: true,
    
    // Error display
    errorDisplayMode: ErrorDisplayMode.always,  // or onFocus, onSubmit
    
    // Focus behavior
    focusBehavior: FocusBehavior.next,     // or submit, none
  ),
)

Preset Options

Use built-in presets for consistency:

options: VooFieldOptions.material     // Material Design defaults
options: VooFieldOptions.comfortable  // Spacious layout
options: VooFieldOptions.compact      // Dense layout
options: VooFieldOptions.minimal      // Minimal styling

๐ŸŽฎ Form Controller

Advanced form control:

final controller = VooFormController(
  form: form,
  onFieldChange: (field, value) {
    print('${field.name} changed to $value');
  },
  onValidationChange: (isValid) {
    print('Form valid: $isValid');
  },
);

// Programmatic control
controller.setValue('username', 'john_doe');
controller.getValue('username');  // 'john_doe'
controller.validate();            // Validate all fields
controller.validateField('email'); // Validate specific field
controller.reset();               // Reset to initial values
controller.clear();               // Clear all values
controller.submit();              // Submit the form

// Field management
controller.enableField('email');
controller.disableField('password');
controller.showField('optional');
controller.hideField('advanced');
controller.focusField('username');

๐Ÿ“ Layouts

Multiple layout options:

Vertical Layout (Default)

VooFormVerticalLayout(
  fields: fields,
  spacing: 16.0,
)

Grid Layout

VooFormGridLayout(
  fields: fields,
  columns: 2,
  spacing: 16.0,
  crossAxisSpacing: 12.0,
)

Stepped Layout (Wizard)

VooFormSteppedLayout(
  steps: [
    VooFormStep(
      title: 'Personal Info',
      fields: [/* fields */],
    ),
    VooFormStep(
      title: 'Contact',
      fields: [/* fields */],
    ),
  ],
)

Tabbed Layout

VooFormTabbedLayout(
  tabs: [
    VooFormTab(
      label: 'Basic',
      fields: [/* fields */],
    ),
    VooFormTab(
      label: 'Advanced',
      fields: [/* fields */],
    ),
  ],
)

Support for dynamic dropdown options:

// Async dropdown with search
VooField.dropdown<City>(
  name: 'city',
  label: 'City',
  enableSearch: true,
  asyncOptionsLoader: (query) async {
    final cities = await api.searchCities(query);
    return cities.map((city) => VooFieldOption(
      value: city,
      label: city.name,
      subtitle: city.state,
      icon: Icons.location_city,
    )).toList();
  },
)

๐Ÿงช Testing

VooForms is designed for easy testing:

testWidgets('Form submission', (tester) async {
  final form = VooForm(
    fields: [
      VooField.text(name: 'username'),
      VooField.email(name: 'email'),
    ],
    onSubmit: expectAsync1((values) {
      expect(values['username'], 'testuser');
      expect(values['email'], 'test@example.com');
    }),
  );
  
  await tester.pumpWidget(MaterialApp(home: form));
  
  // Enter values
  await tester.enterText(
    find.byType(TextFormField).first,
    'testuser',
  );
  await tester.enterText(
    find.byType(TextFormField).last,
    'test@example.com',
  );
  
  // Submit
  await tester.tap(find.text('Submit'));
  await tester.pump();
});

๐Ÿ›๏ธ Architecture

VooForms follows clean architecture principles:

Presentation Layer (UI/Widgets)
        โ†“
Domain Layer (Entities/Business Logic)  
        โ†“
Data Layer (Models/Repositories)

Atomic Design Pattern

  • Atoms: Basic input widgets (text field, checkbox, etc.)
  • Molecules: Composed components (field with label, field with error)
  • Organisms: Complete forms and layouts

Key Principles

  • One class per file - Better organization and navigation
  • No _buildXXX methods - Using helper classes instead
  • Clean imports - No relative imports, properly ordered
  • Type safety - Generics used throughout
  • SOLID principles - Single responsibility, open/closed, etc.

๐ŸŽจ Theming

VooForms respects your app's Material theme:

MaterialApp(
  theme: ThemeData(
    colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
    useMaterial3: true,
  ),
  home: MyApp(),
)

๐Ÿ“– Documentation

๐Ÿค Contributing

Contributions welcome! Please ensure:

  • Follow clean architecture principles
  • One class per file
  • No _buildXXX methods returning widgets
  • Add tests for new features
  • Update documentation

๐Ÿ“„ License

MIT License - see LICENSE file for details.

๐Ÿš€ What's New in 0.1.14

  • Major Technical Debt Cleanup - Eliminated all _buildXXX methods
  • Better Organization - 25+ classes split into individual files
  • Improved DX - Each validator/formatter in its own file
  • Clean Architecture - Proper separation of concerns throughout
  • Enhanced Testing - 92.6% test success rate

See CHANGELOG.md for full details.

Libraries

voo_forms