Flutter GetX框架对接NewsAPI时模型类型转换报错如何解决
问题解决方法
核心错误1:JSON解析结构和接口返回不匹配
NewsAPI的接口返回最外层是单个JSON对象(包含status、totalResults、articles三个字段),而非JSON数组,你当前的解析逻辑错误将外层结构当做数组处理,是触发类型转换错误的核心原因。
核心错误2:空安全类型声明不匹配
接口返回的author、urlToImage、source.id等字段可能为null,但你在模型中声明为非空的String类型且加了required修饰,空值赋值给非空变量会直接抛出类型异常。
具体修改步骤
1. 修正JSON序列化模型
修改newsFromJson方法的返回值,同时修正所有可空字段的类型声明,示例代码如下:
// To parse this JSON data, do // // final newsResponse = newsFromJson(jsonString); import 'package:meta/meta.dart'; import 'dart:convert'; // 修正:返回单个News对象,不是数组 News newsFromJson(String str) => News.fromJson(json.decode(str)); String newsToJson(News data) => json.encode(data.toJson()); class News { News({ required this.status, required this.totalResults, required this.articles, }); final String status; final int totalResults; final List<Article> articles; factory News.fromJson(Map<String, dynamic> json) => News( status: json["status"], totalResults: json["totalResults"], articles: List<Article>.from(json["articles"].map((x) => Article.fromJson(x))), ); Map<String, dynamic> toJson() => { "status": status, "totalResults": totalResults, "articles": List<dynamic>.from(articles.map((x) => x.toJson())), }; } class Article { Article({ required this.source, this.author, // 去掉required,改为可空类型 required this.title, this.description, // 可空 required this.url, this.urlToImage, // 可空 required this.publishedAt, this.content, // 可空 }); final Source source; final String? author; // 类型改为String? final String title; final String? description; final String url; final String? urlToImage; final DateTime publishedAt; final String? content; factory Article.fromJson(Map<String, dynamic> json) => Article( source: Source.fromJson(json["source"]), author: json["author"], title: json["title"], description: json["description"], url: json["url"], urlToImage: json["urlToImage"], publishedAt: DateTime.parse(json["publishedAt"]), content: json["content"], ); Map<String, dynamic> toJson() => { "source": source.toJson(), "author": author, "title": title, "description": description, "url": url, "urlToImage": urlToImage, "publishedAt": publishedAt.toIso8601String(), "content": content, }; } class Source { Source({ this.id, // 可空,去掉required required this.name, }); final String? id; // 类型改为String? final String name; factory Source.fromJson(Map<String, dynamic> json) => Source( id: json["id"], name: json["name"], ); Map<String, dynamic> toJson() => { "id": id, "name": name, }; }
2. 修正远程请求类remoteservice.dart
修改返回值类型为单个News对象:
import 'package:http/http.dart' as http; import 'package:nocovid/models/news.dart'; import 'package:nocovid/utils/constant.dart'; class RemoteServices { static var client = http.Client(); // 修正返回值为News? static Future<News?> fetchNews() async { final String endpoint = 'https://newsapi.org/v2/everything?q=covid19&apiKey=' + kAPIKey; final Uri url = Uri.parse(endpoint); final response = await client.get(url); if (response.statusCode == 200) { var jsonString = response.body; return newsFromJson(jsonString); } else { return null; } } }
3. 修正GetX控制器newscontroller.dart
存储实际需要渲染的文章列表,而非整个响应对象:
import 'package:get/state_manager.dart'; import 'package:nocovid/models/news.dart'; import 'package:nocovid/services/remote_services.dart'; class NewsController extends GetxController { // 修正:存储文章列表Article,不是News var articleList = <Article>[].obs; @override void onInit() { fetchNews(); super.onInit(); } void fetchNews() async { var newsResponse = await RemoteServices.fetchNews(); if (newsResponse != null) { articleList.value = newsResponse.articles; } } }
额外排查技巧
如果修改后仍有问题,可以先打印接口返回的原始JSON字符串,对比字段名、字段类型和模型声明是否完全一致,尤其是大小写、数字/字符串类型混淆的问题。
内容的提问来源于stack exchange,提问作者joisberg
相关产品推荐
相关产品推荐

