Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error: type 'FormControl<dynamic>' is not a subtype of type 'int' #104

Closed
jbt-cii opened this issue Mar 3, 2021 · 12 comments
Closed

Error: type 'FormControl<dynamic>' is not a subtype of type 'int' #104

jbt-cii opened this issue Mar 3, 2021 · 12 comments

Comments

@jbt-cii
Copy link

jbt-cii commented Mar 3, 2021

Hello,

I open a new issue because I can't find a good way to mix controls for input fields containing "integer" values and fields containing "String" values.
Example:

  FormGroup get form => fb.group(<String, dynamic>{
        'my_field_01': FormControl(validators: [Validators.required, Validators.number]),
        'my_field_02': FormControl(validators: [Validators.required]),
        'my_field_03': FormControl(validators: [Validators.required, Validators.number]),
      });

I tried several things from the severals examples you gave.
I tried also that:

  stringRegex
  final RegExp stringRegex = RegExp(r"[a-z]+");
  final RegExp intRegex = RegExp(r"[0-9]{4}");
  FormGroup get form => fb.group({
        'my_field_01': FormControl<int>(validators: [Validators.required, Validators.number]),
        'my_field_02': FormControl<String>(validators: [Validators.required, Validators.pattern(stringRegex)]),
        'my_field_03': FormControl<int>(validators: [Validators.required, Validators.pattern(intRegex)]),
      });

I tried also to use this:

  FormGroup get form => fb.group({
        'my_field_01': fb.control<int>(null, [Validators.required, Validators.number]),
        'my_field_02': fb.control<String>('', [Validators.required]),
        'my_field_03': fb.control<int>(null, [Validators.required, Validators.number]),
      });

I tried also to change the "FormGroup" declaration like this:

  FormGroup get form => FormGroup({
        'my_field_01': FormControl<int>(validators: [Validators.required, Validators.number]),
        'my_field_02': FormControl<String>(validators: [Validators.required]),
        'my_field_03': FormControl<int>(validators: [Validators.required, Validators.number]),
      });

Each time I get an error like this one:

════════ Exception caught by gesture ═══════════════════════════════════════════
The following _TypeError was thrown while handling a gesture:
type 'FormControl<int>' is not a subtype of type 'int'

If you have any clue it will be great! :-)

@joanpablo
Copy link
Owner

Hi @jbt-cii,

This definition:

FormGroup get form => FormGroup({
        'my_field_01': FormControl<int>(validators: [Validators.required, Validators.number]),
        'my_field_02': FormControl<String>(validators: [Validators.required]),
        'my_field_03': FormControl<int>(validators: [Validators.required, Validators.number]),
      });

is just OK. It doesn't throw an exception, so I guess you are having trouble defining the WIdget.

Could you please show me the complete example, the definition of the form group, and how you bind the group with widgets?

@joanpablo
Copy link
Owner

I have also released a new version update and let me know. Thanks

@jbt-cii
Copy link
Author

jbt-cii commented Mar 3, 2021

Thank you for your answer.
It is late now in France but I checked this issue to see if you had a solution.
I have just tried the new version (9.0.2) but I still have the error.
You have the complete example in the issue #101 :
#101

From the complexe example in the issue #101 I changed one thing:

  Widget widgetBuildInitial(context, form) {
    return ReactiveFormBuilder(
      form: () => form,
      builder: (context, form, child) {
        return Column(
          children: [
            myWidgetForm(context, form),
            ReactiveFormConsumer(
              builder: (context, form, child) {
                return myWidgetValidateButton(context, form, () => myFormSubmit(context, form));
              },
            ),
          ],
        );
      },
    );
  }

And I added some logs when I tapped on the button in that function:

  void myFormSubmit(context, form) {
    log("Form button tapped!");
    if (form.valid) {
      this._myFields01 = form.control("my_field_01");
      this._myFields02 = form.control("my_field_02");
      this._myFields03 = form.control("my_field_03");
      log("Form submitted!");
      final myObjectCubit = context.bloc<ComponentCubit>();
      myObjectCubit.mySpecificMethodCubit(this._myFields01, this._myFields02, this._myFields03);
    } else {
      log("Form button tapped but not submitted because the form is not valid!");
    }
  }

=> I see the log "Form button tapped!" and just after I have the error (I don't see the log "Form submitted!"

@jbt-cii
Copy link
Author

jbt-cii commented Mar 4, 2021

And this is the whole error:


════════ Exception caught by gesture ═══════════════════════════════════════════
The following _TypeError was thrown while handling a gesture:
type 'FormControl<int>' is not a subtype of type 'int'

When the exception was thrown, this was the stack
#0      _MyPageState.myFormSubmit                                                                       package:myProject/…/page/my_page.dart:129
#1      _MyPageState.widgetBuildInitial.<anonymous closure>.<anonymous closure>.<anonymous closure>     package:myProject/…/page/my_page.dart:155
#2      genericButtonValidate.<anonymous closure>                                                       package:myProject/…/widget/generic_buttons.dart:16
#3      _InkResponseState._handleTap                                                                    package:flutter/…/material/ink_well.dart:991
#4      GestureRecognizer.invokeCallback
package:flutter/…/gestures/recognizer.dart:182
...
Handler: "onTap"
Recognizer: TapGestureRecognizer#e9d35
    debugOwner: GestureDetector
    state: ready
    won arena
    finalPosition: Offset(195.0, 577.9)
    finalLocalPosition: Offset(39.3, 22.5)
    button: 1
    sent tap down
════════════════════════════════════════════════════════════════════════════════

@jbt-cii
Copy link
Author

jbt-cii commented Mar 4, 2021

And this is the part for the button tapped that was not indicated in the issue #101:


Widget myWidgetValidateButton(context, form, action) {
  return genericButtonValidate(context, form, action);
}

Widget genericButtonValidate(context, form, action) {
  return GenericFormButton(
    buttonName: "validate",
    buttonTitle: "validate",
    disabled: (form.valid) ? false : true,
    onPressed: () {
      log("Validate button pressed!");
      action();
    },
  );
}

class GenericFormButton extends StatefulWidget {
  final Function(int, String) onChangedStep;
  final Function() onPressed;
  final bool disabled;
  final String buttonName;
  final String buttonTitle;

  const GenericFormButton({
    Key key,
    @required this.buttonName,
    @required this.buttonTitle,
    this.disabled,
    this.onPressed,
    this.onChangedStep
  }) : super(key: key);

  @override
  _GenericFormButtonState createState() {
    print("GenericFormButton createState");
    return _GenericFormButtonState();
  }
}

class _GenericFormButtonState extends State<GenericFormButton> {
  String _buttonName = "";
  String _buttonTitle = "";
  dynamic _buttonAction;
  bool _disabled = false;

  @override
  Widget build(BuildContext context) {
    this._buttonName = widget.buttonName;
    this._buttonTitle = widget.buttonTitle;
    this._buttonAction = null;
    this._disabled = widget.disabled == null ? false : widget.disabled;

    return Container(
      child: Row(
        children: [
          Expanded(
            child: TextButton(
              child: Text(
                this._buttonTitle,
              ),
              onPressed: this._buttonAction,
            ),
          ),
        ],
      ),
    );
  }
}

@joanpablo
Copy link
Owner

Hi @jbt-cii,

I have recreated your code the best I could, and everything works just fine. The only pieces I haven't tested was the ones related to your Cubit controllers. So I guess that maybe this function:

myObjectCubit.mySpecificMethodCubit(this._myFields01, this._myFields02, this._myFields03);

is expecting "int" as arguments maybe is expecting 2 "int" and a "String". but you are passing FormControl.
This expression:

form.control("my_field_01");

returns a FormControl not the value of the FormControle. To get access to the value you need to use: formControl.value:

// get the control
this._myFields01 = form.control("my_field_01");

// print the value of the control
print(this._myFields01.value);

So you may change the function call to:

myObjectCubit.mySpecificMethodCubit(this._myFields01.value, this._myFields02.value, this._myFields03.value);

@jbt-cii
Copy link
Author

jbt-cii commented Mar 4, 2021

Yes!
Your idea was good.
Now I have another error:

════════ Exception caught by gesture ═══════════════════════════════════════════
The following NoSuchMethodError was thrown while handling a gesture:
Class 'StatelessElement' has no instance method 'bloc'.
Receiver: Instance of 'StatelessElement'
Tried calling: bloc<ComponentCubit>()

But it does not concern your package anymore :-D

So the whole solution seems to be that:


class _MyPageState extends State<MyPage> {
  FormControl _myFields01;
  FormControl _myFields02;
  FormControl _myFields03;
  FormGroup get form => FormGroup({
	'my_field_01': FormControl<int>(validators: [Validators.required, Validators.number]),
	'my_field_02': FormControl<String>(validators: [Validators.required]),
	'my_field_03': FormControl<int>(validators: [Validators.required, Validators.number]),
  });

...

  void myFormSubmit(context, form) {
    log("Form button tapped!");
    if (form.valid) {
      this._myFields01 = form.control("my_field_01");
      this._myFields02 = form.control("my_field_02");
      this._myFields03 = form.control("my_field_03");
      log("Form submitted!");
      final myObjectCubit = context.bloc<ComponentCubit>();
      myObjectCubit.mySpecificMethodCubit(this._myFields01.value, this._myFields02.value, this._myFields03.value);
    } else {
      log("Form button tapped but not submitted because the form is not valid!");
    }
  }

...

}

Thanks a lot for your help!!!

@jbt-cii jbt-cii closed this as completed Mar 4, 2021
@joanpablo
Copy link
Owner

Hi @jbt-cii,

Is good to know you have solved the issue. My last advice is to always try to declare you FormGroup within your controller/viewmodel/bloc/cubit/etc. That way you don't need a stateful widget because the cubit or bloc/controller will always hold the state and you have access to it from your widgets(using context.bloc). That way you declare your form group (that is the model of your inputs data) in the controller.

@jbt-cii
Copy link
Author

jbt-cii commented Mar 4, 2021

Thank you for the advice.
I will try that and I will tell you if I succeed to do it :-)

@jbt-cii
Copy link
Author

jbt-cii commented Mar 8, 2021

Hi @joanpablo,

I try to follow your advice but I can't figure how to do that.
Do you mean that the "form" widget (ReactiveForm), the fields "ReactiveTextField" and also the "ReactiveFormConsumer" part have to be implemented inside the Cubit (or Bloc) file?

I can't understand the way to link a "front" widget declaring a form (what I have to declare ? a classic "Form" widget or "ReactiveFormBuilder") with a Cubit/Bloc part in which I have to declare the FormGroup.

Do you have an example of that type of usage?

@joanpablo
Copy link
Owner

Hi @jbt-cii,

I will give you a very simple example with Provider plugin, you can see that is the same with Cubit.

// your bloc/controller/viewmodel/etc
class SignInViewModel {
  final form = FormGroup({
    'email': FormControl<String>(validators: [Validators.required, Validators.email]),
    'password': FormControl<String>(validators: [Validators.required, Validators.minLength(8)])
  });

  void signIn() {
    final credentials = this.form.value;
    // ... make some business logic
    // ...
  }

}
// simple sign-in view
class SignInScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final viewModel = Provider.of<SignInViewModel>(context, listen: false);
    return ReactiveForm(
      formGroup: viewModel.form,
      child: Column(
        children: <Widget>[
          ReactiveTextField(
            formControlName: 'email',
          ),
          ReactiveTextField(
            formControlName: 'password',
            obscureText: true,
          ),
          ReactiveFormConsumer(
            builder: (context, form, child) {
              return RaisedButton(
                child: Text('Submit'),
                // if form valid, sign-in
                onPressed: form.valid ? viewModel.signIn : null,
              );
            },
          ),
        ],
      ),
    );
  }
}

@jbt-cii
Copy link
Author

jbt-cii commented Mar 8, 2021

It was not so easy in my case but I succedeed defining it inside my Cubit part.
Thank you!!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants