Flutter中使用Bloc在组件重建时保存输入值的方案
问题分析与解决方案
你的核心问题有两个:一是**TextEditingController在build方法内创建**,每次Bloc状态变化触发组件重建时,控制器会被重新初始化,导致输入值丢失;二是表单输入值没有纳入Bloc的状态管理,切换选项时无法持久化数据。
下面是具体的修改方案,将所有输入状态交由Bloc管理,同时优化控制器的生命周期:
第一步:更新Bloc的状态与事件
修改Bloc代码,把所有输入字段加入状态,并添加对应的输入事件来更新状态:
class FormBloc extends Bloc<FormEvent, MyFormState> { FormBloc() : super(MyFormState.initial()) { on<RadioButtonFormEvent>(_setRadioButtonState); on<NameChangedEvent>(_onNameChanged); on<PriceChangedEvent>(_onPriceChanged); on<FormOneChangedEvent>(_onFormOneChanged); on<FormTwoChangedEvent>(_onFormTwoChanged); on<FormThreeChangedEvent>(_onFormThreeChanged); } void _setRadioButtonState(RadioButtonFormEvent event, Emitter<MyFormState> emit) { emit(state.copyWith( buttonIndex: event.buttonIndex, buttonName: event.buttonName, )); } void _onNameChanged(NameChangedEvent event, Emitter<MyFormState> emit) { emit(state.copyWith(name: event.name)); } void _onPriceChanged(PriceChangedEvent event, Emitter<MyFormState> emit) { emit(state.copyWith(price: event.price)); } void _onFormOneChanged(FormOneChangedEvent event, Emitter<MyFormState> emit) { emit(state.copyWith(formOne: event.value)); } void _onFormTwoChanged(FormTwoChangedEvent event, Emitter<MyFormState> emit) { emit(state.copyWith(formTwo: event.value)); } void _onFormThreeChanged(FormThreeChangedEvent event, Emitter<MyFormState> emit) { emit(state.copyWith(formThree: event.value)); } } class MyFormState extends Equatable { final int buttonIndex; final String buttonName; final String name; final String price; final String formOne; final String formTwo; final String formThree; const MyFormState({ required this.buttonIndex, required this.buttonName, required this.name, required this.price, required this.formOne, required this.formTwo, required this.formThree, }); // 初始状态初始化 factory MyFormState.initial() { return const MyFormState( buttonIndex: 0, buttonName: "option one", name: "", price: "", formOne: "", formTwo: "", formThree: "", ); } // 状态复制方法,用于局部更新 MyFormState copyWith({ int? buttonIndex, String? buttonName, String? name, String? price, String? formOne, String? formTwo, String? formThree, }) { return MyFormState( buttonIndex: buttonIndex ?? this.buttonIndex, buttonName: buttonName ?? this.buttonName, name: name ?? this.name, price: price ?? this.price, formOne: formOne ?? this.formOne, formTwo: formTwo ?? this.formTwo, formThree: formThree ?? this.formThree, ); } @override List<Object?> get props => [buttonIndex, buttonName, name, price, formOne, formTwo, formThree]; } abstract class FormEvent extends Equatable {} class RadioButtonFormEvent extends FormEvent { final int buttonIndex; final String buttonName; const RadioButtonFormEvent({ required this.buttonIndex, required this.buttonName, }); @override List<Object?> get props => [buttonIndex, buttonName]; } // 新增各输入字段的变更事件 class NameChangedEvent extends FormEvent { final String name; const NameChangedEvent(this.name); @override List<Object?> get props => [name]; } class PriceChangedEvent extends FormEvent { final String price; const PriceChangedEvent(this.price); @override List<Object?> get props => [price]; } class FormOneChangedEvent extends FormEvent { final String value; const FormOneChangedEvent(this.value); @override List<Object?> get props => [value]; } class FormTwoChangedEvent extends FormEvent { final String value; const FormTwoChangedEvent(this.value); @override List<Object?> get props => [value]; } class FormThreeChangedEvent extends FormEvent { final String value; const FormThreeChangedEvent(this.value); @override List<Object?> get props => [value]; }
第二步:优化表单UI组件
将FormInput改为StatefulWidget,把控制器移到initState中初始化并绑定Bloc状态,避免每次重建丢失数据:
class FormInput extends StatefulWidget { const FormInput({super.key}); @override State<FormInput> createState() => _FormInputState(); } class _FormInputState extends State<FormInput> { late final TextEditingController nameController; late final TextEditingController priceController; late final TextEditingController formOneController; late final TextEditingController formTwoController; late final TextEditingController formThreeController; late final FormBloc formBloc; @override void initState() { super.initState(); formBloc = BlocProvider.of<FormBloc>(context); final initialState = formBloc.state; // 从Bloc初始状态初始化控制器 nameController = TextEditingController(text: initialState.name) ..addListener(_onNameChanged); priceController = TextEditingController(text: initialState.price) ..addListener(_onPriceChanged); formOneController = TextEditingController(text: initialState.formOne) ..addListener(_onFormOneChanged); formTwoController = TextEditingController(text: initialState.formTwo) ..addListener(_onFormTwoChanged); formThreeController = TextEditingController(text: initialState.formThree) ..addListener(_onFormThreeChanged); } @override void dispose() { // 销毁控制器避免内存泄漏 nameController.dispose(); priceController.dispose(); formOneController.dispose(); formTwoController.dispose(); formThreeController.dispose(); super.dispose(); } // 输入变化时触发Bloc事件更新状态 void _onNameChanged() { formBloc.add(NameChangedEvent(nameController.text)); } void _onPriceChanged() { formBloc.add(PriceChangedEvent(priceController.text)); } void _onFormOneChanged() { formBloc.add(FormOneChangedEvent(formOneController.text)); } void _onFormTwoChanged() { formBloc.add(FormTwoChangedEvent(formTwoController.text)); } void _onFormThreeChanged() { formBloc.add(FormThreeChangedEvent(formThreeController.text)); } @override Widget build(BuildContext context) { final formState = context.watch<FormBloc>().state; final optionOneIsActive = formState.buttonIndex == 0; final optionTwoIsActive = formState.buttonIndex == 1; return Container( padding: EdgeInsets.only( bottom: MediaQuery.of(context).viewInsets.bottom, top: 15, left: 15, right: 15), child: Column( mainAxisSize: MainAxisSize.min, children: [ TextField( controller: nameController, decoration: const InputDecoration(hintText: 'Name'), ), const SizedBox(height: 10), TextField( controller: priceController, keyboardType: TextInputType.number, decoration: const InputDecoration(hintText: 'Price'), ), const SizedBox(height: 15), const OptionsWidget(), Visibility( visible: optionOneIsActive, child: TextField( controller: formOneController, keyboardType: TextInputType.number, decoration: const InputDecoration(hintText: 'Form one'), )), Visibility( visible: optionTwoIsActive, child: Column( children: [ TextField( controller: formTwoController, keyboardType: TextInputType.number, decoration: const InputDecoration(hintText: 'Form two'), ), TextField( controller: formThreeController, keyboardType: TextInputType.number, decoration: const InputDecoration(hintText: 'Form three'), ), ], )), const SizedBox(height: 10), ElevatedButton( onPressed: () { BlocProvider.of<ProductsListBloc>(context).add( AddProductEvent( productName: formState.name, productPrice: int.parse(formState.price), productDescOne: formState.formOne, productDescTwo: formState.formTwo, productDescThree: formState.formThree, formOption: formState.buttonName, ), ); }, child: const Text('Create New'), ), ], ), ); } } class OptionsWidget extends StatelessWidget { const OptionsWidget({super.key}); Widget CustomRadioButton(String text, int index, BuildContext context) { final formBloc = BlocProvider.of<FormBloc>(context); final formState = context.watch<FormBloc>().state; final isSelected = formState.buttonIndex == index; return OutlinedButton( onPressed: () { formBloc.add(RadioButtonFormEvent( buttonIndex: index, buttonName: text, )); }, style: OutlinedButton.styleFrom( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), side: BorderSide(color: isSelected ? Colors.blue : Colors.grey), ), child: Text( text, style: TextStyle( color: isSelected ? Colors.blue : Colors.grey, ), )); } @override Widget build(BuildContext context) { return Row( children: [ CustomRadioButton("option one", 0, context), const SizedBox(width: 15), CustomRadioButton("option two", 1, context), ], ); } }
关键改动说明
- 状态持久化:所有输入字段都存入Bloc的
MyFormState,切换选项时状态不会丢失,提交时直接从Bloc状态取值,保证数据一致性。 - 控制器生命周期优化:控制器在
initState中初始化,绑定Bloc初始值并添加监听,只在组件销毁时释放,避免重建时重置输入。 - UI逻辑简化:直接通过Bloc状态判断选项是否激活,无需在
build中重复计算状态。
内容的提问来源于stack exchange,提问作者Vitali Danichkin
相关产品推荐
相关产品推荐

