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

如何将单选按钮及复选框选项值添加至购物车列表

购物车单选/复选框选品功能修正方案

你的现有代码存在几个核心问题:单选/复选框操作时直接添加整个Menu对象,无法区分具体选中的选项;单选重复选择会多次添加同一餐品;复选框取消选中时没有移除对应项;购物车也缺乏去重逻辑。以下是修正后的完整实现步骤:

1. 重构购物车数据模型

创建CartItem类封装餐品和选中的选项,精准区分不同选择组合:

class CartItem {
  final Menu menu;
  int? selectedSize; // 存储单选尺寸值(2/3)
  List<String> selectedChoices = []; // 存储选中的附加选项

  CartItem({
    required this.menu,
    this.selectedSize,
    List<String>? selectedChoices,
  }) : selectedChoices = selectedChoices ?? [];

  // 基于餐品唯一ID判断是否为同一餐品,方便去重/更新
  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is CartItem &&
          runtimeType == other.runtimeType &&
          menu.id == other.menu.id; // 假设Menu类有唯一id字段

  @override
  int get hashCode => menu.id.hashCode;
}

2. 更新CartNotifier逻辑

修改购物车管理类,支持添加带选项的项、更新单选选项、移除复选选项:

class CartNotifier extends ChangeNotifier {
  final List<CartItem> _cart = [];

  List<CartItem> get cart => _cart;

  // 更新餐品的单选尺寸(存在则覆盖,不存在则新增)
  void updateMealSize(Menu menu, int size) {
    final existingItem = _cart.firstWhere(
      (item) => item.menu.id == menu.id,
      orElse: () => CartItem(menu: menu),
    );

    if (!_cart.contains(existingItem)) {
      _cart.add(existingItem);
    }
    existingItem.selectedSize = size;
    notifyListeners();
  }

  // 切换餐品的复选选项(选中添加,取消移除)
  void toggleMealChoice(Menu menu, String choice) {
    final existingItem = _cart.firstWhere(
      (item) => item.menu.id == menu.id,
      orElse: () => CartItem(menu: menu),
    );

    if (!_cart.contains(existingItem)) {
      _cart.add(existingItem);
    }

    if (existingItem.selectedChoices.contains(choice)) {
      existingItem.selectedChoices.remove(choice);
      // 无任何选中项时,移除整个购物车项
      if (existingItem.selectedSize == null && existingItem.selectedChoices.isEmpty) {
        _cart.remove(existingItem);
      }
    } else {
      existingItem.selectedChoices.add(choice);
    }
    notifyListeners();
  }

  // 移除整个购物车项
  void removeCartItem(CartItem item) {
    _cart.remove(item);
    notifyListeners();
  }
}

3. 修正单选按钮与复选框交互逻辑

修改单选/复选框的onChanged回调,调用更新后的购物车方法:

// 先在State类中定义存储选中状态的变量
Map<int, int> _selectedSizes = {}; // key: menu.id, value: 选中的尺寸值
Map<int, List<String>> _selectedChoices = {}; // key: menu.id, value: 选中的选项列表

// 单选按钮示例(尺寸选择)
RadioListTile(
  value: 2,
  groupValue: _selectedSizes[e.id],
  onChanged: ((value) {
    setState(() {
      _selectedSizes[e.id] = value as int;
    });
    ref.read(cartProvider).updateMealSize(e, value as int);
  }),
  title: Text(e.mealSize_2),
),
RadioListTile(
  value: 3,
  groupValue: _selectedSizes[e.id],
  onChanged: ((value) {
    setState(() {
      _selectedSizes[e.id] = value as int;
    });
    ref.read(cartProvider).updateMealSize(e, value as int);
  }),
  title: Text(e.mealSize_3),
),

// 复选框示例(附加选项)
CheckboxListTile(
  controlAffinity: ListTileControlAffinity.leading,
  title: Text(e.mealChoice_1),
  value: _selectedChoices[e.id]?.contains(e.mealChoice_1) ?? false,
  onChanged: (val) {
    setState(() {
      _selectedChoices[e.id] ??= [];
      val == true 
          ? _selectedChoices[e.id]!.add(e.mealChoice_1) 
          : _selectedChoices[e.id]!.remove(e.mealChoice_1);
    });
    ref.read(cartProvider).toggleMealChoice(e, e.mealChoice_1);
  },
),
CheckboxListTile(
  controlAffinity: ListTileControlAffinity.leading,
  title: Text(e.mealChoice_2),
  value: _selectedChoices[e.id]?.contains(e.mealChoice_2) ?? false,
  onChanged: (val) {
    setState(() {
      _selectedChoices[e.id] ??= [];
      val == true 
          ? _selectedChoices[e.id]!.add(e.mealChoice_2) 
          : _selectedChoices[e.id]!.remove(e.mealChoice_2);
    });
    ref.read(cartProvider).toggleMealChoice(e, e.mealChoice_2);
  },
),

4. 购物车列表展示修正

更新弹窗逻辑,显示具体选中的选项:

Padding(
  padding: const EdgeInsets.fromLTRB(24, 0, 24, 24),
  child: MaterialButton(
    padding: const EdgeInsets.all(16),
    color: Colors.black,
    onPressed: () async {
      showModalBottomSheet(
        context: context,
        builder: (context) {
          return Consumer<CartNotifier>(
            builder: (context, cartModel, child) {
              return Column(
                children: [
                  const Padding(
                    padding: EdgeInsets.all(16.0),
                    child: Text("购物车", style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
                  ),
                  cartModel.cart.isEmpty
                      ? const Expanded(child: Center(child: Text("购物车为空")))
                      : Expanded(
                          child: ListView.builder(
                            itemCount: cartModel.cart.length,
                            itemBuilder: (context, index) {
                              final cartItem = cartModel.cart[index];
                              return ListTile(
                                title: Text(cartItem.menu.mealName),
                                subtitle: Column(
                                  crossAxisAlignment: CrossAxisAlignment.start,
                                  children: [
                                    if (cartItem.selectedSize != null)
                                      Text("尺寸: ${cartItem.selectedSize == 2 ? cartItem.menu.mealSize_2 : cartItem.menu.mealSize_3}"),
                                    if (cartItem.selectedChoices.isNotEmpty)
                                      Text("附加选项: ${cartItem.selectedChoices.join(", ")}"),
                                  ],
                                ),
                                trailing: IconButton(
                                  icon: const Icon(Icons.delete),
                                  onPressed: () => cartModel.removeCartItem(cartItem),
                                ),
                              );
                            },
                          ),
                        ),
                ],
              );
            },
          );
        },
      );
    },
    child: const Text(
      "查看购物车",
      style: TextStyle(color: Color.fromARGB(255, 247, 245, 245)),
    ),
  ),
),

关键说明

  • 用CartItem封装餐品和选项,避免同一餐品重复添加,同时精准记录用户选择
  • 单选操作直接更新对应餐品的尺寸,不会重复添加
  • 复选框操作切换选项状态,无选中项时自动移除购物车项
  • 使用Consumer监听购物车变化,确保弹窗实时更新

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 21:21:49