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

如何将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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 10:07:14