Do I use Strategy pattern correctly in my dart code? - dart

I'm trying to apply a strategy pattern in my dart code, but I don't know why the overridden method in child class doesn't get called.
Below is an example push notification message that will be sent to mobile devices, the data in "content" node can be different depends on the "type" value. In order to deserialize the message correctly, I created classes as below,
{
"priority": "high",
"to": "123342"
"notification": {
"body": "this is a body",
"title": "this is a title"
},
"data": {
"click_action": "FLUTTER_NOTIFICATION_CLICK",
"status": "done",
"type": "Order",
"content" : {
"order_id" : "[order_id]"
}
},
}
class Message<T extends BaseDataContent> {
String to;
String priority;
Notification notification;
Data<T> data;
Message({this.to, this.priority, this.notification, this.data});
Message.fromJson(Map<String, dynamic> json) {
to = json['to'];
priority = json['priority'];
notification = json['notification'] != null
? new Notification.fromJson(json['notification'])
: null;
data = json['data'] != null
? new Data.fromJson(json['data'])
: null;
}
}
class Notification {
String title;
String body;
Notification({this.title, this.body});
Notification.fromJson(Map<String, dynamic> json) {
title = json['title'];
body = json['body'];
}
}
class Data<T extends BaseDataContent> {
String click_action;
String status;
String type;
T content;
Data({this.click_action, this.status, this.type, this.content});
Data.fromJson(Map<String, dynamic> json) {
click_action = json['click_action'];
status = json['status'];
type = json['type'];
content = json['content'] != null?
this.content.deserializeContent(json['content'])
: null;
}
}
abstract class BaseDataContent{
BaseDataContent deserializeContent(Map<String, dynamic> jsonString);
}
class OrderMessageContent extends BaseDataContent {
int orderId;
OrderMessageContent({this.orderId}) : super();
#override
OrderMessageContent deserializeContent(Map<String, dynamic> jsonString){
///Deserialize Content json.
}
}
To test my code, I wrote some demo code as below
String json = '{"priority": "high", "to": "343434", "data":{"click_action":"343434","content":{"order_id" : "1234"}}}';
var jsonResponse = jsonDecode(json);
var result = Message<OrderMessageContent>.fromJson(jsonResponse);
The code is failed when it reaches to line
this.content.deserializeContent(json['content'])
The error message is "NoSuchMethodError: the method deserializeContent was called on null. Receiver:null". I don't know why deserializeContent method in OrderMessageContent doesn't get called, please help.
Thanks.

In Data constructor you called a method on a null object, where you called deserializeContent. You should either make that method static or initialize content first.
Data.fromJson(Map<String, dynamic> json) {
click_action = json['click_action'];
status = json['status'];
type = json['type'];
content = json['content'] != null?
this.content.deserializeContent(json['content'])
: null;
}

Related

Using JSON Files for localization in GetX

I am trying to read translation files eg. en.json to use with GetX localization. I tried the following code but no success. Is there any better way of doing it? Somebody suggested using auto_localize with GetX, but I'll really appreciate if I can proceed without an extra package
Map<String, Map<String, String>> get keys => {
"ar": readJson("ar"),
"en": readJson("en"),
}
I tried loading the localization information using the following function
// Fetch content from the json file
Map<String, String> readJson(String languageCode) {
Map data = {};
rootBundle
.loadString('assets/translations/$languageCode.json')
.then((response) {
data = json.decode(response);
});
return data.map((key, value) {
return MapEntry(key, value.toString());
});
}
DebugPrint() gets shows that the files were successfully loaded. However, trying to display the loaded Maps doesn't work
The readJson should be Future and because of that the empty Map returned at the end.
The way i did
create languages.json file and put all texts to it.
[
{
"name": "English",
"code": "en_US",
"texts": {
"hello": "Hello World"
}
},
{
"name": "Arabic",
"code": "ar_AE",
"texts": {
"hello": "مرحبا بالعالم"
}
}
]
implement localization class like this
class LocalizationService extends Translations {
#override
Map<String, Map<String, String>> get keys => {};
static Future<void> initLanguages() async {
final _keys = await readJson();
Get.clearTranslations();
Get.addTranslations(_keys);
}
static Future<Map<String, Map<String, String>>> readJson() async {
final res = await rootBundle.loadString('assets/languages.json');
List<dynamic> data = jsonDecode(res);
final listData = data.map((j) => I18nModel.fromJson(j)).toList();
final keys = Map<String, Map<String, String>>();
listData.forEach((value) {
final String translationKey = value.code!;
keys.addAll({translationKey: value.texts!});
});
return keys;
}
}
As you see after getting the Map data, i used Get.addTranslations(); to set language keys.
this is a GetX function to add languages on air!
create model class to parse json data
class I18nModel {
String? name;
String? code;
Map<String, String>? texts;
I18nModel(
{this.name, this.code, this.texts});
I18nModel.fromJson(Map<String, dynamic> json) {
name = json['name'];
code = json['code'];
if (json['texts'] != null) {
texts = Map<String, String>.from(json['texts']);
}
}
}
And finally call await LocalizationService.initLanguages(); before going to your first Route.

Swagger 2 Use fields instead of getters+setters

I have the following java REST service
#GET
#Path("/{customerId}")
#Operation(
summary = "Test summary",
description = "Test desciption",
responses = {
#ApiResponse(
responseCode = "200",
content = #Content(
schema = #Schema(implementation = CustomerDto.class),
mediaType = "application/json"),
description = "CustomerDto description"),
#ApiResponse(
responseCode = "400",
description = "Description for 400 status")
//Here you can add all response statuses and their's descriptions
})
public CustomerDto findCustomerById(#Parameter(description = "Id of the customer", required = true) #PathParam("customerId") Long customerId) {
return apiFacade.getCustomerDtoByCustomerId(customerId);
}
Simple dto-class
public class CustomerDto {
private TestInfo testInfo;
}
Simple subclass
public class TestInfo {
private Long testId;
private Long checkId;
public TestInfo(Long testId, Long checkId) {
this.testId = testId;
this.checkId = checkId;
}
public Long getTestId() {
return testId;
}
public Long getCheckId() {
return checkId;
}
public boolean isDecisionMade() {
return testId != null || checkId != null;
}
}
I'd like to see TestInfo-json without 'decisionMade' section. Can you help me It is possible?
"TestInfo" : {
"type" : "object",
"properties" : {
"testId" : {
"type" : "integer",
"format" : "int64"
},
"checkId" : {
"type" : "integer",
"format" : "int64"
},
"decisionMade" : {
"type" : "boolean"
}
}
}
Is it possible to tell swagger use class-fields instead of getter+setter?
I use swagger version 2.2.2. Generate json via BaseOpenApiResource. All configs are default.
I have found the decision:
Json.mapper().setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
Json.mapper().setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);

failed due to: Bad state: No builder factory for BuiltList

I am using builtvalue for my PODO class
Following is my json response
{
"status": 1,
"msg": "Success",
"allotmentMasterID": "1",
"allotmentInfoID": "1",
"category": [
{
"categoryID": "1",
"categoryName": "Major",
"selectedCount": "0",
"status": 1
},
{
"categoryID": "2",
"categoryName": "Mandatory",
"selectedCount": "0",
"status": 0
},
{
"categoryID": "3",
"categoryName": "Minor",
"selectedCount": "0",
"status": 0
}
]
}
I have created a built value for this
Following are the classes
library specialisation_model_first_screen;
import 'package:built_collection/built_collection.dart';
import 'package:built_value/built_value.dart';
import 'package:built_value/serializer.dart';
part 'specialisation_model_first_screen.g.dart';
abstract class SpecialisationModelFirstScreen
implements
Built<SpecialisationModelFirstScreen,
SpecialisationModelFirstScreenBuilder> {
SpecialisationModelFirstScreen._();
factory SpecialisationModelFirstScreen(
[updates(SpecialisationModelFirstScreenBuilder b)]) =
_$SpecialisationModelFirstScreen;
#nullable
#BuiltValueField(wireName: 'status')
int get status;
#nullable
#BuiltValueField(wireName: 'msg')
String get msg;
#nullable
#BuiltValueField(wireName: 'allotmentMasterID')
String get allotmentMasterID;
#nullable
#BuiltValueField(wireName: 'allotmentInfoID')
String get allotmentInfoID;
#nullable
#BuiltValueField(wireName: 'category')
BuiltList<Category> get category;
static Serializer<SpecialisationModelFirstScreen> get serializer =>
_$specialisationModelFirstScreenSerializer;
}
abstract class Category implements Built<Category, CategoryBuilder> {
Category._();
factory Category([updates(CategoryBuilder b)]) = _$Category;
#nullable
#BuiltValueField(wireName: 'categoryID')
String get categoryID;
#nullable
#BuiltValueField(wireName: 'categoryName')
String get categoryName;
#nullable
#BuiltValueField(wireName: 'selectedCount')
String get selectedCount;
#nullable
#BuiltValueField(wireName: 'status')
int get status;
static Serializer<Category> get serializer => _$categorySerializer;
}
library serializers;
import 'package:built_value/serializer.dart';
part 'package:dice_clutter/models/serializers/serializers.g.dart';
#SerializersFor(const [SpecialisationModelFirstScreen])
Serializers serializers = _$serializers;
Serializers standardSerializers = (serializers.toBuilder()..addPlugin(StandardJsonPlugin())).build();
Following is my serializers.g.dart file
part of serializers;
Serializers _$serializers = (new Serializers().toBuilder()
..add(SpecialisationModelFirstScreen.serializer))
.build();
When i am making an api request i am getting the response correctly but following error is thrown
failed due to: Bad state: No builder factory for BuiltList<Category>. Fix by adding one, see SerializersBuilder.addBuilderFactory.
Is this a bug in build value library itself or am i doing something wrong?
In some cases - and I do not fully understand specific reasons - my objects fail to deserialize with StandartJsonPlugin.
I had to edit my serializers.g.dart file as follows
Serializers _$serializers = (new Serializers().toBuilder()
..add(SpecialisationModelFirstScreen.serializer)
..add(Category.serializer)
..addBuilderFactory(
const FullType(
BuiltList, const [const FullType(Category)]),
() => new ListBuilder<Category>())
)
.build();
Thanks to Tensor Programming Video on Youtube
There is some bug in the builtvalue library as it is not able to generate serializers.g.dart properly in some cases. Hope It gets resolved in the future
You need to add the Category class to the list of classes that can be serialized:
#SerializersFor(const [
SpecialisationModelFirstScreen,
Category, <-- Category available for serialization
])
then regenerate your built_value classes.
More details can be found in this answer
base on JSON and serialization - Flutter and your code ,
you are missing fromJson ,
using JSON to Dart
see the fromJson function
class Autogenerated {
int status;
String msg;
String allotmentMasterID;
String allotmentInfoID;
List<Category> category;
Autogenerated(
{this.status,
this.msg,
this.allotmentMasterID,
this.allotmentInfoID,
this.category});
Autogenerated.fromJson(Map<String, dynamic> json) {
status = json['status'];
msg = json['msg'];
allotmentMasterID = json['allotmentMasterID'];
allotmentInfoID = json['allotmentInfoID'];
if (json['category'] != null) {
category = new List<Category>();
json['category'].forEach((v) {
category.add(new Category.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['status'] = this.status;
data['msg'] = this.msg;
data['allotmentMasterID'] = this.allotmentMasterID;
data['allotmentInfoID'] = this.allotmentInfoID;
if (this.category != null) {
data['category'] = this.category.map((v) => v.toJson()).toList();
}
return data;
}
}
class Category {
String categoryID;
String categoryName;
String selectedCount;
int status;
Category(
{this.categoryID, this.categoryName, this.selectedCount, this.status});
Category.fromJson(Map<String, dynamic> json) {
categoryID = json['categoryID'];
categoryName = json['categoryName'];
selectedCount = json['selectedCount'];
status = json['status'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['categoryID'] = this.categoryID;
data['categoryName'] = this.categoryName;
data['selectedCount'] = this.selectedCount;
data['status'] = this.status;
return data;
}
}

Convert a class into JSON or List

How to convert this class into JSON or List?
class cliente {
int id;
String nome;
String apelido;
String sexo;
String status;
}
Edit
I'm changed my class and works fine to my case:
class client {
Map<String, dynamic> fields => {
"id": "",
"name": "",
"nickname": "",
"sex": "",
"status": "",
}
Then I use:
client.fields["id"] = 1;
client.fields["name"] = "matheus";
sqlite.rowInsert("insert into client(id, name)", client.fields.Keys.toList(), client.fields.Values.toList());
Just create a method inside your class and return a Map<String, dynamic>
class cliente {
int id;
String nome;
String apelido;
String sexo;
String status;
Map<String, dynamic> toJson() => {
'id': id,
'nome': nome,
'apelido': apelido,
'sexo': sexo,
'status': status,
};
}
And use it for example :
final dataObject = new client();
...fill your object
final jsonData = dataObject.toJson();
Also you can try using this package to avoid writing all of your fields : https://pub.dartlang.org/packages/json_serializable

Need help to parsing JSON in Flutter

I am trying to get data from the internet in Flutter.
But I am getting an error on JSON parsing.
Can anyone tell me what is the problem?
I am trying to get data from this URL
https://swapi.co/api/starships/
Example JSON
{
"count": 37,
"next": "https://swapi.co/api/starships/?page=2",
"previous": null,
"results": [
{
"name": "Executor",
"model": "Executor-class star dreadnought",
"manufacturer": "Kuat Drive Yards, Fondor Shipyards",
"cost_in_credits": "1143350000",
"length": "19000",
"max_atmosphering_speed": "n/a",
"crew": "279144",
"passengers": "38000",
"cargo_capacity": "250000000",
"consumables": "6 years",
"hyperdrive_rating": "2.0",
"MGLT": "40",
"starship_class": "Star dreadnought",
"pilots": [],
"films": [
"https://swapi.co/api/films/2/",
"https://swapi.co/api/films/3/"
],
"created": "2014-12-15T12:31:42.547000Z",
"edited": "2017-04-19T10:56:06.685592Z",
"url": "https://swapi.co/api/starships/15/"
},
]
}
Model class
class RestModel {
final String name;
final String model;
final String manufacturer;
final String cost_in_credits;
final String length;
final String max_atmosphering_speed;
final String crew;
final String passengers;
final String cargo_capacity;
final String consumables;
final String hyperdrive_rating;
final String MGLT;
final String starship_class;
final List films;
final String pilots;
final String created;
final String edited;
final String url;
RestModel(
{this.name,
this.model,
this.manufacturer,
this.cost_in_credits,
this.length,
this.max_atmosphering_speed,
this.crew,
this.passengers,
this.cargo_capacity,
this.consumables,
this.hyperdrive_rating,
this.MGLT,
this.starship_class,
this.films,
this.pilots,
this.created,
this.edited,
this.url});
factory RestModel.fromJson(Map<String, dynamic> json) {
return RestModel(
name: json["name"],
model: json["model"],
manufacturer: json["manufacturer"],
cost_in_credits: json["cost_in_credits"],
max_atmosphering_speed: json["max_atmosphering_speed"],
crew: json["crew"],
passengers: json["passengers"],
cargo_capacity: json["cargo_capacity"],
consumables: json["consumables"],
hyperdrive_rating: json["hyperdrive_rating"],
MGLT: json["MGLT"],
starship_class: json["starship_class"],
films: json["flims"],
pilots: json["pilots"],
created: json["created"],
edited: json["edited"],
url: json["url"],
);
}
}
and the Flutter code is:
final link = "https://swapi.co/api/starships/";
List<RestModel> list;
Future getData() async {
var res = await http
.get(Uri.encodeFull(link), headers: {"Accept":"application/json"});
if (res.statusCode == 200) {
var data = json.decode(res.body);
var rest = data["results"];
for (var model in rest) {
list.add(RestModel.fromJson(model));
}
print("List Size: ${list.length}");
}
}
The main problem is when it tries to fill data from JSON.
RestModel.fromJson(model)
so what I have to change to fix this problem.
Try to cast the data 'results' to List , like this :
var rest = data["results"] as List;
Updated
Now that we know the error log: "No static method 'fromJson' declared in class 'RestModel'"
It's because you are using a static method in this line:
list.add(RestModel.fromJson(model));
You must change the call in order to use the factory constructor, like this :
list.add(new RestModel.fromJson(model));

Resources