flutter model for dynamic json array - flutter

so I have a json file(mock data). It goes something like this:
{
"data":
[
{
"id": "1",
"name": "Kacchi Biriyani",
"videoLink": "https://www.youtube.com/watch?v=K4TOrB7at0Y",
"author": "Alan Ford",
"category":"Biriyani",
"time": "15 min",
"steps": {
"step 1": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
"step 2": "Suspendisse vel sapien elit"
},
"isFavorite": "yes"
},
{
"id": "2",
"name": "Mughal Biriyani",
"videoLink": "https://www.youtube.com/watch?v=aNVviTECNM0",
"author": "Ricky James",
"category":"Biriyani",
"time": "10 min",
"steps": {
"step 1": "Lorem ipsum dolor sit amet, consectetur adipiscing elit",
"step 2": "Suspendisse vel sapien elit",
"step 3": "Proin luctus, quam non dapibus pretium",
"step 4": "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
},
"isFavorite": "yes"
}
]
}
This is what I have as my model:
class RecipeModel {
final String id;
final String name;
final String videoLink;
final String author;
final String category;
final String time;
RecipeModel({
required this.id,
required this.name,
required this.videoLink,
required this.author,
required this.category,
required this.time,
});
factory RecipeModel.fromJson(Map<String, dynamic> json) {
return RecipeModel(
id: json['id'],
name: json['name'],
videoLink: json['videoLink'],
author: json['author'],
category: json['category'],
time: json['time'],
);
}
}
Now as you can see the steps:{...} are dynamic so it can be different for different items. One item can have 5 steps on the other hand another item can have more than 10 steps.
How can I write a dynamic List in my model for steps data that's coming from json?
Update 1:
List<RecipeModel> recipes = [];
Future<List<RecipeModel>> getRecipeData() async {
// var response = await http.get(
// Uri.https("jsonplaceholder.typicode.com", 'users'),
// );
String response = await DefaultAssetBundle.of(context)
.loadString('assets/json/recipe.json');
var result = json.decode(response);
for (var u in result["data"]) {
RecipeModel recipe = RecipeModel(
id: u['id'] ?? "",
name: u['name'] ?? "",
videoLink: u['videoLink'] ?? "",
author: u['author'] ?? "",
category: u['category'] ?? "",
time: u['time'] ?? "",
// steps: u["steps"],
);
recipes.add(recipe);
}

Cast the map of steps into a List<String> type using the from method.
// The steps from your api call.
var json = { "steps": { "step 1": "foo", "step 2": "bar" } }
// Convert the steps from the map to a List.
List<String> steps = List<String>.from(json["steps"].values);
// The result.
steps = ['foo', 'bar']
Update
This is what your RecipeModel should look like.
class RecipeModel {
final String id;
final String name;
final String videoLink;
final String author;
final String category;
final String time;
// The dynamic list of steps.
final List<String> steps;
RecipeModel({
required this.id,
required this.name,
required this.videoLink,
required this.author,
required this.category,
required this.time,
// The dynamic list of steps.
required this.steps,
});
factory RecipeModel.fromJson(Map<String, dynamic> json) {
return RecipeModel(
id: json['id'],
name: json['name'],
videoLink: json['videoLink'],
author: json['author'],
category: json['category'],
time: json['time'],
// The dynamic list of steps.
steps: List<String>.from(json['steps'].values),
);
}

Also just as a suggestion, make your variables match the data type it will be holding. For example, id can be and int instead of String, isFavorite can be a bool, etc. Trust me, it makes using them in dart code much easier.
Ex: if(isFavorite) {...} instead of if(isFavorite.toLowerCase() == "yes") {...}

Related

Flutter - Dart parsing json data array returns type 'List<dynamic>' is not a subtype of type 'List<BusinessTest>'

I'm trying to parse a json file using a custom model, I always get the error type 'List<dynamic>' is not a subtype of type 'List<BusinessTest>' and I don't know how I can fix my code. Also is it a good idea to always use nullable type in variables when you parse json files?
This is a Json example of my data:
{
"businesses": [{
"id": "1",
"alias": "123",
"name": "aaa",
"image_url": "xxx.jpg",
"is_closed": false,
"url": ".com",
"review_count": 26,
"rating": 5.0
},
{
"id": "2",
"alias": "123",
"name": "aaa",
"image_url": "xxx.jpg",
"is_closed": false,
"url": ".com",
"review_count": 26,
"rating": 5.0
}
]
}
Here is the model code I've made in order to parse the Json:
class BusinessSearch {
final List<BusinessTest> businesses;
final int total;
BusinessSearch(this.businesses, this.total);
BusinessSearch.fromJson(Map<String, dynamic> json)
: businesses = json['businesses'],
total = json['total'];
}
class BusinessTest {
final String? name;
final String? imageUrl;
final bool? isClosed;
final String? url;
final int? reviewCount;
BusinessTest(
this.name, this.imageUrl, this.isClosed, this.url, this.reviewCount);
BusinessTest.fromJson(Map<String, dynamic> json)
: name = json['name'],
imageUrl = json['image_url'],
isClosed = json['is_closed'],
url = json['url'],
reviewCount = json['review_count'];
}
This is how I'm trying to parse it:
void getData() async {
try {
String url = 'url';
NetworkHelp network = NetworkHelp(url: url);
var data = await network.getData();
Map<String, dynamic> businessMap = await jsonDecode(data);
var business = BusinessSearch.fromJson(businessMap);
} catch (e) {
print(e);
}
}
You have to update your BusinessSearch model like this.
class BusinessSearch {
BusinessSearch({
this.businesses,
this.total,
});
List<Business> businesses = [];
int total;
factory BusinessSearch.fromJson(Map<String, dynamic> json) => BusinessSearch(
businesses: List<Business>.from(json["businesses"].map((x) => Business.fromJson(x))),
total: json['total']
);
Map<String, dynamic> toJson() => {
"businesses": List<dynamic>.from(businesses.map((x) => x.toJson())),
"total": total,
};
}
class Business {
Business({
this.id,
this.alias,
this.name,
this.imageUrl,
this.isClosed,
this.url,
this.reviewCount,
this.rating,
});
String id;
String alias;
String name;
String imageUrl;
bool isClosed;
String url;
int reviewCount;
int rating;
factory Business.fromJson(Map<String, dynamic> json) => Business(
id: json["id"],
alias: json["alias"],
name: json["name"],
imageUrl: json["image_url"],
isClosed: json["is_closed"],
url: json["url"],
reviewCount: json["review_count"],
rating: json["rating"],
);
Map<String, dynamic> toJson() => {
"id": id,
"alias": alias,
"name": name,
"image_url": imageUrl,
"is_closed": isClosed,
"url": url,
"review_count": reviewCount,
"rating": rating,
};
}

How do I display user's profile in flutter?

I am fetching one row from a database and sending it to flutter where I decode it to receive the following response using var userProfile = json.decode(response.body);
[{id: 1, first_name: First, last_name: Last, name: david, email: david4001#gmail.com, phone_number: 12, user_image: null, email_verification_code: null, email_verification_time: null, created_at: 2022-03-24T17:37:17.000000Z, updated_at: 2022-03-29T07:16:25.000000Z}]
I have a UserProfile class
class UserProfile {
final int id;
final String firstName;
final String lastName;
final String email;
final String phoneNumber;
UserProfile({
required this.id,
required this.firstName,
required this.lastName,
required this.email,
required this.phoneNumber,
});
factory UserProfile.fromJson(Map<String, dynamic> json) {
return UserProfile(
id: json["id"],
firstName: json["first_name"],
lastName: json["first_name"],
email: json["email"],
phoneNumber: json["phone_number"],
);
}
}
I am using the following code to find a suitable way to display the data
UserProfile? userProfile;
if (response.statusCode == 200) {
var userProfile = json.decode(response.body);
List<UserProfile> myProfile = [];
for (var k in userProfile) {
myProfile.add(UserProfile.fromJson(userProfile));
}
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load user data');
}
I am getting the error below
Unhandled Exception: type 'List<dynamic>' is not a subtype of type 'Map<String, dynamic>'
How do I handle the error?
You are passing whole list instead of value.
Try this.
var userProfile = json.decode(response.body);
List<UserProfile> myProfile = [];
for (var k in userProfile) {
myProfile.add(UserProfile.fromJson(k));
}
You will try like this way
const myJson = """
[
{
"id": 1,
"first_name": "First",
"last_name": "Last",
"name": "david",
"email": "david4001#gmail.com",
"phone_number": "12",
"user_image": null,
"email_verification_code": null,
"email_verification_time": null,
"created_at": "2022-03-24T17:37:17.000000Z",
"updated_at": "2022-03-29T07:16:25.000000Z"
}
]
""";
class UserProfile {
UserProfile({
this.id,
this.firstName,
this.lastName,
this.name,
this.email,
this.phoneNumber,
this.userImage,
this.emailVerificationCode,
this.emailVerificationTime,
this.createdAt,
this.updatedAt,
});
int? id;
String? firstName;
String? lastName;
String? name;
String? email;
String? phoneNumber;
dynamic userImage;
dynamic emailVerificationCode;
dynamic emailVerificationTime;
DateTime? createdAt;
DateTime? updatedAt;
factory UserProfile.fromMap(Map<String, dynamic> json) => UserProfile(
id: json["id"],
firstName: json["first_name"],
lastName: json["last_name"],
name: json["name"],
email: json["email"],
phoneNumber: json["phone_number"],
userImage: json["user_image"],
emailVerificationCode: json["email_verification_code"],
emailVerificationTime: json["email_verification_time"],
createdAt: DateTime.parse(json["created_at"]),
updatedAt: DateTime.parse(json["updated_at"]),
);
}
void main() {
final userData = List.from(json.decode(myJson));
print(userData[0]['id']);
print(userData[0]['name']);
}

convereting List of Map into Dart Objects

i have a list of map items in firebase.. like this
{
"0": [
{
"score": 4.5,
"review": "The pizza was amazing!"
},
{
"score": 5.0,
"review": "Very friendly staff, excellent service!"
}
],
"1": [
{
"score": 4.5,
"review": "The pizza was amazing!"
},
{
"score": 5.0,
"review": "Very friendly staff, excellent service!"
}
]
}
I cant convert it into dart objects correctly...
this is just an example my data is different
i tried this
final String uid;
final String name;
final String email;
final bool isAdmin;
final String easypaisa;
final String jazzCash;
final String bankAccount;
final String phoneNumber;
final String profileImage;
final List<String>? isFavorite;
final List<ListOfPackages> activatedPackages;
UserModel({
this.uid = '',
this.name = '',
this.email = '',
this.isAdmin = false,
this.easypaisa = '',
this.jazzCash = '',
this.bankAccount = '',
this.phoneNumber = '',
this.profileImage = '',
final List<String>? isFavorite,
final List<ListOfPackages>? activatedPackages,
}) : isFavorite = isFavorite ?? [],
activatedPackages = activatedPackages ?? [];
}
class ListOfPackages {
final bool acceptedPackage;
final String packageId;
final String packageTime;
final String proofImage;
final String uid;
final String username;
ListOfPackages(
{this.acceptedPackage = false,
this.packageId = '',
this.packageTime = '',
this.proofImage = '',
this.uid = '',
this.username = ''});
}
and here i'm mapping the data from firestore to the UserModel
return UserModel(
name: doc.get("name"),
email: doc.get('email'),
isAdmin: doc.get('isAdmin'),
easypaisa: doc.get('easypaisa'),
jazzCash: doc.get('jazzCash'),
bankAccount: doc.get('bankAccount'),
phoneNumber: doc.get('phoneNumber'),
profileImage: doc.get('profilePic'),
isFavorite: List.from(doc.data().toString().contains('favoritePackages')
? doc.get('favoritePackages')
: []),
activatedPackages: List.from(
doc.data().toString().contains('activatedPackages')
? doc.get('activatedPackages')
: []),
uid: doc.get('uid') ?? '');
}
With this, i'm getting this error
Exception: type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'ListOfPackages'
Can anyone guide me the right way to convert these? Actually most of the tutorials are in FactoryConstructor way so i can't take help from there... I'd appreciate any help.
Can you create a function that takes in a map and converts it to a ListOfPackages object? Then maybe you could do a forEach on doc.get(‘activatedPackages’) to add each ListOfPackages object one at a time to a List of ListOfPackagess, which you could then assign to activatedPackages.
found the solution!
UserModel _userDataFromSnapshot(DocumentSnapshot doc) {
return UserModel(
name: doc.get("name"),
email: doc.get('email'),
isAdmin: doc.get('isAdmin'),
easypaisa: doc.get('easypaisa'),
jazzCash: doc.get('jazzCash'),
bankAccount: doc.get('bankAccount'),
phoneNumber: doc.get('phoneNumber'),
profileImage: doc.get('profilePic'),
isFavorite: List.from(doc.data().toString().contains('favoritePackages')
? doc.get('favoritePackages')
: []),
activatedPackages: (doc.get('activatedPackages') as List<dynamic>)
.map((item) => ListOfPackages(
acceptedPackage: item['acceptedPackage'],
packageId: item['packageId'],
packageTime: item['packageTime'],
proofImage: item['proofImage'],
uid: item['uid'],
username: item['uid'],
))
.toList(),
uid: doc.get('uid') ?? '');
}

Error while Calculating and displaying total of Map Item in Flutter

I have a Map which stores an API response that I intend to use throughout my Application. Although, the API part works just as I expected, the trouble begins when I try displaying the total amount of all the objects. The total amount needs to be calculated in the following way:total += product_selling_price * quantity;.
However, the logic that I have written throws the below error:
Class 'int' has no instance method '[]'.
Receiver: 0
Tried calling: []("product_selling_price")
The JSON response, the method that calculates the total and the widget from which the method is called are as follows. Also, I will mark the lines where the errors are pointed at.
The JSON response:
{
"status": "success",
"data": [
{
"cart_id": 18,
"restaurant_id": "1",
"product_id": "5",
"restaurant_name": "City Club",
"product_name": "Palak Paneer",
"product_description": "Tasty silky gravy with goodness of palak",
"product_image": "/public/assets/product/C6pGz101-42-17.jpg",
"product_selling_price": "180",
"product_status": "active",
"product_quantity": "32",
"quantity": "2"
},
{
"cart_id": 17,
"restaurant_id": "1",
"product_id": "6",
"restaurant_name": "City Club",
"product_name": "Jersey Burger",
"product_description": "Tasty yummy burgir. BURGIRRRRR",
"product_image": "/public/assets/product/1Xf0sr01-43-20.jpg",
"product_selling_price": "185",
"product_status": "active",
"product_quantity": "50",
"quantity": "2"
},
{
"cart_id": 16,
"restaurant_id": "1",
"product_id": "7",
"restaurant_name": "City Club",
"product_name": "Tibetan Soup",
"product_description": "Healthy Soup from the mountains of Tibet",
"product_image": "/public/assets/product/CgMBpm02-03-38.jpg",
"product_selling_price": "120",
"product_status": "active",
"product_quantity": "24",
"quantity": "2"
}
]
}
The class with the method:
class CartItemProvider with ChangeNotifier {
Map<String, dynamic> _cartItems = {};
List<dynamic> _individualItems = [];
Network network = Network();
String baseUrl = 'https://achievexsolutions.in/current_work/eatiano/';
double deliveryCost = 40;
double discountCost = 50;
List<dynamic> get cartItemList {
return [..._cartItemList];
}
Map<String, dynamic> get cartItems {
return {..._cartItems};
}
Future<void> fetchCartItems() async {
SharedPreferences localStorage = await SharedPreferences.getInstance();
final url = Uri.parse(baseUrl + 'api/auth/cart');
final response = await http.get(url, headers: {
'Authorization': 'Bearer ${localStorage.getString('token')}',
'Accept': 'application/json'
});
Cart cartJson = cartFromJson(response.body);
_cartItems = cartJson.toJson();
print('Cart Item $_cartItems');
}
double get itemAmount { //The method in question
double total = 0.0;
total = _cartItems['data'].fold( //The above error gets pointed from this line
0,
(price, value) =>
price +
(double.parse(price['product_selling_price']) * //To This line, at price['product_selling_price']
double.parse(price['quantity'])));
return total;
}
}
The Model Class in case someone needs to take a look:
import 'package:meta/meta.dart';
import 'dart:convert';
Cart cartFromJson(String str) => Cart.fromJson(json.decode(str));
String cartToJson(Cart data) => json.encode(data.toJson());
class Cart {
Cart({
required this.status,
required this.data,
});
final String status;
final List<Datum> data;
factory Cart.fromJson(Map<String, dynamic> json) => Cart(
status: json["status"],
data: List<Datum>.from(json["data"].map((x) => Datum.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"status": status,
"data": List<dynamic>.from(data.map((x) => x.toJson())),
};
}
class Datum {
Datum({
required this.cartId,
required this.restaurantId,
required this.productId,
required this.restaurantName,
required this.productName,
required this.productDescription,
required this.productImage,
required this.productSellingPrice,
required this.productStatus,
required this.productQuantity,
required this.quantity,
});
final int cartId;
final String restaurantId;
final String productId;
final String restaurantName;
final String productName;
final String productDescription;
final String productImage;
final String productSellingPrice;
final String productStatus;
final String productQuantity;
final String quantity;
factory Datum.fromJson(Map<String, dynamic> json) => Datum(
cartId: json["cart_id"],
restaurantId: json["restaurant_id"],
productId: json["product_id"],
restaurantName: json["restaurant_name"],
productName: json["product_name"],
productDescription: json["product_description"],
productImage: json["product_image"],
productSellingPrice: json["product_selling_price"],
productStatus: json["product_status"],
productQuantity: json["product_quantity"],
quantity: json["quantity"],
);
Map<String, dynamic> toJson() => {
"cart_id": cartId,
"restaurant_id": restaurantId,
"product_id": productId,
"restaurant_name": restaurantName,
"product_name": productName,
"product_description": productDescription,
"product_image": productImage,
"product_selling_price": productSellingPrice,
"product_status": productStatus,
"product_quantity": productQuantity,
"quantity": quantity,
};
}
The widget from which the itemAmount getter is called:
class CartDetailScreen extends StatefulWidget {
CartDetailScreenState createState() => CartDetailScreenState();
}
class CartDetailScreenState extends State<CartDetailScreen> {
#override
Widget build(BuildContext context) {
......
// TODO: implement build
return Scaffold(
......
body: ListView(
children: [
........
),
Text(
'₹ ${Provider.of<CartItemProvider>(context).itemAmount //This is where the itemAmount method is accessed from
}',
........
],
));
}
}
I would like to know what I need to do to fix and get rid of this error.
fold function is a reducer, first argument (you name it price) is the precedent value so it is an int not an array of int and second value (you name it value) is the current value.
So you can't use price[...] if current value is an array use value[...]
https://api.dart.dev/stable/1.10.1/dart-core/List/fold.html
Your code should be like this:
double get itemAmount {
double total = 0.0;
total = _cartItems['data'].fold(
0,
(price, value) =>
price +
(double.parse(value['product_selling_price']) *
double.parse(value['quantity'])));
return total;
}

Flutter type "null" is not a sub-type of type "string"

I'm trying to load some data from a json file called recipe.json from my assets folder. The model that I've written is :
class RecipeModel {
final String id;
final String name;
final String videoLink;
final String author;
final String category;
final String time;
RecipeModel({
required this.id,
required this.name,
required this.videoLink,
required this.author,
required this.category,
required this.time,
});
factory RecipeModel.fromJson(Map<String, dynamic> json) {
return RecipeModel(
id: json['id'],
name: json['name'],
videoLink: json['videoLink'],
author: json['author'],
category: json['category'],
time: json['time'],
);
}
}
And the function that fetches the data:
Future _getRecipeData() async {
// var response = await http.get(
// Uri.https("jsonplaceholder.typicode.com", 'users'),
// );
String response = await DefaultAssetBundle.of(context)
.loadString('assets/json/recipe.json');
var result = json.decode(response);
List<RecipeModel> recipes = [];
for (var i in result) {
RecipeModel recipe = RecipeModel(
id: i['id'],
name: i['name'],
videoLink: i['videoLink'],
author: i['author'],
category: i['category'],
time: i['time'],
);
recipes.add(recipe);
}
print(recipes.length);
}
And I'm loading the data when the page loads:
#override
void initState() {
super.initState();
_getRecipeData();
}
But I get an error which says: Unhandled Exception: type 'Null' is not a subtype of type 'String'. Is there something that I'm missing?
Edit 1:
here's my recipe.json file:
[
{
"_id": "1",
"name": "Kacchi Biriyani",
"videoLink": "PQSagzssvUQ",
"author": "Alan Ford",
"category":"Biriyani",
"time": "15 min",
"steps": {
"step 1": "lorel ipsum dolor",
"step 2": "lorel ipsum dolor",
"step 3": "lorel ipsum dolor",
"step 4": "lorel ipsum dolor"
}
},
{
"_id": "2",
"name": "Mughal Biriyani",
"videoLink": "PQSagzssvUQ",
"author": "Ricky James",
"category":"Biriyani",
"time": "10 min",
"steps": {
"step 1": "lorel ipsum dolor",
"step 2": "lorel ipsum dolor",
"step 3": "lorel ipsum dolor",
"step 4": "lorel ipsum dolor"
}
}
]
Update 1:
List<RecipeModel> _recipes = [];
Future _getRecipeData() async {
// var response = await http.get(
// Uri.https("jsonplaceholder.typicode.com", 'users'),
// );
String response = await DefaultAssetBundle.of(context)
.loadString('assets/json/recipe.json');
var data = json.decode(response);
for (var i in data) {
RecipeModel recipe = RecipeModel(
id: i['id'],
name: i['name'],
videoLink: i['videoLink'],
author: i['author'],
category: i['category'],
time: i['time'],
);
_recipes.add(recipe);
}
print(_recipes);
}
#override
void initState() {
super.initState();
_getRecipeData();
print(_recipes);
}
in your fromJson and _getRecipeData your are accessing id like json['id'] and i['id']
however in your data it is _id there is underscore missing.
you can use null operator to avoid nulls also like this
RecipeModel recipe = RecipeModel(
id: i['id'] ?? "",
name: i['name'] ?? "",
videoLink: i['videoLink'] ?? "",
author: i['author'] ?? "",
category: i['category'] ?? "",
time: i['time'] ?? "",
);