Flutter Null Safety:如何处理含Null值的外部API数据?
解决Flutter空安全下API返回Null值的问题
针对你遇到的"Null check operator used on a null value"错误,核心是避免强制非空断言(!)直接作用于可能为null的值,同时在模型类、Widget渲染、数据请求环节做完整的空值处理,以下是具体修改方案:
1. 模型类(AgentContract)适配空安全
将API可能返回Null的字段标记为可空类型(?),并在fromJson方法中为必填字段设置兜底默认值,确保实例化时不会出现非空字段为null的情况:
class AgentContract { // 允许API返回Null的字段标记为可空 final String? contractName; final int? contractId; final String? agentName; // 必须有值的字段,用默认值兜底 final String contractStatus; AgentContract({ this.contractName, this.contractId, this.agentName, required this.contractStatus, }); factory AgentContract.fromJson(Map<String, dynamic> json) { return AgentContract( contractName: json['contract_name'] as String?, contractId: json['contract_id'] as int?, agentName: json['agent_name'] as String?, // 若API返回Null,用默认值替代 contractStatus: json['contract_status'] as String? ?? '未生效', ); } }
2. AgentContractItem:安全渲染可空字段
移除!断言,改用??提供默认文本,或通过条件判断处理空值,确保渲染时不会触发空安全错误:
class AgentContractItem extends StatelessWidget { final AgentContract contract; const AgentContractItem({super.key, required this.contract}); @override Widget build(BuildContext context) { return Card( margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 空值时显示默认文本 Text( contract.contractName ?? '未命名合同', style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), const SizedBox(height: 8), // 数字类型空值转字符串默认值 Text('合同ID:${contract.contractId ?? '无'}'), const SizedBox(height: 8), Text('经办人:${contract.agentName ?? '未指定'}'), const SizedBox(height: 8), Text('状态:${contract.contractStatus}'), ], ), ), ); } }
3. AgentTabScreen:安全处理API返回的列表数据
确保API返回的列表本身不为null,同时处理加载状态和空列表场景:
class AgentTabScreen extends StatefulWidget { const AgentTabScreen({super.key}); @override State<AgentTabScreen> createState() => _AgentTabScreenState(); } class _AgentTabScreenState extends State<AgentTabScreen> { List<AgentContract> _contracts = []; bool _isLoading = true; @override void initState() { super.initState(); _fetchContracts(); } Future<void> _fetchContracts() async { try { final response = await http.get(Uri.parse('你的API地址')); final dynamic responseData = json.decode(response.body); setState(() { // 若API返回非List类型,直接赋值为空列表 _contracts = (responseData as List?) ?.map((item) => AgentContract.fromJson(item as Map<String, dynamic>)) .toList() ?? []; _isLoading = false; }); } catch (e) { setState(() { _contracts = []; _isLoading = false; }); // 可添加错误提示逻辑 } } @override Widget build(BuildContext context) { if (_isLoading) { return const Center(child: CircularProgressIndicator()); } if (_contracts.isEmpty) { return const Center(child: Text('暂无合同数据')); } return ListView.builder( padding: const EdgeInsets.symmetric(vertical: 8), itemCount: _contracts.length, itemBuilder: (context, index) => AgentContractItem(contract: _contracts[index]), ); } }
通用空安全技巧
- 杜绝随意使用
!:只有当你能100%确认值不为null时,才使用强制非空断言 - 嵌套空值处理:对于深层嵌套的可空字段,用
?.链式调用+??兜底,比如contract?.agent?.phone ?? '无联系方式' - 类型安全转换:在
fromJson中尽量使用as?类型转换,避免类型转换错误导致的空值问题
内容的提问来源于stack exchange,提问作者MatDoak
相关产品推荐
相关产品推荐

