License: MIT ci pub package


Functional input validation library based on Fpdart, which is inspired by Formz.


For the most part, FPFormz is similar to the original Formz library, with a few notable differences:

  • FPFormz allows specifying different types for input and validated values, which can be convenient when using non-string type values (e.g. int, enum, value class, etc.).

  • It exposes validated values and errors as functional constructs such as Either or Option, making it easier to manipulate them declaratively.

  • It also provides a way to write validation logic as mixins, which you can combine to handle more complex use cases.


You can install PFFormz by adding the following entry in your pubsec.yaml:

# pubspec.yaml
  fpformz: ^0.1.0

Getting Started

FormInput And Its Derivatives

To define a validatable input, you need to write a class that extends FormInput<V, I, E> whose generic parameters correspond to the type of resulting value, input value, and potential errors, respectively:

class AgeInput extends FormInput<int, String, ValidationError> {
  const AgeInput.pristine(name, value) : super.pristine(name, value);

  const AgeInput.dirty(name, value) : super.dirty(name, value);

  Either<ValidationError, int> validate(String value) =>
      Either.tryCatch(() => int.parse(value),
              (e, s) => ValidationError(name, '$name should be a number.'));

After declaring your input, you can use either pristine or dirty constructor to create an instance:

void example() {
  // To create an unmodified ('pristine') input instance:
  final age = AgeInput.pristine('age', '');

  // Or you can create a modified ('dirty') input instance as below:
  final editedAge = AgeInput.dirty('age', '23');

  print(age.isPristine); // returns 'true'
  print(editedAge.isPristine); // returns 'false'

You can access validation information either as a functional construct, or as a nullable:

void example() {
  print(editedAge.isValid); // returns true
  print(editedAge.result); // returns Right(23)
  print(editedAge.resultOrNull); // returns 23
  print(editedAge.error); // returns None
  print(editedAge.errorOrNull); // returns null

  print(age.isValid); // returns false
  print(age.result); // returns Left(ValidationError)
  print(age.resultOrNull); // returns null
  print(age.error); // returns Some(ValidationError)
  print(age.errorOrNull); // returns ValidationError

And because most input components treat the user input as a String instance, you can simplify the type signature by extending from StringFormInput:

class NameInput extends StringFormInput<String, ValidationError> {
  const NameInput.pristine(name, value) : super.pristine(name, value);

  const NameInput.dirty(name, value) : super.dirty(name, value);

  String convert(String value) => value;

  Either<ValidationError, String> validate(String value) =>
          ? Either.left(ValidationError(name, 'The name cannot be empty.'))
          : super.validate(value);


Like with Formz, you can create a form class to host multiple input fields and validate them together:

class RegistrationForm extends Form {

  final NameInput name;
  final EmailInput email;

  const RegistrationForm({ = const NameInput.pristine('name', ''), = const EmailInput.pristine('email', '')

  get inputs => [name, email];

Then you can validate it using a similar API like that of FormInput:

void example() {
  final form = RegistrationForm();

  print(form.isPristine); // returns true
  print(form.isValid); // returns false

  print(form.result); // it 'short circuits' at the first error encountered
  print(form.errors); // but you can get all errors this way. 


You can also write reusable validation logic as a mixin:

mixin NonEmptyString<V> on FormInput<V, String, ValidationError> {
  ValidationError get whenEmpty => ValidationError(name, 'Please enter $name.');

  Either<ValidationError, V> validate(String value) =>
      value.isEmpty ? Either.left(whenEmpty) : super.validate(value);

And build a concrete input field by adding them to either BaseFormInput or StringFormInput as shown below:

class EmailInput extends StringFormInput<Email, ValidationError>
    with EmailString, NonEmptyString {
  const EmailInput.pristine(name, value) : super.pristine(name, value);

  const EmailInput.dirty(name, value) : super.dirty(name, value);

  Email convert(String value) => Email.parse(value);

It’s recommended to split each validation logic into a separate mixin rather than putting all into an input class to maximise code reuse and achieve separation of concerns (i.e. the ‘S’ in SOLID principles).

FPFormz also ships with a small collection of ready-to-use mixins like NonEmptyString , StringShorterThan, which might be expanded in future versions.

Additional Information

You can find more code examples in our test cases.


View Github