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

使用Riverpod实现购物车时遇空值检查错误求助

购物车页面Null Check错误排查与修复

错误信息

════════ Exception caught by widgets library ═══════════════════════════════════
The following _CastError was thrown building:
Null check operator used on a null value

核心问题分析

  1. CartItem模型设计矛盾:构造函数用required修饰参数,但成员变量却定义为可空类型(String?、int?),导致实例化后仍可能出现null值,后续强制解包(!)时触发错误。
  2. CartRepository逻辑缺陷:
    • addItem参数未指定类型,存在传入null的风险;
    • 用两个独立if判断商品是否存在,逻辑冗余且可能导致重复执行;
    • 初始添加商品时qty设为0,后续更新时直接用value.qty!强制解包,若qty为null会触发错误;
  3. UI层未处理null情况:购物车页面直接用itemsName!强制解包,未考虑字段为null的场景。

修复后的代码

1. CartItem模型(修正可空性)

import 'package:ecommerceriverpod/src/features/cart/domain/item.dart';
import 'package:ecommerceriverpod/src/features/products/domain/product.dart';

class CartItem {
  final String productId;
  final String itemsName;
  final String price;
  final String image;
  int qty; // 数量可修改,不设final

  CartItem({
    required this.productId,
    required this.itemsName,
    required this.price,
    required this.image,
    required this.qty,
  });
}

2. CartRepository控制器(优化逻辑与类型)

class CartRepository extends Notifier<Map<String, CartItem>> {
  @override
  Map<String, CartItem> build() {
    return {};
  }

  void addItem({
    required String productId,
    required String price,
    required String title,
    required String name,
    required String image,
  }) {
    if (state.containsKey(productId)) {
      state.update(
        productId,
        (value) => CartItem(
          productId: value.productId,
          itemsName: value.itemsName,
          price: value.price,
          image: value.image,
          qty: value.qty + 1, // qty已不可空,无需强制解包
        ),
      );
    } else {
      state.putIfAbsent(
        productId,
        () => CartItem(
          productId: productId,
          itemsName: title,
          price: price,
          image: image,
          qty: 1, // 初始数量设为1,符合购物逻辑
        ),
      );
    }
  }

  void removeItem(String id) {
    if (state.containsKey(id)) {
      final currentItem = state[id]!;
      if (currentItem.qty > 1) {
        state.update(
          id,
          (value) => CartItem(
            itemsName: value.itemsName,
            price: value.price,
            image: value.image,
            productId: value.productId,
            qty: value.qty - 1,
          ),
        );
      } else {
        // 数量为1时移除商品,避免空条目
        state.remove(id);
      }
    }
  }

  void clearCart() {
    state.clear();
  }

  int? getCartItem(String id) {
    return state[id]?.qty;
  }
}

final cartRepositoryProvider =
    NotifierProvider<CartRepository, Map<String, CartItem>>(CartRepository.new);

3. 购物车页面(简化逻辑,避免null解包)

class CartPage extends ConsumerWidget {
  const CartPage({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // 提前转换为列表,避免重复计算
    final cartItems = ref.watch(cartRepositoryProvider).values.toList();
    return Scaffold(
      appBar: AppBar(
        title: const Text("Cart"),
      ),
      body: SafeArea(
        child: Column(
          children: [
            Expanded(
              child: ListView.builder(
                itemCount: cartItems.length,
                itemBuilder: (context, index) {
                  final item = cartItems[index];
                  return ListTile(
                    title: Text(item.itemsName),
                    subtitle: Text("\$${item.price} x ${item.qty}"),
                    leading: Image.network(item.image),
                  );
                },
              ),
            )
          ],
        ),
      ),
    );
  }
}

修复说明

  • 模型层:将CartItem的核心字段改为不可空类型,从根源杜绝null值出现;
  • 逻辑层:明确参数类型、优化判断逻辑,初始数量设为1符合用户购物习惯,移除商品时处理数量为0的场景;
  • UI层:提前转换购物车条目为列表,直接访问不可空字段,无需强制解包,同时优化展示内容。

内容的提问来源于stack exchange,提问作者Mohammed M

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 00:47:53