Flutter调用USDA API展示食物营养详情时出现空值问题求助
问题排查与解决方案
1. 确认页面传参正确性
点击列表项跳转至DetailScreen时,必须确保传递了正确的食物标识(如ID):
- 跳转代码示例:
Navigator.push( context, MaterialPageRoute( builder: (context) => DetailScreen(foodId: food.id), // 此处需传入正确的食物ID ), );
DetailScreen构造函数需正确接收参数:
class DetailScreen extends StatelessWidget { final String foodId; const DetailScreen({super.key, required this.foodId}); // ... }
2. 校验API请求逻辑
检查DetailScreen中获取营养数据的API调用:
- 确认请求URL使用了正确的参数:
final response = await http.get(Uri.parse('https://你的API地址/nutrition/${widget.foodId}'));
- 仅在响应状态码为200时解析数据:
if (response.statusCode == 200) { final data = jsonDecode(response.body); final foodNutrition = FoodModel.fromJson(data); } else { // 处理请求失败场景,比如展示错误提示 }
3. 修正Food Model的JSON解析
确保fromJson方法与API返回的字段完全匹配,避免因字段名不符导致数据为null:
class FoodModel { final String? name; final Nutrition? nutrition; FoodModel({this.name, this.nutrition}); factory FoodModel.fromJson(Map<String, dynamic> json) { return FoodModel( name: json['food_name'], // 需与API返回的字段名一致 nutrition: json['nutrition'] != null ? Nutrition.fromJson(json['nutrition']) : null, ); } } class Nutrition { final double? calories; final double? protein; Nutrition({this.calories, this.protein}); factory Nutrition.fromJson(Map<String, dynamic> json) { return Nutrition( calories: json['calories']?.toDouble(), // 处理类型转换,避免int转double报错 protein: json['protein']?.toDouble(), ); } }
- 对可能为null的字段添加空判断,防止解析时抛出异常。
4. 完善页面状态处理
在DetailScreen中处理加载中、加载成功、加载失败三种状态,避免直接展示null数据:
class DetailScreen extends StatefulWidget { final String foodId; const DetailScreen({super.key, required this.foodId}); @override State<DetailScreen> createState() => _DetailScreenState(); } class _DetailScreenState extends State<DetailScreen> { FoodModel? _food; bool _isLoading = true; String? _error; @override void initState() { super.initState(); _fetchNutritionData(); } Future<void> _fetchNutritionData() async { try { final response = await http.get(Uri.parse('https://你的API地址/nutrition/${widget.foodId}')); if (response.statusCode == 200) { final data = jsonDecode(response.body); setState(() { _food = FoodModel.fromJson(data); _isLoading = false; }); } else { setState(() { _error = '请求失败,状态码:${response.statusCode}'; _isLoading = false; }); } } catch (e) { setState(() { _error = '加载出错:$e'; _isLoading = false; }); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('营养详情')), body: _isLoading ? const Center(child: CircularProgressIndicator()) : _error != null ? Center(child: Text(_error!)) : _food == null ? const Center(child: Text('未找到营养数据')) : Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('食物名称:${_food!.name ?? '未知'}', style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold)), const SizedBox(height: 16), Text('热量:${_food!.nutrition?.calories ?? 0} kcal'), Text('蛋白质:${_food!.nutrition?.protein ?? 0} g'), // 其他营养字段展示 ], ), ), ); } }
- 使用空合并运算符
??,当数据为null时展示默认值(如0或“未知”),避免界面空白。
5. 实用调试技巧
- 打印API返回的原始JSON数据,确认字段与Model定义是否一致:
print('API返回数据:${response.body}');
- 检查网络权限:
- Android:在
AndroidManifest.xml中添加<uses-permission android:name="android.permission.INTERNET" /> - iOS:在
Info.plist中配置NSAppTransportSecurity允许HTTP请求(若API为HTTP协议)
- Android:在
内容的提问来源于stack exchange,提问作者PlayzAhmed
相关产品推荐
相关产品推荐

