Flutter Model design pattern - flutter

i have a Product Model
class Product {
int id;
String title;
double mrp;
Product(this.id, this.title, this.mrp);
factory Product.fromJson(int id, String title, double mrp) {
return Product(id, title, mrp);
}
}
I have different json structure for API calls when i get json data as response from server -
1st Example
{
"id": 523,
"title": "test",
"mrp": 23.25
}
2nd Example :
{
"id" : 523,
"title" : "test",
"mrp" : 23.25
"discountedPrice" : 23.00
}
3rd Example :
{
"id" : 523,
"title" : "test",
"mrp" : 23.25
"availableForSale" : true
}
4th Example :
{
"id" : 523,
"title" : "test",
"mrp" : 23.25
"firstCharacterTitle" : "T"
}
As you can see i have different new field like discountedPrice firstCharacterTitle availableForSale API structure in different fetching call of API. and it can be many others in other scenario.
What should i do with Product Model in different cases as i have only 3 permanent fields id title mrp but all others are dependent on scenario ? Should i keep increasing field in my model or anything else? What is the best Practise?

Sorry for longer answer.
You have 2 options
adding those extra parameters and make them nullable.
class Product {
int id;
String title;
double mrp;
String? firstCharacterTitle;
Product(this.id, this.title, this.mrp, {this.firstCharacterTitle});
factory Product.fromJson(
int id,
String title,
double mrp, {
String? firstCharacterTitle,
}) {
return Product(id, title, mrp, firstCharacterTitle: firstCharacterTitle);
}
}
Or work with something dynamic
class Product {
int id;
String title;
double mrp;
Map<String, dynamic> extraParams;
Product(this.id, this.title, this.mrp, this.prms);
factory Product.fromJson(int id, String title, double mrp, Map<String, dynamic> prms) {
return Product(id, title, mrp, prms);
}
}
I also recommend using json_serializable. Cause your fromJson method is not truly fromJson, but it's taking already converted Json parameters.

Related

Dart: give default value to empty or null string? How to do it with less code?

I have a Message object, with optional string title and required string message
I want to display the title, or if it's null or empty, display the message.
Since the title could be an empty string "", I can't just write msg.title ?? msg.message.
Now I'm writing it like this:
// the Message object
String? title;
String message;
// display
Text(msg.title?.isEmpty ?? true
? msg.message
: msg.title!)
This is ridiculously long and complicated. Is there a better way to do this?
Thanks everyone, I've learned a lot from your answers!<3
Seems like your Message class could be cooler. Here's a dartpad with my stuff.
class Message {
String? title;
String message; // could be better named - message property in a message class? Consider `details`
Message({required this.message, this.title});
/// Safe method to get displayed text for this [Message].
/// If the [title] is not null and is not empty, returns the title.
/// If the [title] is null or empty, returns the [message].
String get displayText {
return title == null || title!.isEmpty ? message : title!;
}
}
This would let you use message.displayText when you want to access the message.
// Some message
Message msg = Message(message: "Posted something successfully to the server", title: "Success!");
// The text widget
Text(msg.displayText);
add this extension to string:
extension on String? {
bool isNullEmpty => this == null && this.isEmpty;
}
and use it like this:
Text(msg.title.isNullEmpty ? msg.message: msg.title!)
I came up with this the other day, and will be doing a screencast for my YT channel soon (https://www.youtube.com/c/RandalLSchwartzonDartandFlutter). Here's the code:
void main(List<String> arguments) {
print('${Mine()}');
print('${Mine(name: 'Bob only')}');
print('${Mine(description: 'Guy only')}');
print('${Mine(name: 'Bob and', description: 'Guy')}');
print('${Mine(name: 'Bob', description: '')}');
print('${Mine(name: '', description: 'Guy')}');
print('${Mine(name: 'Bob', description: 'Guy')}');
}
extension IfEmptyOrNull on String? {
operator [](String value) {
if (this == null || this!.isEmpty) {
return value;
}
return this!;
}
}
class Mine {
final String name;
final String description;
Mine({String? name, String? description})
: name = name['No name'],
description = description['No description'];
#override
String toString() => 'Mine(name: $name, description: $description)';
}
The fun part is that someValue[theDefault] is just an array syntax, and even makes sense.

How to create a model for pictures from Unsplash?

I need to use the Unsplash API to display a list of pictures that come to me, the title and the author. The problem is that I do not understand how to create a model for converting JSON that comes to me so that I can get the picture, author and title. JSON has nested elements and I don't understand how to do it. How can i do this?
My JSON for one picture:
{
"id":"RBo6ayiFND0",
"created_at":"2022-07-08T13:04:40-04:00",
"updated_at":"2022-07-22T00:20:05-04:00",
"promoted_at":null,
"width":4160,
"height":6240,
"color":"#c0c0c0",
"blur_hash":"LPIOLgtR%1IT~qsSMxxZx]V#s.RP",
"description":null,
"alt_description":null,
"urls":{
"raw":"https://images.unsplash.com/photo-1657299156528-2d50a9a6a444?ixid=MnwzNDg3MDF8MXwxfGFsbHwxfHx8fHx8Mnx8MTY1ODQ4ODUyOQ\u0026ixlib=rb-1.2.1",
"full":"https://images.unsplash.com/photo-1657299156528-2d50a9a6a444?crop=entropy\u0026cs=tinysrgb\u0026fm=jpg\u0026ixid=MnwzNDg3MDF8MXwxfGFsbHwxfHx8fHx8Mnx8MTY1ODQ4ODUyOQ\u0026ixlib=rb-1.2.1\u0026q=80",
"regular":"https://images.unsplash.com/photo-1657299156528-2d50a9a6a444?crop=entropy\u0026cs=tinysrgb\u0026fit=max\u0026fm=jpg\u0026ixid=MnwzNDg3MDF8MXwxfGFsbHwxfHx8fHx8Mnx8MTY1ODQ4ODUyOQ\u0026ixlib=rb-1.2.1\u0026q=80\u0026w=1080",
"small":"https://images.unsplash.com/photo-1657299156528-2d50a9a6a444?crop=entropy\u0026cs=tinysrgb\u0026fit=max\u0026fm=jpg\u0026ixid=MnwzNDg3MDF8MXwxfGFsbHwxfHx8fHx8Mnx8MTY1ODQ4ODUyOQ\u0026ixlib=rb-1.2.1\u0026q=80\u0026w=400",
"thumb":"https://images.unsplash.com/photo-1657299156528-2d50a9a6a444?crop=entropy\u0026cs=tinysrgb\u0026fit=max\u0026fm=jpg\u0026ixid=MnwzNDg3MDF8MXwxfGFsbHwxfHx8fHx8Mnx8MTY1ODQ4ODUyOQ\u0026ixlib=rb-1.2.1\u0026q=80\u0026w=200",
"small_s3":"https://s3.us-west-2.amazonaws.com/images.unsplash.com/small/photo-1657299156528-2d50a9a6a444"
},
"links":{
"self":"https://api.unsplash.com/photos/RBo6ayiFND0",
"html":"https://unsplash.com/photos/RBo6ayiFND0",
"download":"https://unsplash.com/photos/RBo6ayiFND0/download?ixid=MnwzNDg3MDF8MXwxfGFsbHwxfHx8fHx8Mnx8MTY1ODQ4ODUyOQ",
"download_location":"https://api.unsplash.com/photos/RBo6ayiFND0/download?ixid=MnwzNDg3MDF8MXwxfGFsbHwxfHx8fHx8Mnx8MTY1ODQ4ODUyOQ"
},
"categories":[
],
"likes":21,
"liked_by_user":false,
"current_user_collections":[
],
"sponsorship":{
"impression_urls":[
"https://secure.insightexpressai.com/adServer/adServerESI.aspx?script=false\u0026bannerID=10624831\u0026rnd=[timestamp]\u0026redir=https://secure.insightexpressai.com/adserver/1pixel.gif"
],
"tagline":"Wholesome crispbread from Sweden",
"tagline_url":"https://www.wasa.com/global/",
"sponsor":{
"id":"5tdWPtk6hBg",
"updated_at":"2022-07-22T06:53:49-04:00",
"username":"wasacrispbread",
"name":"Wasa Crispbread",
"first_name":"Wasa Crispbread",
"last_name":null,
"twitter_username":null,
"portfolio_url":"https://www.wasa.com/global/",
"bio":"Things we love:\r\nšŸž Crispbread (naturally) šŸŒ Our planet šŸ˜‹ Delicious food, everyday",
"location":null,
"links":{
"self":"https://api.unsplash.com/users/wasacrispbread",
"html":"https://unsplash.com/#wasacrispbread",
"photos":"https://api.unsplash.com/users/wasacrispbread/photos",
"likes":"https://api.unsplash.com/users/wasacrispbread/likes",
"portfolio":"https://api.unsplash.com/users/wasacrispbread/portfolio",
"following":"https://api.unsplash.com/users/wasacrispbread/following",
"followers":"https://api.unsplash.com/users/wasacrispbread/followers"
},
"profile_image":{
"small":"https://images.unsplash.com/profile-1655151625963-f0eec015f2a4image?ixlib=rb-1.2.1\u0026crop=faces\u0026fit=crop\u0026w=32\u0026h=32",
"medium":"https://images.unsplash.com/profile-1655151625963-f0eec015f2a4image?ixlib=rb-1.2.1\u0026crop=faces\u0026fit=crop\u0026w=64\u0026h=64",
"large":"https://images.unsplash.com/profile-1655151625963-f0eec015f2a4image?ixlib=rb-1.2.1\u0026crop=faces\u0026fit=crop\u0026w=128\u0026h=128"
},
"instagram_username":"wasacrispbread",
"total_collections":0,
"total_likes":0,
"total_photos":73,
"accepted_tos":true,
"for_hire":false,
"social":{
"instagram_username":"wasacrispbread",
"portfolio_url":"https://www.wasa.com/global/",
"twitter_username":null,
"paypal_email":null
}
}
},
"topic_submissions":{
},
"user":{
"id":"5tdWPtk6hBg",
"updated_at":"2022-07-22T06:53:49-04:00",
"username":"wasacrispbread",
"name":"Wasa Crispbread",
"first_name":"Wasa Crispbread",
"last_name":null,
"twitter_username":null,
"portfolio_url":"https://www.wasa.com/global/",
"bio":"Things we love:\r\nšŸž Crispbread (naturally) šŸŒ Our planet šŸ˜‹ Delicious food, everyday",
"location":null,
"links":{
"self":"https://api.unsplash.com/users/wasacrispbread",
"html":"https://unsplash.com/#wasacrispbread",
"photos":"https://api.unsplash.com/users/wasacrispbread/photos",
"likes":"https://api.unsplash.com/users/wasacrispbread/likes",
"portfolio":"https://api.unsplash.com/users/wasacrispbread/portfolio",
"following":"https://api.unsplash.com/users/wasacrispbread/following",
"followers":"https://api.unsplash.com/users/wasacrispbread/followers"
},
"profile_image":{
"small":"https://images.unsplash.com/profile-1655151625963-f0eec015f2a4image?ixlib=rb-1.2.1\u0026crop=faces\u0026fit=crop\u0026w=32\u0026h=32",
"medium":"https://images.unsplash.com/profile-1655151625963-f0eec015f2a4image?ixlib=rb-1.2.1\u0026crop=faces\u0026fit=crop\u0026w=64\u0026h=64",
"large":"https://images.unsplash.com/profile-1655151625963-f0eec015f2a4image?ixlib=rb-1.2.1\u0026crop=faces\u0026fit=crop\u0026w=128\u0026h=128"
},
"instagram_username":"wasacrispbread",
"total_collections":0,
"total_likes":0,
"total_photos":73,
"accepted_tos":true,
"for_hire":false,
"social":{
"instagram_username":"wasacrispbread",
"portfolio_url":"https://www.wasa.com/global/",
"twitter_username":null,
"paypal_email":null
}
}
},
Picture Model:
class PictureModel {
const PictureModel({});
factory PictureModel.fromJson(Map < String, dynamic > json) {
return PictureModel(
...
);
}
}
If I understand correctly you only need:
image
title (I'll assume this is description)
author (I'll assume this is user.name)
You can create the PictureModel as follows:
class PictureModel {
final String author;
final String? title;
final String imageUrl;
const PictureModel({
required this.author,
this.title,
required this.imageUrl,
});
factory PictureModel.fromJson(Map<String, dynamic> json) {
return PictureModel(
author: json['user']['name'],
title: json['description'],
imageUrl: json['urls']['regular'],
);
}
}
Guessing you meant something like below by saying nested.
To simplify it, let's use Photo and Urls properties
class Photo {
Photo({
this.id,
this.urls,
...
});
String? id;
Urls? urls;
...
factory Photo.fromJson(Map<String, dynamic> json) {
return Photo(
id: json['id'],
urls: Urls.fromJson(json['urls']),
...
);
}
}
Then Urls object.
class Urls {
Urls({
this.rawUrl,
this.fullUrl,
this.regularUrl,
this.smallUrl,
this.thumbUrl,
this.smallS3Url,
});
String? rawUrl;
String? fullUrl;
String? regularUrl;
String? smallUrl;
String? thumbUrl;
String? smallS3Url;
factory Urls.fromJson(Map<String, dynamic> json) => Urls(
rawUrl: json['raw'],
fullUrl: json['full'],
regularUrl: json['regular'],
smallUrl: json['small'],
thumbUrl: json['thumb'],
smallS3Url: json['small_s3'],
);
Map<String, dynamic> toJson() => {
'rawUrl': rawUrl,
'fullUrl': fullUrl,
'regularUrl': regularUrl,
'smallUrl': smallUrl,
'thumbUrl': thumbUrl,
'smallS3Url': smallS3Url,
};
}
Simply you can use any JSON to Dart tool, that takes the JSON and returns a dart model like this one.
Or you can do it manually by making nested classes.
firstly you will make a parent class that contains the first level of the JSON file and each element containing other elements inside will be a separate class and you will use it as a type of the element.
so if we took the first level of the file and one other element from the second level such as the element user your code will be something like this:
class Pic {
Pic.fromJson(dynamic json) {
id = json['id'];
createdAt = json['created_at'];
updatedAt = json['updated_at'];
promotedAt = json['promoted_at'];
width = json['width'];
height = json['height'];
color = json['color'];
blurHash = json['blur_hash'];
description = json['description'];
altDescription = json['alt_description'];
urls = Urls.fromJson(json['urls']);
links = Links.fromJson(json['links']);
if (json['categories'] != null) {
categories = [];
json['categories'].forEach((v) {
categories?.add(Categories.fromJson(v));
});
}
likes = json['likes'];
likedByUser = json['liked_by_user'];
if (json['current_user_collections'] != null) {
currentUserCollections = [];
json['current_user_collections'].forEach((v) {
currentUserCollections?.add(CurrentUserCollections.fromJson(v));
});
}
sponsorship = json['sponsorship'] != null ? Sponsorship.fromJson(json['sponsorship']) : null;
topicSubmissions = json['topic_submissions'];
user = User.fromJson(json['user']);
}
String? id;
String? createdAt;
String? updatedAt;
dynamic promotedAt;
int? width;
int? height;
String? color;
String? blurHash;
dynamic description;
dynamic altDescription;
Urls? urls;
Links? links;
List<dynamic>? categories;
int? likes;
bool? likedByUser;
List<dynamic>? currentUserCollections;
Sponsorship? sponsorship;
dynamic topicSubmissions;
User? user;
}
class User {
User.fromJson(dynamic json) {
id = json['id'];
updatedAt = json['updated_at'];
username = json['username'];
name = json['name'];
firstName = json['first_name'];
lastName = json['last_name'];
twitterUsername = json['twitter_username'];
portfolioUrl = json['portfolio_url'];
bio = json['bio'];
location = json['location'];
links = json['links'] != null ? Links.fromJson(json['links']) : null;
profileImage = json['profile_image'] != null ? ProfileImage.fromJson(json['profile_image']) : null;
instagramUsername = json['instagram_username'];
totalCollections = json['total_collections'];
totalLikes = json['total_likes'];
totalPhotos = json['total_photos'];
acceptedTos = json['accepted_tos'];
forHire = json['for_hire'];
social = json['social'] != null ? Social.fromJson(json['social']) : null;
}
String? id;
String? updatedAt;
String? username;
String? name;
String? firstName;
dynamic lastName;
dynamic twitterUsername;
String? portfolioUrl;
String? bio;
dynamic location;
Links? links;
ProfileImage? profileImage;
String? instagramUsername;
int? totalCollections;
int? totalLikes;
int? totalPhotos;
bool? acceptedTos;
bool? forHire;
Social? social;
}
notice that each element that contains other elements inside is with a special data type like Urls Links Sponsorship User in the first class, and Links ProfileImage Social in the second class. so each of these data types is a class on its own but I only mentioned the class User to let you continue the rest of the code.
Happy Coding ;)

How to extract key and value from JSON Object in flutter

I integrated API, I want to get both key and value from API response. I stored both key and value in two separate arrays and display inside the listview.so How to get key and value from api response.
[
{ "Name": "abc"
"Department": "Marketing",
"EmpCode": "ABC123",
"Salary":"20000",
"Pincode": 100023
}]
I got response using this code:
List list = json.decode(response.body);
So how to get Name,department,empcode and abc,marketing separate.
just use a forEach loop and it will extract keys and values for you which you can store according to your requirement and you don't have to worry about how many keys you have
response.forEach((key, value){
print('key is $key');
print('value is $value ');
}
Create a class that will hold your fields and add a fromJson method like so:
class ClassName{
ClassName(
{this.Name,
this.Department,
this.EmpCode,
this.Salary,
this.Pincode,
});
String Name;
String Department;
String EmpCode;
double Salary;
String Pincode;
ClassName.fromJson(Map<String, dynamic> json) {
Name= json['Name'];
Department= json['Department'];
EmpCode= json['EmpCode'];
Salary= json['Salary'];
Pincode= json['Pincode'];
}
}
And then you could do something like that with your json that you get from the API:
var x = json.decode(response.body.toString());
var className = ClassName.fromJson(x);

How can I know what is the most sold product of a list? flutter-dart

I have a list of items from the orders by date, so based on its quantity I want to find what is the most sold product.
This is the class for my items
class CartItem{
String id;
String name;
String image;
int quantity;
double price;
CartItem({
this.id,
this.name,
this.image,
this.quantity,
this.price,
});
}
For example:
List<CartItem> list = orders.products;
list.forEach((element => print('${element.name} : ${element.quantity)');
It will print:
Dress : 1
Boots : 2
Trousers : 3
Dress : 2
Trousers : 2
So based on this example, how can I get the most sold product which is 'Trousers' and the sum of its quantity which is 5 for this case.
Thank you in advance.
You can covert the list to a map so you can see distinct items in the list. but rather than assigning the value to the key, if the key already exists, add the value
as:
list.forEach((item) => {
map2[item.name] = item.quantity + (map2[item.name] ?? 0)
});
you will get the output:
{Dress: 3, Boots: 2, Trousers: 5}
then finally you can sort the map like this:
var sortedKeys = map2.keys.toList(growable:false)
..sort((k1, k2) => map2[k2].compareTo(map2[k1]));
LinkedHashMap sortedMap = new LinkedHashMap
.fromIterable(sortedKeys, key: (k) => k, value: (k) => map2[k]);
print(sortedMap);
sorting os mentioned here as well How to sort map value?
then you can get the first element which is the product with the highest orders
hope this helps
Here is another example:
// Dart 2.6.1
main() {
List list = [
{
"name":"Dress",
"quantity" : 1
},{
"name":"Boots",
"quantity" : 2
},{
"name":"Trousers",
"quantity" : 3
},{
"name":"Dress",
"quantity" : 2
},{
"name":"Trousers",
"quantity" : 2
}
];
var products={
};
String mostSelling="";
int quantity=0;
list.forEach((e) {
if(products[e["name"]]== null){
products[e["name"]]= e["quantity"];
} else {
products[e["name"]]+= e["quantity"];
}
if(products[e["name"]] >quantity){
mostSelling= e["name"];
quantity = products[e["name"]];
}
}
);
print("$mostSelling : $quantity");
}
Output:

pass different class object to function

I have multiple class objects and i want to create a standalone function that will take any class object as parameter.
here is my income class
import 'package:finsec/model/dao.dart';
class Income {
int id, groupId, weekNumber, monthNumber, calendarYear;
String frequency, payoutDate, category, depositTo, description, status;
double expectedAmount, actualAmount;
Income(
this.id,
this.groupId,
this.expectedAmount,
this.actualAmount,
this.frequency,
this.payoutDate,
this.category,
this.depositTo,
this.status,
this.weekNumber,
this.monthNumber,
this.calendarYear,
[this.description]
);
}
here is my expense class object
import 'package:finsec/model/dao.dart';
class Expense{
int id, groupId
String frequency, paidDate, expCategory, description, status;
double expectedAmount, actualAmount;
Expense(
this.id,
this.groupId,
this.expectedAmount,
this.actualAmount,
this.frequency,
this.paidDate,
this.expCategory,
this.status,
[this.description]
);
}
// The base class for the different types of items the list can contain.
abstract class Dao<T> {
//queries
String get createTableQuery;
//abstract mapping methods
T fromMap(Map<String, dynamic> query);
List<T> fromList(List<Map<String,dynamic>> query);
Map<String, dynamic> toMap(T object);
}
import 'package:finsec/model/dao.dart';
import 'package:finsec/model/income/income.dart';
class IncomeDao implements Dao<Income> {
#override
Income fromMap(Map<String, dynamic> query) {
Income income = Income();
income.id = query["id"];
income.groupId = query["groupId"];
income.amount = query["amount"];
income.frequency = query["frequency"];
income.payoutDate = query["payoutDate"];
income.category = query["category"];
income.depositTo = query["depositTo"];
income.status = query["status"];
return income;
}
#override
Map<String, dynamic> toMap(Income object) {
return <String, dynamic>{
"id": object.id,
"groupId": object.groupId,
"amount": object.amount,
"frequency": object.frequency,
"payoutDate": object.payoutDate,
"category": object.category,
"depositTo": object.depositTo,
"status": object.status,
};
}
#override
List<Income> fromList(List<Map<String,dynamic>> query) {
List<Income> income = List<Income>();
for (Map map in query) {
income.add(fromMap(map));
}
return income;
}
}
i want to create function that takes any class object
Future<int> insertTransaction(T object) async {
var dao = new IncomeDao();
dao.toMap(object));
}
basically, i want to be able to call insertTransaction and pass any class object and then pass that object to the dao class. when i called dao.toMap(object)); in insertTransaction function. i get error message such as "The argument type 'T' can't be assigned to the parameter type 'Income'."
I guess flutter is not able to determine if object parameter is Income or expense etc. i tried using casting such as (Income)object but didnt work.
can someone help me on this? im trying to reuse my function (insertTransaction) instead of creating the same function for every object class such as income, expenses, events, etc...
just change T to dynamic
insertTransaction(dynamic object)