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

Flutter中Food实例创建异常及空安全问题求助

Flutter 订单处理问题解决方案

问题1:切换Variation后旧订单实例被更新

原因分析

  1. 创建新Food实例时直接传递原widget.food.variations列表,属于引用传递,新旧实例共享同一个Variation列表,修改原实例的Variation会同步影响列表中的旧实例。
  2. 当selectedVars.contains(widget.food.selectedVar)为true时,直接将原实例widget.food添加到userOrders,后续修改原实例的selectedVar会直接篡改已存在的订单。
  3. 切换Variation的逻辑直接修改widget.food.selectedVar,若原实例已在订单列表中,会导致旧订单被意外更新。

修复方案

  • 给Food和Variation类添加拷贝构造函数,实现深拷贝,确保新实例的所有属性都是独立引用。
  • 创建新订单实例时直接绑定当前选中的Variation,避免依赖原实例状态。
  • 改用食物名称+Variation标识判断重复订单,替代不可靠的引用判断。

代码修改:

首先给实体类添加拷贝构造函数:

class Food {
  String imgUrl;
  String desc;
  String name;
  String waitTime;
  num score;
  int price;
  int pq;
  int quantity;
  bool favourited;
  List<Variation> variations;
  Variation? selectedVar;
  String about;
  bool highlight;

  Food(this.imgUrl, this.desc, this.name, this.pq, this.waitTime, this.score,
      this.price, this.quantity, this.variations, this.about, this.favourited,
      {this.highlight = false});

  // 拷贝构造函数,支持自定义选中规格和数量
  Food.copy(Food original, {Variation? selectedVar, int? quantity})
      : imgUrl = original.imgUrl,
        desc = original.desc,
        name = original.name,
        waitTime = original.waitTime,
        score = original.score,
        price = original.price,
        pq = original.pq,
        quantity = quantity ?? original.quantity,
        favourited = original.favourited,
        // 深拷贝Variation列表,避免引用共享
        variations = original.variations.map((v) => Variation.copy(v)).toList(),
        selectedVar = selectedVar ?? original.selectedVar,
        about = original.about,
        highlight = original.highlight;
}

class Variation {
  String imgURL;
  String title;
  bool selected;
  // 其他属性...

  Variation(this.imgURL, this.title, this.selected);

  // Variation拷贝构造函数
  Variation.copy(Variation original)
      : imgURL = original.imgURL,
        title = original.title,
        selected = original.selected;
}

修改+按钮逻辑:

GestureDetector(
  onTap: () {
    final selectedVar = widget.food.selectedVar;
    if (selectedVar == null) {
      errorDialog(
        context,
        '错误',
        '请先选择规格',
      );
      return;
    }

    // 通过食物名称+规格标题判断是否已添加该组合
    final existingIndex = userOrders.indexWhere((order) => 
      order.name == widget.food.name && 
      order.selectedVar?.title == selectedVar.title
    );

    if (existingIndex != -1) {
      // 已存在则增加数量
      context.read<FoodCount>().increment(userOrders[existingIndex]);
    } else {
      // 创建独立新实例并绑定选中规格
      final newFood = Food.copy(widget.food, selectedVar: selectedVar);
      userOrders.add(newFood);
    }
    context.read<Counter>().update();
    setState(() {});
  },
  child: const Text(
    '+',
    style: TextStyle(
      fontSize: 30,
      fontWeight: FontWeight.bold,
      color: Colors.white38,
    ),
  ),
)

调整切换Variation的逻辑(操作本地状态而非原实例):

// 在组件中维护本地选中状态
Variation? _localSelectedVar;

onTap: () {
  setState(() {
    selectedIndex = index;
    _localSelectedVar = widget.food.variations[index];
    // 后续+按钮使用_localSelectedVar替代widget.food.selectedVar
  });
}

问题2:Checkout组件空指针错误(Null check operator used on a null value)

原因分析

添加订单时虽判断了selectedVar不为空,但后续可能存在订单被修改导致selectedVar置空的情况,且Checkout中直接用!强制解包空值,未处理边界场景。

修复方案

  • 使用安全调用?.和空值合并??处理空值,避免强制解包。
  • 从源头确保所有订单实例的selectedVar不为空。

代码修改:

// 图片部分:用默认图替代空值
Container(
  padding: const EdgeInsets.all(8),
  width: 60,
  height: 60,
  child: Image.asset(
    userOrders[index].selectedVar?.imgURL ?? 'assets/default_food.png',
    fit: BoxFit.fitHeight,
  ),
)

// 标题部分:用默认文本替代空值
Text(
  userOrders[index].selectedVar?.title ?? '未选择规格',
  style: const TextStyle(
    fontSize: 14,
    fontWeight: FontWeight.w300,
    fontStyle: FontStyle.italic,
  ),
)

更严谨的条件渲染写法:

// 图片部分
Container(
  padding: const EdgeInsets.all(8),
  width: 60,
  height: 60,
  child: userOrders[index].selectedVar != null 
      ? Image.asset(userOrders[index].selectedVar!.imgURL, fit: BoxFit.fitHeight)
      : const Icon(Icons.restaurant_menu),
)

// 标题部分
userOrders[index].selectedVar != null 
    ? Text(
        userOrders[index].selectedVar!.title,
        style: const TextStyle(
          fontSize: 14,
          fontWeight: FontWeight.w300,
          fontStyle: FontStyle.italic,
        ),
      )
    : const Text('未选择规格')

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 01:02:51