Flutter - Bind form to model

4.3k views Asked by At

I'm using reactive_forms in Flutter which is a model-driven forms library which takes inspiration from Angular Reactive Forms.

It is fairly simple, to add a form with one formControl called 'nickName':

  final _form = FormGroup({
    'nickName':
        FormControl<String>(validators: [Validators.required]),
  });

My question is, isn't it commonly done and good practice to have the form control "nickName" be bound to a property of a domain model? As this is what is done in another codebase that I have worked on in Angular.

I do have a domain model for this form (although it has more fields than the form contains. The rest of the fields are set in forms on the following pages - like a setup wizard):

class UserRegistrationEntity {
  String nickName;
  String email;
  String confirmEmail;
  String password;
  String confirmPassword;
}

I can create the model like so:

final userRegistration = UserRegistrationEntity();

But how can I now bind the nickName field of userRegistration to my form control? I was expecting the form library to have an equivalent to ngModel field for me to set the field of my model on a form control.

Or is this just not something that is done in Flutter?

example: https://angular.io/api/forms/NgModel#using-ngmodel-on-a-standalone-control

2

There are 2 answers

0
Ferdinand On BEST ANSWER

From my use with reactive_forms in flutter, I have NOT seen any equivalent to ngModel. This is because elements like ReactiveTextField are designed with double-binding capability with widgets (two-way binding)

That's why you don't include properties like onChanged, which would be similar to the ngModel method #ngOnChanges()

So, to bind the nickName field of userRegistration to your form control, you would make use of a ViewModel and Provider.

So your code would look like this:

class YourViewModel {
   final _form = FormGroup({
    'nickName':
        FormControl<String>(validators: [Validators.required]),
  });

//assuming you are signing in
void signIn() {
    final credentials = this.form.value;
    //the rest of your signIn code
  }

}
class YourScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final viewModel = Provider.of<YourViewModel>(context, listen: false);
    return ReactiveForm(
      formGroup: viewModel.form,
      child: Column(
        children: <Widget>[
          ReactiveTextField(
            formControlName: 'nickName',
          ),
          ReactiveFormConsumer(
            builder: (context, form, child) {
              return RaisedButton(
                child: Text('Submit'),
                // if the form is valid, sign-in or whatever you need to do with the form data (I have used signIn)
                onPressed: form.valid ? viewModel.signIn : null,
              );
            },
          ),
        ],
      ),
    );
  }
}

CONCLUSION

ngModel is a directive which binds input,Reactive Forms does the same and maintains separation between views and model, while still sustaining data synchronization. There is, therefore NO apparent need to bind your data to the domain layer, instead reactive_forms removes the need to do that entirely, because that process is built in.

0
BeniaminoBaggins On

Update 2022 - reactive_forms_generator is released. It's a code generator for reactive_forms. It generates a lot of form functionality; one example is that it binds a strongly typed model to the form. It has a FormModel:

abstract class FormModel<TModel> {
  FormModel({required this.form});
  final FormGroup form;
  TModel get model;
}

And form and model are in sync (model is a getter that returns the strongly typed model by retrieving each form value).