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

Flutter新闻App报错:Null is not a subtype of String求助

问题分析与解决方案

核心异常原因

Null is not a subtype of String 异常是因为你在 Article.fromJson 中用 as String 强制转换可能为null的字段,但Dart中null不属于String类型,当接口返回null时就会触发该异常。而 Source.fromJson 暂时没报错,是因为当前请求的NewsAPI返回的所有文章中source.name字段均不为null,并非该方法没有空值风险。


分步修复代码

1. 修复Article类的Json解析逻辑

将所有可能为null的字段改为可空转换或提供默认值,避免强制类型转换:

import 'package:news_app/source_models.dart';

class Article {
  Source source;
  String? author;
  String? title;
  String? description;
  String? url;
  String? content;

  // 移除可空字段的required修饰符,匹配字段的可空类型定义
  Article({
    required this.source,
    this.author,
    this.title,
    this.description,
    this.url,
    this.content,
  });

  factory Article.fromJson(Map<String, dynamic> json) {
    return Article(
      // 防止source字段为null的极端情况
      source: Source.fromJson(json['source'] ?? {}),
      // 直接转为可空String,或用??转为默认空字符串
      author: json['author'] as String?,
      title: json['title']?.toString() ?? '',
      description: json['description'] as String? ?? '',
      url: json['url'] as String? ?? '',
      content: json['content'] as String? ?? '',
    );
  }

  Map<String, dynamic> toJson() => <String, dynamic>{
        "source": source.toJson(),
        "author": author,
        "title": title,
        "description": description,
        "url": url,
        "content": content,
      };
}

2. 修复Source类的潜在空值风险

给source.name添加默认值,避免未来出现相同异常:

class Source {
  String name;

  Source({required this.name});

  factory Source.fromJson(Map<String, dynamic> json) {
    return Source(
      // 当name为null时用默认值替代
      name: json['name'] ?? '未知来源',
    );
  }

  Map<String, dynamic> toJson() => <String, dynamic>{
        "name": name,
      };
}

3. 修复ApiService的空值断言风险

移除不安全的!空值断言,处理请求失败的情况:

import 'package:news_app/Article.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;

class ApiService {
  static Future<List<Article>> article() async {
    final http.Response response = await http.get(Uri.parse('https://newsapi.org/v2/everything?domains=wsj.com&apiKey=eac151759e98448f8712913e2028cfd0'));
    
    if (response.statusCode == 200) {
      Map<String, dynamic> json = jsonDecode(response.body);
      // 防止articles字段为null
      List<dynamic> body = json['articles'] ?? [];
      return body.map((item) => Article.fromJson(item)).toList();
    } else {
      // 请求失败时返回空列表或抛出异常,按需选择
      return [];
      // throw Exception('加载文章失败,状态码:${response.statusCode}');
    }
  }
}

4. 修复UI的显示逻辑

修正ListView的加载状态判断,移除不安全的空值断言:

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:news_app/ApiService.dart';
import 'package:news_app/Article.dart';

class HomeScreen extends StatefulWidget {
  @override
  createState() => _HomeScreen();
}

class _HomeScreen extends State<HomeScreen> {
  bool isWaiting = false;
  List<Article> articles = [];

  @override
  void initState() {
    super.initState();
    IncomingApi();
  }

  void IncomingApi() async {
    setState(() => isWaiting = true);
    articles = await ApiService.article();
    setState(() => isWaiting = false);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home'),
      ),
      body: isWaiting
          ? const Center(child: CircularProgressIndicator())
          : ListView.builder(
              itemCount: articles.length,
              itemBuilder: (context, index) {
                return NewsContainer(context, articles[index]);
              },
            ),
    );
  }

  Widget NewsContainer(BuildContext context, Article article) {
    return Container(
      child: ListTile(
        // 处理description为null的情况
        title: Text(article.description ?? '无描述'),
      ),
    );
  }
}

内容的提问来源于stack exchange,提问作者Emmanuel Armoo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 06:47:45