Null在我的Flutter新闻应用中不是String的子类型。

huangapple go评论56阅读模式
英文:

Null is not a subType of String in my flutter newsapp

问题

正在使用 newsapi.org 开发一个新闻应用,用于从文章列表中获取数据,但在 Article.fromJson 中返回一个 null 不是 String 的异常。我想寻求一些帮助。谢谢。

//这是我的 Article 类

import 'package:news_app/source_models.dart';
class Article{
  Source source;
  String? author;
  String? title;
  String? description;
  String? url;
  String? content;

  Article({required this.source,required this.author,required this.title,required this.description,required this.url,
    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'] !=null?json['title'].toString():'',
      description: json['description'] as String?,
      url: json['url'] as String?,
      content: json['content'] as String?,
    );
  }
  Map<String,dynamic> toJson(Article article) => <String,dynamic>{
    "source": article.source.toJson(),
    "author": article.author,
    "title": article.title,
    "description": article.description,
    "url": article.url,
    "content": article.content,
  };
}


//这是 API

import 'package:news_app/Article.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
class ApiService{
  static Future<List<Article>> article()async{
    List<Article>? articles;
    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);
      List<dynamic> body=json['articles'];
      articles=body.map((item) => Article.fromJson(item)).toList();
    }
    return articles!;
  }
}


//这是 Source 类

class Source{

  String name;

  Source({required this.name});

  factory Source.fromJson(Map<String,dynamic> json){
    return Source(

        name:json['name']);
  }
  Map<String,dynamic> toJson(Source sources) => <String,dynamic>{
    "name": sources.name,
  };
}


//这是我的 UI

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{
  createState()=>_HomeScreen();
}

class _HomeScreen extends State<HomeScreen>{
 bool isWaiting=false;
  List<Article>articles=[];
  void initState(){
    super.initState();
    IncomingApi();
  }

  void IncomingApi()async{
    isWaiting=true;
    var data=await ApiService.article();
    setState(() {
      articles=data;
      isWaiting=false;
    });
  }
  Widget build(BuildContext context){
    return Scaffold(
      appBar: AppBar(
        title: Text('Home'),
      ),
      body:ListView.builder(
          itemCount: articles.length,
          itemBuilder:(context, index) {
            return NewsContainer(context, articles[index]);
          }) );
  }
  Widget NewsContainer(BuildContext context,Article article){
    return Container(
      child:ListTile(
        title:Text(article.description!) ,
      )
    );
  }
}
英文:

Am working on a newsapp using the newsapi.org to fetch data from the list of articles but it returns an exception of null is not a sub type of String in the Article.fromJson and i would like to ask for some help.Thank you

//this is my Article class
import &#39;package:news_app/source_models.dart&#39;;
class Article{
Source source;
String? author;
String? title;
String? description;
String? url;
String? content;
Article({required this.source,required this.author,required this.title,required this.description,required this.url,
required this.content});
factory Article.fromJson(Map&lt;String,dynamic&gt;json){
return Article(
source: Source.fromJson(json[&#39;source&#39;]),
author: json[&#39;author&#39;]as String,
title: json[&#39;title&#39;] !=null?json[&#39;title&#39;].toString():&#39;&#39;,
description: json[&#39;description&#39;]as String,
url: json[&#39;url&#39;]as String,
content: json[&#39;content&#39;]as String,
);
}
Map&lt;String,dynamic&gt;ToJson(Article article)=&gt;&lt;String,dynamic&gt;
{
&quot;source&quot;:article.source,
&quot;author&quot;:article.author,
&quot;title&quot;:article.title,
&quot;description&quot;:article.description,
&quot;url&quot;:article.url,
&quot;content&quot;:article.content,
};
}
//this is the api
import &#39;package:news_app/Article.dart&#39;;
import &#39;dart:convert&#39;;
import &#39;package:http/http.dart&#39; as http;
class ApiService{
static Future&lt;List&lt;Article&gt;&gt;article()async{
List&lt;Article&gt;?articles;
final http.Response response=await http.get(Uri.parse(&#39;https://newsapi.org/v2/everything?domains=wsj.com&amp;apiKey=eac151759e98448f8712913e2028cfd0&#39;));
if(response.statusCode==200){
Map&lt;String,dynamic&gt;json=jsonDecode(response.body);
List&lt;dynamic&gt;body=json[&#39;articles&#39;];
articles=body.map((item) =&gt; Article.fromJson(item)).toList();
}
return articles!;
}
}
//this is the source class
class Source{
String name;
Source({required this.name});
factory Source.fromJson(Map&lt;String,dynamic&gt;json){
return Source(
name:json[&#39;name&#39;]);
}
Map&lt;String,dynamic&gt;ToJson(Source sources){
return &lt;String,dynamic&gt;{
&quot;name&quot;:sources.name,
};
}
}

//this is my UI

import &#39;package:flutter/cupertino.dart&#39;;
import &#39;package:flutter/material.dart&#39;;
import &#39;package:news_app/ApiService.dart&#39;;
import &#39;package:news_app/Article.dart&#39;;
class HomeScreen extends StatefulWidget{
createState()=&gt;_HomeScreen();
}
class _HomeScreen extends State&lt;HomeScreen&gt;{
bool isWaiting=false;
List&lt;Article&gt;articles=[];
void initState(){
super.initState();
IncomingApi();
}
void IncomingApi()async{
isWaiting=true;
var data=await ApiService.article();
setState(() {
articles=data;
isWaiting=false;
});
}
Widget build(BuildContext context){
return Scaffold(
appBar: AppBar(
title: Text(&#39;Home&#39;),
),
body:ListView.builder(
itemCount: articles.length,
itemBuilder:(context, index) {
NewsContainer(context, articles[index]);
return Center(
child:CircularProgressIndicator()
);
}) );
}
Widget NewsContainer(BuildContext context,Article article){
return Container(
child:ListTile(
title:Text(article.description!) ,
)
);
}
}

//I made checks on all the fields but didnt work,i also tried using the futureBuilder and nothing comes out of it and i certainly dont clearly understand why the Source.fromJson didnt produce any exception if truely nothing is being passed on to the Article.fromJson.And please all answers will be appreciated.

答案1

得分: 3

由于一些输入可能为null,你应该使用可空操作符?String类型进行类型转换。

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

  Article({
    required this.source,
    required this.author,
    required this.title,
    required this.description,
    required this.url,
    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'] != null ? json['title'].toString() : '',
      description: json['description'] as String?,
      url: json['url'] as String?,
      content: json['content'] as String?,
    );
  }

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

注意:这段代码是使用Dart编写的,用于处理JSON数据的序列化和反序列化。在这段代码中,String?表示字符串类型可以为空,as String?是将JSON数据转换为可空的字符串类型。可空操作符?用于处理可能为null的情况。

英文:

As some of the inputs are null, you should cast String with nullable operator ?.

class Article{
Source source;
String? author;
String? title;
String? description;
String? url;
String? content;
Article({required this.source,required this.author,required this.title,required this.description,required this.url,
required this.content});
factory Article.fromJson(Map&lt;String,dynamic&gt;json){
return Article(
source: Source.fromJson(json[&#39;source&#39;]),
author: json[&#39;author&#39;]as String?,
title: json[&#39;title&#39;] !=null?json[&#39;title&#39;].toString():&#39;&#39;,
description: json[&#39;description&#39;]as String?,
url: json[&#39;url&#39;]as String?,
content: json[&#39;content&#39;]as String?,
);
}
Map&lt;String,dynamic&gt;ToJson()=&gt;&lt;String,dynamic&gt;
{
&quot;source&quot;: source,
&quot;author&quot;: author,
&quot;title&quot;: title,
&quot;description&quot;: description,
&quot;url&quot;: url,
&quot;content&quot;: content,
};
}

答案2

得分: 0

在你的代码中,作者字符串的键值为null,导致应用程序崩溃,所以你需要添加空安全性。你需要使用??操作符代替!

另外,你可以从API响应中生成模型,查看这个链接

还修复了你的代码中的一些警告。

请使用下面的代码替代你的模型类:

class ArticleResponse {
  String? status;
  int? totalResults;
  List<Articles>? articles;

  ArticleResponse({this.status, this.totalResults, this.articles});

  ArticleResponse.fromJson(Map<String, dynamic> json) {
    status = json['status'];
    totalResults = json['totalResults'];
    if (json['articles'] != null) {
      articles = <Articles>[];
      json['articles'].forEach((v) {
        articles!.add(Articles.fromJson(v));
      });
    }
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = <String, dynamic>{};
    data['status'] = status;
    data['totalResults'] = totalResults;
    if (articles != null) {
      data['articles'] = articles!.map((v) => v.toJson()).toList();
    }
    return data;
  }
}

class Articles {
  Source? source;
  String? author;
  String? title;
  String? description;
  String? url;
  String? urlToImage;
  String? publishedAt;
  String? content;

  Articles(
      {this.source,
      this.author,
      this.title,
      this.description,
      this.url,
      this.urlToImage,
      this.publishedAt,
      this.content});

  Articles.fromJson(Map<String, dynamic> json) {
    source = json['source'] != null ? Source.fromJson(json['source']) : null;
    author = json['author'];
    title = json['title'];
    description = json['description'];
    url = json['url'];
    urlToImage = json['urlToImage'];
    publishedAt = json['publishedAt'];
    content = json['content'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = <String, dynamic>{};
    if (source != null) {
      data['source'] = source!.toJson();
    }
    data['author'] = author;
    data['title'] = title;
    data['description'] = description;
    data['url'] = url;
    data['urlToImage'] = urlToImage;
    data['publishedAt'] = publishedAt;
    data['content'] = content;
    return data;
  }
}

class Source {
  String? id;
  String? name;

  Source({this.id, this.name});

  Source.fromJson(Map<String, dynamic> json) {
    id = json['id'];
    name = json['name'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = <String, dynamic>{};
    data['id'] = id;
    data['name'] = name;
    return data;
  }
}

API调用服务对于映射JSON到模型进行了一些小的更改。

class ApiService {
  static Future<List<Articles>> article() async {
    List<Articles>? articles;
    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); 
      final fetchData = ArticleResponse.fromJson(json); 
      articles = fetchData.articles ?? [];
      
    }

    return articles!;
  }
}

主屏幕小部件如下:

class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});
  
  @override
  createState() => _HomeScreen();
}

class _HomeScreen extends State<HomeScreen> {
  bool isWaiting = false;
  List<Articles> articles = [];
  @override
  void initState() {
    super.initState();
    incomingApi();
  }

  Future<void> incomingApi() async {
    isWaiting = true;
    List<Articles> data = await ApiService.article();
    setState(() {
      articles = data;
      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, Articles article) {
    return ListTile(
      title: Text(article.description ?? '');
    }
  }
}
英文:

in your code author string key value is null so app is crashing so here you need to add null-safety. so you need to user ?? operator instead of !.

also you can generate model from API response checkout this link

Also fixed few warnings in your code.

Prefere bellow code.

Replace your Model classes

class ArticleResponse {
String? status;
int? totalResults;
List&lt;Articles&gt;? articles;
ArticleResponse({this.status, this.totalResults, this.articles});
ArticleResponse.fromJson(Map&lt;String, dynamic&gt; json) {
status = json[&#39;status&#39;];
totalResults = json[&#39;totalResults&#39;];
if (json[&#39;articles&#39;] != null) {
articles = &lt;Articles&gt;[];
json[&#39;articles&#39;].forEach((v) {
articles!.add(Articles.fromJson(v));
});
}
}
Map&lt;String, dynamic&gt; toJson() {
final Map&lt;String, dynamic&gt; data = &lt;String, dynamic&gt;{};
data[&#39;status&#39;] = status;
data[&#39;totalResults&#39;] = totalResults;
if (articles != null) {
data[&#39;articles&#39;] = articles!.map((v) =&gt; v.toJson()).toList();
}
return data;
}
}
class Articles {
Source? source;
String? author;
String? title;
String? description;
String? url;
String? urlToImage;
String? publishedAt;
String? content;
Articles(
{this.source,
this.author,
this.title,
this.description,
this.url,
this.urlToImage,
this.publishedAt,
this.content});
Articles.fromJson(Map&lt;String, dynamic&gt; json) {
source = json[&#39;source&#39;] != null ? Source.fromJson(json[&#39;source&#39;]) : null;
author = json[&#39;author&#39;];
title = json[&#39;title&#39;];
description = json[&#39;description&#39;];
url = json[&#39;url&#39;];
urlToImage = json[&#39;urlToImage&#39;];
publishedAt = json[&#39;publishedAt&#39;];
content = json[&#39;content&#39;];
}
Map&lt;String, dynamic&gt; toJson() {
final Map&lt;String, dynamic&gt; data = &lt;String, dynamic&gt;{};
if (source != null) {
data[&#39;source&#39;] = source!.toJson();
}
data[&#39;author&#39;] = author;
data[&#39;title&#39;] = title;
data[&#39;description&#39;] = description;
data[&#39;url&#39;] = url;
data[&#39;urlToImage&#39;] = urlToImage;
data[&#39;publishedAt&#39;] = publishedAt;
data[&#39;content&#39;] = content;
return data;
}
}
class Source {
String? id;
String? name;
Source({this.id, this.name});
Source.fromJson(Map&lt;String, dynamic&gt; json) {
id = json[&#39;id&#39;];
name = json[&#39;name&#39;];
}
Map&lt;String, dynamic&gt; toJson() {
final Map&lt;String, dynamic&gt; data = &lt;String, dynamic&gt;{};
data[&#39;id&#39;] = id;
data[&#39;name&#39;] = name;
return data;
}
}

API Call service minor changes to map json in model.

class ApiService {
static Future&lt;List&lt;Articles&gt;&gt; article() async {
List&lt;Articles&gt;? articles;
final http.Response response = await http.get(Uri.parse(
&#39;https://newsapi.org/v2/everything?domains=wsj.com&amp;apiKey=eac151759e98448f8712913e2028cfd0&#39;));
if (response.statusCode == 200) {
Map&lt;String, dynamic&gt; json = jsonDecode(response.body); 
final fetchData = ArticleResponse.fromJson(json); // just need to add json response to map model 
articles = fetchData.articles ?? []; // get article array from response.
}
return articles!;
}
}

Home screen widget

class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
createState() =&gt; _HomeScreen();
}
class _HomeScreen extends State&lt;HomeScreen&gt; {
bool isWaiting = false;
List&lt;Articles&gt; articles = [];
@override
void initState() {
super.initState();
incomingApi();
}
Future&lt;void&gt; incomingApi() async {
isWaiting = true;
List&lt;Articles&gt; data = await ApiService.article();
setState(() {
articles = data;
isWaiting = false;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(&#39;Home&#39;),
),
body: isWaiting
? const Center(child: CircularProgressIndicator())
: ListView.builder(
itemCount: articles.length,
itemBuilder: (context, index) {
return newsContainer(context, articles[index]);
}),
);
}
Widget newsContainer(BuildContext context, Articles article) {
return ListTile(
title: Text(article.description ?? &#39;&#39;),
);
}
}

huangapple
  • 本文由 发表于 2023年2月27日 10:56:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/75576415.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定