Introduction
Flutter Floc helps you to create a form within a minute, reducing the boilerplate and providing helpers to link the view to the form state.
Motivations
Creating forms always had been a repetitive task. In Dart specifically, there are some packages that helps you to reduce the needed boilerplate, for instance:
formz is a package providing a low-level API to manage an input state.
flutter_form_bloc is a package providing a high-level API to manage a form, with validations etc... The thing is, flutter_form_bloc is lacking of some functionality, one of them is testing.
We actually needed a strong and testable form manager and we love bloc. For this reason we created our own based on what flutter_form_bloc did.
Getting Started
For help getting started with Flutter, view the online documentation, which offers tutorials, samples, guidance on mobile development, and a full API reference.
Installation
Add the flutter_floc
package to the list of dependency of your Flutter Application (pubspec.yaml):
dependencies:
flutter:
sdk: flutter
flutter_floc: ^0.0.0-dev.8
Usage
Creating a Form BLoC
- Create a new file named
register_form_bloc.dart
(or whatever you like) - Define a class named
RegisterFormBloc
that extendsFormBloc
:
// FormBloc<String>, String could be replaced
// by whatever form response type you like
class RegisterFormBloc extends FormBloc<String> {
}
- Define the form fields:
// username is required
static final username = FormField<String>(
name: 'username',
initialValue: '',
validators: [
FieldValidator(Validator.required),
],
);
// password is required and should be contains at least 6 characters
static final password = FormField<String>(
name: 'password',
initialValue: '',
validators: [
FieldValidator(Validator.required),
FieldValidator(Validator.min6Chars),
],
);
// confirmPassword is required and should have the same value as "password" field
static final confirmPassword = FormField<String>(
name: 'confirmPassword',
initialValue: '',
validators: [
FieldValidator(Validator.required),
FieldValidator(
Validator.confirmPassword,
fieldSubscriptions: [password],
),
],
);
- Add fields to the form inside the form constructor:
class RegisterFormBloc extends FormBloc<String> {
RegisterFormBloc() {
addFields([password, username, confirmPassword]);
}
}
- You can now override the
onSubmit
method that will gets triggered when the form is submitted and valid:
@override
void onSubmit(fields) async {
// you can make HTTP calls here for example.
print(fields['username']);
print(fields['password']);
print(fields['confirmPassword']);
emitSuccess('success response : ok');
}
Now let's add some UI to display all these fancy things.
- Add a
FormBlocListener
(which is based on BlocListener for those who came from thebloc
package) somewhere in your widgets tree.FormBlocListener
provide some handlers that gets triggered during the form lifecycle (onSubmitting
,onSuccess
,onFailure
):
class ExampleForm extends StatelessWidget {
@override
Widget build(BuildContext context) {
return FormBlocListener<ExampleFormBloc, String>(
onSubmitting: (context, state) {
print('Loading...');
},
onSuccess: (context, state) {
print('Success !');
print(state.response);
},
onFailure: (context, state) {
print('Failure !');
print(state.response);
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// Field are inside a TextFieldBlocBuilder
// that gets updated when the field state change.
TextFieldBlocBuilder<ExampleFormBloc>(
fieldName: 'username',
decoration: InputDecoration(hintText: 'Username'),
),
TextFieldBlocBuilder<ExampleFormBloc>(
fieldName: 'password',
obscureText: true,
decoration: InputDecoration(hintText: 'Password'),
),
TextFieldBlocBuilder<ExampleFormBloc>(
fieldName: 'confirmPassword',
obscureText: true,
decoration: InputDecoration(hintText: 'Confirm password'),
),
// Button that force fields validation.
MaterialButton(
onPressed: () {
context.read<ExampleFormBloc>().validate();
},
child: Text('Validate'),
),
// Button that triggers a submit on the form.
MaterialButton(
onPressed: () {
context.read<ExampleFormBloc>().submit();
},
child: Text('Submit'),
)
],
),
);
}
}
That's it! You've made your first FormBLoC.
Validators
You can create simple validators, for example a 6minchars validator like this :
String min6Chars(String value, Map<String, dynamic> fields) {
if (value == null || value.isEmpty || value.runes.length < 6) {
return 'min 6 chars';
}
return null;
}
The return value is null if there is no error. Else it's a string where the return value is considered as the error key
that will gets passed to the UI.
To add this validator to a field, simply add it to the field list validators:
static final 6minField = FormField<String>(
name: '6minField',
initialValue: '',
validators: [
FieldValidator(min6Chars),
],
);
Validators that depends on other value (confirm password case)
A validator can depends on other fields value :
String confirmPassword(String value, Map<String, dynamic> fields) {
if (value != fields['password']) {
return 'different';
}
return null;
}
The second fields
parameter contains the dependent fields. To add a dependency to a validator, simply define them into the validator fieldSubscriptions
named parameter of confirmPassword:
static final confirmPassword = FormField<String>(
name: 'confirmPassword',
initialValue: '',
validators: [
FieldValidator(Validator.required),
FieldValidator(
Validator.confirmPassword,
// We can add other fields here
fieldSubscriptions: [password],
),
],
);
Testing your form
TODO