Flutter中cart与cartCopy数组因对象引用同步变更问题求助
我拥有cart和cartCopy两个数组,数组内存放着结构为{"product_name":"",variants:[],extras:[]}的CartData对象。数据已分别存入cart和cartCopy中。当我更新cart中的variant或extras时,会先创建独立的variantList和extrasList列表,但更新这两个列表时,cart和cartCopy中的对应值也会随之改变,即便我并未对它们进行更新操作。
相关代码
修改变体及设置待编辑商品的Provider类
class ProductDetailsProvider extends ChangeNotifier { List<VariantDetails>? variants = []; List<VariantDetails>? selectedVariants = []; List<AddOnDetails>? selectedExtras = []; addVariant(VariantDetails? item) { var exist = variants?.indexWhere((element) => element.typeID == item?.typeID); if (exist != -1) { variants?[exist!] = item!; } else { variants?.insert(0, item!); } } setProduct(CartData item) async { variants = item.variant; selectedExtras = item.extras; selectedVariants = item.variant; notifyListeners(); } }
加入购物车类
class CartProvider extends ChangeNotifier { List<CartData>? cart = []; List<CartData>? cartCopy = []; addToCart(context, CartData cartItem, isUpdate) async { cart?[exist!] = cartItem; cartCopy?[exist!] = cartItem; } }
每当我更新variant时,cart和cartCopy中的variant也会同步变化。请问如何分离cart中的值,避免这种同步变更?
这是因为Dart中引用类型(比如List、自定义对象)都是传递引用而非复制值。你直接将item.variant赋值给variants、selectedVariants,把item.extras赋值给selectedExtras,本质是让这些变量指向同一块内存地址的列表,所以修改其中一个,其他引用的地方也会跟着变化。要解决这个问题,需要对列表和对象进行深拷贝,切断引用关系。
1. 对商品变体和附加项列表做深拷贝
方案一:浅拷贝(适用于元素为不可变对象的场景)
如果VariantDetails和AddOnDetails是不可变类(属性都是final且不需要修改内部值),可以直接用List的toList()方法创建新列表副本:
setProduct(CartData item) async { variants = item.variant?.toList(); selectedExtras = item.extras?.toList(); selectedVariants = item.variant?.toList(); notifyListeners(); }
方案二:深拷贝(推荐,适用于需要修改对象内部属性的场景)
给VariantDetails和AddOnDetails添加copyWith方法,用于创建对象的独立副本:
class VariantDetails { final String? typeID; // 其他属性... VariantDetails({this.typeID/*, 其他参数*/}); // 实现copyWith方法,生成新对象 VariantDetails copyWith({ String? typeID, // 其他属性的可选参数 }) { return VariantDetails( typeID: typeID ?? this.typeID, // 其他属性同理,用传入值或当前对象的值 ); } } class AddOnDetails { final String? id; // 其他属性... AddOnDetails({this.id/*, 其他参数*/}); AddOnDetails copyWith({ String? id, // 其他属性的可选参数 }) { return AddOnDetails( id: id ?? this.id, // 其他属性同理 ); } }
然后在setProduct方法中,通过map遍历原列表,用copyWith创建新对象并生成新列表:
setProduct(CartData item) async { variants = item.variant?.map((v) => v.copyWith()).toList(); selectedExtras = item.extras?.map((e) => e.copyWith()).toList(); selectedVariants = item.variant?.map((v) => v.copyWith()).toList(); notifyListeners(); }
2. 对CartData对象做深拷贝
在CartProvider的addToCart方法中,直接赋值cartItem会让cart和cartCopy引用同一个对象,需要给CartData也添加copyWith方法实现深拷贝:
class CartData { final String? product_name; final List<VariantDetails>? variant; final List<AddOnDetails>? extras; CartData({this.product_name, this.variant, this.extras}); CartData copyWith({ String? product_name, List<VariantDetails>? variant, List<AddOnDetails>? extras, }) { return CartData( product_name: product_name ?? this.product_name, // 对变体列表做深拷贝 variant: variant?.map((v) => v.copyWith()).toList() ?? this.variant?.map((v) => v.copyWith()).toList(), // 对附加项列表做深拷贝 extras: extras?.map((e) => e.copyWith()).toList() ?? this.extras?.map((e) => e.copyWith()).toList(), ); } }
修改addToCart方法,使用拷贝后的对象:
addToCart(context, CartData cartItem, isUpdate) async { // 创建cartItem的深拷贝,避免cart和cartCopy共享同一对象 final copiedItem = cartItem.copyWith(); cart?[exist!] = copiedItem; cartCopy?[exist!] = copiedItem; }
总结
核心思路就是通过深拷贝创建独立的列表和对象实例,让cart、cartCopy以及编辑用的variants、selectedExtras等变量指向不同的内存空间,从而避免修改一处导致多处同步变化的问题。
内容的提问来源于stack exchange,提问作者VDTe

