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

Flutter运行报错type 'Null' is not a subtype of type 'String'如何解决?

异常原因
  • 日志指向的第80行是异常重抛的位置,不是问题根源:try块内部产生的类型异常被catch捕获后,第80行throw error重新抛出,因此栈追踪将该行标记为异常触发点。
  • 核心问题是Flutter 2+ 强制空安全校验不通过:
    1. 接口返回的产品数据存在空字段:当Firebase返回的某条产品数据缺失title/description/price/imageUrl等字段时,直接通过value['xxx']取值会得到Null,但你的Product类对应属性声明为非空String/num类型,Null无法赋值给非空类型,触发类型不匹配异常。
    2. 接口返回整体为空的情况未做兜底:如果网络请求失败、数据库权限不足导致返回空数据,json.decode(response.body) as Map<String, dynamic>直接强转时,Null无法转成Map类型,也会触发相同逻辑的异常。
    3. 课程代码是Flutter 2空安全推出前的写法,当时允许隐式处理空值,因此原版代码可运行,升级到空安全版本后就会崩溃。
空安全适配修复方案

步骤1:处理接口返回空数据的兜底逻辑

修改json.decode相关代码,先做类型校验,避免直接强转:

Future<void> fetchAndSetProducts() async {
    var url = Uri.parse(
        'https://flutter-39ecc-default-rtdb.firebaseio.com/products.json');
    try {
      final response = await http.get(url);
      final responseData = json.decode(response.body);
      // 新增空校验和类型校验
      if (responseData == null || responseData is! Map<String, dynamic>) {
        _items = [];
        notifyListeners();
        return;
      }
      final extractedData = responseData;
      final List<Product> loadedProducts = [];
      // 后续保留原有逻辑

步骤2:给所有字段增加空值兜底默认值

构造Product实例时,对每个取值的字段做空判断,空值时传入默认值,保证赋值给非空属性的永远是非空值:

extractedData.forEach((key, value) {
  loadedProducts.add(Product(
      id: key,
      title: value['title'] ?? '未命名商品',
      description: value['description'] ?? '暂无商品描述',
      // 额外处理数值类型的转换异常
      price: (value['price'] as num?)?.toDouble() ?? 0.0,
      isFavorite: value['isFavorite'] as bool? ?? false,
      imageUrl: value['imageUrl'] ?? 'https://via.placeholder.com/200'
  ));
});

如果业务允许对应字段为空,也可以直接修改Product类的属性声明为可空类型,比如将String title改为String? title,后续使用字段时做好空判断即可。

步骤3:补充异步调用的异常捕获

你当前调用fetchAndSetProducts时只处理了正常返回的逻辑,没有捕获重抛出来的异常,会导致未处理异常报错,修改调用代码:

var _isInit = true;
var _isLoading = false;
@override
void didChangeDependencies() {
  if (_isInit) {
    setState(() {
      _isLoading = true;
    });
    // 新增listen: false避免不必要的重建,新增catchError捕获异常
    Provider.of<Products>(context, listen: false).fetchAndSetProducts().then((_) {
      setState(() {
        _isLoading = false;
      });
    }).catchError((error) {
      // 这里可自行添加加载失败的提示逻辑
      setState(() {
        _isLoading = false;
      });
    });
  }
  _isInit = false;
  super.didChangeDependencies();
}

内容的提问来源于stack exchange,提问作者Cláudio Vitor Dantas

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 14:54:02