Flutter abstract class with factory methods - flutter

I am new to working with dart/flutter!
Tell me pls how to correctly implement an abstract class in which a factory method is needed
I have a large number of models with the same methods (factory Model.fromJsom, listFromJson)! I want to make an interface for such models, but I don’t understand how to make a factory method and a static in abstract class
Examples
class Post extends Equatable {
Post({
required this.id,
required this.title,
});
final int id;
final String title;
#override
List<Object> get props => [
id,
title,
];
factory Post.fromJson(dynamic json) {
return Post(
id: json['id'] as int,
title: json['name'] as String,
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'title': title,
};
}
static List<Post> listFromJson(List<dynamic> json) {
return json.map((value) => Post.fromJson(value)).toList();
}
#override
String toString() {
return '''Post { id: $id, title: $title }''';
}
}

I find it's always a headache to work with JSON array directly. The solution I find is to wrap the List inside another class, in this case ListPost as follows:
class Post {
final int id;
final String title;
Post({required this.id, required this.title});
factory Post.fromJson(Map<String, dynamic> json) => Post(
id: json['id'] as int,
title: json['title'] as String,
);
Map<String, dynamic> toJson() => <String, dynamic>{
'id': id,
'title': title,
};
}
class ListPost {
final List<Post> list;
ListPost({required this.list});
factory ListPost.fromJson(Map<String, dynamic> json) => ListPost(
list: (json['list'] as List<dynamic>)
.map((e) => Post.fromJson(e as Map<String, dynamic>))
.toList(),
);
Map<String, dynamic> toJson() => <String, dynamic>{
'list': list,
};
}
And the example JSON is like this:
{
"list": [
{"id": 1, "title": "TITLE_1"},
{"id": 2, "title": "TITLE_2"}
]
}
I hope this is workable for you.

You can simply use this website it will convert your json to a dart file and you can easily call the methods .fromJson and .toJson to convert from json to entity and vice versa quicktype website
JSON source
{
"greeting": "Welcome to quicktype!",
"instructions": [
"Type or paste JSON here",
"Or choose a sample above",
"quicktype will generate code in your",
"chosen language to parse the sample data"
]
}
Dart file
import 'dart:convert';
Welcome welcomeFromJson(String str) => Welcome.fromJson(json.decode(str));
String welcomeToJson(Welcome data) => json.encode(data.toJson());
class Welcome {
Welcome({
this.greeting,
this.instructions,
});
String greeting;
List<String> instructions;
factory Welcome.fromJson(Map<String, dynamic> json) => Welcome(
greeting: json["greeting"] == null ? null : json["greeting"],
instructions: json["instructions"] == null ? null : List<String>.from(json["instructions"].map((x) => x)),
);
Map<String, dynamic> toJson() => {
"greeting": greeting == null ? null : greeting,
"instructions": instructions == null ? null : List<dynamic>.from(instructions.map((x) => x)),
};
}

Related

How to do a toJson encode method on Dart/Flutter?

I used the fromJson method to recover a Struct with a List from Json decode http request and receiver it on my class, but now i want to do a reverse, i want to pass the data on my class to my toJson method and send him to a Json encode http POST. Please, i new on Dart/Flutter, someone know how to do this?
import 'dart:convert';
List<Itens> userFromJson(String str) =>
List<Itens>.from(jsonDecode(str).map((x) => Itens.fromJson(x)));
class Coletas {
final int codigo;
final String dataIni;
late String? dataFin;
late String? status;
final List<Itens> itemList;
Coletas(
{
required this.dataIni,
this.dataFin,
this.status,
required this.codigo,
required this.itemList
}
);
factory Coletas.fromJson(Map<String, dynamic> json) {
return Coletas(
dataIni: json['dtData'],
codigo: json['iCodigo'],
itemList: List<Itens>.from(json['stItens'].map((x) => Itens.fromJson(x))),
);
}
Map<String, dynamic> toMap() {
return {
'codigo': codigo,
'dataIni': dataIni,
'dataFin': dataFin,
'status': status
};
}
}
class Itens {
final int? id;
final int codigo;
late int quantidade;
late String? status;
final String codigoEAN;
Itens({
this.id,
this.status,
required this.codigo,
required this.codigoEAN,
required this.quantidade,
});
Map<String, dynamic> toJson(){
return {
'icodigo' : codigo,
'sCodigoBarras': codigoEAN,
'iQtd': quantidade
};
}
factory Itens.fromJson(Map<String, dynamic> json) {
return Itens(
codigo: json['iCodigo'],
codigoEAN: json['sCodigoBarras'],
quantidade: json['iQtd'],
);
}
Map<String, dynamic> toMap() {
return {
'id': id,
'status': status,
'codigo': codigo,
'codigoEAN': codigoEAN,
'quantidade': quantidade,
};
}
}
I tried to pass ever item on List separeted so, but not happen i expected.
Map<String, dynamic> toJSon(Coletas value) =>
{
'dtData' : dataIni,
'iCodigo': codigo,
'stItens': [],
};
For a better structure - format and use you can look at the flutter serialization documentation : https://docs.flutter.dev/development/data-and-backend/json.
It explains how to create your model and how to generate them to create fromJson and toJson Model based on the defined data. (https://docs.flutter.dev/development/data-and-backend/json#creating-model-classes-the-json_serializable-way)
It will helps you with your parsing - sending - receiving data.
I think you should assign Coletas as
Map<String, dynamic> toJSon(Coletas value) =>
{
'dtData' : value.dataIni,
'iCodigo': value.codigo,
'stItens': value.itemList,
};

How to show lists in snapshot.data in flutter

I am new to flutter and I am learning APIs. My API model has a list named result. Can you please tell me how can I display that list and all other variables inside it by using snapshot.data
I am trying to declare
snapshot.data!.result but it is not working. Please give me the solution to this problem
Model code is below
class LeaguesModel {
LeaguesModel({
required this.success,
required this.result,
});
int success;
List<Result> result;
factory LeaguesModel.fromJson(Map<String, dynamic> json) => LeaguesModel(
success: json["success"],
result: List<Result>.from(json["result"].map((x) => Result.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"success": success,
"result": List<dynamic>.from(result.map((x) => x.toJson())),
};
}
class Result {
Result({
required this.leagueKey,
required this.leagueName,
required this.countryKey,
required this.countryName,
required this.leagueLogo,
required this.countryLogo,
});
String leagueKey;
String leagueName;
String countryKey;
String countryName;
String leagueLogo;
String countryLogo;
factory Result.fromJson(Map<String, dynamic> json) => Result(
leagueKey: json["league_key"],
leagueName: json["league_name"],
countryKey: json["country_key"],
countryName: json["country_name"],
leagueLogo: json["league_logo"],
countryLogo: json["country_logo"],
);
Map<String, dynamic> toJson() => {
"league_key": leagueKey,
"league_name": leagueName,
"country_key": countryKey,
"country_name": countryName,
"league_logo": leagueLogo,
"country_logo": countryLogo,
};
static List<Result> jsontoApiModel(List<dynamic> emote) => emote.map<Result>((item) => Result.fromJson(item)).toList();
}

JsonSerializable: How to toJson/fromJson correspond to subclass type?

Let say I have data structure like this
#JsonSerializable(explicitToJson: true)
class CartItem {
final Product product;
...
}
#JsonSerializable()
class Product {
final String id;
...
}
#JsonSerializable()
class Food extends Product {
final String foodName;
...
}
#JsonSerializable()
class Furniture extends Product {
final String furnitureName;
...
}
What I want is when I use CartItem.toJson() it export to Json map correspond to what type of Product is.
For example
var x = CartItem(
product: Food(
id: '1100112',
foodName: 'Hamburger',
),
)
print(x.toJson());
this should result in
{
'product': {
'id': '1100112',
'foodName': 'Hamburger',
}
}
also when use fromJson, product will has type Food
x.fromJson(x.toJson()) as Food //This should not error
I assume you're using the package json_serialization. For each class you would want to define some of the following methods:
For example:
factory CartItem.fromJson(Map<String, dynamic> json) => _$CartItemFromJson(json);
Map<String, dynamic> toJson() => _$CartItemToJson(this);
Then you would run the generating command line:
flutter pub run build_runner build
This will auto-generate a .g.dart file corresponding to your
model file.
Reference: https://pub.dev/packages/json_serializable
Otherwise, if you wish to define the methods manually:
class CartItem {
final Product product;
CartItem({required this.product});
/// fromJson
factory CartItem.fromJson(Map<String, dynamic> json) => CartItem(
product: Product.fromJson(json["product"])
);
/// toJson
Map<String, dynamic> toJson() => {
"product": product.toJson()
};
}
class Product {
final String id;
Product({required this.id});
/// fromJson
factory Product.fromJson(Map<String, dynamic> json) => Product(
id: json["id"]
);
/// toJson
Map<String, dynamic> toJson() => {
"id": id
};
}
class Food extends Product {
final String foodName;
Food({required String id, required this.foodName}) : super(id: id);
/// fromJson
factory Food.fromJson(Map<String, dynamic> json) => Food(
id: json["id"],
foodName: json["foodName"]
);
/// toJson
#override
Map<String, dynamic> toJson() => {
"id": id,
"foodName": foodName
};
}
class Furniture extends Product {
final String furnitureName;
Furniture({required String id, required this.furnitureName}) : super(id: id);
/// fromJson
factory Furniture.fromJson(Map<String, dynamic> json) => Furniture(
id: json["id"],
furnitureName: json["furnitureName"]
);
/// toJson
#override
Map<String, dynamic> toJson() => {
"id": id,
"furnitureName": furnitureName
};
}
After defining, you can do:
var x = CartItem(
product: Food(
id: '1100112',
foodName: 'Hamburger',
),
)
print(x.toJson());

Conversion error from object to json and back in flutter

I am trying to convert a list of objects as a json string in shared preferences.
Object class
SuggestionModel suggestionModelFromJson(String str) =>
SuggestionModel.fromJson(json.decode(str));
String suggestionModelToJson(SuggestionModel data) =>
json.encode(data.toJson());
class SuggestionModel {
SuggestionModel({
this.category,
this.icon,
this.subs,
});
eCategory? category;
IconData? icon;
List<Sub>? subs;
factory SuggestionModel.fromJson(Map<String, dynamic> json) =>
SuggestionModel(
category: json["category"],
icon: json["icon"],
subs: List<Sub>.from(json["subs"].map((x) => Sub.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"category": category,
"icon": icon,
"subs": List<dynamic>.from(subs!.map((x) => x.toJson())),
};
}
class Sub {
Sub({
this.subCategory,
this.values,
});
String? subCategory;
List<Value>? values;
factory Sub.fromJson(Map<String, dynamic> json) => Sub(
subCategory: json["sub_category"],
values: List<Value>.from(json["values"].map((x) => Value.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"sub_category": subCategory,
"values": List<dynamic>.from(values!.map((x) => x.toJson())),
};
}
class Value {
Value({
this.subName,
this.selected,
});
String? subName;
bool? selected;
factory Value.fromJson(Map<String, dynamic> json) => Value(
subName: json["sub_name"],
selected: json["selected"],
);
Map<String, dynamic> toJson() => {
"sub_name": subName,
"selected": selected,
};
}
When I try to do
List<SuggestionModel> list;
String encodedData = jsonEncode(list);
it gives me an error
Converting object to an encodable object failed: Instance of 'SuggestionModel'
Im not following where the exact issue comes from. tried debugging and still no luck
How can I rectify this?
Update. I've changed the enum to a String and removed the IconData field. And the above issue had resolved.
Now when I try to get the saved Json string and convert that back to list of objects. I get an error
Unhandled Exception: type 'List<dynamic>' is not a subtype of type 'List<SuggestionModel>'
at this line
var t = await _dataManager.getSelectedList(s);
var addedObj = json.decode(json.decode(t!));
//here...
var list = addedObj.map((e) => SuggestionModel.fromJson(e)).toList();
Try this:
String encodedData = jsonEncode(list.map((e) => e.toJson()).toList());
So the first thing was to update the enum property of Model class to String and remove IconData.
Then for the second issue.
update the decoding function like
List<SuggestionModel> list =
addedObj.map((e) => SuggestionModel.fromJson(e)).toList();

How create a interface (class) in Dart for an nested Map

In my Flutter app I receive notifications with a custom payload, like:
{ notification:
{
title: Test,
body: AAAA
},
data:
{
productId: Axe,
page: Products,
click_action: FLUTTER_NOTIFICATION_CLICK
}
}
Everything works well. I also can handle the payload of the notification by access it through:
message['data']['page']
But I rather would like to use an Interface/Class to name the data by key, for example:
message.data.page and message.data.productId
So I tried:
class NotificationMessage {
Map notification;
Map data;
NotificationMessage(this.notification, this.data);
}
...
NotificationMessage _message = message; // Assigning the payload to the final
...
This where I get stuck: here I got the error: A value of type 'Map<dynamic, dynamic>' can't be assigned to a variable of type 'NotificationMessage'.
My class isn't finished yet, but how to continue?
I' aware of json_serializable, but before any tooling, I would like to understand it fully.
First, you need to build the two models for notification and data as follows
class DataMessage {
final String productId;
final String page;
final String click_action;
DataMessage(this.productId, this.page, this.click_action);
factory DataMessage.fromJson(Map<dynamic, dynamic> json) {
return DataMessage(
json['productId'] as String,
json['page'] as String,
json['click_action'] as String,
);
}
}
class NotificationMessage {
final String title;
final String body;
NotificationMessage(this.title, this.body);
factory NotificationMessage.fromJson(Map<dynamic, dynamic> json) {
return NotificationMessage(
json['title'] as String,
json['body'] as String,
);
}
}
The factory method convert map types into model classes.
Then you have to build a model for the response message as follows
class Message {
final NotificationMessage notification;
final DataMessage data;
Message(this.notification, this.data);
factory Message.fromJson(Map<dynamic, dynamic> json) {
final Map<dynamic, dynamic> mapNotification = json['notification'];
final Map<dynamic, dynamic> mapData = json['data'];
final dataModel = DataMessage.fromJson(mapData);
final notificationModel = NotificationMessage.fromJson(mapNotification);
return Message(
notificationModel as NotificationMessage,
dataModel as DataMessage,
);
}
}
Note that the factory method allows you to convert the maps for each model to a class model
So you can define your response as a class
Map<dynamic, dynamic> messageResponse = {
'notification': {'title': 'Test', 'body': 'AAAA'},
'data': {
'productId': 'Axe',
'page': 'Products',
'click_action': 'FLUTTER_NOTIFICATION_CLICK'
}
};
final Message message = Message.fromJson(messageResponse);
print(message.data.productId);
print(message.data.page);
print(message.data.click_action);
print(message.notification.title);
print(message.notification.body);
Hope that can help you
Instance of the json object. this way you can use a map for any instance without modifying the class
{ "notification":
{
"title": "Test",
"body": "AAAA"
},
"data":
{
"productId": "Axe",
"page": "Products",
"click_action": "FLUTTER_NOTIFICATION_CLICK"
}
}
Class looks like;
class NotificationMessage {
NotificationMessage({
this.notification,
this.data,
});
final Notification notification;
final Data data;
factory NotificationMessage.fromJson(Map<String, dynamic> json) => NotificationMessage(
notification: Notification.fromJson(json["notification"]),
data: Data.fromJson(json["data"]),
);
Map<String, dynamic> toJson() => {
"notification": notification.toJson(),
"data": data.toJson(),
};
}
class Data {
Data({
this.productId,
this.page,
this.clickAction,
});
final String productId;
final String page;
final String clickAction;
factory Data.fromJson(Map<String, dynamic> json) => Data(
productId: json["productId"],
page: json["page"],
clickAction: json["click_action"],
);
Map<String, dynamic> toJson() => {
"productId": productId,
"page": page,
"click_action": clickAction,
};
}
class Notification {
Notification({
this.title,
this.body,
});
final String title;
final String body;
factory Notification.fromJson(Map<String, dynamic> json) => Notification(
title: json["title"],
body: json["body"],
);
Map<String, dynamic> toJson() => {
"title": title,
"body": body,
};
}
A function to build as
Future<NotificationMessage> getNotf() {
var parsedJson = json.decode(//your received object);
return NotificationMessage.fromJson(parsedJson);
}
Now you can receive this in a builder such as
_futureNotifications =Future<NotificationMessage>
_futureNotifications = getNotf(); //this is a function and can be used after a gesture or initState
FutureBuilder<NotificationMessage>(
future: _futureNotifications,
builder: (context, snapshot) {
}