Flutter Bloc模式下实现开关切换控制组件显隐的问题
问题解决方案
核心问题分析
你的代码目前存在两个关键问题:
- Bloc每次emit新状态时,只传递部分字段,导致其他状态值丢失(比如触发
StandardAirEvent后,standardAir和answer2会变为null) - 页面仍依赖本地
standard变量,没有完全通过Bloc状态统一管理开关状态
修正步骤
1. 修正Bloc逻辑(保留完整状态)
每次emit新状态时,要基于当前状态的已有值进行更新,避免数据丢失:
class VelBloc extends Bloc<VelocityEvent, VelocityState> { VelBloc() : super(VelocityDataState(answer1: 0, answer2: 0, standardAir: true)) { on<InitialVelocityEvent>((event, emit) => emit(VelocityDataState(answer1: 0, answer2: 0, standardAir: true))); on<StandardAirEvent>((event, emit) { final currentState = state as VelocityDataState; emit(VelocityDataState( answer1: sqrt(event.velocity) * 1.225, answer2: currentState.answer2, standardAir: currentState.standardAir, )); }); on<AirDensityEvent>((event, emit) { final currentState = state as VelocityDataState; emit(VelocityDataState( answer1: currentState.answer1, answer2: sqrt(event.velocity) * event.airDensity, standardAir: currentState.standardAir, )); }); on<IsStandard>((event, emit) { final currentState = state as VelocityDataState; emit(VelocityDataState( answer1: currentState.answer1, answer2: currentState.answer2, standardAir: event.standardAir, )); }); } }
2. 修正事件和状态的不可变性
移除must_be_immutable注释,使用final字段保证不可变性:
// Event 部分 part of 'vel_air_bloc.dart'; @immutable abstract class VelocityEvent {} class InitialVelocityEvent extends VelocityEvent {} class StandardAirEvent extends VelocityEvent { final double velocity; StandardAirEvent({required this.velocity}); } class AirDensityEvent extends VelocityEvent { final double velocity; final double airDensity; AirDensityEvent({required this.velocity, required this.airDensity}); } class IsStandard extends VelocityEvent { final bool standardAir; IsStandard({required this.standardAir}); } // State 部分 part of 'vel_air_bloc.dart'; @immutable abstract class VelocityState {} class VelocityDataState extends VelocityState { final double? answer1; final double? answer2; final bool? standardAir; VelocityDataState({this.answer1, this.answer2, this.standardAir}); }
3. 页面中用Bloc状态控制开关和组件显隐
移除本地standard变量,直接从Bloc状态获取值,开关切换时触发事件更新状态:
class _VelocityAirState extends State<VelocityAir> { FirebaseServices firebaseServices = FirebaseServices(); String title = 'Velocity of Air'; final TextEditingController _velocityController = TextEditingController(); final TextEditingController _airDensity = TextEditingController(); @override void initState() { super.initState(); context.read<VelBloc>().add(InitialVelocityEvent()); } @override void dispose() { _velocityController.dispose(); _airDensity.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return BlocBuilder<VelBloc, VelocityState>(builder: (context, state) { if (state is VelocityDataState) { final isStandardAir = state.standardAir ?? true; return Scaffold( // 其他原有代码保持不变 body: ListView( // 其他原有代码保持不变 children: <Widget>[ // 其他原有代码保持不变 Padding( padding: const EdgeInsets.all(8.0), child: Card( // 其他原有代码保持不变 child: Column( // 其他原有代码保持不变 children: <Widget>[ Padding( padding: const EdgeInsets.all(20.0), child: CustomTextField( onChanged: () { if (_velocityController.text.trim().isNotEmpty && isStandardAir) { context.read<VelBloc>().add(StandardAirEvent( velocity: double.parse(_velocityController.text))); } }, // 其他原有属性保持不变 )), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[ const Text( "Standard Air?", style: TextStyle( color: Colors.white, fontWeight: FontWeight.w600), ), Padding( padding: const EdgeInsets.all(8.0), child: Text( isStandardAir ? "Yes" : "No", style: TextStyle( color: isStandardAir ? CupertinoColors.activeGreen : CupertinoColors.destructiveRed, fontWeight: FontWeight.w600), ), ), Padding( padding: const EdgeInsets.all(10.0), child: CupertinoSwitch( value: isStandardAir, thumbColor: Colors.white, trackColor: ColorConstants.borderColor, activeColor: ColorConstants.lightGreen, onChanged: (value) { context.read<VelBloc>().add(IsStandard(standardAir: value)); }, ), ), ], ), // 示例:根据开关状态控制组件显隐 if (!isStandardAir) Padding( padding: const EdgeInsets.all(20.0), child: CustomTextField( hintText: 'Enter air density', controller: _airDensity, // 其他属性自行补充 ), ), ], ), ), ), ], ), ); } return const SizedBox(); }); } }
总结
- 不需要在Bloc中使用void方法,严格遵循事件触发-状态更新的Bloc核心流程即可
- 不要混用
setState和Bloc,所有状态(包括开关状态)都交给Bloc统一管理,保证状态一致性 - 每次emit新状态时,必须保留当前状态的其他字段值,避免状态丢失
内容的提问来源于stack exchange,提问作者Mark McKeon
相关产品推荐
相关产品推荐

