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(),
],
),
),
),
);
}
}
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));
}
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),
),
)
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.
im trying to retrieve tasks from project in another words i want to rebuild my screen on viewing tasks with specific forgin key projectId i got this error while working on future provider
enter image description here
those are api/method/
enter image description here
enter image description here
enter image description here
here i want to view tasks only when i pass projectId
import 'package:flutter/material.dart';
import 'package:project/model/task_model.dart';
import 'package:project/project_dtetailForTest.dart';
import 'package:provider/provider.dart';
import 'new_project.dart';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
class TaskById extends StatefulWidget {
//const TaskById({ Key? key }) : super(key: key);
final int project_id;
final Task task =Task();
TaskById({required this.project_id}):super();
#override
State<TaskById> createState() => _TaskByIdState();
}
class _TaskByIdState extends State<TaskById> {
#override
Widget build(BuildContext context) {
final taskP= Provider.of<TaskProvider>(context);
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
elevation: 0,
bottom: PreferredSize(
child: Container(
color: const Color(0xff94adb4),
height: 2,
width: 320,
),
preferredSize: const Size.fromHeight(4.0)),
centerTitle: true,
backgroundColor:const Color(0xff076792),
title: const Text(
'Project1',
style: TextStyle(color: Colors.white,
fontSize: 35,
fontWeight:FontWeight.w700,
shadows: [
Shadow(
color: Color(0xa6A2B6D4),
blurRadius:20),
] ),
),
leading: IconButton(
onPressed: () {
Navigator.pop(context);
},
icon: const Icon(
Icons.arrow_back,
size: 44,
),
),
actions:[Container(padding: const EdgeInsets.fromLTRB(0, 0, 6, 3),
child: IconButton(
color:Colors.white ,
icon: const Icon(
Icons.search,
size: 44,
),
onPressed: () {},
),
),]
),//TaskProvider(){
// this.fetchTask();
// }
body: FutureProvider(
create: (context) => TaskProvider().fetchTask() ,
initialData: [CircularProgressIndicator()],
child:Center(child: Consumer<Task>(
builder: (context, task, widget) {
return (taskP !=null) ? Column(
children: [
Row(
children: const [
Padding(padding: EdgeInsets.all(7),
child: Padding(
padding: EdgeInsets.fromLTRB(24,10,10,8),
child: Text('Project1',
style: TextStyle(
color: Color(0xff076792),
fontSize: 40,
fontWeight: FontWeight.w600
),
),
),
),
],
),
// dynamic list
Expanded(
child: ListView.builder(
itemCount: taskP.task.length ,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.fromLTRB(28, 12, 28, 0),
child: GestureDetector(
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => projectDetails()));
},
child: Card(child:Container(padding: const EdgeInsets.fromLTRB(18,8,9,4),
width: 100,
height: 100,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(19.0),
color: const Color(0xff076792),
boxShadow: const [
BoxShadow(color: Colors.black, spreadRadius: 1),
BoxShadow(color: Color(0xa6A2B6D4),
offset: Offset(7,5),
blurRadius:20),
],
),
child: Column(children: [
Row(mainAxisAlignment: MainAxisAlignment.start,
children: [
Text('Starting '+taskP.task[index].start.toString(),style:
const TextStyle(color: Color.fromARGB(255, 133, 186, 202),fontSize: 18,),
),
],
),
const Spacer(),
Row(mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(taskP.task[index].title??'',style:
const TextStyle(color: Colors.white,fontSize: 34,)),
],
),
const Spacer(),
Row(mainAxisAlignment: MainAxisAlignment.end,
children: [
Text('Ending '+taskP.task[index].end.toString(),style:
const TextStyle(color: Color.fromARGB(255, 133, 186, 202),
fontSize: 18,
)),
],
),
]),
),),
),
);
}),
)
],
)
: CircularProgressIndicator();
},
) ,
)
// widget.project_id),
// builder: (context, snapshot){
// if (snapshot.hasError) print(snapshot.error);
// return snapshot.hasData
// ? TaskList(taskP: snapshot.data)
// : Center(child: CircularProgressIndicator());
// }),
)
);
}
}
here how i pass projectId
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => TaskById(project_id: projectP.project[index].id!,)));
},
here the api , model,
// To parse this JSON data, do
//
// final task = taskFromJson(jsonString);
import 'dart:collection';
import 'dart:core';
import 'package:flutter/foundation.dart';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
//List<Task> taskFromJson(String str) => List<Task>.from(json.decode(str).map((x) => Task.fromJson(x)));
//String taskToJson(List<Task> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Task {
String? url;
int? id;
String? owner;
String? project;
String? title;
DateTime? start;
DateTime? end;
String? desc;
int? project_id;
Task(
{this.url,
this.id,
this.owner,
this.project,
this.title,
this.start,
this.end,
this.desc,
this.project_id});
factory Task.fromJson(Map<String, dynamic> json) => Task(
url :json["url"],
id: json["id"],
owner: json["owner"],
project: json["project"],
title: json["title"],
start: DateTime.parse(json["start"]),
end: DateTime.parse(json["end"]),
desc: json["desc"],
project_id: json["project_id"],
);
Map<String, dynamic> toJson() => {
"url": url,
"id": id,
"owner": owner,
"project": project,
"title": title,
"start": start?.toIso8601String(),
"end": end?.toIso8601String(),
"desc": desc,
"project_id": project_id,
};
}
class TaskProvider with ChangeNotifier{
TaskProvider(){
this.fetchTask();
}
List<Task> _task = [];
List<Task> get task {
return [..._task];
}
void addTask(Task task) async {
final response = await http.post(Uri.parse('http://mostafahamed.pythonanywhere.com/project/task'),
headers: {"Content-Type": "application/json"}, body: json.encode(task));
if (response.statusCode == 201) {
task.id = json.decode(response.body)['id'];
_task.add(task);
notifyListeners();
print('suceeed add ed task');
print(response.body);
}else{
print(response.body);
print('failed add ed task');
}
}
void deleteTask(Task task) async {
final response =
await http.delete(Uri.parse('http://mostafahamed.pythonanywhere.com/project/task${task.id}/'));
if (response.statusCode == 204) {
_task.remove(task);
notifyListeners();
}
}
Future fetchTask([project_id]) async{
final response = await http
.get(Uri.parse('http://mostafahamed.pythonanywhere.com/tasks/?format=json'));
if (response.statusCode==200){
var data = json.decode(response.body)as List;
_task=data.map<Task>((json) => Task.fromJson(json)).toList();
notifyListeners();
print('sucess view list');
return _task;
}
else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load tasks');
}
}
}
the error i got when i click on a project wiget to diplay its tasks on new screen
The following ProviderNotFoundException was thrown building Consumer<Task>(dirty):
Error: Could not find the correct Provider<Task> above this Consumer<Task> Widget
This happens because you used a `BuildContext` that does not include the provider
of your choice. There are a few common scenarios:
- You added a new provider in your `main.dart` and performed a hot-reload.
To fix, perform a hot-restart.
- The provider you are trying to read is in a different route.
Providers are "scoped". So if you insert of provider inside a route, then
other routes will not be able to access that provider.
- You used a `BuildContext` that is an ancestor of the provider you are trying to read.
Make sure that Consumer<Task> is under your MultiProvider/Provider<Task>.
This usually happens when you are creating a provider and trying to read it immediately.
For example, instead of:
child: Text(context.watch<Example>()),
),
}
consider using `builder` like so:
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// we use `builder` to obtain a new `BuildContext` that has access to the provider
builder: (context) {
// No longer throws
return Text(context.watch<Example>()),
}
),
}
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;
}
}