You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 13:55:36