Flutter - Form BLoC

Style: Wyatt Analysis SDK: Flutter

Form Bloc for Flutter.

Features

  • Form

    • FormInputValidator:
      • Store data
      • Validate this data
    • FormInputMetadata:
      • Store infos and options about an input.
    • FormInput:
      • Associaite a key, to a validator and metadata.
    • FormData: collection of inputs
      • Contain all inputs
      • Basic set operation
  • FormDataCubit

    • Data management behind a form.
      • Use entries to pass a FormData object
      • You can use several pre configured FormInputValidator for validation
      • You can use updateFormData() to change FormData during runtime (intersection, union, difference or replace)
  • Consistent

    • Every class have same naming convention

Getting started

Simply add wyatt_form_bloc in pubspec.yaml, then

import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';

Usage

Firstly, you have to create your form inputs.

static List<FormInput> getNormalEntries() {
    return const [
        FormInput(formFieldName, Name.pure()),
        FormInput(formFieldEmail, Email.pure()),
        FormInput(formFieldPhone, Phone.pure()),
        FormInput(formFieldList, ListOption<String>.pure(defaultValue: ['checkbox3'])),
        FormInput(formFieldRadio, TextString.pure()),
        FormInput(formFieldPro, Boolean.pure(), metadata: FormInputMetadata<void>(name: 'business')),
        FormInput(formFieldHidden, Boolean.pure(), metadata: FormInputMetadata<void>(export: false)),
    ];
}

static List<FormInput> getBusinessEntries() {
    const entries = [
        FormInput(formFieldSiren, Siren.pure()),
        FormInput(formFieldIban, Iban.pure()),
    ];
    return getNormalEntries() + entries;
}

static FormData getNormalFormData() {
    return FormData(getNormalEntries());
}

static FormData getProFormData() {
    return FormData(getBusinessEntries());
}

Let's create functions to enable dynamic changes in runtime.

When creating metadata with FormInputMetadata<T>, T is the type of extra object.

Then a FormData is a collection of inputs. You can validate, contains, intersection, difference, union, or clone this data.

After that, you have to extends FormDataCubit.

class CustomFormCubit extends FormDataCubit {
  CustomFormCubit({required FormData inputs}) : super(inputs: inputs);

  @override
  Future<void> submitForm() {
    log(state.data.toMap().toString());
    // Handle all your logic here.
    emit(...)
  }
}

The submited form is in state.data !

Don't forget to create and provide it !

FormDataCubit _formCubit = CustomFormCubit(inputs: getNormalFormData());

return BlocProvider(
    create: (context) => _formCubit,
    child: const WidgetTree(),
);

You can now create the first form input !

class _EmailInput extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocBuilder<FormDataCubit, FormDataState>(
      builder: (context, state) {
        return TextField(
          onChanged: (email) => context
              .read<FormDataCubit>()
              .dataChanged(formFieldEmail, Email.dirty(email)),
          keyboardType: TextInputType.emailAddress,
          decoration: InputDecoration(
            labelText: 'email',
            helperText: '',
            errorText: state.data.isNotValid(formFieldEmail)
                ? 'invalid email'
                : null,
          ),
        );
      },
    );
  }
}

Then, you can create the trigger

class _SignUpButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocBuilder<FormDataCubit, FormDataState>(
      buildWhen: (previous, current) => previous.status != current.status,
      builder: (context, state) {
        return state.status.isSubmissionInProgress
            ? const CircularProgressIndicator()
            : ElevatedButton(
                onPressed: state.status.isValidated
                    ? () => context.read<FormDataCubit>().submitForm()
                    : null,
                child: const Text('SIGN UP'),
              );
      },
    );
  }
}

With state.status you can access the status of the form to check if there is an error or if it's in submission.