Flutter snapshot data is empty - json

I'm new in flutter, I try parse data from rest api.
Api return:
http://www.json-generator.com/api/json/get/cqwVqdOFrC?indent=2
Eg. Here get data from json api eg. length is 3231
class ApiService {
static Future<dynamic> _get(String url) async {
try {
final response = await http.get(url);
var jsonData = json.decode(response.body);
if (response.statusCode == 200) {
print(response.body.length); //3231
return jsonData;
} else {
return null;
}
}
}
but here is snapshot.hasData = False, Why?
return Scaffold(
appBar: AppBar(title: Text('Posts'),),
body: FutureBuilder(
future: ApiService.getUserList(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
print(snapshot.hasData.toString()); //False no data
static Future<List<dynamic>> getUserList() async {
return await _get('${Urls.BASE_API_URL}');
}
Without items at the beginning of json all work fine. My web server return items at the beginning. Any solutions?

api returns Map<String, dynamic> not <List<dynamic>>
class ListaAbitudini extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return new _ListaAbitudiniState();
}
}
class _ListaAbitudiniState extends State<ListaAbitudini> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Posts'),
),
body: FutureBuilder(
future: ApiService.getUserList(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if(snapshot.hasData)
print("True");
return Container();
}));
}
}
class ApiService {
static Future<Map<String, dynamic>> getUserList() async {
return await _get('http://www.json-generator.com/api/json/get/cqwVqdOFrC?indent=2');
}
static Future<dynamic> _get(String url) async {
try {
final response = await http.get(url);
var jsonData = json.decode(response.body);
if (response.statusCode == 200) {
print(response.body.length); //3231
return jsonData;
} else {
return null;
}
} catch (e) {
print(e);
}
}
}

Have to try calling,
future: ApiService.getUserList().then((onValue) {
return onValue;
}

Usually you get hasData = false when hasError = true. Try to look for a snapahot error (snapshot.error)

Related

Flutter - How to parse JSON data?

I want to parse my JSON data and display all the nodeValues of the rows and not just [7] (which contains the word hello), however my FutureBuilder doesn't display the JSON data (stuck on CircularProgressIndicator) even though i'm following the correct JSON path.
//Updated code
class Feed {
String title;
Feed({this.title});
factory Feed.fromJson(Map<String, dynamic> json) {
return Feed(
title: json["data"]["tables"][0]["rows"][7]["cols"][1]["nodeValue"]);
}
}
//I am making a post method to an API that returns me a JSON output.
Future<List<Feed>> post() async {
final Response<String> result =
await Dio().get('https://example.com');
String _baseUrl = "https://html2json.com/api/v1";
var options = Options(
contentType: "application/x-www-form-urlencoded; charset=UTF-8",
followRedirects: false,
);
final response = json.decode(result.data);
final responseJson = await Dio().post(
_baseUrl,
data: response,
options: options,
);
if (responseJson.statusCode == 200) {
return (response as List).map((json) => Feed.fromJson(json)).toList();
} else {
return null;
}
}
//This is stuck on CircularProgressIndicator();
FutureBuilder(
future: post(),
builder: (context, AsyncSnapshot<List<Feed>> snap) {
if (snap.hasData) {
return ListView.builder(
itemCount: snap.data.length,
itemBuilder: (BuildContext context, int index) {
return Text(snap.data[index].title);
});
} else {
return CircularProgressIndicator();
}
});
I changed a few things to make your code work with the json place holder. You were using response.statusCode == 200, but response has no status code, the status code is on the var link.
class Feed {
String title;
Feed({this.title});
factory Feed.fromJson(Map<String, dynamic> json) {
return Feed(title: json["title"]);
}
}
Future<List> post() async {
final Response<String> result = await Dio().get('https://jsonplaceholder.typicode.com/todos');
final response = json.decode(result.data);
if (result.statusCode == 200) {
return (response as List)
.map((json) => Feed.fromJson(json))
.toList();
} else {
return null;
}
}
return FutureBuilder(
future: post(),
builder: (context, AsyncSnapshot<List> snap) {
if (snap.hasData) {
return ListView.builder(
itemCount: snap.data.length,
itemBuilder: (BuildContext context, int index) {
return Text(snap.data[index].title);
});
} else {
return CircularProgressIndicator();
}
});

JSON Array to JSON Object

I'm using a HTTP request that gets a JSON array and pushes this into a JSON object which is read by a list view. I'm having difficulty forcing the JSON array into a JSON object so I'm currently calling each object once via json.decode(response.body)[0]. How can I cast the JSON Array to a JSON Object and have the list view read this entire JSON object?
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
Future<Post> fetchPost() async {
final url = <my_url>;
final response =
await http.get(url);
if (response.statusCode == 200) {
// If the call to the server was successful, parse the JSON.
print(json.decode(response.body));
// TODO: Identify a way to convert JSON Array to JSON Object
return Post.fromJson(json.decode(response.body)[0]);
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load post');
}
}
class Post {
final String title;
Post({this.title});
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
title: json['title']
);
}
}
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
MyApp({Key key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Future<Post> post;
#override
void initState() {
super.initState();
post = fetchPost();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Fetch Data Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: Text('Fetch Data Example'),
),
body: Center(
child: FutureBuilder<Post>(
future: post,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data.title);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner.
return CircularProgressIndicator();
},
),
),
),
);
}
}
try this,
Future<List<Post>> fetchPost() async {
final url = <my_url>;
final response =
await http.get(url);
if (response.statusCode == 200) {
// If the call to the server was successful, parse the JSON.
print(json.decode(response.body));
List<dynamic> responseList = json.decode(response.body);
// TODO: Identify a way to convert JSON Array to JSON Object
List<Post> tempList = [];
responseList.forEach((f) {
tempList.add(Post.fromJson(f));
});
return tempList;
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load post');
}
}
class Post {
final int id;
final String title;
Post({this.id, this.title});
factory Post.fromJson(Map<String, dynamic> json) {
return Post(id: json['id'], title: json['title']);
}
}
class _Frag_CommitteeState extends State<Frag_Committee> {
Future<List<Post>> post;
#override
void initState() {
super.initState();
post = fetchPost();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Fetch Data Example'),
),
body: Center(
child: FutureBuilder<List<Post>>(
future: post,
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return Text(snapshot.data[index].title);
});
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner.
return CircularProgressIndicator();
},
),
),
);
}
}

Flutter snapshot.data.length return wrong value

Below is my example snapshot.data.length return 2 but in my example is 15 what is wrong here?
class ApiService {
static Future<dynamic> _get(String url) async {
try {
final response = await http.get(url);
var jsonData = json.decode(response.body);
return jsonData;
}
}
static Future<Map<String, dynamic>> getUserList() async {
return await _get('${Urls.BASE_API_URL}');
}
.........
body: FutureBuilder(
future: ApiService.getUserList(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
final posts = snapshot.data;
print(posts.length); //Return 2
Json URL: http://www.json-generator.com/api/json/get/cqwVqdOFrC?indent=2
No it's 2 not 15
{"items":[],"first":[]}
if you want the items only
final posts = snapshot.data["items"];
print(posts.length);
It will be 15

Getting error type'_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'Iterable<dynamic>' while fetching data from API in flutter

I am new to flutter and I tried fetching data from API but I got the error
type'_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'Iterable<dynamic>'.
I am fetching news data from the API. I tried this for simple API and it worked and when I tried it for a complex API with some changes in the dart code I got this error.
Sorry if I didn't explain correctly. I have pasted all the code that has been used for this API.
I am not getting any solution. I am posting my code here.
post.dart
class Post {
List<Articles> articles;
Post({this.articles});
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
articles: json['articles'].map((value) => new Articles.fromJson(value)).toList(),
);
}
}
article.dart
class Articles{
final String title;
final String description;
final String url;
final String urlToImage;
final String publishedAt;
final String content;
Articles({this.title, this.description, this.url, this.urlToImage, this.publishedAt, this.content});
factory Articles.fromJson(Map<String, dynamic> json) {
return Articles(
title: json['title'],
description: json['description'],
url: json['url'],
urlToImage: json['urlToImage'],
publishedAt: json['publishedAt'],
content: json['content'],
);
}
}
technology_post.dart
Future<List<Post>> fetchPost() async {
final response = await http.get('https://newsapi.org/v2/top-headlines?sources=techcrunch&apiKey=47ada2986be0434699996aaf4902169b');
if (response.statusCode == 200) {
var responseData = json.decode(response.body);
List<Post> posts = [];
for(var item in responseData){
Post news = Post.fromJson(item);
posts.add(news);
}
return posts;
} else {
throw Exception('Failed to load post');
}
}
class Technology extends StatelessWidget{
final Future<List<Post>> post;
Technology({Key key, this.post}) : super (key : key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FutureBuilder<List<Post>>(
future: post,
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemBuilder: (BuildContext context, int index){
var dataStored = "";
for(var i = 0; i < 10; i++){
dataStored = snapshot.data.articles[i].title;
return ListTile(
title: Text(dataStored),
);
}
}
);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return CircularProgressIndicator();
},
),
),
);
}
}
homescreen.dart
TabBarView(
children: [
Technology(post: fetchPost()),
Text('General'),
Text('Cricket')
]
I have posted all the required code I hope. If you want to see the API you can see that here
Sorry if I have pasted much code here.
Why am I getting this error and how can I resolve this.
Thanks in advance.
According to your json, there is no List but only Post as json is json object.
So change your fetchPost() function as follows:
Future<Post> fetchPost() async {
final response = await http.get(
'https://newsapi.org/v2/top-headlines?
sources=techcrunch&apiKey=$YOUR_API_KEY');
if (response.statusCode == 200) {
var responseData = jsonDecode(response.body);
var post = Post.fromJson(responseData);
return post;
} else {
throw Exception('Failed to load post');
}
}
NOTE : Remove your api key from your question and paste json only for privacy.
And change your technology class to
class Technology extends StatelessWidget {
final Future<Post> post;
Technology({Key key, this.post}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FutureBuilder<Post>(
future: post,
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemBuilder: (BuildContext context, int index) {
return Text(snapshot.data.articles[0].publishedAt);
});
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return CircularProgressIndicator();
},
),
),
);
}
}
and your main problem is also that you have not cast json['articles'] to list. you should change Post.fromJson function to
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
articles: (json['articles'] as List).map((value) => new Articles.fromJson(value)).toList(),
);
}
This should solve your problem.
You should check correct response type Int with String. I see your API status: "ok" and sure you check correctly.

Sort JSON in alphabetical order - Flutter

I'd like to be able to return my profileList to my ListView in alphabetical order.
I have my "All people" class which has a ListView widget using the json and creating a list of people.
The code below is from my All People class where I'm fetching the json.
class AllPeople extends StatefulWidget {
final String title;
AllPeople(this.title);
#override
AllPeopleState createState() => AllPeopleState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Listviews"),
),
);
}
class AllPeopleState extends State<AllPeople> {
List data;
List<Profile> profiles;
Future<String> getData() async {
var response = await http.get(
Uri.encodeFull("http://test.mallcomm.co.uk/json_feeds/users.json"),
headers: {"Accept": "application/json"});
fetchPeople().then((List<Profile> p) {
this.setState(() {
data = json.decode(response.body);
profiles = p;
});
});
return "Success!";
}
#override
void initState() {
this.getData();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('CMS Users'),
),
body: ListView.builder(
padding: EdgeInsets.only(top: 20.0, left: 4.0),
itemExtent: 70.0,
itemCount: data == null ? 0 : data.length,
itemBuilder: (BuildContext context, int index) {
return Card(
elevation: 10.0,
child: InkWell(
onTap: () {
Navigator.push(
context,
new MaterialPageRoute(
builder: (BuildContext context) =>
new PeopleDetails("Profile Page", profiles[index]),
));
},
child: ListTile(
leading: CircleAvatar(
child: Text(profiles[index].getInitials()),
backgroundColor: Colors.deepPurple,
radius: 30.0,
),
title: Text(
data[index]["firstname"] + "." + data[index]["lastname"]),
subtitle: Text(
data[index]["email"] + "\n" + data[index]["phonenumber"]),
),
),
);
}),
);
}
}
Future<List<Profile>> fetchPeople() async {
try {
http.Response response =
await http.get('http://test.mallcomm.co.uk/json_feeds/users.json');
List<dynamic> responseJson = json.decode(response.body);
List<Profile> profileList =
responseJson.map((d) => new Profile.fromJson(d)).toList();
profileList.sort((a, b) {
return a.lastName.toLowerCase().compareTo(b.lastName.toLowerCase());
});
return profileList;
} catch (e) {
print(e.toString());
}
return null;
}
I then have my "User profile" class which is storing my json Profile
class Profile {
final String firstName;
final String lastName;
final String phoneNumber;
final String userEmail;
bool verifiedValue = false;
bool approvedValue = false;
bool securityApprovedValue = false;
bool blockedValue = false;
Profile({this.firstName, this.lastName, this.phoneNumber, this.userEmail});
factory Profile.fromJson(Map<String, dynamic> json) {
return new Profile(
firstName: json["firstname"],
lastName: json["lastname"],
phoneNumber: json["phonenumber"],
userEmail: json["email"],
);
}
I've tried to do something like
profileList.sort((a,b) {
return a.lastName.toLowerCase().compareTo(b.lastName.toLowerCase());
});
just before I return profileList but it didn't work. I've tried looking at some different examples but If i'm honest I don't understand it too well.
The sort function you suggested does seem to work as you'd expect (but, of course, only compares last name - you might want to compare first name if the last names are equal). I tidied up a bit, to produce this working example:
import 'dart:convert';
import 'dart:async';
import 'package:http/http.dart' as http;
main() async {
fetchPeople().then((list) {
list.forEach(print);
});
}
Future<List<Profile>> fetchPeople() async {
try {
http.Response response =
await http.get('http://test.mallcomm.co.uk/json_feeds/users.json');
List<dynamic> responseJson = json.decode(response.body);
List<Profile> profileList =
responseJson.map((d) => new Profile.fromJson(d)).toList();
profileList.sort((a, b) {
return a.lastName.toLowerCase().compareTo(b.lastName.toLowerCase());
});
return profileList;
} catch (e) {
print(e.toString());
}
}
class Profile {
final String firstName;
final String lastName;
final String phoneNumber;
final String userEmail;
bool verifiedValue = false;
bool approvedValue = false;
bool securityApprovedValue = false;
bool blockedValue = false;
Profile({this.firstName, this.lastName, this.phoneNumber, this.userEmail});
factory Profile.fromJson(Map<String, dynamic> json) {
return new Profile(
firstName: json["firstname"],
lastName: json["lastname"],
phoneNumber: json["phonenumber"],
userEmail: json["email"],
);
}
#override
String toString() {
return 'Profile: $firstName $lastName';
}
}
Here's a State example that works.
class _MyHomePageState extends State<MyHomePage> {
List<Profile> profiles = [];
#override
void initState() {
super.initState();
_refresh();
}
void _refresh() {
fetchPeople().then((list) {
setState(() {
profiles = list;
});
});
}
Future<List<Profile>> fetchPeople() async {
try {
http.Response response =
await http.get('http://test.mallcomm.co.uk/json_feeds/users.json');
List<dynamic> responseJson = json.decode(response.body);
List<Profile> profileList =
responseJson.map((d) => new Profile.fromJson(d)).toList();
profileList.sort((a, b) {
return a.lastName.toLowerCase().compareTo(b.lastName.toLowerCase());
});
return profileList;
} catch (e) {
print(e.toString());
return [];
}
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new ListView.builder(
itemBuilder: (context, i) => new Text('${profiles[i].firstName} ${profiles[i].lastName}'),
itemCount: profiles.length,
),
);
}
}