Flutter Bloc中关闭showDatePicker后遮罩未关闭问题求助
Flutter Bloc中使用showDatePicker的问题解决
问题描述
近期开始使用Flutter Bloc,调用showDatePicker时遇到困惑。了解到snackbar、dialog这类组件需要在Bloc的listener中显示,但不知道如何正确实现。以下是当前的代码:
事件类
class DatePickerEvent extends CollectionEvent { DateTime? startDate, endDate; final bool isStartDate; DatePickerEvent({ this.startDate, this.endDate, required this.isStartDate}); @override List<Object?> get props => [startDate, endDate, isStartDate]; }
状态类
class PickDateState extends CollectionState { final DateTime startDate; final DateTime endDate; const PickDateState({required this.startDate, required this.endDate}); @override List<Object?> get props => [startDate, endDate]; }
Bloc事件处理代码
on<DatePickerEvent>((event, emit) async { emit(PickDateState( startDate: event.startDate ?? DateTime.now(), endDate: event.endDate ?? DateTime.now() )); });
日期选择函数
Future _pickDates(BuildContext context, {required bool isStartDate}) async { var initialDate = DateTime.now(); if (isStartDate && _startDate != null) { initialDate = _startDate!; } else if (!isStartDate && _endDate != null) { initialDate = _endDate!; } final DateTime? pickedDate = await showDatePicker( context: context, initialDate: initialDate, firstDate: DateTime(1990), lastDate: DateTime.now(), helpText: AppLocalization.of(context).getTranslatedValue(isStartDate ? 'start_date' : 'end_date'), fieldHintText: "dd/mm/yyyy", ); if(!mounted) return; if (pickedDate != null) { if (isStartDate) { _startDate = pickedDate; WidgetsBinding.instance.addPostFrameCallback((timeStamp) { context.read<CollectionBloc>().add(DatePickerEvent(isStartDate: true, startDate: pickedDate)); }); } else { // _endDate = pickedDate; context.read<CollectionBloc>().add(DatePickerEvent(isStartDate: false, endDate: pickedDate)); } } }
UI调用代码
ElevatedButton( style: ElevatedButton.styleFrom(backgroundColor: kSecondaryPrimaryColor), child: Text(_startDate == null ? "start_date" : DateFormat('dd-MM-yyyy').format(_startDate!)), onPressed: () { _pickDates(context, isStartDate: true); }, ),
解决方案
1. 移除UI层本地状态,统一依赖Bloc状态
UI层不需要维护_startDate、_endDate变量,直接从Bloc的PickDateState中读取数据,避免状态不一致问题。
2. 用BlocListener监听状态变化,处理弹窗/提示
如果需要在日期更新后显示snackbar或dialog,通过BlocListener监听状态变化,在回调中执行对应操作,确保上下文有效且符合Bloc规范。
修改后代码示例
调整UI层代码
// 在页面build方法中嵌套BlocListener和BlocBuilder BlocListener<CollectionBloc, CollectionState>( listener: (context, state) { // 状态为PickDateState时,显示更新提示 if (state is PickDateState) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("日期已更新:${DateFormat('dd-MM-yyyy').format(state.startDate)}")), ); } }, child: BlocBuilder<CollectionBloc, CollectionState>( builder: (context, state) { DateTime? startDate; DateTime? endDate; if (state is PickDateState) { startDate = state.startDate; endDate = state.endDate; } return ElevatedButton( style: ElevatedButton.styleFrom(backgroundColor: kSecondaryPrimaryColor), child: Text(startDate == null ? "start_date" : DateFormat('dd-MM-yyyy').format(startDate)), onPressed: () { _pickDates(context, isStartDate: true, currentDate: startDate); }, ); }, ), )
修改日期选择函数
去掉本地状态维护,直接从参数获取当前日期:
Future _pickDates(BuildContext context, {required bool isStartDate, DateTime? currentDate}) async { final initialDate = currentDate ?? DateTime.now(); final DateTime? pickedDate = await showDatePicker( context: context, initialDate: initialDate, firstDate: DateTime(1990), lastDate: DateTime.now(), helpText: AppLocalization.of(context).getTranslatedValue(isStartDate ? 'start_date' : 'end_date'), fieldHintText: "dd/mm/yyyy", ); if (!mounted || pickedDate == null) return; // 直接向Bloc发送事件,无需维护本地状态 context.read<CollectionBloc>().add( DatePickerEvent( isStartDate: isStartDate, startDate: isStartDate ? pickedDate : null, endDate: !isStartDate ? pickedDate : null, ), ); }
优化Bloc事件处理逻辑
确保状态更新时保留未修改的日期值,避免被重置为默认值:
on<DatePickerEvent>((event, emit) async { // 获取当前状态中的日期,保留未修改的部分 DateTime currentStartDate = DateTime.now(); DateTime currentEndDate = DateTime.now(); if (state is PickDateState) { currentStartDate = state.startDate; currentEndDate = state.endDate; } emit(PickDateState( startDate: event.startDate ?? currentStartDate, endDate: event.endDate ?? currentEndDate, )); });
关键说明
showDatePicker是UI层异步操作,直接在UI触发即可,选择完成后将结果通过事件传递给Bloc。- 所有需要状态变化后触发的弹窗、提示,都放在
BlocListener的回调中处理,保证上下文有效性。 - 移除UI层本地状态,统一从Bloc获取数据,确保状态唯一、一致。
内容的提问来源于stack exchange,提问作者imran sifat
相关产品推荐
相关产品推荐

