如何将Flutter Bloc/Cubit与SQLite结合实现购物车功能?
基于Flutter Bloc/Cubit结合SQLite实现购物车状态管理
一、状态定义
先定义覆盖购物车全流程的状态类,处理异步操作的不同阶段:
abstract class CartState {} // 加载中状态 class CartLoading extends CartState {} // 加载完成,持有购物车商品列表 class CartLoaded extends CartState { final List<CartItem> items; CartLoaded(this.items); } // 操作成功(添加/移除),返回更新后的列表和提示信息 class CartOperationSuccess extends CartState { final List<CartItem> updatedItems; final String message; CartOperationSuccess(this.updatedItems, this.message); } // 操作失败状态 class CartError extends CartState { final String errorMessage; CartError(this.errorMessage); }
二、使用Cubit实现(简洁高效)
Cubit无需定义事件,直接通过方法触发状态变化,适合购物车这类简单场景:
class CartCubit extends Cubit<CartState> { final DatabaseProvider _databaseProvider; CartCubit(this._databaseProvider) : super(CartLoading()) { // 初始化时自动加载购物车数据 _loadCartItems(); } // 私有方法:加载购物车所有商品 Future<void> _loadCartItems() async { try { final items = await _databaseProvider.getAllItems(); emit(CartLoaded(items)); } catch (e) { emit(CartError('加载购物车失败: ${e.toString()}')); } } // 对外暴露:添加商品到购物车 Future<void> addItem(CartItem item) async { try { await _databaseProvider.addItemToCart(item); // 添加后重新拉取最新数据 final updatedItems = await _databaseProvider.getAllItems(); emit(CartOperationSuccess(updatedItems, '商品已加入购物车')); // 最终回到加载完成状态,保持UI稳定 emit(CartLoaded(updatedItems)); } catch (e) { emit(CartError('加入购物车失败: ${e.toString()}')); } } // 对外暴露:从购物车移除商品 Future<void> removeItem(String id) async { try { await _databaseProvider.removeItemFromCart(id); final updatedItems = await _databaseProvider.getAllItems(); emit(CartOperationSuccess(updatedItems, '商品已移除')); emit(CartLoaded(updatedItems)); } catch (e) { emit(CartError('移除商品失败: ${e.toString()}')); } } }
三、如果使用Bloc(事件驱动模式)
若偏好事件-状态的解耦模式,可按以下方式实现:
1. 事件定义
abstract class CartEvent {} // 加载购物车事件 class LoadCartItems extends CartEvent {} // 添加商品事件 class AddCartItem extends CartEvent { final CartItem item; AddCartItem(this.item); } // 移除商品事件 class RemoveCartItem extends CartEvent { final String id; RemoveCartItem(this.id); }
2. Bloc实现
class CartBloc extends Bloc<CartEvent, CartState> { final DatabaseProvider _databaseProvider; CartBloc(this._databaseProvider) : super(CartLoading()) { // 绑定事件处理逻辑 on<LoadCartItems>(_onLoadCartItems); on<AddCartItem>(_onAddCartItem); on<RemoveCartItem>(_onRemoveCartItem); // 初始化时触发加载事件 add(LoadCartItems()); } // 处理加载购物车事件 Future<void> _onLoadCartItems(LoadCartItems event, Emitter<CartState> emit) async { try { final items = await _databaseProvider.getAllItems(); emit(CartLoaded(items)); } catch (e) { emit(CartError('加载购物车失败: ${e.toString()}')); } } // 处理添加商品事件 Future<void> _onAddCartItem(AddCartItem event, Emitter<CartState> emit) async { try { await _databaseProvider.addItemToCart(event.item); final updatedItems = await _databaseProvider.getAllItems(); emit(CartOperationSuccess(updatedItems, '商品已加入购物车')); emit(CartLoaded(updatedItems)); } catch (e) { emit(CartError('加入购物车失败: ${e.toString()}')); } } // 处理移除商品事件 Future<void> _onRemoveCartItem(RemoveCartItem event, Emitter<CartState> emit) async { try { await _databaseProvider.removeItemFromCart(event.id); final updatedItems = await _databaseProvider.getAllItems(); emit(CartOperationSuccess(updatedItems, '商品已移除')); emit(CartLoaded(updatedItems)); } catch (e) { emit(CartError('移除商品失败: ${e.toString()}')); } } }
四、UI层使用示例
1. 注入状态管理实例
在父组件中提供Cubit/Bloc:
// 假设已初始化DatabaseProvider实例 final databaseProvider = DatabaseProvider(); // Cubit方式 BlocProvider( create: (context) => CartCubit(databaseProvider), child: const MyApp(), ); // Bloc方式 BlocProvider( create: (context) => CartBloc(databaseProvider), child: const MyApp(), );
2. 加入购物车按钮
// Cubit方式 ElevatedButton( onPressed: () { // 假设已有CartItem实例item context.read<CartCubit>().addItem(item); }, child: const Text('加入购物车'), ); // Bloc方式 ElevatedButton( onPressed: () { context.read<CartBloc>().add(AddCartItem(item)); }, child: const Text('加入购物车'), );
3. 购物车页面展示数据
BlocBuilder<CartCubit, CartState>( // Bloc方式替换为CartBloc builder: (context, state) { if (state is CartLoading) { return const Center(child: CircularProgressIndicator()); } if (state is CartError) { return Center(child: Text(state.errorMessage)); } if (state is CartLoaded) { final items = state.items; if (items.isEmpty) { return const Center(child: Text('购物车为空')); } return ListView.builder( itemCount: items.length, itemBuilder: (context, index) { final cartItem = items[index]; return ListTile( title: Text(cartItem.name), subtitle: Text('¥${cartItem.price}'), trailing: IconButton( icon: const Icon(Icons.delete), onPressed: () { // Cubit方式 context.read<CartCubit>().removeItem(cartItem.id); // Bloc方式 // context.read<CartBloc>().add(RemoveCartItem(cartItem.id)); }, ), ); }, ); } return const SizedBox(); }, );
核心说明
- 每次操作后重新拉取数据库数据,确保UI展示的是最新状态
- 通过不同状态类区分加载、成功、错误场景,优化用户体验
- Cubit适合简单业务场景,代码更简洁;Bloc适合复杂的事件驱动场景,扩展性更强
内容的提问来源于stack exchange,提问作者Heike Heinrich
相关产品推荐
相关产品推荐

