Search code examples
flutterdartflutter-reactive-forms

Flutter - Bind form to model


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


Solution

  • 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.