You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 23:08:31