Form Builder Validators offer a set of validators for any FormField widget or widgets that extend the FormField class - e.g., TextFormField, DropdownFormField, et cetera. It provides standard ready-made validation rules and a way to compose new validation rules combining multiple rules, including custom ones.
It also includes the l10n / i18n of error text messages to multiple languages.
We are looking for maintainers to contribute to the development and maintenance of Flutter Form Builder Ecosystem. It is very important to keep the project alive and growing, so we need your help to keep it up to date and with new features. You can contribute in many ways, we describe some of them in Support section.
- Features
 - Validators
 - Supported languages
 - Use
 - Migrations
 - How this package works
 - Support
 - Roadmap
 - Ecosystem
 - Thanks to
 
- Ready-made validation rules
 - Compose multiple reusable validation rules
 - Default error messages in multiple languages
 
This package comes with several of the most common FormFieldValidators such as required, numeric, email,
URL, min, max, minLength, maxLength, minWordsCount, maxWordsCount, IP, credit card, etc., with default errorText messages.
Generally, the validators are separated in three main groups:
- Field Requirement Validators: makes a field optional or required.
 - Type Validators: checks the type of the field.
 - Other validators: make any other kind of check that is not related to null/emptiness or type validation.
 
Generally, we build a validator composing those three types in the following way:
<fieldRequirementValidator>(<typeValidator>(<otherValidator>()))
For example:
- Make the field required, check if it is of type 
numor aStringparsable to num and then check if it is greater than 10.Validators.required(Validators.num(Validators.greaterThan(10))) 
As easy as that! This validator is meant to be used with form fields like in the following example:
// Example from the folder `examples`
TextFormField(
  decoration: const InputDecoration(
    labelText: 'Min Value Field',
    prefixIcon: Icon(Icons.exposure_neg_1),
  ),
  keyboardType: TextInputType.number,
  validator: Validators.required(Validators.num(Validators.greaterThan(10))),
  textInputAction: TextInputAction.next,
  autovalidateMode: AutovalidateMode.always,
);Validators.equalLength(expectedLength): Checks if the field contains a collection (must be aString,Iterable, orMap) with length equalsexpectedLength.Validators.minLength(min): Checks if the field contains a collection (must be aString,Iterable, orMap) with length greater than or equal tomin.Validators.maxLength(max): Checks if the field contains a collection (must be aString,Iterable, orMap) with length less than or equal tomax.Validators.betweenLength(min, max): Checks if the field contains a collection (must be aString,Iterable, orMap) with length betweenminandmax, inclusive.
Validators.and(validators): Validates the field by requiring it to pass all validators in thevalidatorslist.Validators.or(validators): Validates the field by requiring it to pass at least one of the validators in thevalidatorslist.
Validators.validateIf(condition, v): Validates the field with validatorvonly ifconditionistrue.Validators.skipIf(condition, v): Validates the field with validatorvonly ifconditionisfalse.
Validators.debugPrintValidator(): Print, for debug purposes, the user input value.
Validators.equal(value): Checks if the field contains an input that is equal tovalue(==).Validators.notEqual(value): Checks if the field contains an input that is not equal tovalue(!=).
Validators.required(next): Makes the field required by checking if it contains a non-null and non-empty value, passing it to thenextvalidator as a not-nullable type.Validators.optional(next): Makes the field optional by passing it to thenextvalidator if it contains a non-null and non-empty value. If the field is null or empty, null is returned.Validators.validateWithDefault(defaultValue, next): Validates the field withnextvalidator. If the input is null, it uses thedefaultValueinstead.
Validators.transformAndValidate<IN, OUT>(transformFunction, next:next): Transforms an input fromINtype toOUTtype through the functiontransformFunctionand pass it to thenextvalidator.
Validators.after(reference): Checks if the field contains aDateTimethat is afterreference.Validators.before(reference): Checks if the field contains aDateTimethat is beforereference.Validators.betweenDateTime(minReference, maxReference): Checks if the field contains aDateTimethat is afterminReferenceand beforemaxReference.
Validators.maxFileSize(max, base:base): Checks if the field contains a file size that is less than themaxsize withbase1000 or 1024.
Validators.bic(): Checks if the field contains a valid BIC (Bank Identifier Code).Validators.iban()- Checks if the field contains a valid IBAN (International Bank Account Number).
Validators that check a generic type user input.
Validators.inList(values): Checks if the field contains a value that is in the listvalues.Validators.notInList(values): Checks if the field DOES NOT contain a value that is in the listvalues.Validators.isTrue(): Checks if the field contains a boolean or a parsableStringof thetruevalue.Validators.isFalse(): Checks if the field contains a boolean or a parsableStringof thefalsevalue.Validators.satisfy(condition): Checks if the field satisfies thecondition.
Validators.colorCode(): checks if the field contains a valid color.Validators.isbn(): checks if the field contains a valid ISBN code.
Validators.ip(): Checks if the field contains a properly formattedInternet Protocol(IP) address. It may check for eitherIPv4, orIPv6or even for both.Validators.url(): Checks if the field contains a properly formattedUniform Resource Locators(URL).Validators.macAddress(): Checks if the field is a valid MAC address.
Validators.between(min, max): Checks if the field contains a number that is in the inclusive range [min, max].Validators.greaterThan(reference): Checks if the field contains a number that is greater thanreference.Validators.greaterThanOrEqualTo(reference): Checks if the field contains a number that is greater than or equal toreference.Validators.lessThan(reference): Checks if the field contains a number that is less thanreference.Validators.lessThanOrEqualTo(reference): Checks if the field contains a number that is less than or equal toreference.
Validators.matchesAllowedExtensions(extensions): Checks if the field contains aStringthat is in the listextensions.
Validators.contains(substring)- Checks if the field contains thesubstring.Validators.startsWith()- Checks if the field starts withprefix.Validators.endsWith()- Checks if the field ends withsuffix.Validators.lowercase()- Checks if the field does not have any upper case char.Validators.uppercase()- Checks if the field does not have any lower case char.Validators.hasMinUppercaseChars(min:min)- Checks if the field has a minimum number of uppercase chars.Validators.hasMinLowercaseChars(min:min)- Checks if the field has a minimum number of lowercase chars.Validators.hasMinNumericChars(min:min)- Checks if the field has a minimum number of numeric chars.Validators.hasMinSpecialChars(min:min)- Checks if the field has a minimum number of special chars.Validators.match(regExp)- Checks if the field matches with the regular expressionregExp.Validators.notMatch(regExp)- Checks if the field does not match with the regular expressionregExp.Validators.uuid()- Checks if the field is a valid Universally Unique Identifier (UUID).Validators.maxWordsCount(max)- Checks if the field contains no more thanmaxwords.Validators.minWordsCount(min)- Checks if the field contains no less thanminwords.
Validators.string(next): Checks if the field contains a validStringand passes the input asStringto thenextvalidator.Validators.int(next): Checks if the field contains a validintor parsableStringtointand passes the input asintto thenextvalidator.Validators.double(next): Checks if the field contains a validdoubleor parsableStringtodoubleand passes the input asdoubleto thenextvalidator.Validators.num(next): Checks if the field contains a validnumor parsableStringtonumand passes the input asnumto thenextvalidator.Validators.bool(next): Checks if the field contains a validboolor parsableStringtobooland passes the input asboolto thenextvalidator.Validators.dateTime(next): Checks if the field contains a validDateTimeor parsableStringtoDateTimeand passes the input asDateTimeto thenextvalidator.
Validators.email(): Checks if the field contains a valid email.Validators.password(): Checks if the field contains a valid password. A password may require some conditions to be met in order to be considered as valid.Validators.phoneNumber(): Checks if the field contains a valid phone number.
Validators support default errorText messages in these languages:
- Albanian (al)
 - Arabic (ar)
 - Bangla (bn)
 - Bosnian (bs)
 - Bulgarian (bg)
 - Catalan (ca)
 - Chinese Simplified (zh_Hans)
 - Chinese Traditional (zh_Hant)
 - Croatian (hr)
 - Czech (cs)
 - Danish (da)
 - Dutch (nl)
 - English (en)
 - Estonian (et)
 - Finnish (fi)
 - Farsi/Persian (fa)
 - French (fr)
 - German (de)
 - Greek (el)
 - Hebrew (he)
 - Hungarian (hu)
 - Hindi (hi)
 - Indonesian (id)
 - Italian (it)
 - Japanese (ja)
 - Kurdish (ku)
 - Korean (ko)
 - Khmer (km)
 - Lao (lo)
 - Latvian (lv)
 - Malay (ms)
 - Mongolian (mn)
 - Norwegian (no)
 - Polish (pl)
 - Portuguese (pt)
 - Romanian (ro)
 - Russian (ru)
 - Slovak (sk)
 - Slovenian (sl)
 - Spanish (es)
 - Swahili (sw)
 - Swedish (se)
 - Tamil(ta)
 - Thai (th)
 - Turkish (tr)
 - Ukrainian (uk)
 - Vietnamese (vi)
 
And you can still add your custom error messages.
The default error message is in English. To allow for localization of default error messages within your app, add FormBuilderLocalizations.delegate in the list of your app's localizationsDelegates.
For example, in your MaterialApp:
return MaterialApp(
    supportedLocales: [
        Locale('de'),
        Locale('en'),
        Locale('es'),
        Locale('fr'),
        Locale('it'),
        ...
    ],
    localizationsDelegates: [
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        FormBuilderLocalizations.delegate, // here
    ],In the following example, we have a required field for the Name of the user:
TextFormField(
    decoration: InputDecoration(labelText: 'Name'),
    autovalidateMode: AutovalidateMode.always,
    validator: Validators.required(),
),See pub.dev example tab or github code for more details
The Validators class comes with a handy static function named and(), which takes a list of Validator functions. Composing allows you to create once and reuse validation rules across multiple fields, widgets, or apps.
On validation, each validator is run, and if any validator returns a non-null value (i.e., a String), validation fails, and the errorText for the field is set as the first returned string or as a composition of all failures.
In the following example, we have a form field for the user's Age, which is required, must be numeric (with custom error message if not numeric given by "La edad debe ser numérica."), must be less than 70, and, finally, must be non-negative (with custom non-negative validator):
TextFormField(
    decoration: InputDecoration(labelText: 'Age'),
    keyboardType: TextInputType.number,
    autovalidateMode: AutovalidateMode.always,
    validator: Validators.required(
      Validators.and(<Validator<String>>[
        Validators.num(Validators.lessThan(70), (_) => 'La edad debe ser numérica.'),
        /// Include your own custom `Validator` function, if you want.
        /// Ensures positive values only. We could also have used `Validators.greaterThanOrEqualTo(0)` instead.
        (String? val) {
            if (val != null) {
              final num? number = num.tryParse(val);
              if (number == null) return null;
              if (number < 0) return 'We cannot have a negative age';
            }
            return null;
        }
      ]))
),see override_form_builder_localizations_en for more detail.
- Deprecate 
FormBuilderValidatorsclass with its static methods as validators. Instead, you should useValidatorsclass. - Check the file migrations for detailed instructions.
 
- All validators now first check for null or empty value and return an error if so. You can set 
checkNullOrEmptytofalseif you want to avoid this behavior. dateString()changed todate()for constancy in api naming. Simply change the name to fix the code.- The positional parameter for the validator  
match()is not aStringpattern anymore, but aRegExpregex. 
Remove context as a parameter to validator functions. For example, FormBuilderValidators.required(context) becomes FormBuilderValidators.required() without context passed in.
This package comes with several of the most common Validators and FormFieldValidators such as required, numeric, mail,
URL, min, max, minLength, maxLength, minWordsCount, maxWordsCount, IP, credit card, etc., with default errorText messages.
- But what is a 
FormFieldValidator? It is a function that takes user input of any nullable type and returns either null (for valid input) or an error message string (for invalid input). The input parameter can beString?,int?,num?, or any other nullable type that represents user-provided data. The input must be nullable since form fields may be empty, resulting in null values. 
For example, here's a FormFieldValidator that checks if a number is even:
String? isEven(int? userInput) {
  return (userInput != null && userInput % 2 == 0) ? null : 'This field must be even';
}The challenge with the previous approaches (< v.11.x.x) is that we must handle null checks in every validator implementation. This leads to:
- Tangled validator logic: Each validator must handle both validation rules and null checking,making the code harder to understand and maintain.
 - Code duplication: When composing validators, the same null-checking logic must be repeated across multiple validators, violating the DRY principle.
 - Poor separation of concerns: A validator named 
isEvenshould focus solely on checking if a number is even. It shouldn't need to handle null cases or type validation - those are separate responsibilities that deserve their own focused validators. - Verbose implementations: The combination of null checks, type validation, and business logic in each validator results in unnecessarily lengthy code that's harder to test and maintain.
 - Potential problems with null safety: imagine an unsafe version of isEven that simply uses the 
!operator to throw an error during runtime if the user input is null: 
/// `userInput` may not be null.
String? isEven(int? userInput) {
  return (userInput! % 2 == 0) ? null : 'This field must be even';
}From version 12.0.0, this package introduces a more precise approach that separates null-value handling from the actual validation logic. Instead of the previous isEven implementation, we can compose two focused validators:
String? Function(int) isEven(){ 
  return (int userInput){
    return (userInput % 2 == 0) ? null:'This field must be even';
  };
}
String? Function(int?) required(String? Function(int) next){
  return (int? userInput) {
    return (userInput != null) ? next(userInput):'This field is required';
  };
}
// Important: isEven() does not return a FormFieldValidator, but the composition required(isEven()), does.
final validator = required(isEven());By introducing this level of indirection, we achieve:
- Clean separation between null checks and validation logic
 - More composable validators
 - Specific error messages for missing vs invalid input
 - Type-safe validator chains
 
You have some ways to contribute to this package.
- Beginner: Reporting bugs or requesting new features
 - Intermediate: Answer questions, implement new features (from issues or not), and create pull requests
 - Advanced: Join organization like a member and help to code, manage issues, discuss new features, and other things
 
See the contribution file for more details
We welcome efforts to internationalize/localize the package by translating the default validation errorText strings for built-in validation rules.
- 
Add ARB files
Create one ARB file inside the
lib/l10nfolder for each locale you need to add support. Name the files in the following way:intl_<LOCALE_ISO_CODE>.arb. For example:intl_fr.arborintl_fr_FR.arb. - 
Translate the error messages
Copy and paste the contents of
intl_en.arbinto your newly created ARB file. Then translate the error messages by overwriting the default messages. - 
Generate localization code
To generate boilerplate code for localization, run the generate command inside the package directory where
pubspec.yamlfile is located:flutter gen-l10nThe command will automatically create/update files inside the
lib/localizationdirectory, including your newly added locale support. The files in here are only necessary for local development and will not be committed to Github. - 
Update README
Remember to update README, adding the new language (and language code) under Supported languages section in alphabetic order, so that everyone knows your new language is now supported!
 - 
Submit PR
Submit your PR and be of help to millions of developers all over the world!
 
- Add a new validator to one of the folders in the 
src/validatorsfolder. - Implement it as a function which returns 
Validator<T>withTbeing the type of the user input to be validated. - Add the @macro tag for documentation using the template name: 
validator_<validator_snake_case_name>. This will refer to the actual documentation, which will be on theValidatorsstatic method. - If your validator uses localized error message, you can use 
FormBuilderLocalizations.current.<name_of_localized_message>Next we have the example of the numeric validatorgreaterThan. As we can see, it has its@macrodocstring, it uses a localized error message (FormBuilderLocalizations.current.greaterThanErrorText(reference)) and it returnsValidator<T extends num>:/// {@macro validator_greater_than} Validator<T> greaterThan<T extends num>(T reference, {String Function(T input, T reference)? greaterThanMsg}) { return (T input) { return input > reference ? null : greaterThanMsg?.call(input, reference) ?? FormBuilderLocalizations.current.greaterThanErrorText(reference); }; }
 - Add the validator as static method to 
form_builder_validators.dartin theValidatorsclass. Do not forget to add documentation to the new static method, using the@templateelement to give a name to the docstring. Follow the pattern:validator_<validator_snake_case_name>. Here, an example of how to add the static method of the validator to theValidatorsclass:final class Validators{ //Other validators... /// {@template validator_greater_than} /// Creates a validator function that checks if a numeric input exceeds `reference`. /// /// ## Type Parameters /// - `T`: A numeric type that extends [num], allowing `int`, `double` or /// `num` validations /// /// ## Parameters /// - `reference` (`T`): The threshold value that the input must exceed /// - `greaterThanMsg` (`String Function(T input, T reference)?`): Optional custom error /// message generator that takes the input value and threshold as parameters /// /// ## Returns /// Returns a [Validator] function that: /// - Returns `null` if the input is greater than the threshold value `reference` /// - Returns an error message string if validation fails, either from the custom /// `greaterThanMsg` function or the default localized error text /// /// ## Examples /// ```dart /// // Basic usage with integers /// final ageValidator = greaterThan<int>(18); /// /// // Custom error message /// final priceValidator = greaterThan<double>( /// 0.0, /// greaterThanMsg: (_, ref) => 'Price must be greater than \$${ref.toStringAsFixed(2)}', /// ); /// ``` /// /// ## Caveats /// - The validator uses strict greater than comparison (`>`) /// {@endtemplate} static Validator<T> greaterThan<T extends c.num>(T reference, {String Function(c.num input, c.num reference)? greaterThanMsg}) => val.greaterThan(reference, greaterThanMsg: greaterThanMsg); } // OBS.: the core package is imported with prefix c to avoid name collision!
 - Implement tests
 - Add to validators with name and description
 - Add message error translated on all languages (yes, all languages). To accomplish this need:
a. Add property to all 
intl_*.arbfiles, in alphabetic order. b. Translate message in all languages. c. Runflutter gen-l10ncommand - Run dart 
dart fix --applyanddart format . - Submit PR
 
You can ask questions or search for answers on Github discussion or on StackOverflow
Donate or become a sponsor of Flutter Form Builder Ecosystem
Take a look at our fantastic ecosystem and all packages in there