best_form_validator

pub package License pub points

Demo

Best Form Validator Demo

A high-performance, zero-initialization form validation package for Flutter and Dart.

Instant. Context-free. No async setup.
Drop validators directly into TextFormField.validator โ€” no BuildContext, no await, no boilerplate.

Why best_form_validator?

Feature Standard Flutter best_form_validator
Phone regex Write your own Built-in 52 countries ๐ŸŒ
Local number formats Manual conversion Auto-normalized โœ…
Async startup N/A Zero-init (synchronous) ๐Ÿš€
Translations Manual if-else 25 languages built-in
Chaining validators Nested callbacks MultiValidator([...])
URL validation Write your own Built-in HTTP/HTTPS โœ…
Financial validation External packages Native Luhn / IBAN ๐Ÿ’ณ
Unicode names ASCII regex only Arabic, CJK, Devanagariโ€ฆ

Features

  • Zero-Initialization โ€” synchronous, no startup code required
  • Phone Validation โ€” 52 countries, accepts local formats (03001234567) and international formats (+923001234567)
  • GB / UK alias โ€” both 'GB' (ISO 3166-1) and 'UK' work interchangeably
  • Email & Password โ€” battle-tested regex + configurable strength rules
  • URL Validation โ€” HTTP/HTTPS URLs with proper scheme and host checks
  • Password Strength โ€” PasswordValidator.getPasswordStrength() returns a 0โ€“5 score
  • Unicode Names โ€” Latin, Arabic, Urdu, Hindi, Chinese, Japanese, Korean, and more
  • Age Validation โ€” handles the birthday-not-yet-occurred edge case correctly
  • Time Validation โ€” strict HH:mm / HH:mm:ss (rejects 25:99:99)
  • Financial Checks โ€” Credit Card (Luhn Mod-10) & IBAN (MOD-97), both publicly exported
  • MultiValidator โ€” chain validators, returns the first error
  • Localization โ€” 25 language error messages, runtime-switchable
  • PhoneInputFormatter โ€” TextInputFormatter for auto-spacing phone numbers

Installation

dependencies:
  best_form_validator: ^1.4.0
flutter pub get

Quick Start

import 'package:best_form_validator/best_form_validator.dart';

TextFormField(
  validator: (value) => Validators.validateEmail(value),
)

No initialization needed. Works immediately.


Localization Setup

Add FormLocalizations.delegate to your MaterialApp and call Validators.setLocale in the builder:

import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:best_form_validator/best_form_validator.dart';

MaterialApp(
  localizationsDelegates: [
    GlobalMaterialLocalizations.delegate,
    GlobalWidgetsLocalizations.delegate,
    GlobalCupertinoLocalizations.delegate,
    FormLocalizations.delegate,
  ],
  supportedLocales: const [
    Locale('en'), Locale('es'), Locale('fr'), Locale('de'),
    Locale('ar'), Locale('ur'), Locale('hi'), Locale('zh'),
    Locale('pt'), Locale('ru'), Locale('it'), Locale('tr'),
    Locale('ja'), Locale('ko'), Locale('id'), Locale('ms'),
    Locale('th'), Locale('vi'), Locale('nl'), Locale('sv'),
    Locale('no'), Locale('fi'), Locale('da'), Locale('el'),
    Locale('fa'),
  ],
  builder: (context, child) {
    Validators.setLocale(Localizations.localeOf(context));
    return child!;
  },
)

Usage

Email

Validators.validateEmail('user@example.com');  // null (valid)
Validators.validateEmail('');                  // 'Email is required'
Validators.validateEmail('bad-email');         // 'Enter a valid email'

// Custom messages
Validators.validateEmail(
  'bad',
  requiredError: 'Please enter your email',
  invalidError: 'That email looks wrong',
);

URL

Validators.validateUrl('https://example.com');   // null (valid)
Validators.validateUrl('http://localhost:8080'); // null (valid)
Validators.validateUrl('example.com');           // error (missing scheme)
Validators.validateUrl('');                      // 'URL is required'

// Accept bare domains (prepends https://)
Validators.validateUrl('example.com', allowMissingScheme: true); // null (valid)

// Optional field
Validators.validateUrl('', isRequired: false);  // null (valid)

// Custom messages
Validators.validateUrl(
  'bad',
  requiredError: 'Please enter your website',
  invalidError: 'That URL looks wrong',
);

Phone โ€” international & local formats

Both +923001234567 and 03001234567 are accepted for Pakistan:

// International format
Validators.validatePhone('+12025551234', 'US');      // null (valid)
Validators.validatePhone('+923001234567', 'PK');     // null (valid)

// Local format โ€” auto-normalized before validation
Validators.validatePhone('03001234567', 'PK');       // null (valid)
Validators.validatePhone('07911123456', 'GB');       // null (valid)

// Both GB and UK are accepted for the United Kingdom
Validators.validatePhone('+447911123456', 'GB');     // null (valid)
Validators.validatePhone('+447911123456', 'UK');     // null (valid)

Normalize a local number to international format independently:

String intl = Validators.normalizePhone('03001234567', 'PK');
// โ†’ '+923001234567'

Supported countries (52): US, CA, GB, UK, FR, DE, IN, AU, BR, CN, JP, MX, RU, ZA, NG, EG, KE, GH, SA, AE, IT, ES, SE, NO, FI, DK, NL, BE, CH, AT, PT, GR, TR, IR, PK, BD, LK, TH, MY, SG, ID, PH, VN, KR, HK, TW, NZ, AF, AL, DZ, AR, AZ, BH

TextFormField(
  decoration: InputDecoration(labelText: 'Phone', hintText: '+1 234 567 8900'),
  inputFormatters: [PhoneInputFormatter()],       // auto-formats as user types
  validator: (v) => Validators.validatePhone(v, 'US'),
  keyboardType: TextInputType.phone,
)

Password

// Basic (min 6 chars)
Validators.validatePassword('secret');

// Full strength check
Validators.validatePassword(
  'MyP@ssw0rd',
  checkLength: true,
  minLength: 8,
  checkNumberAndLetter: true,
  checkSpecialCharacter: true,
  checkLowerCase: true,
  checkUpperCase: true,
);

// Password strength score (0โ€“5)
int score = PasswordValidator.getPasswordStrength('MyP@ssw0rd'); // 5

Name โ€” Unicode aware

Validators.validateName('John Doe');      // null โ€“ Latin
Validators.validateName('Josรฉ Garcรญa');   // null โ€“ accented
Validators.validateName('ู…ุญู…ุฏ ุนู„ูŠ');     // null โ€“ Arabic
Validators.validateName('็Ž‹่Šณ');          // null โ€“ Chinese
Validators.validateName('เคฐเคพเคœ เค•เฅเคฎเคพเคฐ');    // null โ€“ Hindi (Devanagari)
Validators.validateName('Mary-Jane');    // null โ€“ hyphenated
Validators.validateName("O'Brien");      // null โ€“ apostrophe
Validators.validateName('John123');      // error โ€“ digits not allowed

Age

Validators.validateAge('2000-01-15', 18);          // null if โ‰ฅ18
Validators.validateAge(DateTime(2000, 1, 15), 18); // DateTime also accepted

Validators.validateAge(
  '2010-06-01',
  18,
  invalidError: 'You must be at least 18 years old',
);

Date

Validators.validateDate('2024-01-15'); // null (valid)
Validators.validateDate('not-a-date'); // error

Time

Validators.validateTime('14:30');      // null โ€“ HH:mm
Validators.validateTime('14:30:00');   // null โ€“ HH:mm:ss
Validators.validateTime('25:99:99');   // error โ€“ out of range (fixed in v1.3.0)
Validators.validateTime('00:00:00');   // null โ€“ midnight

Financial โ€” Credit Card & IBAN

// Credit card (Luhn Mod-10) โ€” spaces and dashes stripped automatically
Validators.validateCreditCard('4111 1111 1111 1111'); // null (valid Visa test)
Validators.validateCreditCard('1234 5678 9012 3456'); // error

// Direct class usage
FinancialValidators.validateCreditCard('4111111111111111');

// IBAN (MOD-97) โ€” spaces stripped, case-insensitive
Validators.validateIBAN('GB82 WEST 1234 5698 7654 32'); // null (valid)
Validators.validateIBAN('INVALID');                      // error

// In a TextFormField
TextFormField(
  validator: (v) => Validators.validateCreditCard(v),
  keyboardType: TextInputType.number,
)

MultiValidator โ€” chain validators

TextFormField(
  validator: MultiValidator([
    (v) => Validators.validateName(v),
    (v) => (v != null && v.length < 2) ? 'Too short' : null,
  ]),
)

Complete Form Example

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

class RegistrationForm extends StatefulWidget {
  const RegistrationForm({super.key});
  @override
  State<RegistrationForm> createState() => _RegistrationFormState();
}

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

  void _submit() {
    if (_formKey.currentState!.validate()) {
      ScaffoldMessenger.of(context)
          .showSnackBar(const SnackBar(content: Text('Success!')));
    }
  }

  @override
  Widget build(BuildContext context) {
    return Form(
      key: _formKey,
      child: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          TextFormField(
            decoration: const InputDecoration(labelText: 'Full Name'),
            validator: (v) => Validators.validateName(v),
          ),
          const SizedBox(height: 12),
          TextFormField(
            decoration: const InputDecoration(labelText: 'Email'),
            validator: (v) => Validators.validateEmail(v),
            keyboardType: TextInputType.emailAddress,
          ),
          const SizedBox(height: 12),
          TextFormField(
            decoration: const InputDecoration(
              labelText: 'Phone',
              hintText: '+1 555 000 0000 or local format',
            ),
            inputFormatters: [PhoneInputFormatter()],
            validator: (v) => Validators.validatePhone(v, 'US'),
            keyboardType: TextInputType.phone,
          ),
          const SizedBox(height: 12),
          TextFormField(
            decoration: const InputDecoration(
              labelText: 'Password',
              helperText: 'Min 8 chars, upper, lower, number, special',
            ),
            obscureText: true,
            validator: (v) => Validators.validatePassword(
              v,
              checkLength: true,
              minLength: 8,
              checkNumberAndLetter: true,
              checkSpecialCharacter: true,
              checkLowerCase: true,
              checkUpperCase: true,
            ),
          ),
          const SizedBox(height: 12),
          TextFormField(
            decoration: const InputDecoration(
              labelText: 'Birth Date',
              hintText: 'yyyy-MM-dd',
            ),
            validator: (v) => Validators.validateAge(v, 18,
                invalidError: 'Must be at least 18 years old'),
          ),
          const SizedBox(height: 24),
          ElevatedButton(
            onPressed: _submit,
            child: const Text('Register'),
          ),
        ],
      ),
    );
  }
}

API Reference

Validators (convenience faรงade)

Method Description
validateEmail(value) Standard email format
validateUrl(value, {...}) HTTP/HTTPS URL with scheme and host checks
validatePhone(value, isoCode) 52 countries, auto-normalizes local formats
normalizePhone(value, isoCode) Convert local to international format
validatePassword(value, {...}) Configurable password rules
validateName(value) Unicode-aware name validation
validateAge(value, minimumAge) Minimum age from birth date
validateDate(value) Parseable date string
validateTime(value) Strict HH:mm or HH:mm:ss
validateCreditCard(value) Luhn algorithm (Mod-10)
validateIBAN(value) ISO 13616 MOD-97
setLocale(locale) Set global error message language

PasswordValidator

Method Description
validate(value, {...}) Same as Validators.validatePassword
getPasswordStrength(value) Returns int 0โ€“5 entropy score

PhoneValidator

Method Description
validatePhoneNumber(value, isoCode) Direct phone validation
normalizePhone(value, isoCode) Local โ†’ international conversion

UrlValidator

Method Description
validate(value, {...}) Same as Validators.validateUrl

FinancialValidators

Method Description
validateCreditCard(value) Luhn check, strips spaces/dashes
validateIBAN(value) MOD-97 check, strips spaces

MultiValidator

MultiValidator([validator1, validator2, ...])

Calls each validator in order and returns the first non-null error.

PhoneInputFormatter

A TextInputFormatter that auto-spaces phone numbers as the user types (+XXX XXX XXX XXX).


Supported Languages (25)

en es fr de ar ur hi zh pt ru it tr ja ko id ms th vi nl sv no fi da el fa


Platform Support

Android iOS Web Windows macOS Linux
โœ… โœ… โœ… โœ… โœ… โœ…

Requirements

  • Dart SDK: >=3.0.0 <4.0.0
  • Flutter: >=3.0.0
  • Dependencies: intl ^0.20.2 (only direct dependency)

Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/my-feature)
  3. Commit your changes (git commit -m 'Add my feature')
  4. Push to the branch (git push origin feature/my-feature)
  5. Open a Pull Request

Please file bugs and feature requests in our issue tracker.


License

MIT โ€” see LICENSE.


Maintained By

GreeLogix โ€” Flutter, Laravel & AI Development Agency
๐ŸŒ https://greelogix.com
๐Ÿ“ฉ hello@greelogix.com


Other Flutter Packages by GreeLogix

Package Description
quick_popup_manager Smart popup, dialog & overlay management
smart_form_toolkit Advanced form toolkit with ready-to-use widgets
safe_json_mapper Type-safe JSON parsing and mapping
flutter_telescope Debugging, logging & app insights toolkit

See CHANGELOG.md for a full history of changes.

Libraries

best_form_validator
A comprehensive Flutter package for form validation.