since im newbie in flutter i follow a tutorial to build a news app, so i made the class of fetching data and all its fine, when i'm executing the app i have the error in the getting data method ! can some one explain what'is wrong in this code !!
the error code :
[ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'Iterable<dynamic>'
the Class code :
class FetchDataClass {
late String author;
late String title;
late String description;
late String url;
late String urlToImage;
late String publishedAt;
late String content;
FetchDataClass(this.author, this.title, this.description, this.url, this.urlToImage, this.publishedAt, this.content );
FetchDataClass.fromJson(Map<String, dynamic> jsonData) {
author = jsonData['author'];
title = jsonData['title'];
description = jsonData['description'];
url = jsonData['url'];
urlToImage = jsonData['urlToImage'];
publishedAt = jsonData['publishedAt'];
content = jsonData['content'];
}
}
Fetshing data :
List<FetchDataClass> listofdata = List<FetchDataClass>.empty();
Future<List<FetchDataClass>> loadNews() async {
var response = await http.get(Uri.parse('https://newsapi.org/v2/everything?q=coronavirus&from=2021-09-10&sortBy=publishedAt&apiKey='));
List<FetchDataClass> news = List<FetchDataClass>.empty();
if(response.statusCode == 200) {
dynamic notesJson = json.decode(response.body);
for(dynamic noteJson in notesJson) { /// here the issue
print(11111);
news.add(FetchDataClass.fromJson(noteJson));
}
}
return news;
}
#override
void initState() {
loadNews().then((value) {setState(() {
listofdata.addAll(value);
});});
super.initState();
}
If your API have all data match with model you can try this
class FetchDataClass {
String author;
String title;
String description;
String url;
String urlToImage;
String publishedAt;
String content;
FetchDataClass(
{required this.author,
required this.title,
required this.description,
required this.url,
required this.urlToImage,
required this.publishedAt,
required this.content});
factory FetchDataClass.fromJson(Map<String, dynamic> jsonData) {
return FetchDataClass(
author: jsonData['author'],
title: jsonData['title'],
description: jsonData['description'],
url: jsonData['url'],
urlToImage: jsonData['urlToImage'],
publishedAt: jsonData['publishedAt'],
content: jsonData['content'],
);
}
}
and Fetch data service
List<FetchDataClass> listofdata = List<FetchDataClass>.empty();
Future<List<FetchDataClass>> loadNews() async {
var response = await http.get(Uri.parse('https://newsapi.org/v2/everything?q=coronavirus&from=2021-09-10&sortBy=publishedAt&apiKey='));
List<FetchDataClass> news = List<FetchDataClass>.empty();
if(response.statusCode == 200) {
final notesJson = json.decode(response.body);
///final news = List<FetchDataClass>.from(
/// notesJson.map((model) => FetchDataClass.fromJson(model)));
final news = FetchDataClass.fromJson(model);
}
return news;
}
Cause when you json in model type is Map<String,dynamic> but in fetching data you set dynamic notesJson it's wrong type for this. I fixed for you model and service call api, try and if some issue you can text for me to help.
Related
I am building a data model for soccer matches and leagues that will (hopefully) allow me to show expandable lists of soccer leagues containing future matches.
My structure works great for soccer matches, where I am able to show a list of soccer matches already. My issue occurs when I try to incorporate a League class. The league data comes from a different endpoint than the soccer match data.
My idea was to have a List<SoccerMatch> inside League so that a future expandable list can take all data from the League-class (List is not currently incorporated). What would be the smartest way to do this? Below is my data model for soccer data (and my API calls below that).
class League {
int id;
String name;
String country;
String logoUrl;
String flag;
String season;
// ====== Can I add a List<SoccerMatch> here? ====
League(
this.id, this.name, this.country, this.logoUrl, this.flag, this.season);
factory League.fromJson(Map<String, dynamic> json) {
return League(
json['league']['id'],
json['league']['name'],
json['country']['name'],
json['league']['logo'],
json['country']['flag'],
json['seasons'][0]['year'].toString(),
);
}
}
class SoccerMatch {
Fixture fixture;
Team home;
Team away;
Goal goal;
SoccerMatch(this.fixture, this.home, this.away, this.goal);
factory SoccerMatch.fromJson(Map<String, dynamic> json) {
return SoccerMatch(
Fixture.fromJson(json['fixture']),
Team.fromJson(json['teams']['home']),
Team.fromJson(json['teams']['away']),
Goal.fromJson(json['goals']));
}
}
class Fixture {
int id;
String date;
Status status;
Fixture(this.id, this.date, this.status);
factory Fixture.fromJson(Map<String, dynamic> json) {
return Fixture(json['id'], json['date'], Status.fromJson(json['status']));
}
}
class Status {
String elapsedTime;
String long;
String short;
Status(this.elapsedTime, this.long, this.short);
factory Status.fromJson(Map<String, dynamic> json) {
return Status(json['elapsed'].toString(), json['long'], json['short']);
}
}
class Team {
int id;
String name;
String logoUrl;
bool? winner;
Team(this.id, this.name, this.logoUrl, this.winner);
factory Team.fromJson(Map<String, dynamic> json) {
return Team(json['id'], json['name'], json['logo'], json['winner']);
}
}
class Goal {
String home;
String away;
Goal(this.home, this.away);
factory Goal.fromJson(Map<String, dynamic> json) {
return Goal(json['home'].toString(), json['away'].toString());
}
}
This is my API management class. The getLeague() method currently returns a list of one single League instance which is not best practice. Though I couldn't get it to just return the league instance - partly because I don't fully understand the handling of the response body, I guess, but also because I am confused by how to handle it with two different endpoints.
import 'dart:convert';
import 'package:http/http.dart';
import 'package:spark/soccer_model.dart';
import 'auth/secrets.dart';
class SoccerApi {
static String apiKey = soccerApiKey;
final String apiUrl = "https://api-football-v1.p.rapidapi.com/v3/";
Map<String, String> headers = {
"X-RapidAPI-Key": apiKey,
"X-RapidAPI-Host": "api-football-v1.p.rapidapi.com"
};
Future<List<SoccerMatch>> getAllMatches(id) async {
Response response = await get(
Uri.parse("${apiUrl}fixtures?season=2022&league=$id"),
headers: headers);
if (response.statusCode == 200) {
var body = jsonDecode(response.body);
List<dynamic> matchesList = body['response'];
//print("API Service: $body");
List<SoccerMatch> matches = matchesList
.map((dynamic item) => SoccerMatch.fromJson(item))
.toList();
return matches;
} else {
throw (Exception('Error accessing API'));
}
}
Future<List<League>> getLeague(leagueName, country) async {
Response response = await get(
Uri.parse(
"${apiUrl}leagues?current=true&name=$leagueName&country=$country"),
headers: headers);
if (response.statusCode == 200) {
var body = jsonDecode(response.body);
List<dynamic> leagueList = body['response'];
List<League> leagues =
leagueList.map((dynamic item) => League.fromJson(item)).toList();
print(leagues);
return leagues;
} else {
throw (Exception('Error accessing API'));
}
}
}
I have create a class model to receive json data from API
class Competition {
int id;
String name;
String code;
String type;
String emblem;
Competition({
required this.id,
required this.name,
required this.code,
required this.type,
required this.emblem,
});
factory Competition.fromJson(Map<String, dynamic> data) {
return Competition(
id: data["matches"][0]["competition"]['id'],
name: data["matches"][0]["competition"]['name'],
code: data["matches"][0]["competition"]['code'],
type: data["matches"][0]["competition"]['type'],
emblem: data["matches"][0]["competition"]['emblem'],
);
}
}
the code work properly, every time index [0] change the coming data change for the next competition details
i want to make this index change automatically for fetching all data at one time, this index represent list data["matches"].length
My Api service look like
class ServiceApi {
Future<Competition > getmatches() async {
http.Response response = await http.get(
Uri.parse('_url'),
headers: {'X-Auth-Token': '_key'});
String body = response.body;
Map<String, dynamic> data = jsonDecode(body);
Competition compitition = Competition.fromJson(data);
int numberOfCompetition = data["matches"].length;
return matches;
}
}
You can map it like this
List<Competition> compititionList = data['matches'].map((item) => Competition.fromJson(item));
print(compittionList[0].name);
Btw theres a typo(spelling mistake) in competition (just incase you want to fix it)
And thr model should be
class Competition {
int id;
String name;
String code;
String type;
String emblem;
Competition({
required this.id,
required this.name,
required this.code,
required this.type,
required this.emblem,
});
factory Competition.fromJson(Map<String, dynamic> data) {
return Competition(
id: data["competition"]['id'],
name: data["competition"]['name'],
code: data["competition"]['code'],
type: data["competition"]['type'],
emblem: data["competition"]['emblem'],
);
}
}
I'm trying to parse a remote json but I always get this error _TypeError (type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'String'), I tried to simplify as much as possible the examples because my model is a bit complex and the JSON has more than 5000 words.
Here's my function:
Future<void> updateCrypto(String symbol) async {
Uri url = Uri.https(); // url where I get the json
try {
final response = await http.get(url);
final parsedJson = json.decode(response.body) as Map<String, dynamic>;
final Cryptocurrency updatedCrypto = Cryptocurrency.fromJson(parsedJson);
} catch (error) {
throw (error);
}
}
My model:
class Cryptocurrency with ChangeNotifier {
Cryptocurrency({
required this.id,
required this.symbol,
required this.name,
...
});
late final String id;
late final String symbol;
late final String name;
...
factory Cryptocurrency.fromJson(Map<String, dynamic> json) {
return Cryptocurrency(
id: json['id'],
symbol: json['symbol'],
name: json['name'],
...
}
}
Json example (cut because it's a 5000 words json file):
{"id":"bitcoin","symbol":"btc","name":"Bitcoin", }
I like to modify the entity and use case like
import 'dart:convert';
class Cryptocurrency with ChangeNotifier {
final String id;
final String symbol;
final String name;
Cryptocurrency({
required this.id,
required this.symbol,
required this.name,
});
Map<String, dynamic> toMap() {
final result = <String, dynamic>{};
result.addAll({'id': id});
result.addAll({'symbol': symbol});
result.addAll({'name': name});
return result;
}
factory Cryptocurrency.fromMap(Map<String, dynamic> map) {
return Cryptocurrency(
id: map['id'] ?? '',
symbol: map['symbol'] ?? '',
name: map['name'] ?? '',
);
}
String toJson() => json.encode(toMap());
factory Cryptocurrency.fromJson(String source) =>
Cryptocurrency.fromMap(json.decode(source));
}
And use case
final response = await http.get(Uri.parse(url));
final parsedJson = json.decode(response.body);
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
final Cryptocurrency updatedCrypto = Cryptocurrency.fromJson(data);
I have string like this,
{id:1, name: lorem ipsum, address: dolor set amet}
And I need to convert that string to json, how I can do it in dart flutter? thank you so much for your help.
You have to use json.decode. It takes in a json object and let you handle the nested key value pairs. I'll write you an example
import 'dart:convert';
// actual data sent is {success: true, data:{token:'token'}}
final response = await client.post(url, body: reqBody);
// Notice how you have to call body from the response if you are using http to retrieve json
final body = json.decode(response.body);
// This is how you get success value out of the actual json
if (body['success']) {
//Token is nested inside data field so it goes one deeper.
final String token = body['data']['token'];
return {"success": true, "token": token};
}
Create a model class
class User {
int? id;
String? name;
String? address;
User({this.id, this.name, this.address});
User.fromJson(Map<String, dynamic> json) {
id = json['id'];
name = json['name'];
address = json['address'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['name'] = this.name;
data['address'] = this.address;
return data;
}
}
In the logic section
String data ='{id:1, name: lorem ipsum, address: dolor set amet}';
var encodedString = jsonEncode(data);
Map<String, dynamic> valueMap = json.decode(encodedString);
User user = User.fromJson(valueMap);
Also need to import
import 'dart:convert';
You can also convert JSON array to list of Objects as following:
String jsonStr = yourMethodThatReturnsJsonText();
Map<String,dynamic> d = json.decode(jsonStr.trim());
List<MyModel> list = List<MyModel>.from(d['jsonArrayName'].map((x) => MyModel.fromJson(x)));
And MyModel is something like this:
class MyModel{
String name;
int age;
MyModel({this.name,this.age});
MyModel.fromJson(Map<String, dynamic> json) {
name= json['name'];
age= json['age'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['name'] = this.name;
data['age'] = this.age;
return data;
}
}
String name = "{click_action: FLUTTER_NOTIFICATION_CLICK, sendByImage: https://ujjwalchef.staging-server.in/uploads/users/1636620532.png, status: done, sendByName: mohittttt, id: HM11}";
List<String> str = name.replaceAll("{","").replaceAll("}","").split(",");
Map<String,dynamic> result = {};
for(int i=0;i<str.length;i++){
List<String> s = str[i].split(":");
result.putIfAbsent(s[0].trim(), () => s[1].trim());
}
print(result);
}
You must need to use this sometimes
Map<String, dynamic> toJson() {
return {
jsonEncode("phone"): jsonEncode(numberPhone),
jsonEncode("country"): jsonEncode(country),
};
}
This code give you a like string {"numberPhone":"+225657869", "country":"CI"}. So it's easy to decode it's after like that
json.decode({"numberPhone":"+22565786589", "country":"CI"})
You must import dart:encode libary. Then use the jsonDecode function, that will produce a dynamic similar to a Map
https://api.dartlang.org/stable/2.2.0/dart-convert/dart-convert-library.html
For converting string to JSON we have to modify it with custom logic, in here first we remove all symbols of array and object and then we split text with special characters and append with key and value(for map).
Please try this code snippet in dartpad.dev
import 'dart:developer';
void main() {
String stringJson = '[{product_id: 1, quantity: 1, price: 16.5}]';
stringJson = removeJsonAndArray(stringJson);
var dataSp = stringJson.split(',');
Map<String, String> mapData = {};
for (var element in dataSp) {
mapData[element.split(':')[0].trim()] = element.split(':')[1].trim();
}
print("jsonInModel: ${DemoModel.fromJson(mapData).toJson()}");
}
String removeJsonAndArray(String text) {
if (text.startsWith('[') || text.startsWith('{')) {
text = text.substring(1, text.length - 1);
if (text.startsWith('[') || text.startsWith('{')) {
text = removeJsonAndArray(text);
}
}
return text;
}
class DemoModel {
String? productId;
String? quantity;
String? price;
DemoModel({this.productId, this.quantity, this.price});
DemoModel.fromJson(Map<String, dynamic> json) {
log('json: ${json['product_id']}');
productId = json['product_id'];
quantity = json['quantity'];
price = json['price'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['product_id'] = productId;
data['quantity'] = quantity;
data['price'] = price;
return data;
}
}
Hello I am learning flutter and am getting a json from an api but when decoding it in my model I am getting the error listed below. I know it has to do with my pagination as the location result is fine, but cant figure out what I need to do to fix the pagination result. I tried a bunch of tutorials but am having no luck. Can anyone point me in the right direction in order to fix this issue? Thanks in advance
Error
Exception occured: type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'List<dynamic>' in type cast*
location.dart
class Locations {
final String id;
final String name;
final String street;
final String city;
final String state;
Locations(
{
this.id,
this.name,
this.street,
this.city,
this.state,
}
);
Locations.fromJSON(Map<String, dynamic> json)
: id = json['id'],
name = json['name'],
street = json['street'],
city = json['city'],
state = json['state'];
}
class Pagination {
final int pages;
final int totalResults;
Pagination(this.pages, this.totalResults);
Pagination.fromJSON(Map<String, dynamic> json)
: pages = json['pages'],
totalResults = json['count'];
}
class LocationsResponse {
final List<Locations> results;
final List<Pagination> pagination;
final String error;
LocationsResponse(this.results, this.pagination, this.error);
LocationsResponse.fromJSON(Map<String, dynamic> json)
: results = (json["results"] as List).map((i) => new Locations.fromJSON(i)).toList(),
pagination = (json['pagination'] as List).map((i) => new Pagination.fromJSON(i)).toList(),
error = "";
LocationsResponse.withError(String errorValue)
: results = List(),
pagination = List(),
error = errorValue;
}
Example Return
{
results:[
{
id:1,
name:House,
street:1234 This Street,
city:Example City,
state:Ca
}
],
pagination:{
count:1,
pages:0
}
}
pagination is JSONObject not JSONArray.
change code like this:
LocationsResponse.fromJSON(Map<String, dynamic> json)
: results = (json["results"] as List).map((i) => new Locations.fromJSON(i)).toList(),
pagination = Pagination.fromJSON(json['pagination']),
error = "";