Flutter Formz Explained – O curso intensivo completo

Tempo de leitura: 4 minutes

O Formz é um pacote Dart desenvolvido pela Very Good Ventures para simplificar a representação e a validação de formulários em aplicativos Dart. Esse pacote fornece uma representação de formulário unificada que facilita o gerenciamento dos dados dos campos de formulário e de seu status de validação.

Depois de entender completamente como usá-lo, você nunca mais vai querer perdê-lo.

E, por isso, vamos dar uma olhada profunda no Formz hoje!

Vamos começar!

Instalação

A primeira coisa que temos de fazer depois de criar nosso aplicativo é adicionar o Formz. Para isso, usaremos o comando flutter pub add formz. A versão do formz no momento da redação deste artigo é a 0.7.0, mas é bem provável que este tutorial também funcione se você usar uma versão mais recente, pois os princípios básicos não serão alterados.

Criar um módulo do Formz

O Formz é estruturado em modelos, em que cada módulo valida coisas diferentes. Por exemplo, um módulo pode ser usado para validar um endereço de e-mail, uma senha ou uma data. A aparência é a seguinte:

import 'package:formz/formz.dart';

// Isso representa todos os estados ou campos errados que podem ter.
// Uma senha pode ser muito curta, não ter nenhum dígito ou letra maiúscula.
enum PasswordInputError { tooShort, noDigit, noUppercase }

class PasswordInput extends FormzInput<String, PasswordInputError> {
  // PasswordInput.pure representa uma entrada de formulário não modificada
  const PasswordInput.pure() : super.pure('');

  // Enquanto o super.dirty representa uma entrada de formulário modificada
  const PasswordInput.dirty({String value = ''}) : super.dirty(value);

  // Substituir o validador para lidar com a validação de um determinado valor de entrada.
  @override
  PasswordInputError validator(String value) {
    // Essa parte é onde estará toda a nossa lógica de validação
  }
}

Talvez você tenha uma pergunta: O que o .pure e o .dirty fazem?

É bem simples: Digamos que você tenha um campo de texto. Ao inicializar o widget, você usa PasswordInput.pure(), porque o usuário ainda não inseriu nada. Quando o usuário digita algo no campo de texto, você usa PasswordInput.dirty(value: 'Inputted Value'). Portanto, dirty é basicamente uma forma de dizer que a entrada foi alterada. E isso ajudará a alterar o estado dos botões (por exemplo, de desativado para ativado) ou outros tipos de coisas.

Agora que sabemos disso, é claro que queremos validar nossa entrada.

Para o nosso exemplo de senha, isso poderia ser algo assim:

class PasswordInput extends FormzInput<String, PasswordInputError> {
  // [...] - form input representations

  @override
  PaswordInputError? validator(String value) {
    // Verifique se a senha é muito curta
    if (value.length < 8) {
      return PasswordInputError.tooShort;
    }

    // Verificar se a senha contém um dígito
    if (!value.contains(RegExp(r'[0-9]'))) {
      return PasswordInputError.noDigit;
    }

    // Verificar se a senha contém uma letra maiúscula
    if (!value.contains(RegExp(r'[A-Z]'))) {
      return PasswordInputError.noUppercase;
    }

    // Se tudo estiver bem, retorne null
    return null;
  }
}

É isso aí! Você terminou. Bem… em parte. É claro que precisamos interagir com nossa entrada:

 

Interagir com o FormzInput

Primeiro, precisamos criar uma variável que armazene nossa PasswordInput:

PasswordInput passwordInputValue = PasswordInput.pure();

Como já foi dito acima, quando o usuário digita algo no campo de texto, simplesmente atribuímos um novo valor à variável:

passwordInputValue = PasswordInput.dirty(value: newValue);

Agora, com tudo isso configurado, podemos finalmente usar o formz de forma adequada:

// Retorna o valor da entrada
print(passwordInputValue.value);

// Verifica se a entrada é válida ou não. 
// Se o valor retornado de seu validador for nulo, ele é válido. Se não for, não é.
print(paswordInputValue.isValid);
print(PasswordInputValue.isNotValid);

// Retorna o erro de sua entrada. Se for nulo, a entrada é válida.
// Exemplo de saída: PasswordInputError.tooShort
print(passwordInputValue.error);

Especialmente o último, passwordInputValue.error, vai ajudá-lo tremendamente, aumentando a experiência do usuário. Você não precisa apenas dar a ele uma mensagem dizendo “Sua senha está incorreta”, mas agora pode dizer facilmente “Sua senha precisa ter pelo menos 8 caracteres”, “Você precisa incluir pelo menos um número” etc.

E esse é o poder do Formz. A construção do nosso modelo minúsculo lhe dá muito controle sobre o formulário e sua validação.

 

Mais recursos do Formz

Validação automática de entrada

Um recurso especial do Formz é a validação automática de várias entradas. Para isso, crie uma classe que estenda o FormzMixin e adicione como parâmetros diferentes Formz Models:

class LoginForm with FormzMixin {
  LoginForm({
    this.email= const EmailInput.pure(),
    this.password = const PasswordInput.pure(),
  });

  final EmailInput email;
  final PasswordInput password;

  @override
  List<FormzInput> get inputs => [email, password];
}

void main() {
  // The output would be false in this case
  print(LoginForm().isValid);
}

 

Cache de validações

O cálculo de algumas validações é bastante caro, por exemplo, se a entrada for bastante complexa. Para evitar validações desnecessárias, você pode simplesmente armazenar em cache as validações de diferentes entradas usando o mixin FormzInputErrorCacheMixin.

Aqui está um exemplo da documentação do formz:

import 'package:formz/formz.dart';

enum EmailValidationError { invalid }

class Email extends FormzInput<String, EmailValidationError>
    with FormzInputErrorCacheMixin { // É literalmente só isso, nada mais
  Email.pure([super.value = '']) : super.pure();

  Email.dirty([super.value = '']) : super.dirty();

  static final _emailRegExp = RegExp(
    r'^[a-zA-Z\d.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z\d-]+(?:\.[a-zA-Z\d-]+)*$',
  );

  @override
  EmailValidationError? validator(String value) {
    return _emailRegExp.hasMatch(value) ? null : EmailValidationError.invalid;
  }
}

Exemplo mais completo usando o package Formz.

// Importando o pacote Formz
import 'package:formz/formz.dart';

// Definindo erros de validação de entrada
enum NameInputError { empty }

// Estendendo FormzInput e fornecendo o tipo de entrada e tipo de erro.
class NameInput extends FormzInput<String, NameInputError> {
  // Chamando super.pure para representar uma entrada de formulário não modificada.
  const NameInput.pure() : super.pure('');

  // Chamando super.dirty para representar uma entrada de formulário modificada.
  const NameInput.dirty({String value = ''}) : super.dirty(value);

  // Sobrescrevendo o validador para lidar com a validação de um valor de entrada dado.
  @override
  NameInputError? validator(String value) {
    return value.isEmpty ? NameInputError.empty : null;
  }
}

// Interagindo com um FormzInput
void main() {
  const name = NameInput.pure();
  print(name.value);  // ''
  print(name.isValid);  // false
  print(name.error);  // NameInputError.empty

  const joe = NameInput.dirty(value: 'joe');
  print(joe.value);  // 'joe'
  print(joe.isValid);  // true
  print(joe.error);  // null
}