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

Flutter Riverpod复杂对象深层修改:Freezed与IList实践疑问

回答

关于Freezed和fast_immutable_collections的使用

完全正确。这两个工具是Riverpod生态中处理复杂不可变状态的黄金组合:

  • Freezed 自动生成不可变类和copyWith方法,完美契合Riverpod的不可变状态要求,同时自带JSON序列化支持,大幅简化数据解析工作。
  • fast_immutable_collections(IList) 提供真正的不可变列表,彻底避免原生List的隐性可变风险,性能优于原生不可变列表,还提供了一系列便捷的修改方法(所有操作均返回新列表)。

实现Meal的增删改方法

以下是针对你需求的具体实现,重点处理了嵌套层级的null安全,同时利用Freezed的copyWith和IList的特性生成全新状态:

@riverpod
class MenuNotifier extends _$MenuNotifier {
  @override
  Menu build() {
    return const Menu(
      currency: 'Kč',
      staticMenu: null,
      weeklyMenu: null,
    );
  }

  void initializeMenu() async {
    final menu = await ServerHelper.initializeLocalJson();
    state = menu;
  }

  Future<void> updateOnServer() async {
    ServerHelper.sendMenuToServer(state);
  }

  void updateMealInMealGroup({
    required String mealGroupUid,
    required String mealUid,
    required Meal meal,
  }) {
    state = state.copyWith(
      staticMenu: state.staticMenu?.copyWith(
        mealGroupList: state.staticMenu?.mealGroupList?.map((group) {
          if (group.id == mealGroupUid) {
            return group.copyWith(
              mealList: group.mealList?.map((m) {
                if (m.id == mealUid) return meal;
                return m;
              }).toIList(),
            );
          }
          return group;
        }).toIList(),
      ),
    );
  }

  void addMealToMealGroup({
    required String mealGroupUid,
    required Meal meal,
  }) {
    state = state.copyWith(
      staticMenu: state.staticMenu?.copyWith(
        mealGroupList: state.staticMenu?.mealGroupList?.map((group) {
          if (group.id == mealGroupUid) {
            final updatedMeals = group.mealList ?? IList.empty();
            return group.copyWith(
              mealList: updatedMeals.add(meal),
            );
          }
          return group;
        }).toIList(),
      ),
    );
  }

  void deleteMealinMealGroup({
    required String mealGroupUid,
    required String mealUid,
  }) {
    state = state.copyWith(
      staticMenu: state.staticMenu?.copyWith(
        mealGroupList: state.staticMenu?.mealGroupList?.map((group) {
          if (group.id == mealGroupUid) {
            return group.copyWith(
              mealList: group.mealList?.removeWhere((m) => m.id == mealUid),
            );
          }
          return group;
        }).toIList(),
      ),
    );
  }
}

代码说明

  1. null安全处理:所有嵌套层级均使用?.处理null场景,避免初始化时staticMenu为null导致的空指针异常。
  2. 逐层生成新实例:从最外层的Menu开始,依次修改StaticMenu、MealGroup、MealList,每一层都通过copyWith生成全新的不可变实例,符合Riverpod的状态更新要求。
  3. IList核心操作:
    • map遍历集合,修改目标元素后通过toIList()生成新列表。
    • add直接添加元素并返回新列表(IList的add不会修改原列表)。
    • removeWhere过滤掉目标元素,返回新列表。

额外优化建议

  • 可以为MealGroup添加扩展方法(如updateMeal、addMeal、deleteMeal),将列表操作逻辑封装到实体类中,简化Notifier的代码复杂度。
  • 如果后续状态更新逻辑更复杂,可结合Freezed的when/map方法处理不同状态分支,进一步增强代码可读性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 15:45:10