Flutter调用NewsAPI持续加载无数据问题排查求助
问题描述
尝试使用NewsAPI实现新闻列表ListView,集成到App后页面一直处于加载状态,无法展示数据,相关代码如下:
首页代码
import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:medreminder/NewsArticle/components/list_tile.dart'; import 'package:medreminder/NewsArticle/models/article_models.dart'; import 'package:medreminder/NewsArticle/services/api_service.dart'; class NewsHomePage extends StatelessWidget { //const NewsHomePage({super.key}); ApiService client = ApiService(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Get.isDarkMode?Colors.grey[600]:Colors.white, leading: IconButton( onPressed: ()=>Get.back(), icon: Icon(Icons.arrow_back_ios, color: Get.isDarkMode?Colors.white:Colors.grey ), ), title: Text("News & Article", style: TextStyle( color: Get.isDarkMode?Colors.white:Colors.black ),), ), body: FutureBuilder( future: client.getArticle(), builder: (BuildContext context, AsyncSnapshot<List<Article>> snapshot) { if(snapshot.hasData){ List<Article>? articles = snapshot.data; return ListView.builder( itemCount: articles!.length, itemBuilder: (context, index) => listTile(articles[index]) ); } return Center(child: CircularProgressIndicator(),); }, ), ); } }
API服务代码
import 'dart:convert'; import 'package:http/http.dart'; import 'package:medreminder/NewsArticle/models/article_models.dart'; class ApiService { final endPointUrl = "https://newsapi.org/v2/top-headlines?country=us&apiKey=cacee27fff544eb39d5fb29b28ca3788"; Future<List<Article>> getArticle() async{ Response res = await get(Uri.parse(endPointUrl)); if(res.statusCode==200){ Map<String, dynamic> json = jsonDecode(res.body); List<dynamic> body = json['articles']; List<Article> articles = body.map((dynamic item) => Article.fromJson(item)).toList(); return articles; }else{ throw("Cant get the News"); } } }
模型类代码
import 'source_models.dart'; class Article{ Source source; String author; String title; String description; String url; String urlToImage; String publishedAt; String content; Article({ required this.source, required this.author, required this.title, required this.description, required this.url, required this.urlToImage, required this.publishedAt, required this.content }); factory Article.fromJson(Map<String, dynamic> json){ return Article( source: Source.fromJson(json['source']), author: json['author'] as String, title: json['title'] as String, description: json['description'] as String, url: json['url'] as String, urlToImage: json['urlToImage'] as String, publishedAt: json['publishedAt'] as String, content: json['content'] as String ); } }
自定义列表项代码
import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:medreminder/NewsArticle/models/article_models.dart'; Widget listTile(Article article){ return Container( margin: EdgeInsets.all(12), padding: EdgeInsets.all(8), decoration: BoxDecoration( color: Get.isDarkMode?Colors.white:Colors.black, borderRadius: BorderRadius.circular(18), boxShadow: [ BoxShadow( color: Get.isDarkMode?Colors.white:Colors.black, blurRadius: 3 ) ] ), child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( decoration: BoxDecoration( image: DecorationImage( image: NetworkImage(article.urlToImage) ), borderRadius: BorderRadius.circular(8), ), ), SizedBox(height: 9), Container( decoration: BoxDecoration( color: Colors.red, borderRadius: BorderRadius.circular(30), ), child: Text(article.source.name), ), SizedBox(height: 8,), Text(article.title, style: TextStyle( fontWeight: FontWeight.bold, fontSize: 16, ),) ], ), ); }
排查与解决方案
1. 补充网络权限配置
Flutter项目需要手动配置网络权限才能发起HTTP请求:
- Android:在
android/app/src/main/AndroidManifest.xml中添加
<uses-permission android:name="android.permission.INTERNET" />
- iOS:在
ios/Runner/Info.plist中添加
<key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <true/> </dict>
2. 完善FutureBuilder的状态与错误处理
当前代码未处理请求错误和中间状态,修改后可直接查看报错信息:
body: FutureBuilder( future: client.getArticle(), builder: (BuildContext context, AsyncSnapshot<List<Article>> snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return Center(child: CircularProgressIndicator()); } else if (snapshot.hasError) { return Center(child: Text('加载失败: ${snapshot.error}')); } else if (snapshot.hasData && snapshot.data!.isNotEmpty) { List<Article> articles = snapshot.data!; return ListView.builder( itemCount: articles.length, itemBuilder: (context, index) => listTile(articles[index]) ); } else { return Center(child: Text('暂无新闻数据')); } }, ),
3. 修复模型类的空值处理
NewsAPI返回的字段可能存在null,直接强制转换会抛出异常,修改Article.fromJson:
factory Article.fromJson(Map<String, dynamic> json){ return Article( source: Source.fromJson(json['source']), author: json['author'] ?? '', title: json['title'] ?? '', description: json['description'] ?? '', url: json['url'] ?? '', urlToImage: json['urlToImage'] ?? 'https://via.placeholder.com/400x200', publishedAt: json['publishedAt'] ?? '', content: json['content'] ?? '' ); }
同时确保Source模型类也处理空值:
class Source { String id; String name; Source({required this.id, required this.name}); factory Source.fromJson(Map<String, dynamic> json) { return Source( id: json['id'] ?? '', name: json['name'] ?? '' ); } }
4. 修复列表项布局问题
图片容器未设置宽高导致无法显示,同时优化标签样式:
Widget listTile(Article article){ return Container( margin: EdgeInsets.all(12), padding: EdgeInsets.all(8), decoration: BoxDecoration( color: Get.isDarkMode?Colors.white:Colors.black, borderRadius: BorderRadius.circular(18), boxShadow: [ BoxShadow( color: Get.isDarkMode?Colors.grey[300]!:Colors.grey[800]!, blurRadius: 3 ) ] ), child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( height: 200, width: double.infinity, decoration: BoxDecoration( image: DecorationImage( image: NetworkImage(article.urlToImage), fit: BoxFit.cover, ), borderRadius: BorderRadius.circular(8), ), ), SizedBox(height: 9), Container( decoration: BoxDecoration( color: Colors.red, borderRadius: BorderRadius.circular(30), ), padding: EdgeInsets.symmetric(horizontal: 12, vertical: 4), child: Text( article.source.name, style: TextStyle(color: Colors.white, fontSize: 12), ), ), SizedBox(height: 8,), Text( article.title, style: TextStyle( fontWeight: FontWeight.bold, fontSize: 16, color: Get.isDarkMode?Colors.black:Colors.white ), maxLines: 2, overflow: TextOverflow.ellipsis, ) ], ), ); }
5. 验证API请求有效性
直接在浏览器中访问你的endPointUrl,检查:
- 返回的JSON格式是否正常
- API密钥是否有效、是否超出免费版请求配额
- 网络环境是否存在代理/防火墙拦截请求
内容的提问来源于stack exchange,提问作者NyK
相关产品推荐
相关产品推荐

