flutter_form_bloc

Pub

A Flutter package with flutter widgets that helps to create forms with form_bloc package.


To see complex examples check the example project.


Before to use this package you need to know the core concepts of bloc package and the basics of flutter_bloc


Widgets

Note

FormBloc, TextFieldBloc, SelectFieldBloc, and BooleanFieldBloc are blocs, so you can use BlocBuilder or BlocListener of flutter_bloc for make any widget you want compatible with any FieldBloc.

If you want me to add other widgets please let me know, or make a pull request.

Example

dependencies:
  form_bloc: ^0.2.0
  flutter_form_bloc: ^0.1.0
  flutter_bloc: ^0.20.1
import 'package:form_bloc/form_bloc.dart';

class SimpleLoginFormBloc extends FormBloc<String, String> {
  final emailField = TextFieldBloc<String>(
    validators: [Validators.validEmail],
  );
  final passwordField = TextFieldBloc<String>(
    validators: [Validators.notEmpty],
  );

  @override
  List<FieldBloc> get fieldBlocs => [emailField, passwordField];

  @override
  Stream<FormBlocState<String, String>> onSubmitting() async* {
    // Login logic...
    await Future<void>.delayed(Duration(seconds: 2));
    yield currentState.toSuccess();
  }
}
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_form_bloc/flutter_form_bloc.dart';
import 'package:flutter_form_bloc_example/forms/simple_login_form_bloc.dart';
import 'package:flutter_form_bloc_example/widgets/widgets.dart';

class SimpleLoginForm extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider<SimpleLoginFormBloc>(
      builder: (context) => SimpleLoginFormBloc(),
      child: Builder(
        builder: (context) {
          final formBloc = BlocProvider.of<SimpleLoginFormBloc>(context);

          return Scaffold(
            appBar: AppBar(title: Text('Simple login')),
            body: FormBlocListener<SimpleLoginFormBloc, String, String>(
              onSubmitting: (context, state) => LoadingDialog.show(context),
              onSuccess: (context, state) {
                LoadingDialog.hide(context);
                Navigator.of(context).pushReplacementNamed('/success');
              },
              onFailure: (context, state) {
                LoadingDialog.hide(context);
                Notifications.showSnackBarWithError(
                    context, state.failureResponse);
              },
              child: ListView(
                children: <Widget>[
                  TextFieldBlocBuilder<String>(
                    textFieldBloc: formBloc.emailField,
                    formBloc: formBloc,
                    errorBuilder: (context, error) => error,
                    keyboardType: TextInputType.emailAddress,
                    decoration: InputDecoration(
                      labelText: 'Email',
                      prefixIcon: Icon(Icons.email),
                    ),
                  ),
                  TextFieldBlocBuilder<String>(
                    textFieldBloc: formBloc.passwordField,
                    formBloc: formBloc,
                    errorBuilder: (context, error) => error,
                    suffixButton: SuffixButton.obscureText,
                    decoration: InputDecoration(
                      labelText: 'Password',
                      prefixIcon: Icon(Icons.lock),
                    ),
                  ),
                  Padding(
                    padding: const EdgeInsets.all(8.0),
                    child: RaisedButton(
                      onPressed: formBloc.submit,
                      child: Center(child: Text('LOGIN')),
                    ),
                  ),
                ],
              ),
            ),
          );
        },
      ),
    );
  }
}

Basic usage

1. Import it

import 'package:form_bloc/form_bloc.dart';

2. Create a class that extends FormBloc<SuccessResponse, FailureResponse>

FormBloc<SuccessResponse, FailureResponse>

SuccessResponse The type of the success response.

FailureResponse The type of the failure response.

For example, the SuccessResponse type and FailureResponse type of SimpleLoginFormBloc will be String.

import 'package:form_bloc/form_bloc.dart';

class SimpleLoginFormBloc extends FormBloc<String, String> {}

2. Create Field Blocs

You need to create field blocs, and these need to be final.

You can create:

For example the SimpleLoginFormBloc will have two TextFieldBloc<String>, so the Error type will be String, and the validators must return a error of String type.

import 'package:form_bloc/form_bloc.dart';

class SimpleLoginFormBloc extends FormBloc<String, String> {
  final emailField = TextFieldBloc<String>(
    validators: [Validators.validEmail],
  );
  final passwordField = TextFieldBloc<String>(
    validators: [Validators.notEmpty],
  );

}

3. Implement the get method fieldBlocs

You need to override the get method fieldBlocs and return a list with all FieldBlocs.

For example the SimpleLoginFormBloc must return a List with emailField and passwordField.

import 'package:form_bloc/form_bloc.dart';

class SimpleLoginFormBloc extends FormBloc<String, String> {
  final emailField = TextFieldBloc<String>(
    validators: [Validators.validEmail],
  );
  final passwordField = TextFieldBloc<String>(
    validators: [Validators.notEmpty],
  );

  @override
  List<FieldBloc> get fieldBlocs => [emailField, passwordField];
}

4. Implement the method onSubmitting

onSubmitting return a Stream<FormBlocState<SuccessResponse, FailureResponse>> and will called when the form is submitting.

You must call all your business logic of this form here, and yield the corresponding state.

You can yield a new state using:

For example onSubmitting of LoginFormBloc will return a Stream<FormBlocState<String, String>> and yield currentState.toSuccess().

import 'package:form_bloc/form_bloc.dart';

class SimpleLoginFormBloc extends FormBloc<String, String> {
  final emailField = TextFieldBloc<String>(
    validators: [Validators.validEmail],
  );
  final passwordField = TextFieldBloc<String>(
    validators: [Validators.notEmpty],
  );

  @override
  List<FieldBloc> get fieldBlocs => [emailField, passwordField];

  @override
  Stream<FormBlocState<String, String>> onSubmitting() async* {
    // Login logic...
    await Future<void>.delayed(Duration(seconds: 2));
    yield currentState.toSuccess();
  }
}

5. Create a Form Widget

You need to create a widget with access to the FormBloc. In this case I will use BlocProvider for do it.

class SimpleLoginForm extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider<SimpleLoginFormBloc>(
      builder: (context) => SimpleLoginFormBloc(),
      child: Builder(
        builder: (context) {
          final formBloc = BlocProvider.of<SimpleLoginFormBloc>(context);

          return Scaffold();
        },
      ),
    );
  }
}

6. Add FormBlocListener for manage form state changes

You need to add a FormBlocListener.

In this example:

  • I will show a loading dialog when the state is loading.
  • I will hide the dialog when the state is success, and navigate to success screen.
  • I will hide the dialog when the state is failure, and show a snackBar with the error.
...
          return Scaffold(
            appBar: AppBar(title: Text('Simple login')),
            body: FormBlocListener<SimpleLoginFormBloc, String, String>(
              onSubmitting: (context, state) => LoadingDialog.show(context),
              onSuccess: (context, state) {
                LoadingDialog.hide(context);
                Navigator.of(context).pushReplacementNamed('/success');
              },
              onFailure: (context, state) {
                LoadingDialog.hide(context);
                Notifications.showSnackBarWithError(
                    context, state.failureResponse);
              },
              child: 
              ),
            ),
          );
...          

6. Connect the Field Blocs with Field Blocs Builder

In this example I will use TextFieldBlocBuilder<String> for connect with emailField and passwordField of SimpleLoginFormBloc.

...
              child: ListView(
                children: <Widget>[
                  TextFieldBlocBuilder<String>(
                    textFieldBloc: formBloc.emailField,
                    formBloc: formBloc,
                    errorBuilder: (context, error) => error,
                    keyboardType: TextInputType.emailAddress,
                    decoration: InputDecoration(
                      labelText: 'Email',
                      prefixIcon: Icon(Icons.email),
                    ),
                  ),
                  TextFieldBlocBuilder<String>(
                    textFieldBloc: formBloc.passwordField,
                    formBloc: formBloc,
                    errorBuilder: (context, error) => error,
                    suffixButton: SuffixButton.obscureText,
                    decoration: InputDecoration(
                      labelText: 'Password',
                      prefixIcon: Icon(Icons.lock),
                    ),
                  ),
                ],
              ),
...          

7. Add a widget for submit the FormBloc

In this example I will add a RaisedButton and pass submit method of FormBloc to submit the form

...
              child: ListView(
                children: <Widget>[
                  ...,
                  Padding(
                    padding: const EdgeInsets.all(8.0),
                    child: RaisedButton(
                      onPressed: formBloc.submit,
                      child: Center(child: Text('LOGIN')),
                    ),
                  ),
                ],
              ),
...          

Credits

This package uses the following packages:

Libraries

flutter_form_bloc