英文:
Why is Bloc state not updating when using reactive_forms in flutter?
问题
Here is the translated content:
我正在使用Flutter与reactive_forms
包和BLoC。我尝试将FormGroup
外包给BLoC状态类,并使用BlocBuilder
小部件将其提供给ReactiveFormBuilder
。
Flutter BLoC - 个人资料状态:
BLoC状态
class ProfileState extends Equatable {
final initAdditionalDocForm = FormGroup({
"doc_images": FormControl(),
"doc_number": FormControl(),
"doc_type_id": FormControl()
});
final FormGroup initPersonalForm = FormGroup({
'op_first_name': FormControl<String>(
validators: [Validators.minLength(4), Validators.required]),
'op_last_name': FormControl<String>(
validators: [Validators.minLength(4), Validators.required]),
'op_dob': FormControl<DateTime>(validators: [Validators.required]),
'op_gender': FormControl<String>(
validators: [Validators.required], value: "Not Specified"),
'op_email': FormControl<String>(
validators: [Validators.required, Validators.email]),
'op_pet_name': FormControl<String>(),
'op_mobile_no': FormControl<int>(validators: [
Validators.number,
]),
'op_alternate_mobile_no': FormControl<int>(validators: [
Validators.number,
]),
'veh_driving_license_no':
FormControl<String>(validators: [Validators.required]),
'veh_license_validity':
FormControl<DateTime>(validators: [Validators.required]),
'op_pan_no': FormControl<String>(),
'AdditionalDoc': FormArray([]),
'op_address_pin_code': FormControl<String>(
validators: [Validators.required, Validators.number]),
'op_address_state': FormControl<String>(validators: [Validators.required]),
'op_address_city': FormControl<String>(validators: [Validators.required]),
'op_address_line_1': FormControl<String>(validators: [Validators.required]),
'op_address_line_2': FormControl<String>(),
'op_landmark': FormControl<String>(),
'op_profile': FormControl<String>() //Base 64 Profile Picture
});
static const _vehNoRegex =
r"^[A-Z]{2}[ -][0-9]{1,2}(?: [A-Z])?(?: [A-Z]*)? [0-9]{4}$";
final initVehicleInfoForm = FormGroup({
//vehicle images maintained in the screen it self due to naming convention of API
'veh_no_person': FormControl<int>(validators: [Validators.number]),
'veh_charge_per_person':
FormControl<int>(validators: [Validators.number, Validators.min(20)]),
'veh_registration_no': FormControl<String>(
validators: [Validators.pattern(_vehNoRegex), Validators.required]),
'veh_city': FormControl<String>(validators: [Validators.required]),
'veh_wheel_type': FormControl<int>(value: 4),
'veh_capacity': FormControl<int>(),
'veh_dimension': FormControl<String>(),
'veh_color': FormControl<String>(validators: [Validators.required]),
'veh_type':
FormControl<int>(validators: [Validators.required], value: 1), //1 or 2
'veh_fuel_type': FormControl<String>(validators: [Validators.required]),
'veh_3km_15km': FormControl<int>(validators: [Validators.number]),
'veh_above_15km': FormControl<int>(validators: [Validators.number]),
'AdditionalDoc': FormArray([]),
});
final initBusinessProfileForm = FormGroup({
'doc_pan':
FormControl<String>(validators: [Validators.required]), //Base64 Image
'op_bu_address_city':
FormControl<String>(validators: [Validators.required]),
'op_bu_address_line_1':
FormControl<String>(validators: [Validators.required]),
'op_bu_address_line_2':
FormControl<String>(validators: [Validators.required]),
'op_bu_address_pin_code':
FormControl<int>(validators: [Validators.required]),
'op_bu_address_state':
FormControl<String>(validators: [Validators.required]),
'op_payment_mode': FormControl<String>(validators: [Validators.required]),
'op_bu_email': FormControl<String>(
validators: [Validators.required, Validators.email]),
'op_bu_gstn_available':
FormControl<bool>(validators: [Validators.required]),
'op_bu_gstn_no': FormControl<String>(validators: [Validators.required]),
'op_bu_landmark': FormControl<String>(validators: [Validators.required]),
'op_bu_name': FormControl<String>(validators: [Validators.required]),
'op_bu_pan_no': FormControl<String>(validators: [Validators.required]),
'AdditionalDoc': FormArray([]),
});
///Bank Details
final initPaymentInfoForm = FormGroup({
'op_bank_name': FormControl<String>(validators: [Validators.required]),
'op_bank_ifsc': FormControl<String>(validators: [Validators.required]),
'op_bank_account_number':
FormControl<int>(validators: [Validators.required]),
});
late final FormGroup personalProfileForm;
late final FormGroup personalProfileAdditionalDocGroup;
late final FormGroup vehicleInfoForm;
late final FormGroup businessProfileForm;
late final FormGroup paymentInfoForm;
ProfileState({
FormGroup? personalForm,
FormGroup? additionalDoc,
FormGroup? vehicleForm,
FormGroup? businessForm,
FormGroup? paymentForm,
}) : super() {
personalProfileAdditionalDocGroup = additionalDoc ?? initAdditionalDocForm;
personalProfileForm = personalForm ?? initPersonalForm;
vehicleInfoForm = vehicleForm ?? initVehicleInfoForm;
businessProfileForm = businessForm ?? initBusinessProfileForm;
paymentInfoForm = paymentForm ?? initPaymentInfoForm;
}
@override
List<Object> get props => [
personalProfileForm,
personalProfileAdditionalDocGroup,
businessProfileForm,
vehicleInfoForm,
paymentInfoForm,
];
ProfileState copyWith({
FormGroup? personalForm,
FormGroup? additionalDoc,
FormGroup? vehicleForm,
FormGroup? businessForm,
FormGroup? paymentForm,
}) =>
ProfileState(
personalForm: personalForm ?? personalProfileForm,
additionalDoc: additionalDoc ?? personalProfileAdditionalDocGroup,
vehicleForm: vehicleInfoForm,
businessForm: businessProfileForm,
paymentForm: paymentInfoForm,
);
}
BLoC方法
在主Bloc类内部,当表单填写完成时触发了一个事件方法。
Future<void> _onSaveProfile(ProfileEvent event, Emitter emit) async {
try {
emit(ProfileSaving());
if (event is SaveProfileData) {
state.copyWith(personalForm: event.personalProfile);
}
emit(ProfileSaved());
} catch (e) {
print(e);
}
}
UI小部件
响应式表单构建器如下:
BlocBuilder<ProfileBloc, ProfileState>(
builder: (context, state) {
return ReactiveFormBuilder(
form: () => state.personalProfileForm,
builder: (context, form, _) => ListView(
children: [
/* 名字 */
profileRowBuilder(
context,
const ReactiveTextFieldCustomV1(
formControlName: "op_first_name",
hintText: "",
showLabel: true,
<details>
<summary>英文:</summary>
I am using flutter with `reactive_forms` package and BLoC. I tried to outsource the `FormGroup` to BLoC state class and providing it to the `ReactiveFormBuilder` using `BlocBuilder` widget.
Flutter Bloc - Profile State:
### BLOC STATE
```dart
class ProfileState extends Equatable {
final initAdditionalDocForm = FormGroup({
"doc_images": FormControl(),
"doc_number": FormControl(),
"doc_type_id": FormControl()
});
final FormGroup initPersonalForm = FormGroup({
'op_first_name': FormControl<String>(
validators: [Validators.minLength(4), Validators.required]),
'op_last_name': FormControl<String>(
validators: [Validators.minLength(4), Validators.required]),
'op_dob': FormControl<DateTime>(validators: [Validators.required]),
'op_gender': FormControl<String>(
validators: [Validators.required], value: "Not Specified"),
'op_email': FormControl<String>(
validators: [Validators.required, Validators.email]),
'op_pet_name': FormControl<String>(),
'op_mobile_no': FormControl<int>(validators: [
Validators.number,
]),
'op_alternate_mobile_no': FormControl<int>(validators: [
Validators.number,
]),
'veh_driving_license_no':
FormControl<String>(validators: [Validators.required]),
'veh_license_validity':
FormControl<DateTime>(validators: [Validators.required]),
'op_pan_no': FormControl<String>(),
'AdditionalDoc': FormArray([]),
'op_address_pin_code': FormControl<String>(
validators: [Validators.required, Validators.number]),
'op_address_state': FormControl<String>(validators: [Validators.required]),
'op_address_city': FormControl<String>(validators: [Validators.required]),
'op_address_line_1': FormControl<String>(validators: [Validators.required]),
'op_address_line_2': FormControl<String>(),
'op_landmark': FormControl<String>(),
'op_profile': FormControl<String>() //Base 64 Profile Picture
});
static const _vehNoRegex =
r"^[A-Z]{2}[ -][0-9]{1,2}(?: [A-Z])?(?: [A-Z]*)? [0-9]{4}$";
final initVehicleInfoForm = FormGroup({
//vehicle images maintained in the screen it self due to naming convention of API
'veh_no_person': FormControl<int>(validators: [Validators.number]),
'veh_charge_per_person':
FormControl<int>(validators: [Validators.number, Validators.min(20)]),
'veh_registration_no': FormControl<String>(
validators: [Validators.pattern(_vehNoRegex), Validators.required]),
'veh_city': FormControl<String>(validators: [Validators.required]),
'veh_wheel_type': FormControl<int>(value: 4),
'veh_capacity': FormControl<int>(),
'veh_dimension': FormControl<String>(),
'veh_color': FormControl<String>(validators: [Validators.required]),
'veh_type':
FormControl<int>(validators: [Validators.required], value: 1), //1 or 2
'veh_fuel_type': FormControl<String>(validators: [Validators.required]),
'veh_3km_15km': FormControl<int>(validators: [Validators.number]),
'veh_above_15km': FormControl<int>(validators: [Validators.number]),
'AdditionalDoc': FormArray([]),
});
final initBusinessProfileForm = FormGroup({
'doc_pan':
FormControl<String>(validators: [Validators.required]), //Base64 Image
'op_bu_address_city':
FormControl<String>(validators: [Validators.required]),
'op_bu_address_line_1':
FormControl<String>(validators: [Validators.required]),
'op_bu_address_line_2':
FormControl<String>(validators: [Validators.required]),
'op_bu_address_pin_code':
FormControl<int>(validators: [Validators.required]),
'op_bu_address_state':
FormControl<String>(validators: [Validators.required]),
'op_payment_mode': FormControl<String>(validators: [Validators.required]),
'op_bu_email': FormControl<String>(
validators: [Validators.required, Validators.email]),
'op_bu_gstn_available':
FormControl<bool>(validators: [Validators.required]),
'op_bu_gstn_no': FormControl<String>(validators: [Validators.required]),
'op_bu_landmark': FormControl<String>(validators: [Validators.required]),
'op_bu_name': FormControl<String>(validators: [Validators.required]),
'op_bu_pan_no': FormControl<String>(validators: [Validators.required]),
'AdditionalDoc': FormArray([])
});
///Bank Details
final initPaymentInfoForm = FormGroup({
'op_bank_name': FormControl<String>(validators: [Validators.required]),
'op_bank_ifsc': FormControl<String>(validators: [Validators.required]),
'op_bank_account_number':
FormControl<int>(validators: [Validators.required]),
});
late final FormGroup personalProfileForm;
late final FormGroup personalProfileAdditionalDocGroup;
late final FormGroup vehicleInfoForm;
late final FormGroup businessProfileForm;
late final FormGroup paymentInfoForm;
ProfileState({
FormGroup? personalForm,
FormGroup? additionalDoc,
FormGroup? vehicleForm,
FormGroup? businessForm,
FormGroup? paymentForm,
}) : super() {
personalProfileAdditionalDocGroup = additionalDoc ?? initAdditionalDocForm;
personalProfileForm = personalForm ?? initPersonalForm;
vehicleInfoForm = vehicleForm ?? initVehicleInfoForm;
businessProfileForm = businessForm ?? initBusinessProfileForm;
paymentInfoForm = paymentForm ?? initPaymentInfoForm;
}
@override
List<Object> get props => [
personalProfileForm,
personalProfileAdditionalDocGroup,
businessProfileForm,
vehicleInfoForm,
paymentInfoForm,
];
ProfileState copyWith({
FormGroup? personalForm,
FormGroup? additionalDoc,
FormGroup? vehicleForm,
FormGroup? businessForm,
FormGroup? paymentForm,
}) =>
ProfileState(
personalForm: personalForm ?? personalProfileForm,
additionalDoc: additionalDoc ?? personalProfileAdditionalDocGroup,
vehicleForm: vehicleInfoForm,
businessForm: businessProfileForm,
paymentForm: paymentInfoForm,
);
}
BLOC Method
Inside the main Bloc Class there is a event method which is fired when the form is filled.
Future<void> _onSaveProfile(ProfileEvent event, Emitter emit) async {
try {
emit(ProfileSaving());
if (event is SaveProfileData) {
state.copyWith(personalForm: event.personalProfile);
}
emit(ProfileSaved());
} catch (e) {
print(e);
}
}
UI Widget
The reactive form builder is:
BlocBuilder<ProfileBloc, ProfileState>(
builder: (context, state) {
return ReactiveFormBuilder(
form: () => state.personalProfileForm,
builder: (context, form, _) => ListView(
children: [
/* First & Last Name */
profileRowBuilder(
context,
const ReactiveTextFieldCustomV1(
formControlName: "op_first_name",
hintText: "",
showLabel: true,
labelText: "First Name",
shortHeight: true,
),
........
ElevatedButton(
onPressed: () {
context
.read<ProfileBloc>()
.add(SaveProfileData(personalProfile: form));
// print(
// state.personalProfileForm.control('op_first_name').value);
// print(_drivingLicenseNo);
},
child: Text("Press"),
),
ElevatedButton(
onPressed: () {
print(
state.personalProfileForm.control('op_first_name').value);
// state.personalProfileForm.controls.forEach((key, value) {
// print("$key: ${value.value}");
// });
},
child: Text("State"),
),
],
),
);
},
);
}
The first button is firing the event and I am passing the entire form to the event and as shown in the _onProfileSave()
method, it is copied to state. But the form values which are typed in the textfield are null
when I click the 2nd button to view the state.
Would be great to receive a solution. Also would be great to receive any suggestions on how to use reactive_forms with BLoC.
I tried to debug it and check via VS Code debugger, but the form
inside of ReactiveBuilder there are values but when the event is fired, in the state the form values are still null
. They are not copied.
答案1
得分: 0
也许不会发出新状态,因为它们是==
。最近我也遇到了同样的问题,我通过以下方法解决了它:
emit(state.copyWith(FormGroup(event.controls)));
英文:
Maybe is not emitting the new state because they are ==
. Same thing happened to me recently with FormGroup
i solved with this
emit(state.copyWith(FormGroup(event.controls)));
答案2
得分: 0
As bloc requires an event to be fired when updating the state, it had to fire an event when the form changes and use .copyWith
(if you have it set up). As this overhead is not necessary in Cubits, it works with cubits. So I generally follow Cubits for such use cases and Blocs for others like auth, etc.
英文:
As bloc requires an event to be fired when updating the state, it had to fire an event when then form changes and use .copyWith
(if you have it setup). As this overhead is not necessary in Cubits, it works with cubits. So I generally follow Cubits for such use cases and Blocs for others like auth etc.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论