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

