基于Riverpod的State Notifier实现购物车:数据库数据加载咨询
使用Riverpod整合数据库数据到购物车State Notifier的实现方案
我正在尝试用Riverpod实现购物车功能,想知道怎么从数据库获取商品数据并整合到State Notifier里。我预想的步骤是:
- 向State Notifier发起购物车数据请求
- 创建更新方法来更新本地购物车
- 应用重启后回到步骤1重新获取数据
以下是我的相关代码实现:
我的cart_controller(需从数据库获取数据)
class CartRepository extends Notifier<Map<String, CartItem>> { @override Map<String, CartItem> build() { return {}; } void addItem( {required String productId, price, itemsName, name, itemsImage}) { if (state.containsKey(productId)) { state.update( productId, (value) => CartItem( productId: value.productId, itemsName: value.itemsName, price: value.price, itemsImage: value.itemsImage, qty: value.qty! + 1)); } if (!state.containsKey(productId)) { state.putIfAbsent( productId, () => CartItem( productId: productId, itemsName: itemsName, price: price, itemsImage: itemsImage, qty: 1), ); } ref.notifyListeners(); } } final cartRepositoryProvider = NotifierProvider<CartRepository, Map<String, CartItem>>(CartRepository.new);
我的请求代码
class CartRepository { Map<String, CartItem> cart = {}; Future<Map<String, CartItem>> getCartRequest() async { try { final box = Hive.box(Constants.authStorageKey); final token = box.get('token'); final url = Uri.parse('http://192.168.1.9:5000/api/v1/products/getcartItems'); final headers = { "Authorization": "Bearer $token", }; final response = await http.get(url, headers: headers); if (response.statusCode == 200) { final jsonData = json.decode(response.body)['product']; for (var productData in jsonData) { CartItem product = CartItem.fromJson( productData); // Replace with your own deserialization logic print(product); } } return cart; } catch (e) { return cart; print(e); } } } final remoteCartRepository = Provider((ref) => CartRepository()); final productsListProvider = FutureProvider<Map<String, CartItem>>((ref) async { final repository = ref.watch(remoteCartRepository); return repository.getCartRequest(); });
我的CartItem模型
class CartItem { String? productId; String? itemsName; String? price; String? itemsImage; int? qty; CartItem({ required this.productId, required this.itemsName, required this.price, required this.itemsImage, required this.qty, }); CartItem.fromJson(Map<String, dynamic> json) { productId = json['itemsId']; itemsName = json['itemsName']; itemsImage = json['itemsImage']; price = json['itemsPrice']; } }
整合解决方案
1. 合并重复类并重构逻辑
你当前存在两个同名的CartRepository类,会导致冲突,需要将数据请求与状态管理合并到同一个Notifier类中,同时修复状态更新的正确方式(Riverpod要求通过替换整个state对象更新状态,不能直接修改原state)。
2. 修改后的完整代码
合并后的CartRepository(状态管理+数据请求)
class CartRepository extends Notifier<Map<String, CartItem>> { @override Map<String, CartItem> build() { // 应用启动/重启时自动加载远程购物车数据 _loadCartFromRemote(); return {}; } // 从远程接口获取购物车数据并更新状态 Future<void> _loadCartFromRemote() async { try { final box = Hive.box(Constants.authStorageKey); final token = box.get('token'); final url = Uri.parse('http://192.168.1.9:5000/api/v1/products/getcartItems'); final headers = {"Authorization": "Bearer $token"}; final response = await http.get(url, headers: headers); if (response.statusCode == 200) { final jsonData = json.decode(response.body)['product']; Map<String, CartItem> fetchedCart = {}; for (var productData in jsonData) { CartItem product = CartItem.fromJson(productData); // 给qty设置默认值1(接口返回无此字段时) product.qty = product.qty ?? 1; fetchedCart[product.productId!] = product; } // 替换整个state对象完成状态更新 state = fetchedCart; } } catch (e) { print(e); // 可选:请求失败时加载本地缓存数据 } } // 添加商品到购物车(含数量递增逻辑) void addItem({required String productId, required price, required itemsName, required itemsImage}) { if (state.containsKey(productId)) { state = { ...state, productId: CartItem( productId: state[productId]!.productId, itemsName: state[productId]!.itemsName, price: state[productId]!.price, itemsImage: state[productId]!.itemsImage, qty: state[productId]!.qty! + 1, ), }; } else { state = { ...state, productId: CartItem( productId: productId, itemsName: itemsName, price: price, itemsImage: itemsImage, qty: 1, ), }; } // Riverpod会自动监听state变化并通知UI,无需手动调用notifyListeners } // 可选:添加商品数量递减/删除商品方法 void decreaseQty(String productId) { if (state.containsKey(productId) && state[productId]!.qty! > 1) { state = { ...state, productId: CartItem( productId: state[productId]!.productId, itemsName: state[productId]!.itemsName, price: state[productId]!.price, itemsImage: state[productId]!.itemsImage, qty: state[productId]!.qty! - 1, ), }; } else { removeItem(productId); } } void removeItem(String productId) { if (state.containsKey(productId)) { state = Map.from(state)..remove(productId); } } } final cartRepositoryProvider = NotifierProvider<CartRepository, Map<String, CartItem>>(CartRepository.new);
修复后的CartItem模型
class CartItem { String? productId; String? itemsName; String? price; String? itemsImage; int? qty; CartItem({ required this.productId, required this.itemsName, required this.price, required this.itemsImage, required this.qty, }); CartItem.fromJson(Map<String, dynamic> json) { productId = json['itemsId']; itemsName = json['itemsName']; itemsImage = json['itemsImage']; price = json['itemsPrice']; // 兼容接口返回的qty字段(如果存在) qty = json['qty']; } }
3. 关键说明
- 自动初始化:
build方法在Provider首次被监听时执行,应用重启后会重新触发,因此_loadCartFromRemote会自动执行,实现重启后重新获取数据。 - 状态更新规范:Riverpod的Notifier必须通过创建新的state对象来更新状态,不能直接修改原state的内容,所以使用
state = {...state, ...}的方式生成新Map。 - 错误处理:请求失败时可扩展逻辑,比如加载Hive本地缓存的购物车数据,提升用户体验。
内容的提问来源于stack exchange,提问作者Mohammed M
相关产品推荐
相关产品推荐

