如何将单选按钮及复选框选项值添加至购物车列表
购物车单选/复选框选品功能修正方案
你的现有代码存在几个核心问题:单选/复选框操作时直接添加整个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
相关产品推荐
相关产品推荐

