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

Flutter中showCupertinoModalPopup日期选择器结合Bloc状态UI不更新

问题解决:Bloc状态已发射但UI未更新

状态已成功发射,列表数据已更新(打印可见),但UI始终不刷新。弹窗正常弹出,时间能设置到任务中,但UI组件没有更新。尝试过在showCupertinoBottomSheet的.then中调用emit,也用过StatefulBuilder,均无效。


核心原因

问题出在Equatable的状态比较逻辑和列表的引用复用:

  • 直接修改了原状态中列表的元素,没有创建新的列表引用。Equatable在比较listOfDailyRoutineOption时,只判断列表的引用是否相同,而非内部元素是否变化。引用不变的情况下,Bloc会认为状态没有更新,不会触发UI重建。
  • 如果DailyRoutineModel是可变类,直接修改元素属性也会导致Equatable无法识别变化(因为元素引用没变)。

修复方案

1. 修改Cubit中的showDatePicker方法,创建新的列表和模型实例

不要直接复用原列表的引用,而是创建列表副本,并通过模型的copyWith方法生成新的实例(确保模型是不可变的):

void showDatePicker(context, index) async {
    DateTime date = DateTime.now();
    await showCupertinoModalPopup(
      context: context,
      builder: (BuildContext builder) {
        return Container(
          height: MediaQuery.of(context).copyWith().size.height * 0.25,
          color: Colors.white,
          child: Column(
            children: [
              Expanded(
                child: CupertinoDatePicker(
                  mode: CupertinoDatePickerMode.time,
                  onDateTimeChanged: (value) {
                    date = value;
                  },
                  initialDateTime: DateTime.now(),
                ),
              ),
              CupertinoButton(
                child: const Text('Done'),
                onPressed: () {
                  String formattedTime = DateFormat.jm().format(date);
                  // 1. 创建原列表的副本,生成新的引用
                  var updatedList = List<DailyRoutineModel>.from(state.listOfDailyRoutineOption);
                  // 2. 使用copyWith创建新的模型实例,替换原位置的元素
                  updatedList[index] = updatedList[index].copyWith(time: formattedTime);
                  // 3. 发射包含新列表的状态
                  emit(state.copyWith(listOfDailyRoutineOption: updatedList));
                  Navigator.of(context).pop();
                },
              )
            ],
          ),
        );
      },
    );
  }

2. 确保DailyRoutineModel是不可变类并继承Equatable

如果你的DailyRoutineModel还不是不可变的,需要修改成以下结构,确保Equatable能正确比较模型实例:

class DailyRoutineModel extends Equatable {
  final String title;
  final String time;

  const DailyRoutineModel({required this.title, required this.time});

  @override
  List<Object> get props => [title, time];

  // 添加copyWith方法,用于生成新的实例
  DailyRoutineModel copyWith({
    String? title,
    String? time,
  }) {
    return DailyRoutineModel(
      title: title ?? this.title,
      time: time ?? this.time,
    );
  }
}

3. 验证状态类的copyWith方法

你的OnBoardState的copyWith方法已经正确传递新的列表引用,无需修改,但要确保传入的是新创建的列表(步骤1已经做到)。


为什么之前的尝试无效

  • 在.then中调用emit:核心问题是状态没有被识别为变化,即使延迟emit,引用还是原列表,Equatable仍然认为状态未变。
  • 使用StatefulBuilder:UI组件依赖Bloc状态,Bloc没有触发重建,StatefulBuilder也无法感知到状态变化。

内容的提问来源于stack exchange,提问作者Osama Buzdar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 03:22:52