Closed jbt-cii closed 3 years ago
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?
I have also released a new version update and let me know. Thanks
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 : https://github.com/joanpablo/reactive_forms/issues/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!"
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
════════════════════════════════════════════════════════════════════════════════
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,
),
),
],
),
);
}
}
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);
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!!!
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
Thank you for the advice. I will try that and I will tell you if I succeed to do it :-)
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?
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,
);
},
),
],
),
);
}
}
It was not so easy in my case but I succedeed defining it inside my Cubit part. Thank you!!!
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:
I tried several things from the severals examples you gave. I tried also that:
I tried also to use this:
I tried also to change the "FormGroup" declaration like this:
Each time I get an error like this one:
If you have any clue it will be great! :-)