Form validations with Bloc + Verify
Tags: flutter, dart, bloc
Published: 2020-04-06 13:21:25

---
title: Form validations with Bloc + Verify
published: true
description:
tags: flutter, dart, bloc
---

Not too long ago I published a Dart package to pub.dev called [verify](https://pub.dev/packages/verify#-readme-tab-),
Its a package to do validations. In this post I want to overview some of its
capabilities and explain some approaches on how this could be used in a real
world application.

## Verify overview

Verify defines a Validator as any function with the following signature:
This type of ideas arise naturally in function programming languages.

```dart
typedef Validator<S, T> = Either<List<ValidationError>, T> Function(S subject);
```

As can be observed the Validator is generic over the input and output type of the function
and has a contract on what errors through an abstract class ValidationError.

For those who are familiar to these abstractions can already see how definitions of map, flatMap etc... can easily be derived.

Everything else boils down to creating different ways to combine these functions into more complex validators and having good ergonomics for developers to easily create custom validators.

Check the official [API](https://pub.dev/packages/verify#-readme-tab-) for more details.

### Let build something

I won't get to theoretical with Verify instead jump straight into an example.
I will demonstrate how a sign up form page could be built and validated with
bloc and verify packages.

Are form will be real simple with an email, password and password confirmation input fields.
If you want to see the full source for this checkout out the [examples](https://github.com/DanielCardonaRojas/verify/tree/master/example)
folder of the package repository.

### Bloc setup

As you probably already know when building a reactive UI with Bloc, first thing is modeling state, and events.

For this we will use the freezed package to have value equality and some other goodies.

**Events**

Lets start out with modeling out events. We will need to be able to modify our states email, password and password confirmation fields. We usually would have submit event too. But lets keep it simple since we won't be connecting to service in this example.

```dart
@freezed
abstract class SignUpEvent with _$SignUpEvent {
  const factory SignUpEvent.setEmail(String email) = SignUpSetEmail;
  const factory SignUpEvent.setPassword(String password) = SignUpSetPassword;
  const factory SignUpEvent.setPasswordConfirmation(String password) =
      SignUpSetPasswordConfirmation;
}
```

**State**

Lets start modeling our state. We now we have to track the email, password and password
confirmation field so we put all those in our state. We will also need to display validation errors this seems logical to track with a `Map` that contains errors grouped by
input field type, so that we can easily get the errors for an specific field.

```dart
enum SignUpFormField {
  email,
  password,
  passwordConfirmation,
}

@freezed
abstract class SignUpState with _$SignUpState {
  const factory SignUpState(
      {String email,
      String password,
      String confirmation,
      Map<SignUpFormField, List<String>> errors}) = _SignUpState;

  factory SignUpState.initial() {
    return SignUpState(errors: {
      SignUpFormField.email: [],
      SignUpFormField.password: [],
      SignUpFormField.passwordConfirmation: [],
    });
  }
}
```

the base bloc implementation should be pretty straight forward.

```dart
Stream<SignUpState> mapEventToState(SignUpEvent event) async* {
    final newState = event.when(
        setEmail: (str) => state.copyWith(email: str),
        setPassword: (str) => state.copyWith(password: str),
        setPasswordConfirmation: (str) => state.copyWith(confirmation: str),
    );
    yield newstate;

}
```

## Integrate validations with Verify

To start out with verify, you first must define some error type and implement
ValidationError. To do this we will equip our error type with the field it references.

```dart
class SignUpError implements ValidationError {
  final SignUpFormField field;
  final String message;

  SignUpError({@required this.field, @required this.message});

  @override
  String get errorDescription => message; // ValidationError requirement
}
```

Now that we have our error type in place. Let's define a validator.
I will show one way of doing this, but the same result could be achieved using the
provided verify combinators in different ways. Checkout out the [docs]() to see
all available options.

Notice how we can bypass validators for some conditions. For instance we want to
have matching password and confirmation, but also want to ignore these checks if the confirmation field is null or perhaps empty.

```dart
final Validator_<SignUpState> signUpValidation = Verify.all<SignUpState>([
  Verify.subject<SignUpState>().checkField(
      (state) => state.password,
      Verify.property((s) => s.length > 4,
          error: SignUpError(
            field: SignUpFormField.password,
            message: 'incorrect length',
          ))),
  Verify.subject<SignUpState>().checkField(
      (state) => state.email,
      Verify.property((s) => s.contains('@'),
          error: SignUpError(
            field: SignUpFormField.email,
            message: 'invalid format',
          ))),
  Verify.property((SignUpState state) => state.confirmation == state.password,
      error: SignUpError(
        field: SignUpFormField.passwordConfirmation,
        message: 'not match',
      )).ignoreWhen((state) => state.confirmation == null),
]);
```

### Bloc integration

Finally lets update our bloc to validate the state after input updates:

```dart
Stream<SignUpState> mapEventToState(SignUpEvent event) async* {
    final newState = event.when(
        setEmail: (str) => state.copyWith(email: str),
        setPassword: (str) => state.copyWith(password: str),
        setPasswordConfirmation: (str) => state.copyWith(confirmation: str),
    );

    final errors = signUpValidation
        .verify<SignUpError>(newState)
        .groupedErrorsBy((error) => error.field)
        .messages;

    yield newState.copyWith(errors: errors);

}
```

The UI should be straightforward so I won't discuss that. If you want to see the complete example
head over to the [examples](https://github.com/DanielCardonaRojas/verify/tree/master/example) folder of the repository.

{% github DanielCardonaRojas/verify %}