Flutter - How to parse JSON data? - json

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();
}
});

Related

Unable to display the parsed JSON in Future Builder in flutter

I was trying to fetch data results from a REST API and then display it in the UI.
So everything went well the JSON was parsed well the try and catch method was working fine.
But somehow the code was not able to display the parsed results in the UI.
Neither it gave me an error or exception.
I have been struggling to attain the desired result for quite the past few days.
This is how the JSON looks like:
{
"data-description": "This api will return an array of objects to be placed in the order status timeline on the second screen",
"order-status": "Success",
"status-objects": [
{
"type": "Payment",
"status": "completed",
"date": "2021-07-02T00:00:00",
"time": "12:00AM"
},
{
"type": "Units Allocated",
"status": "by Axis",
"date": "2021-07-13T00:00:00",
"time": "12:00AM"
}
]
}
What type of UI I want to achieve after doing this.
In order to make the problem more clearly, I will be attaching my code snippets.
Model Class
Transaction transactionFromJson(String str) =>
Transaction.fromJson(json.decode(str));
String transactionToJson(Transaction data) => json.encode(data.toJson());
class Transaction {
Transaction({
required this.dataDescription,
required this.orderStatus,
required this.statusObjects,
});
String dataDescription;
String orderStatus;
List<StatusObject> statusObjects;
factory Transaction.fromJson(Map<String, dynamic> json) => Transaction(
dataDescription: json["data-description"],
orderStatus: json["order-status"],
statusObjects: List<StatusObject>.from(
json["status-objects"].map((x) => StatusObject.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"data-description": dataDescription,
"order-status": orderStatus,
"status-objects":
List<dynamic>.from(statusObjects.map((x) => x.toJson())),
};
}
class StatusObject {
StatusObject({
required this.type,
required this.status,
required this.date,
required this.time,
});
String type;
String status;
DateTime date;
String time;
factory StatusObject.fromJson(Map<String, dynamic> json) => StatusObject(
type: json["type"],
status: json["status"],
date: DateTime.parse(json["date"]),
time: json["time"],
);
Map<String, dynamic> toJson() => {
"type": type,
"status": status,
"date": date.toIso8601String(),
"time": time,
};
}
API_Manager where the parsing and fetching took place
Service Class
class API_Manager {
static Future<Transaction> getDetails() async {
var client = http.Client();
var transactions;
try {
var response = await client.get(
Uri.https("https://hereistheurl", "/accounts/test-data/"));
if (response.statusCode == 200) {
var jsonString = response.body;
var jsonMap = jsonDecode(jsonString);
transactions = Transaction.fromJson(jsonMap);
}
} catch (e) {
return transactions;
}
return transactions;
}
}
The UI component where I wanted to display the parsed JSON:
Code
FutureBuilder<Transaction>(
future: API_Manager.getDetails(),
builder: (context, snapshot) {
if (snapshot.hasData) {
var data = snapshot.data!.statusObjects;
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) =>
Text('$index : ${data[index].status}'),
);
}
return Text('Something was wrong!');
},
),
I am quite sure that I have been missing a very small piece of code to make it work.
I have been working on this piece of code for quite a few days but am unable to do it.
I request you, people, to please help me out in attaining the result or point out the piece of code that I have left out.
Will appreciate it if you could help me in any possible way.
Bro, your are using wrongly *builder widgets, I've made some corrections at your code:
Service class
typedef JMap = Map<String, dynamic>;
class API_Manager {
static Future<Transaction> getData() {
return Future.delayed(Duration(seconds: 1), () => raw_response)
.then((response) {
return Transaction.fromJson(jsonDecode(response) as JMap);
});
}
}
const raw_response = '''
{
"data-description": "This api will return an array of objects to be placed in the order status timeline on the second screen",
"order-status": "Success",
"status-objects": [
...
]
}
''';
Presentation
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Hi!'),
centerTitle: true,
),
body: Container(
child: FutureBuilder<Transaction>(
future: API_Manager.getData(),
builder: (context, snapshot) {
if (snapshot.hasError)
return Text(snapshot.error.toString());
if (snapshot.connectionState == ConnectionState.waiting)
return Center(child: CircularProgressIndicator());
if (snapshot.connectionState == ConnectionState.done && snapshot.hasData) {
final data = snapshot.data!.statusObjects;
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) => Text('$index : ${data[index].status}'),
);
}
return Text('Something was wrong!');
},
),
),
);
}
}
Result:
UPDATED(1)
static Future<Transaction?> getDetails(String url) {
return http.get(Uri.parse(url))
.then((response) {
if (response.statusCode == 200)
return Transaction.fromJson(jsonDecode(response.body) as JMap);
return null;
})
.catchError((err) { print(err); });
}

snapshot.hasData is true but the snapshot.data.length is null, Flutter

I'm calling an API from a component that returns some data, but the problem is snapshot.hasData is true but the snapshot.data.length is 0. Am I parsing the data wrong(again)? The code:
API call service component:
Future<List<User>> getUsers() async {
final response = await APIRequest(Method.GET, '/users');
if (response.statusCode == 200) {
var body = jsonDecode(response.body)['data'];
print('this is the response body: ' + response.body); // it returns data completely
List<User> users = [];
body.map((e) {
User user = User.fromJson(e);
users.add(user);
});
return users;
} else {
print('Error occurred! Data is not fetched!');
}
}
The user list component:
Future<List<User>> _getUserList() async {
var _userData = await APIcalls.instance.getUsers();
return _userData;
}
FutureBuilder<List<User>>(
future: _getUserList(),
builder: (context, snapshot) {
if (snapshot.hasData) {
print(snapshot.hasData.toString()); // returns true
print(snapshot.data.length); // returns 0
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
print(snapshot.data.length);
return Text(snapshot.data[index].userId);
},
);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return Container();
},
)
Try this:
List<User> users = List.from(body).map((e) => User.fromJson(Map.from(e))).toList();
return users;

Flutte - JSON deserialized data fetched to FutureBuilder return null value

I'm trying to return ListView of photos via FutureBuilder fetched with data from API. While values are added to List in my service and assigned properly, values fetched to List Builder are null. I don't know why calling service method to return Future<List> returns list of null.
My model:
class Photo {
String id;
String photoDesc;
String photoAuthor;
String photoUrlSmall;
String photoUrlFull;
String downloadUrl;
int likes;
Photo(
{this.id,
this.photoDesc,
this.photoAuthor,
this.photoUrlSmall,
this.photoUrlFull,
this.downloadUrl,
this.likes});
Photo.fromJson(Map<String, dynamic> json) {
id = json['id'];
photoDesc = json['description'] != null
? json['description']
: json['alt_description'];
photoAuthor = json['user']['name'] != null
? json['user']['name']
: json['user']['username'];
photoUrlSmall = json['urls']['small'];
photoUrlFull = json['urls']['full'];
downloadUrl = json['links']['download'];
likes = json['likes'];
}
}
My service:
Future<List<Photo>> getRandomData1() async {
List<Photo> randomPhotos;
_response = await http.get(Uri.parse(
'$unsplashUrl/photos/random/?client_id=${_key.accessKey}&count=30'));
if (_response.statusCode == 200) {
return randomPhotos = (json.decode(_response.body) as List).map((i) {
Photo.fromJson(i);
print(Photo.fromJson(i).id); // **i.e. printing returns proper values**
}).toList();
} else {
print(_response.statusCode);
throw 'Problem with the get request';
}
}
My builder:
class RandomPhotosListView extends StatefulWidget {
#override
_RandomPhotosListViewState createState() => _RandomPhotosListViewState();
}
class _RandomPhotosListViewState extends State<RandomPhotosListView> {
final UnsplashApiClient unsplashApiClient = UnsplashApiClient();
Future _data;
ListView _randomPhotosListView(data) {
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
return Text("${data[index]}");
});
}
#override
void initState() {
super.initState();
_data = unsplashApiClient.getRandomData1();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _data,
builder: (context, snapshot) {
print(snapshot.data); // i.e. snapshot.data here is list of nulls
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
print(snapshot.error);
return Text("error: ${snapshot.error}");
} else if (snapshot.hasData) {
return _randomPhotosListView(snapshot.data);
}
return Center(child: CircularProgressIndicator());
},
);
}
}
The reason you are having this issue is because you need to return the parsed object in your mapping function.
Solution #1 - add the return keyword to your existing code
return randomPhotos = (json.decode(_response.body) as List).map((i) {
return Photo.fromJson(i); // <-- added return keyword here
}).toList();
Solution #2 - Use Arrow to define the return
This solution suits your code since you aren't manipulating the Photo object or any other data within the mapping.
return randomPhotos = (json.decode(_response.body) as List).map((i) => Photo.fromJson(i)).toList();
Either solution should work. Choose what suits your coding style and your needs.

Flutter snapshot data is empty

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)

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.