How to update a map with map data from API in Flutter - json

I am new to Flutter and finding it difficult to update _users with a map I get from API. I get build errors even though no actual errors are shown in VSCode.
Here is my code.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:http/http.dart' as http;
class Users with ChangeNotifier {
final String id;
final String firstName;
final String lastName;
final String emailAddress;
final String phoneNumber;
final String classId;
final String roleId;
final String dateCreated;
final String uid;
bool isActive;
final String profilePhoto;
Users({
required this.id,
required this.firstName,
required this.lastName,
required this.emailAddress,
required this.phoneNumber,
required this.classId,
required this.roleId,
required this.dateCreated,
required this.uid,
required this.isActive,
required this.profilePhoto,
});
Map<String, Users> _users = {};
Map<String, Users> get users {
return {..._users};
}
Future<void> fetchUser() async {
try {
final String idToken =
await FirebaseAuth.instance.currentUser!.getIdToken();
final reauthUrl = Uri.parse(
'https://example-backend.azurewebsites.net/api/v1/User/reauth');
final reauthResponse = await http.post(reauthUrl,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: json.encode({"token": idToken}));
final returnedTokenData =
json.decode(reauthResponse.body) as Map<String, dynamic>;
final serverToken = returnedTokenData['token'];
final url =
Uri.parse('https://example-backend.azurewebsites.net/api/v1/User');
final response = await http.get(
url,
headers: {'Authorization': 'Bearer $serverToken'},
);
final responseData = json.decode(response.body) as Map<String, dynamic>;
Map<String, dynamic> userData = {};
userData.update(id, (value) {
return Users(
id: responseData['id'],
firstName: responseData['firstName'],
lastName: responseData['lastName'],
emailAddress: responseData['emailAddress'],
phoneNumber: responseData['phoneNumber'],
classId: responseData['classId'],
roleId: responseData['roleId'],
dateCreated: responseData['dateCreated'],
uid: responseData['uid'],
isActive: responseData['isActive'],
profilePhoto: responseData['profilePhoto'],
);
});
print(userData);
notifyListeners();
} catch (error) {
rethrow;
}
}
}

One way to fix the issue is to not use maps and assign the values from the API directly to the variables in your class. The variables should not be final to be able to reassign values to them.
final responseData = json.decode(response.body) as Map<String, dynamic>;
id = responseData['id'];
firstName = responseData['firstName'];
lastName = responseData['lastName'];
emailAddress = responseData['emailAddress'];
phoneNumber = responseData['phoneNumber'];
classId = responseData['classId'];
roleId = responseData['roleId'];
dateCreated = responseData['dateCreated'];
uid = responseData['uid'];
isActive = responseData['isActive'];
profilePhoto = responseData['profilePhoto'];

Related

Trying to parse a JSON file but it returns me _TypeError (type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'String')

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

Flutter Fetching and looping in json data

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.

i want to convert String into Json string in flutter?

I have decoded my response.body i.e var jsonData = jsonDecode(response.body); and its working fine
But when i convert it into object and saved into local storage using sharedpref like this
if (response.statusCode == 200) {
jsonData['categoryList'].forEach((data) => {
categoryList.add(new ExpertCategory(
id: jsonData['_id'],
color: jsonData['color'],
type: jsonData['category_name'],
icon: ":)"))
});
print(categoryList) ;
localStorage.setCategoryData(categoryList.toString());
It stored in it And whenever i try to decode this its not working i.e
localStorage.getCategoryData().then((data) => {
userMap = jsonDecode(data),
});
class LocalStorage {
Future setCategoryData(data) async {
final prefs = await SharedPreferences.getInstance();
prefs.setString('category', data);
}
Future getCategoryData() async {
final prefs = await SharedPreferences.getInstance();
final category = prefs.getString('category');
return category;
}
}
import 'package:flutter/foundation.dart';
class ExpertCategory {
final String id;
final String type;
final String icon;
final String color;
const ExpertCategory( {
#required this.id,
#required this.type,
#required this.icon,
#required this.color,
});
}
its not the same as before,its showing error and after fixing some 1st element of string '['
is showing. please help with this thanks in advance.
Change your ExpertCategory model to this:
import 'package:flutter/material.dart';
class ExpertCategory {
String id;
String type;
String icon;
String color;
ExpertCategory(
{#required this.id,
#required this.type,
#required this.icon,
#required this.color});
ExpertCategory.fromJson(Map<String, dynamic> json) {
id = json['id'];
type = json['type'];
icon = json['icon'];
color = json['color'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['type'] = this.type;
data['icon'] = this.icon;
data['color'] = this.color;
return data;
}
}
For your LocalStorage class, there are two approaches to setting your data into SharedPreferences. One using setString, the other being setStringList since you are storing the list of categories.
Checkout both approaches below.
APPROACH 1
class LocalStorage {
Future setCategoryData(List<ExpertCategory> data) async {
final prefs = await SharedPreferences.getInstance();
prefs.setString(
'category', jsonEncode(data.map((e) => e.toJson()).toList()));
}
Future<List<ExpertCategory>> getCategoryData() async {
final prefs = await SharedPreferences.getInstance();
final category = prefs.getString('category');
return List<ExpertCategory>.from(
List<Map<String, dynamic>>.from(jsonDecode(category))
.map((e) => ExpertCategory.fromJson(e))
.toList());
}
}
APPROACH 2
class LocalStorage {
Future setCategoryData(List<ExpertCategory> data) async {
final prefs = await SharedPreferences.getInstance();
prefs.setStringList('category',
List<String>.from(data.map((e) => jsonEncode(e.toJson())).toList()));
}
Future<List<ExpertCategory>> getCategoryData() async {
final prefs = await SharedPreferences.getInstance();
final category = prefs.getStringList('category');
return List<ExpertCategory>.from(
category.map((e) => ExpertCategory.fromJson(jsonDecode(e))).toList());
}
}
And finally you set your data into the SharedPreferences using
localStorage.setCategoryData(categoryList);

Save and retrieve json object upon first launch flutter

I have an issue guys. Upon my app launch, I need to save and retrieve json object on app launch. So am making a simple get request that returns user details from my response which am saving in as an object with shared preferences. So after saving, I deserialise immediately so i could use anytime i want. The problem am having is that it doesn't work at first launch. Am only able to retrieve the saved object after second launch. I have tried a lot of troubleshooting but no real success.
UserClass
class User {
final String first_name;
final String email;
final String last_name;
final String country;
final String gender;
final String phone;
final String profile_image;
final String created_at;
final String updated_at;
final String category;
final String industry;
final String bio_interest;
final String fav_quote;
final String current_job;
final String state_of_origin;
int id = 0;
User(this.first_name, this.email, this.last_name, this.country, this.gender, this.phone,
this.profile_image, this.created_at, this.updated_at, this.category, this.industry, this.bio_interest,
this.fav_quote, this.current_job, this.state_of_origin, this.id);
Map<String, dynamic> toJson() => {
'first_name': first_name,
'email': email,
'last_name': last_name,
'country': country,
'gender': gender,
'phone': phone,
'profile_image': profile_image,
'created_at': created_at,
'updated_at': updated_at,
'category': category,
'industry': industry,
'bio_interest': bio_interest,
'fav_quote': fav_quote,
'current_job': current_job,
'state_of_origin': state_of_origin,
'id': id,
};
User.fromJson(Map<String, dynamic> json):
first_name = json['first_name'],
email = json['email'],
last_name = json['last_name'],
country = json['country'],
gender = json['gender'],
phone = json['phone'],
profile_image = json['profile_image'],
created_at = json['created_at'],
updated_at = json['updated_at'],
category = json['category'],
industry = json['industry'],
bio_interest = json['bio_interest'],
fav_quote = json['fav_quote'],
current_job = json['current_job'],
state_of_origin = json['state_of_origin'],
id = json['id'];
}
NetworkClass
class Network(){
static Future fetch(var authToken, var endPoint) async {
var uri = host + endPoint;
try {
final response = await http.get(
uri,
headers: {'Accept': 'application/json', 'Content-Type': 'application/json','Authorization': authToken, },
);
final responseJson = json.decode(response.body);
SharedPref sharedPref = SharedPref();
sharedPref.save("user", responseJson);
return responseJson;
} catch (exception) {
print(exception);
if (exception.toString().contains('SocketException')) {
return 'NetworkError';
} else {
return null;
}
}
}
}
MainScreen
String _firstName = "";
String _lastName = "";
String _nationality = "";
String _fav_quote = "";
String _industry = "";
String names = "";
String _profile_image = "";
String _appBarText = "Welcome";
#override
void initState() {
super.initState();
checkLoginState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
}
Future checkLoginState() async {
sharedPreferences = await SharedPreferences.getInstance();
if (sharedPreferences.getString("token") == null) {
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(builder: (BuildContext context) => LoginScreen()),
(Route<dynamic> route) => false);
} else {
Network.fetch("Bearer " + sharedPreferences.getString("token"),
AuthUtils.endPointProfile);
//read value from shared preference
User user = User.fromJson(await sharedPref.read("user"));
//that line only works from second launch
setState(() {
//these variables dont get set on first launch
_appBarText = "Welcome, " + user.first_name;
id = user.id;
_firstName = user.first_name;
_lastName = user.last_name;
_nationality = user.country;
_fav_quote = user.fav_quote;
_industry = user.industry;
_profile_image = user.profile_image;
names = _firstName + " " + _lastName;
});
try {
} catch (Excepetion) {
// do something
}
}
}
}
I am not sure how you are storing data in your SharedPref class, but to achieve the functionality you mentioned you can follow below steps:
After fetching the data from the server you can store your data as string into SharedPreferences as follows
final SharedPreferences _prefs = await SharedPreferences.getInstance();
await _prefs.setString("users", response.body);
// your response.body should be a `String`,
// if it is not `String` or it is a JSON you can convert it by
// json.encode(response.body);
You can retrieve the data from the SharedPreferences anywhere if you want as bellow
final SharePreferences _prefs = await SharedPreferences.getInstance();
String _userString = _prefs.getString("user");
final User _user = User.fromMap(json.decode(_userString));
using the await keyword while fetching the data was what worked for me.
await Network.fetch("Bearer " + sharedPreferences.getString("token"),
AuthUtils.endPointProfile);
}
}

how to convert json string to json object in dart flutter?

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