i was trying to create a news app with news api, but i got a weird error when 1 tile of the news actually showedup but there's also an error message in the app.
i need help with how do i get rid of this error.
so below here is the codes for my app
here is my homepage code
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(),);
},
),
);
}
}
My APIService class
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=gb&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");
}
}
}
my News/Article model class
import 'source_models.dart';
import 'package:medreminder/NewsArticle/components/list_tile.dart';
import 'package:medreminder/NewsArticle/models/source_models.dart';
class Articles {
List<Articles>? articles;
Articles({this.articles});
Articles.fromJson(Map<String, dynamic> json) {
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>{};
if (articles != null) {
data['articles'] = articles!.map((v) => v.toJson()).toList();
}
return data;
}
}
class Article {
Source? source;
String? author;
String? title;
String? description;
String? url;
String? urlToImage;
String? publishedAt;
String? content;
Article(
{source,
author,
title,
description,
url,
urlToImage,
publishedAt,
content});
Article.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({id, 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;
}
}
and lastly my custometile class
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: Colors.black,
borderRadius: BorderRadius.circular(18),
boxShadow: [BoxShadow(color: 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,
),
)
],
),
);
}
let me know if you guys needs to see the other codes if neccesary. thankyou so much guys
add FutureBuilder return Type and check it!
body: FutureBuilder<List<Article>>(
future: client.getArticle(),
builder: (BuildContext context, AsyncSnapshot<List<Article>> snapshot) {
if(snapshot.hasData&&snapshot.data!=null&& snapshot.data!.isNotEmpty){
List<Article>? articles = snapshot.data;
return ListView.builder(
itemCount: articles!.length,
itemBuilder: (context, index) => listTile(articles[index])
);
}
return Center(child: CircularProgressIndicator(),);
},
),
);
it is because you have used ! null check operator which is causing error when the value is null try replacing this operator by this ? operator like this.
change this line
itemCount: articles!.length,
to this
itemCount: articles?.length,
In API response some of response data has null value : article.urlImage is null, which cause this issue. To handle it either you have to provide different container style or default image.
Code as below: In listTile for different container style
article.urlToImage != null
? Container(
decoration: BoxDecoration(
image:
DecorationImage(image: NetworkImage(article.urlToImage!)),
borderRadius: BorderRadius.circular(8),
),
)
: Container(),
OR In listTile for default image :
Container(
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(article.urlToImage != null
? article.urlToImage!
: "Your defualt image here")),
borderRadius: BorderRadius.circular(8),
),
)
Related
I am trying to get a JSON response from this server. I am new to flutter and I'm confused about how to get it done.
can anybody point out what I did wrong???
class RESTAPIService{
String apiUrl = "https://mocki.io/v1/048e68cc-9ddb-4aca-8264-6e9f8f273fd2";
Future<List<User>> getUsers() async{
final response = await http.get(Uri.parse(apiUrl));
print(response.body);
if(response.statusCode == 200){
throw getUsersList(response.body);
}else{
throw Exception("Couldn't fetch data");
}
}
List<User> getUsersList(String responseBody){
final parsebody = json.decode(responseBody).cast<Map<String, dynamic>>();
return parsebody.map<User>({(json) => User(name: "", city: "", image: "").fromJson(json)}).toList();
}
}
I did my conversions like bellow.
class User{
String name;
String city;
String image;
User({required this.name, required this.city, required this.image});
Map<String, dynamic> fromJson(Map<String, dynamic> json){
name = json['name'];
city = json['city'];
image = json['image'];
return json;
}
Map<String, dynamic> toJson(){
final Map<String, dynamic> data = new Map<String, dynamic>();
data['name'] = this.name;
data['city'] = this.city;
data['image'] = this.image;
return data;
}
}
I called the json data here. When the app runs it only shows "Loading..." and won't show the json response. But I do get the json response from the server. it just doesn't show in my app UI.
child: FutureBuilder(
future: apiService.getUsers(),
builder: (context, snapShot){
if(snapShot.hasData){
return ListView.builder(
itemCount: snapShot.data!.length,
itemBuilder: (context, index){
return InkWell(
onTap: (){
},
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
blurRadius: 3, spreadRadius: 3,
color: Colors.grey.withOpacity(0.3),
)
]
),
margin: EdgeInsets.symmetric(vertical: 10, horizontal: 10),
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 10),
child: ListTile(
title: Text(snapShot.data![index].name ?? '', style: TextStyle(
fontSize: 18, color: Colors.black,
),),
subtitle: Text(snapShot.data![index].city ?? '', style: TextStyle(
fontSize: 15, color: Colors.black,
),),
leading: ClipOval(
child: Image.network(snapShot.data![index].image, fit: BoxFit.cover, width: 60, height: 60,),
),
),
),
);
}
);
}else{
return Container(
child: Center(
child: Text("Loading...", style: TextStyle(
color: Colors.black, fontSize: 15,
)),
),
);
}
},
),`
Your response is list,
So it should be
List<User> getUsersList(String responseBody) {
final parsebody = json.decode(responseBody) as List?;
return (parsebody?.map((e) => User.fromMap(e)))?.toList() ?? [];
}
Try using factory model like
class User {
String name;
String city;
String image;
User({required this.name, required this.city, required this.image});
Map<String, dynamic> toMap() {
final result = <String, dynamic>{};
result.addAll({'name': name});
result.addAll({'city': city});
result.addAll({'image': image});
return result;
}
factory User.fromMap(Map<String, dynamic> map) {
return User(
name: map['name'] ?? '',
city: map['city'] ?? '',
image: map['image'] ?? '',
);
}
String toJson() => json.encode(toMap());
factory User.fromJson(String source) => User.fromMap(json.decode(source));
}
So i tried to create a listview of a news with this API https://newsapi.org/s/us-news-api but when i tried to implement it in my App it keeps loading and never show the data.
here is my homepage code
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(),);
},
),
);
}
}
my API Service
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");
}
}
}
here is my model class
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
);
}
}
Adding my customtile code
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,
),)
],
),
);
}
i dont know how this happening but i followed everysecond in a yt tutorial but still got this never stop loading. let me know if you guys needs to see my other codes if necessary. thankyou guys
I check your code, you have to update your Article class. Because some values are null in API response, so that causes issue. To handle that You have to generate dart model class for APIs response by using json to dart convertor.
Here is updated class:
class Articles {
List<Articles>? articles;
Articles({this.articles});
Articles.fromJson(Map<String, dynamic> json) {
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>{};
if (articles != null) {
data['articles'] = articles!.map((v) => v.toJson()).toList();
}
return data;
}
}
class Article {
Source? source;
String? author;
String? title;
String? description;
String? url;
String? urlToImage;
String? publishedAt;
String? content;
Article(
{source,
author,
title,
description,
url,
urlToImage,
publishedAt,
content});
Article.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({id, 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;
}
}
and listTile widget will be :
Widget listTile(Article article) {
return Container(
margin: EdgeInsets.all(12),
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(18),
boxShadow: [BoxShadow(color: 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,
),
)
],
),
);
}
I run this API in postman and get response in html format. There in response they mentioned to get reponse by this API
https://newsapi.org/v2/top-headlines?country=gb&apiKey=API_KEY
Try this one, Maybe it works.
Having an error in execution and returning a null value when the app is debugging
Getting The Bellow Error Debug Code
lib/quran/surat_list_detail.dart:16:27: Error: Field '_ayatList'
should be initialized because its type 'Future<List>'
doesn't allow null.
'Future' is from 'dart:async'.
'List' is from 'dart:core'.
'SuratAyat' is from 'package:issaq_pro/model/surat_ayat_model.dart' ('lib/model/surat_ayat_model.dart'). Future<List>
_ayatList;
import 'package:flutter/material.dart';
import 'package:issaq_pro/model/surat_ayat_model.dart';
class SuratListDetail extends StatefulWidget {
final String suratId;
final String suratName;
final String totalAyat;
SuratListDetail(this.suratId, this.suratName, this.totalAyat);
#override
_SuratListDetailState createState() => _SuratListDetailState();
}
class _SuratListDetailState extends State<SuratListDetail> {
Future<List<SuratAyat>> _ayatList;
#override
void initState() {
super.initState();
_ayatList = getSuratAyatData(widget.suratId);
}
ListView _suratListView(data) {
return ListView.builder(
itemCount: data.length,
itemBuilder: (BuildContext context, index) {
return Card(
elevation: 8,
child: _tile(
data[index].ayatNumber,
data[index].ayatText,
),
);
},
);
}
ListTile _tile(
String ayatNumber,
String ayatText,
) =>
ListTile(
contentPadding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
leading: Container(
padding: EdgeInsets.only(right: 12),
decoration: BoxDecoration(
border: Border(
right: BorderSide(width: 1, color: Colors.black),
),
),
child: CircleAvatar(
backgroundColor: Colors.amberAccent,
child: Text(
'$ayatNumber',
style: TextStyle(
color: Colors.black,
fontSize: 12,
),
),
),
),
title: Text(
ayatText,
style: TextStyle(fontSize: 24),
),
trailing: Icon(
Icons.keyboard_arrow_right,
color: Colors.white,
size: 30,
),
);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
widget.suratName,
style: TextStyle(color: Colors.white),
),
backgroundColor: Colors.black,
),
body: SafeArea(
child: Center(
child: FutureBuilder<List<SuratAyat>>(
future: _ayatList,
builder: (BuildContext context,AsyncSnapshot snapshot) {
if (snapshot.hasData) {
List<SuratAyat> data = snapshot.data;
return _suratListView(data);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return CircularProgressIndicator();
},
),
),
),
);
}
}
MODELS :
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
class QuranSurat {
final String id;
final String suratName;
final String suratText;
final String translate;
final String totalAyat;
QuranSurat(
{required this.id,
required this.suratName,
required this.suratText,
required this.translate,
required this.totalAyat});
factory QuranSurat.createSuratList(Map<String, dynamic> json) {
return QuranSurat(
id: json['id'].toString(),
suratName: json['surat_name'],
suratText: json['surat_text'],
translate: json['surat_terjemahan'],
totalAyat: json['count_ayat'].toString());
}
}
Future<List<QuranSurat>> getSuratList() async {
try {
final apiUrl = 'https://quran.kemenag.go.id/index.php/api/v1/surat';
final response = await http.get(Uri.parse(apiUrl));
List result = json.decode(response.body)['data'];
return result
.map((surat) => new QuranSurat.createSuratList(surat))
.toList();
} catch (err) {
throw err;
}
}
just add late before Future it should work
late Future<List<SuratAyat>> _ayatList;
I am trying to fetch the data from API using StreamBuilder, Kindly help with this error
Future<dynamic>' is not a subtype of type 'Stream<List<dynamic>>.
Basically we are fetching data and displaying data with help of ListView.buider and StreamBuilder:
Search_product.dart
import 'dart:async';
import 'package:beauty/user.dart';
import 'package:flutter/material.dart';
import 'package:beauty/backend.dart';
class SearchProduct extends StatefulWidget {
#override
_SearchProductState createState() => _SearchProductState();
}
class _SearchProductState extends State<SearchProduct> {
List<Welcome> _welcome;
Backend backend = new Backend();
bool waiting = true;
Stream _stream;
#override
void initState() {
// TODO: implement initState
super.initState();
Backend.getData().then((welcome) {
setState(() {
_welcome = welcome;
waiting = false;
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFFEBEAEF),
appBar: AppBar(
title: Row(children: [
SizedBox(
width: 70.0,
),
Text(
'Search Product',
style: TextStyle(
color: Colors.black,
),
),
SizedBox(
width: 70.0,
),
Flexible(
child: CircleAvatar(
child: Image.asset('assets/her.png'),
),
),
]),
leading: IconButton(
icon: Icon(
Icons.arrow_back,
color: Colors.black,
),
onPressed: () {},
),
backgroundColor: Color(0xFFEBEAEF),
),
body: Column(children: [
Container(
color: Colors.white70,
child: TextField(
decoration: InputDecoration(
prefixIcon: Icon(
Icons.search,
color: Colors.grey,
),
hintText: 'Search for product',
contentPadding:
EdgeInsets.symmetric(vertical: 10.0, horizontal: 20.0),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12.0)),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.white70, width: 1.0),
borderRadius: BorderRadius.all(Radius.circular(12.0)),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.white70, width: 2.0),
borderRadius: BorderRadius.all(Radius.circular(12.0)),
),
),
style: TextStyle(color: Colors.black),
),
),
Container(
child: waiting
? SizedBox(
width: 50,
height: 50,
child: CircularProgressIndicator(),
)
: Expanded(
child: StreamBuilder(
stream: Backend.getData(),
builder: (BuildContext context,
AsyncSnapshot<List<dynamic>> snapshot) {
return ListView.builder(
padding: EdgeInsets.all(8),
itemCount: null == _welcome ? 0 : _welcome.length,
itemBuilder: (BuildContext context, int index) {
Welcome welcome = _welcome[index];
return Card(
child: Column(
children: <Widget>[
ListTile(
leading: CircleAvatar(
radius: 30,
backgroundImage: NetworkImage(snapshot
.data[index].welcome.image),
),
title: Text(
snapshot.data[index].welcome.title),
subtitle: Text(snapshot
.data[index].welcome.description),
trailing: Text(snapshot
.data[index].welcome.price
.toString()),
)
],
),
);
});
}),
),
),
]));
}
}
Backend.dart
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:beauty/user.dart';
class Backend {
static getData() async {
try {
var url = Uri.parse('https://fakestoreapi.com/products');
var response = await http.get(url);
if (response.statusCode == 200) {
return welcomeFromJson(response.body);
} else {
List error_message = ['error'];
return error_message;
}
} catch (e) {
print(e);
}
}
}
user.dart
import 'dart:convert';
List<Welcome> welcomeFromJson(String str) =>
List<Welcome>.from(json.decode(str).map((x) => Welcome.fromJson(x)));
String welcomeToJson(List<Welcome> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Welcome {
Welcome({
this.id,
this.title,
this.price,
this.description,
this.category,
this.image,
this.rating,
});
int id;
String title;
double price;
String description;
Category category;
String image;
Rating rating;
factory Welcome.fromJson(Map<String, dynamic> json) => Welcome(
id: json["id"],
title: json["title"],
price: json["price"].toDouble(),
description: json["description"],
category: categoryValues.map[json["category"]],
image: json["image"],
rating: Rating.fromJson(json["rating"]),
);
Map<String, dynamic> toJson() => {
"id": id,
"title": title,
"price": price,
"description": description,
"category": categoryValues.reverse[category],
"image": image,
"rating": rating.toJson(),
};
}
enum Category { MEN_S_CLOTHING, JEWELERY, ELECTRONICS, WOMEN_S_CLOTHING }
final categoryValues = EnumValues({
"electronics": Category.ELECTRONICS,
"jewelery": Category.JEWELERY,
"men's clothing": Category.MEN_S_CLOTHING,
"women's clothing": Category.WOMEN_S_CLOTHING
});
class Rating {
Rating({
this.rate,
this.count,
});
double rate;
dynamic count;
factory Rating.fromJson(Map<String, dynamic> json) => Rating(
rate: json["rate"].toDouble(),
count: json["count"],
);
Map<String, dynamic> toJson() => {
"rate": rate,
"count": count,
};
}
class EnumValues<T> {
Map<String, T> map;
Map<T, String> reverseMap;
EnumValues(this.map);
Map<T, String> get reverse {
if (reverseMap == null) {
reverseMap = map.map((k, v) => new MapEntry(v, k));
}
return reverseMap;
}
}
The response type from Backend.getData is a Future, not a Stream.
Replace
StreamBuilder(
stream: Backend.getData(),
builder: (BuildContext context, AsyncSnapshot<List<dynamic>> snapshot) {
with
FutureBuilder(
future: Backend.getData(),
builder: (BuildContext context, AsyncSnapshot<List<dynamic>> snapshot) {
//show visual loading
if(!snapshot.hasData) return Center(child: CircularProgressIndicator());
The error occurred because of type mismatch.
Current
Expected
Expected data
List<dynamic>
List<Welcome>?
stream
Future<dynamic>
Stream<List<Welcome>?>
snapshot
AsyncSnapshot<List<dynamic>>
AsyncSnapshot<List<Welcome>?>
StreamBuilder expects a stream instead of Future<T>.
If you expect new data coming from Future, you may consider using FutureBuilder instead.
So, here's how we fix it.
Create a StreamController
Pass the stream currently controlled by StreamController to StreamBuilder so it can listen for new data event
Call getData(), wait until it completes and returns expected data List<Welcome>? (can be a list of Welcome if successful or null if failed)
Next, send the data as data event to the stream
We also have to check the event data and connectionState, then handle them accordingly, e.g. display CircularProgressIndicator() while waiting for data, or certain widget when data exists.
The revised code follows the above steps so it should be easy to understand.
search_product.dart
import 'package:flutter/material.dart';
import 'user.dart';
import 'backend.dart';
class SearchProduct extends StatefulWidget {
#override
_SearchProductState createState() => _SearchProductState();
}
class _SearchProductState extends State<SearchProduct> {
Backend backend = Backend();
#override
void initState() {
super.initState();
backend.addDataToStream();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFFEBEAEF),
appBar: AppBar(
title: Row(children: [
SizedBox(
width: 70.0,
),
Text(
'Search Product',
style: TextStyle(
color: Colors.black,
),
),
SizedBox(
width: 70.0,
),
Flexible(
child: CircleAvatar(
//child: Image.asset('assets/her.png'),
),
),
]),
leading: IconButton(
icon: Icon(
Icons.arrow_back,
color: Colors.black,
),
onPressed: () {},
),
backgroundColor: Color(0xFFEBEAEF),
),
body: Column(
children: [
Container(
color: Colors.white70,
child: TextField(
decoration: InputDecoration(
prefixIcon: Icon(
Icons.search,
color: Colors.grey,
),
hintText: 'Search for product',
contentPadding:
EdgeInsets.symmetric(vertical: 10.0, horizontal: 20.0),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12.0)),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.white70, width: 1.0),
borderRadius: BorderRadius.all(Radius.circular(12.0)),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.white70, width: 2.0),
borderRadius: BorderRadius.all(Radius.circular(12.0)),
),
),
style: TextStyle(color: Colors.black),
),
),
Expanded(
child: StreamBuilder<List<Welcome>?>(
initialData: null,
stream: backend.welcomeStream,
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
case ConnectionState.none:
return Center(
child: CircularProgressIndicator(),
);
case ConnectionState.active:
case ConnectionState.done:
if (snapshot.hasError) {
return Text('Error happened!');
}
var data = snapshot.data;
if (data == null) {
return Text('No data!');
}
return ListView.builder(
padding: EdgeInsets.all(8),
itemCount: data.length,
itemBuilder: (BuildContext context, int index) {
Welcome? welcome = data[index];
return Card(
child: Column(
children: <Widget>[
ListTile(
leading: CircleAvatar(
radius: 30,
backgroundImage: welcome.image == null
? null
: NetworkImage(welcome.image!),
),
title: Text(welcome.title ?? ''),
subtitle: Text(welcome.description ?? ''),
trailing: Text(welcome.price.toString()),
)
],
),
);
},
);
}
},
),
),
],
),
);
}
}
backend.dart
import 'dart:async';
import 'package:http/http.dart' as http;
import 'user.dart';
class Backend {
StreamController<List<Welcome>?> _welcomeController = StreamController();
Stream<List<Welcome>?> get welcomeStream => _welcomeController.stream;
Future<void> addDataToStream() async {
await getData()
.then((List<Welcome>? value) => _welcomeController.add(value));
}
static Future<List<Welcome>?> getData() async {
try {
var url = Uri.parse('https://fakestoreapi.com/products');
var response = await http.get(url);
if (response.statusCode == 200) {
return welcomeFromJson(response.body);
} else {
print(response.statusCode);
return null;
}
} catch (e) {
print(e);
}
}
}
user.dart
import 'dart:convert';
List<Welcome> welcomeFromJson(String str) =>
List<Welcome>.from(json.decode(str).map((x) => Welcome.fromJson(x)));
String welcomeToJson(List<Welcome> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Welcome {
Welcome({
this.id,
this.title,
this.price,
this.description,
this.category,
this.image,
this.rating,
});
int? id;
String? title;
double? price;
String? description;
Category? category;
String? image;
Rating? rating;
factory Welcome.fromJson(Map<String, dynamic> json) => Welcome(
id: json["id"],
title: json["title"],
price: json["price"].toDouble(),
description: json["description"],
category: categoryValues.map[json["category"]],
image: json["image"],
rating: Rating.fromJson(json["rating"]),
);
Map<String, dynamic> toJson() => {
"id": id,
"title": title,
"price": price,
"description": description,
"category": categoryValues.reverse?[category],
"image": image,
"rating": rating?.toJson(),
};
}
enum Category { MEN_S_CLOTHING, JEWELERY, ELECTRONICS, WOMEN_S_CLOTHING }
final categoryValues = EnumValues({
"electronics": Category.ELECTRONICS,
"jewelery": Category.JEWELERY,
"men's clothing": Category.MEN_S_CLOTHING,
"women's clothing": Category.WOMEN_S_CLOTHING
});
class Rating {
Rating({
this.rate,
this.count,
});
double? rate;
dynamic count;
factory Rating.fromJson(Map<String, dynamic> json) => Rating(
rate: json["rate"].toDouble(),
count: json["count"],
);
Map<String, dynamic> toJson() => {
"rate": rate,
"count": count,
};
}
class EnumValues<T> {
Map<String, T> map;
Map<T, String>? reverseMap;
EnumValues(this.map);
Map<T, String>? get reverse {
if (reverseMap == null) {
reverseMap = map.map((k, v) => new MapEntry(v, k));
}
return reverseMap;
}
}
I am trying to make API calls in a flutter. While parsing a simple JSON it works fine but while I try to parse a List it shows error how can I do that?
My JSON
{
"posts": [
{
"id": 1,
"title": "Post 1"
},
{
"id": 2,
"title": "Post 2"
},
{
"id": 3,
"title": "Post 3"
}
],
"comments": [
{
"id": 1,
"body": "some comment",
"postId": 1
},
{
"id": 2,
"body": "some comment",
"postId": 1
}
],
"profile": {
"name": "typicode"
}
}
My PODO Class
class Welcome {
List<Post> posts;
List<Comment> comments;
Profile profile;
Welcome({
this.posts,
this.comments,
this.profile,
});
factory Welcome.fromJson(Map<String, dynamic> json) => Welcome(
posts: List<Post>.from(json["posts"].map((x) => Post.fromJson(x))),
comments: List<Comment>.from(json["comments"].map((x) => Comment.fromJson(x))),
profile: Profile.fromJson(json["profile"]),
);
Map<String, dynamic> toJson() => {
"posts": List<dynamic>.from(posts.map((x) => x.toJson())),
"comments": List<dynamic>.from(comments.map((x) => x.toJson())),
"profile": profile.toJson(),
};
}
class Comment {
int id;
String body;
int postId;
Comment({
this.id,
this.body,
this.postId,
});
factory Comment.fromJson(Map<String, dynamic> json) => Comment(
id: json["id"],
body: json["body"],
postId: json["postId"],
);
Map<String, dynamic> toJson() => {
"id": id,
"body": body,
"postId": postId,
};
}
class Post {
int id;
String title;
Post({
this.id,
this.title,
});
factory Post.fromJson(Map<String, dynamic> json) => Post(
id: json["id"],
title: json["title"],
);
Map<String, dynamic> toJson() => {
"id": id,
"title": title,
};
}
class Profile {
String name;
Profile({
this.name,
});
factory Profile.fromJson(Map<String, dynamic> json) => Profile(
name: json["name"],
);
Map<String, dynamic> toJson() => {
"name": name,
};
}
My api_call.dart file
import 'dart:convert';
import 'package:api/api/modal.dart';
import 'package:http/http.dart';
class HttpService{
final String url = "https://my-json-server.typicode.com/typicode/demo/db";
Future<List<Welcome>> getPost() async {
Response response = await get(url);
if(response.statusCode == 200) {
List<dynamic> body = jsonDecode(response.body);
List<Welcome> wel = body.map((e) => Welcome.fromJson(e)).toList();
return wel;
}
}
}
My widget.dart
import 'package:api/api/api_fetcher.dart';
import 'package:api/api/modal.dart';
import 'package:flutter/material.dart';
class News extends StatefulWidget {
#override
_NewsState createState() => _NewsState();
}
class _NewsState extends State<News> {
final HttpService http = HttpService();
#override
Widget build(BuildContext context) {
return SafeArea(
child: DefaultTabController(
length: 4,
child: Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Colors.white,
title: Text(
'Trending Topics',
style: TextStyle(color: Colors.black, fontSize: 25),
),
automaticallyImplyLeading: false,
elevation: 0,
actions: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Center(
child: Text(
'see all',
style: TextStyle(fontSize: 20, color: Colors.grey[600]),
),
),
),
],
bottom: TabBar(
labelColor: Colors.black,
unselectedLabelColor: Colors.black,
tabs: <Widget>[
Tab(
text: 'Tech',
),
Tab(
text: 'Art',
),
Tab(
text: 'Sports',
),
Tab(
text: 'Nation',
),
],
),
),
body: TabBarView(
children: <Widget>[
Container(
child: FutureBuilder(
future: http.getPost(),
builder: (context, snapshot) {
if(snapshot.hasData){
List<Welcome> welc = snapshot.data;
return ListView(
children: welc.map((Welcome welcome) => ListTile(
title: Text(welcome.posts.length.toString()),
)),
);
}
},
),
),
Container(),
Container(),
Container(),
],
),
),
),
);
}
}
It shows me an error while I try to access the posts through the ListTile.
Give me a solution, please.
Thanks in advance.
This json have one Welcome class, so your function that getPost() should be return just Welcome, consequence you should change your widget that show just welcome with list of post and/or list of comment. Try like this:
class Welcome {
List<Post> posts;
List<Comment> comments;
Profile profile;
Welcome({
this.posts,
this.comments,
this.profile,
});
factory Welcome.fromJson(Map<String, dynamic> json) {
return Welcome(
posts: json['posts'] != null
? json['posts'].map<Post>((json) => Post.fromJson(json)).toList()
: null,
comments: json['comments'] != null
? json['comments']
.map<Comment>((json) => Comment.fromJson(json))
.toList()
: null,
profile:
json['profile'] != null ? Profile.fromJson(json['profile']) : null,
);
}
}
class HttpService {
static String url = "https://my-json-server.typicode.com/typicode/demo/db";
static Future<Welcome> getPost() async {
Response response = await get(url);
if (response.statusCode == 200) {
final parsed = json.decode(response.body).cast<String, dynamic>();
return Welcome.fromJson(parsed);
}
}
}
In your code, you used map to map some objects and then assigned them to children of a widget, also use .toList() after the map.
Something that I couldn't get from you code is that you have a Wellcome model that contains a list of posts and comments and a profile, your sample JSON is equal to one Welcome but in your code when you fetched the data you tried to map it as a list of Wellcome also in your widget.
The other thing is in the future builder, it's better to define what type it is going to get:
...
FutureBuilder<Welcome>(
future: http.getPost(),
builder: (context, AsyncSnapshot<Welcome> snapshot) {
...
Also future takes some times to complete so in cases it doesn't have data(not completed yet) you should return another widget, here I simply used:
return Center(child: CircularProgressIndicator());
I changed your code a little bit, here is the result:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(MaterialApp(
home: News(),
));
}
class Welcome {
List<Post> posts;
List<Comment> comments;
Profile profile;
Welcome({
this.posts,
this.comments,
this.profile,
});
factory Welcome.fromJson(Map<String, dynamic> json) => Welcome(
posts: List<Post>.from(json["posts"].map((x) => Post.fromJson(x))),
comments: List<Comment>.from(
json["comments"].map((x) => Comment.fromJson(x))),
profile: Profile.fromJson(json["profile"]),
);
Map<String, dynamic> toJson() => {
"posts": List<dynamic>.from(posts.map((x) => x.toJson())),
"comments": List<dynamic>.from(comments.map((x) => x.toJson())),
"profile": profile.toJson(),
};
}
class Comment {
int id;
String body;
int postId;
Comment({
this.id,
this.body,
this.postId,
});
factory Comment.fromJson(Map<String, dynamic> json) => Comment(
id: json["id"],
body: json["body"],
postId: json["postId"],
);
Map<String, dynamic> toJson() => {
"id": id,
"body": body,
"postId": postId,
};
}
class Post {
int id;
String title;
Post({
this.id,
this.title,
});
factory Post.fromJson(Map<String, dynamic> json) => Post(
id: json["id"],
title: json["title"],
);
Map<String, dynamic> toJson() => {
"id": id,
"title": title,
};
}
class Profile {
String name;
Profile({
this.name,
});
factory Profile.fromJson(Map<String, dynamic> json) => Profile(
name: json["name"],
);
Map<String, dynamic> toJson() => {
"name": name,
};
}
class HttpService {
final String url = "https://my-json-server.typicode.com/typicode/demo/db";
Future<Welcome> getPost() async {
var response = await http.get(url);
if (response.statusCode == 200) {
var body = json.decode(response.body);
return Welcome.fromJson(body);
}
}
}
class News extends StatefulWidget {
#override
_NewsState createState() => _NewsState();
}
class _NewsState extends State<News> {
final HttpService http = HttpService();
#override
Widget build(BuildContext context) {
return SafeArea(
child: DefaultTabController(
length: 4,
child: Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Colors.white,
title: Text(
'Trending Topics',
style: TextStyle(color: Colors.black, fontSize: 25),
),
automaticallyImplyLeading: false,
elevation: 0,
actions: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Center(
child: Text(
'see all',
style: TextStyle(fontSize: 20, color: Colors.grey[600]),
),
),
),
],
bottom: TabBar(
labelColor: Colors.black,
unselectedLabelColor: Colors.black,
tabs: <Widget>[
Tab(
text: 'Tech',
),
Tab(
text: 'Art',
),
Tab(
text: 'Sports',
),
Tab(
text: 'Nation',
),
],
),
),
body: TabBarView(
children: <Widget>[
Container(
child: FutureBuilder<Welcome>(
future: http.getPost(),
builder: (context, AsyncSnapshot<Welcome> snapshot) {
if (snapshot.hasData) {
Welcome welc = snapshot.data;
return ListView(
children: welc.posts
.map((post) => ListTile(
title: Text(post.title),
))
.toList());
}
return Center(child: CircularProgressIndicator());
},
),
),
Container(),
Container(),
Container(),
],
),
),
),
);
}
}