Generate Forms

Generate form widget and link it automatically with screen viewModel depending on reactive_forms package.
xflutter_cli generate form

Generate new form inside workspace

  • Make sure to run the command in the workspace root directory instead of (application or package) directory.
  • Use the src option to specify the (app or package) in which you want to generate the form.
The form path must match the container's screen path

Generated Code:

  • CLI generate a isolated Form Widget, so you can re-use it in any implicit screen (mobile, tablet or desktop):
    class RegisterForm extends StatelessWidget {
      const RegisterForm({super.key});
    
      @override
      Widget build(BuildContext context) {
        return InstanceBuilder<RegisterViewModel>(
          builder: (RegisterViewModel viewModel) {
            final formGroup = viewModel.params.registerForm;
    
            return ReactiveForm(
              formGroup: formGroup,
              child: Column(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  // form fields
                  Column(
                    children: [
                      // name
                      CustomizedReactiveFormField<String>(
                        formControlName: 'name',
                        labelText: 'name'.tr(),
                        keyboardType: TextInputType.text,
                        textInputAction: TextInputAction.next,
                        onSubmitted: (_) => formGroup.focus('email'),
                      ),
                      const SizedBox(height: formSpacing),
    
                      // email
                      CustomizedReactiveFormField<String>(
                        formControlName: 'email',
                        labelText: 'email'.tr(),
                        keyboardType: TextInputType.emailAddress,
                        textInputAction: TextInputAction.next,
                        onSubmitted: (_) => formGroup.focus('phone'),
                      ),
                      const SizedBox(height: formSpacing),
    
                      // phone
                      CustomizedReactivePhoneField(
                        formControlName: 'phone',
                        labelText: 'phone'.tr(),
                        textInputAction: TextInputAction.next,
                        onSubmitted: () => formGroup.focus('password'),
                      ),
                      const SizedBox(height: formSpacing),
    
                      // password
                      CustomizedReactiveFormField<String>(
                        formControlName: 'password',
                        labelText: 'password'.tr(),
                        keyboardType: TextInputType.text,
                        textInputAction: TextInputAction.next,
                        obscureText: true,
                        onSubmitted: (_) => formGroup.focus('confirm_password'),
                      ),
                      const SizedBox(height: formSpacing),
    
                      // confirmPassword
                      CustomizedReactiveFormField<String>(
                        formControlName: 'confirm_password',
                        labelText: 'confirm_password'.tr(),
                        keyboardType: TextInputType.text,
                        textInputAction: TextInputAction.done,
                        obscureText: true,
                        onSubmitted: (_) {},
                      ),
                      const SizedBox(height: formSpacing),
                    ],
                  ),
    
                  // submit widget
                  Column(
                    children: [
                      SizedBox(
                        width: double.infinity,
                        child: CustomizedButton(
                          child: Text('next'.tr()),
                          callback: () {
                            if (formGroup.valid) {
                              debugPrint(formGroup.value.toString());
                            } else {
                              formGroup.markAllAsTouched();
                            }
                          },
                        ),
                      ),
                    ],
                  ),
                ],
              ),
            );
          },
        );
      }
    }
  • CLI generate the code in the ViewModel Params if the screen generate with state-management,
    otherwise the formGroup will be generated inside the form widget as standalone form to re-use it everywhere:
    class RegisterParams {
       final registerForm = FormGroup({
        'name': FormControl<String>(
          validators: [Validators.required],
        ),
        'email': FormControl<String>(
          validators: [
            Validators.required,
            Validators.email,
          ],
        ),
        'phone': FormControl<PhoneNumber>(
          validators: [
            PhoneValidators.validMobile,
            PhoneValidators.required,
          ],
        ),
        'password': FormControl<String>(
          validators: [
            Validators.required,
            Validators.minLength(8),
          ],
        ),
        'confirm_password': FormControl<String>(
          validators: [Validators.required],
        ),
      }, validators: [
        // matching 'password' and 'confirm_password' formControls
        const MatchingValidator(
          controlName: 'password',
          matchingControlName: 'confirm_password',
        ),
      ]);
    }

Result:

  • xflutter_cli_modules_application
    • android
    • assets
    • ios
    • lib
      • modules
        • authentication
          • presentation
            • screens
              • register
                • mobile
                • viewmodels
                  • register_viewmodel.dart
                  • register_params.dart
                • widgets
                  • register_form.dart
                • register_screen.dart
      • main.dart
    • analysis_options.yaml
    • build.yaml
    • flutter_launcher_icons.yaml
    • flutter_native_splash.yaml
    • pubspec.yaml
    • xflutter_cli.yaml

Usage:

Name
Type
Description
Allowed Values
Example
srcoptionspecify one of the modules to execute command in (monorepo workspace or standalone modular-app)-
pathoptiongenerate form path-
fieldsoptiondeclaring generated form-fields directly instead of inserting them one by one by the terminaltext, email, boolean, password, phone_number, number_integer, number_double, confirm_passwordname:text,user_mail:email
sourceoptionimport file from specific module, this is helpful when you have multiple files with same name and one of them should be imported in the generated file, so you need to tell CLI which one should be imported-filename.dart:my_app1
sourcesoptionimport file from multiple modules, this is helpful when you have multiple files with same name and many of them of them should be imported in the generated file, so you need to tell CLI which files should be imported-models.dart:my_package1, models.dart:my_package2