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);
Related
I'm trying to deserialize a json file and create instances from it but whatever way I use, I end up stucked because of the dynamic type :
type '_Map<String, dynamic>' is not a subtype of type 'Map<String, int>'
Here's my model :
class Race {
final String name;
final Map<String, int> abilitiesUpdater;
const Race({
required this.name,
required this.abilitiesUpdater
});
static fromJson(json) => Race(name: json['name'], abilitiesUpdater: json['abilitiesUpdater']);
}
Here's how I'm trying to deserialize the json file :
class RacesApi {
static Future<List<Race>> getRacesLocally(BuildContext context) async {
final assetBundle = DefaultAssetBundle.of(context);
final String fileContent = await assetBundle.loadString('Assets/COC_Monstres/Races.json');
List<dynamic> parsedListJson = jsonDecode(fileContent);
List<Race> racesList = List<Race>.from(parsedListJson.map<Race>((dynamic i) => Race.fromJson(i)));
return racesList;
}
}
Here's my json file :
[
{
"name": "Vampire",
"abilitiesUpdater": {
"DEX": 2,
"CHA": 2
}
},
{
"name": "Créature du lagon",
"abilitiesUpdater": {
"FOR": 2,
"CON": 2
}
},
...
]
How can I properly cast this json object to fit into my class ?
This works:
class Race {
final String name;
// changed to dynamic
final Map<String, dynamic> abilitiesUpdater;
const Race({required this.name, required this.abilitiesUpdater});
static fromJson(json) =>
Race(name: json['name'], abilitiesUpdater: json['abilitiesUpdater']);
}
Maybe after get the object you can parse that dynamic into int if you need it.
Change your Model Class to this:
class Race {
Race({
required this.name,
required this.abilitiesUpdater,
});
late final String name;
late final AbilitiesUpdater abilitiesUpdater;
Race.fromJson(Map<String, dynamic> json){
name = json['name'];
abilitiesUpdater = AbilitiesUpdater.fromJson(json['abilitiesUpdater']);
}
Map<String, dynamic> toJson() {
final _data = <String, dynamic>{};
_data['name'] = name;
_data['abilitiesUpdater'] = abilitiesUpdater.toJson();
return _data;
}
}
class AbilitiesUpdater {
AbilitiesUpdater({
required this.FOR,
required this.CON,
});
late final int FOR;
late final int CON;
AbilitiesUpdater.fromJson(Map<String, dynamic> json){
FOR = json['FOR'];
CON = json['CON'];
}
Map<String, dynamic> toJson() {
final _data = <String, dynamic>{};
_data['FOR'] = FOR;
_data['CON'] = CON;
return _data;
}
}
you can cast the json['abilitiesUpdater'] as Map<String, int> because internally flutter will set it default as Map<String, dynamic>
Code
class Race {
final String name;
final Map<String, int> abilitiesUpdater;
const Race({
required this.name,
required this.abilitiesUpdater
});
static fromJson(json) => Race(name: json['name'], abilitiesUpdater: json['abilitiesUpdater']) as Map<String,int>;
}
it is working fine with me i tried it here is the link to the code https://dartpad.dev/?id=550918b56987552eb3d631ce8cb9e063.
If you still getting error you can try this
class Race {
final String name;
final Map<String, int> abilitiesUpdater;
Race({
required this.name,
required this.abilitiesUpdater
});
static fromJson(json) => Race(name: json['name'], abilitiesUpdater: (json['abilitiesUpdater']as Map<String,int>)) ;
}
or you can try this
class Race {
final String name;
final Map<String, int> abilitiesUpdater;
const Race({required this.name, required this.abilitiesUpdater});
static fromJson(json) => Race(
name: json['name'],
abilitiesUpdater: json['abilitiesUpdater']
.map((key, value) => MapEntry<String, int>(key, value as int)),
);
}
Edit : To have something a little bit more handy and scalable, I created an extension, and it works fine eventhough I have to cast twice the object...
My model :
// import my extension
class Race {
Race({
required this.name,
required this.abilitiesUpdater,
});
late final String name;
late final Map<String, int> abilitiesUpdater;
// late final AbilitiesUpdater abilitiesUpdater;
Race.fromJson(Map<String, dynamic> json){
name = json['name'];
abilitiesUpdater = (json['abilitiesUpdater'] as Map<String, dynamic>).parseToStringInt();
}
}
My extension :
extension Casting on Map<String, dynamic> {
Map<String, int> parseToStringInt() {
final Map<String, int> map = {};
forEach((key, value) {
int? testInt = int.tryParse(value.toString());
if (testInt != null) {
map[key] = testInt;
} else {
debugPrint("$value can't be parsed to int");
}
});
return map;
}
}
Once again, any help on cleaning this is appreciated !
Original answer :
Thanks to Sanket Patel's answer, I ended up with a few changes that made my code works. However I'm pretty clueless on why I can't directly cast a
Map<String, dynamic>
object into a
Map<String, int>
one.
Any info on this would be appreciated :)
Here's how I changed my model class in the end :
class Race {
Race({
required this.name,
required this.abilitiesUpdater,
});
late final String name;
late final AbilitiesUpdater abilitiesUpdater;
Race.fromJson(Map<String, dynamic> json){
name = json['name'];
abilitiesUpdater = AbilitiesUpdater.fromJson(json['abilitiesUpdater']);
}
}
class AbilitiesUpdater {
final Map<String, int> abilitiesUpdater = {};
AbilitiesUpdater.fromJson(Map<String, dynamic> json){
json.forEach((key, value) {
abilitiesUpdater[key] = int.parse(value.toString());
});
}
}
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'];
I am trying to create a medium clone using https://conduit.productionready.io and when I am calling API https://conduit.productionready.io/api/articles I am storing data in Article model and then printing using the map.toList() method but
I am getting an error: flutter - Error: A value of type 'Articles' can't be assigned to a variable of type 'int'.
lib/screens/myhome_screen.dart:47:49: Error: A value of type 'Articles' can't be assigned to a variable of type 'int'.
- 'Articles' is from 'package:conduit/models/globally_articles.dart' ('lib/models/globally_articles.dart').
Text("${globalArticles.articles[index].title}"),
my_homescreen.dart
import 'dart:convert';
import 'package:conduit/models/globally_articles.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class GlobalFeed extends StatefulWidget {
#override
_GlobalFeedState createState() => _GlobalFeedState();
}
class _GlobalFeedState extends State<GlobalFeed> {
bool isLoading = true;
GlobalArticles globalArticles;
#override
void initState() {
// TODO: implement initState
super.initState();
getGlobalArticles();
setState(() {
isLoading = false;
});
}
getGlobalArticles() async {
var url = 'https://conduit.productionready.io/api/articles';
var response = await http.get(url);
setState(() {
var jsonData = jsonDecode(response.body);
globalArticles = GlobalArticles.fromJson(jsonData);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: globalArticles.articles.map((index) {
return Card(
child: Column(
children: [
Text("${globalArticles.articles[index].title}"),
],
),
);
}).toList()),
),
);
}
}
globalArticles.dart
class GlobalArticles {
List<Articles> articles;
int articlesCount;
GlobalArticles({this.articles, this.articlesCount});
GlobalArticles.fromJson(Map<String, dynamic> json) {
if (json['articles'] != null) {
articles = new List<Articles>();
json['articles'].forEach((v) {
articles.add(new Articles.fromJson(v));
});
}
articlesCount = json['articlesCount'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
if (this.articles != null) {
data['articles'] = this.articles.map((v) => v.toJson()).toList();
}
data['articlesCount'] = this.articlesCount;
return data;
}
}
class Articles {
String title;
String slug;
String body;
String createdAt;
String updatedAt;
List<String> tagList;
String description;
Author author;
bool favorited;
int favoritesCount;
Articles(
{this.title,
this.slug,
this.body,
this.createdAt,
this.updatedAt,
this.tagList,
this.description,
this.author,
this.favorited,
this.favoritesCount});
Articles.fromJson(Map<String, dynamic> json) {
title = json['title'];
slug = json['slug'];
body = json['body'];
createdAt = json['createdAt'];
updatedAt = json['updatedAt'];
tagList = json['tagList'].cast<String>();
description = json['description'];
author =
json['author'] != null ? new Author.fromJson(json['author']) : null;
favorited = json['favorited'];
favoritesCount = json['favoritesCount'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['title'] = this.title;
data['slug'] = this.slug;
data['body'] = this.body;
data['createdAt'] = this.createdAt;
data['updatedAt'] = this.updatedAt;
data['tagList'] = this.tagList;
data['description'] = this.description;
if (this.author != null) {
data['author'] = this.author.toJson();
}
data['favorited'] = this.favorited;
data['favoritesCount'] = this.favoritesCount;
return data;
}
}
class Author {
String username;
String bio;
String image;
bool following;
Author({this.username, this.bio, this.image, this.following});
Author.fromJson(Map<String, dynamic> json) {
username = json['username'];
bio = json['bio'];
image = json['image'];
following = json['following'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['username'] = this.username;
data['bio'] = this.bio;
data['image'] = this.image;
data['following'] = this.following;
return data;
}
}
Sorry, My bad...
I replaced
Text("${globalArticles.articles[index].title}"),
with
Text("${index.title}"),
and it's working fine
im trying to store my List data in a disk storage using shared preferences, however idk how to convert the json back to DateTime format when trying to fetch data again from memory when i rebuild/restart the app.
here's my code:
String id;
String title;
double amount;
DateTime date;
Transaction({
#required this.id,
#required this.title,
#required this.amount,
#required this.date,
});
Transaction.fromMap(Map map)
: this.id = map['id'],
this.title = map['title'],
this.date = map['date'],
this.amount = map['amount'];
Map toMap() {
return {
'id': this.id,
'title': this.title,
'date': this.date.toIso8601String(),
'amount': this.amount,
};
}
}
heres where im using sharedPreferences
#override
void initState() {
initSharedPreferences();
super.initState();
}
initSharedPreferences() async {
sharedPreferences = await SharedPreferences.getInstance();
loadData();
}
void saveData() {
setState(() {
List<String> spList =
transactions.map((item) => json.encode(item.toMap())).toList();
var list = sharedPreferences.setStringList('list', spList);
print(list);
});
}
void loadData() {
List<String> spList = sharedPreferences.getStringList('list');
transactions =
spList.map((item) => Transaction.fromMap(json.decode(item))).toList();
setState(() {});
print(transactions);
}
In Transaction.fromMap constructor
replace
this.date = map['date'],
with
this.date = DateTime.parse(map['date']),
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;
}
}