In Flutter, an object's fromJson method generated by freezed occured type cast error, DateTime -> String - flutter

I have used freezed library to manage my Remote DTO classes and Ui Models.
In this question case, my Object LifeDiagnosisResult DTO and Ui Model only have one difference - createdAt field.
When I put data, I used SharedPreference (because backend is not yet built) and put my List value by jsonEncode function.
When I get a response from SharedPreference (because backend is not yet built) , I used jsonDecode to get Map Object.
To achieve my final wish, List Object, I added createdAt field like this.
void _handleListResponseSuccess(List<String> response) {
List<LifeDiagnosisResultUiModel>? uiModelList = response.map((e) {
Map<String, dynamic> map = jsonDecode(e) as Map<String, dynamic>;
map['createdAt'] = DateTime.now();
return LifeDiagnosisResultUiModel.fromJson(map);
}).toList();
if (uiModelList.isNotEmpty) {
setLifeDiagnosisResultUiModel(uiModelList[uiModelList.length - 1]);
}
_rxList(uiModelList);
}
But, when I ran this code, type casting error was caused.
The error message is this.
error type 'DateTime' is not a subtype of type 'String' in type cast
And this is my Ui Model's createdAt field.
I think Map's createdAt could not find correct field in Ui Model.
But I have no idea why...
Could you tell me what is wrong?

I found the answer...
it is not a real answer of my question, but it can explain why this issue happen.
In dart, Map's DateTime cannot be converted to Object's DateTime.
I dont know a detail process, but in type casting, Map's DateTime is converted to String type.

I don't know if I get it right, but if you want to test your fromJson method you could do it like this:
Map<String,dynamic> response={'att1':1234,'att2':'oneTwoThree','createdAt':DateTime(2022,08,22)};
return LifeDiagnosisResultUiModel.fromJson(response);

Related

Why does casting this string in this way work but not the other way?

I am reading data from Firestore. I was getting an error that said type 'Timestamp' is not a subtype of type 'String' in type cast, so I changed the createDate line below to use .toString() instead of doing as String. This solved the problem, but why did this work?
Notification.fromJson(Map<dynamic, dynamic>? json): //Transform JSON into Notification
createDate = (json?['createDate']).toString(), //This works
modifiedDate = json?['modifiedDate'] as String; //This gives the error: 'type 'Timestamp' is not a subtype of type 'String' in type cast'
The format of both of these fields is October 5, 2022 at 10:49 PM UTC-5.
Like it is said in error message, you are getting this error, because the value's type, which is coming from json is Timestamp, not String. It is better to make createDate and modifiedDate fields in Timestamp type instead of String, because it provides it's own methods, that make your work easier. Converting to String (and probably, you are parsing this String in somewhere) is redundant.
In Flutter every object class have toString() method, which is return a value with String type
(json?['createDate']).toString();
So if you write that code above, it will return a value with String type and then assign that value to createDate variable. It means you not assign json?['createDate'].
modifiedDate = json?['modifiedDate'] as String;
That line above will error because you assign that json to modifiedDate which is have different type. Cast (as) only work if the object/variable have same hierarchy, like
Child child = Child();
Parent parent = child as Parent();
json?['createDate'] is a Timestamp type object that just contains numbers describing a point in time. As you can see from the API documentation, it has a method called toString that knows how to convert that to formatted date string. Since a Timestamp it is not a subclass of String, Dart does not allow it to be cast to one. Perhaps you want to cast it to Timestamp instead, since that's what it is?

Instance member 'fromJson' can't be accessed using static access

I ran into a problem with my Flutter app. The error I get in news_service.dart is "The sample member 'fromJson' cannot be accessed using static access." error. Anyone know what this is about? I really don't understand... Thanks in advance!!
if (response.body.isNotEmpty) {
final responseJson = json.decode(response.body);
News news = News.fromJson(responseJson);
return news.articles;
}
return null;
}
}
Try to replace News.fromJson(responseJson); with News().fromJson(responseJson);
You should instantiate New class then call instance method fromJson this method is not static so can't be accessed directly.
final news = News();
//now call news.fromJson(responseJson)
The fromJson function needs to either be static, or a (factory) constructor. It is hard to give an exact working answer because you have not provided additional information that was asked from you, but the following sample fromJson implementation should work with little modification from your part:
News.fromJson(Map<String, dynamic> json)
: title = json['title'],
content = json['content'];
I am assuming your News data model has title and content fields, feel free to change it according to your exact data model.

How to initialize Either Right to an empty value in flutter

I recently made the switch to null-safety in my flutter project, which brings a new kind of problem when using the Either type (from the dartz package)
For example, before I would have some properties in my class like this:
Either<Failure, List<Product>> _products;
I would then have a function to get the products, and consume it in my view.
However, now with null safety, I need to initialize this property, because it should never be null, instead I would like to have an empty list.
If I do this
Either<Failure, List<Product?>> _products = [];
I get this error
A value of type 'List<dynamic>' can't be assigned to a variable of type 'Either<Failure, List<Product?>>'.
So my question would be, how can I initialize this property to the right value of Either, with an empty list?
Have this go:
Either<Failure, List<Product?>> _products = right([]);
you can use the new 'late' keyword that fixes this issue
late Either<Failure, List<Product?>> _products;
read more about it here

Deserialise JSON response from API into type in Dart

I'm going out of my mind trying to figure this out because it's such a simple thing to do and there's no information on it anywhere.
I have an API call that is returning a Student which is defined like this
class Student {
String photo;
String upn;
String fullName;
String studentClass;
String studentYear;
}
I'm using the dart http library to make a call to an API that returns an array of a number of students. All I want to do is deserialise the string returned by the API into a typed list so that I can actually do something with it.
studentFuture.then((res) {
log(res.body.toString());
List<dynamic> dynamicList = jsonDecode(res.body);
var student = dynamicList[0] as Student;
});
Trying to cast it to Student using as doesn't work, I just get this
Unhandled Exception: type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'Student' in type cast
All I want is to be able to do something like this like you can in C#
var obj = DeserialiseJson<Type>(jsonString);
No matter what I try I can't get the string to be deserialised into an object, how do I do this?
You can't do this in Dart/Flutter in the way that you want. This is because in order to accomplish this functionality in C#, Java, or any other strongly-typed language that supports this feature, the language uses reflection under the hood to map JSON fields to object fields.
Dart also has reflection functionality in the dart:mirror library. However, this library is disabled in Flutter due to its tendency to create large app footprints due to all the debug information for various types and fields and things needing to be included in the compiled binary. (That's the very simple explanation: the official reason given by the Flutter team goes into more detail.)
What this means is when you deserialize a JSON string, you get an object that is essentially a List<dynamic> or Map<String, dynamic> (depending on the JSON string in question; it may even just be some primitive value). Assuming the latter, you cannot directly convert between a Map and a Student because they are completely different types with nothing in common. There is no implicit way to take the Map and generate a Student because the reflection tools necessary to do so don't exist within a Flutter app. As a result, you get an error saying you cannot convert a Map to a Student, as you can see.
To work around this, you are encouraged to add a toJson/fromJson method to your model class so you can explicitly generate an instance of the class from a deserialized JSON string:
class Student {
String photo;
String upn;
String fullName;
String studentClass;
String studentYear;
factory Student.fromJson(Map<String, dynamic> jsonData) {
return Student()
..photo = jsonData['photo']
..upn = jsonData['upn']
..fullName = jsonData['fullName']
..studentClass = jsonData['studentClass']
..studentYear = jsonData['studentYear'];
}
}
And use it like so:
studentFuture.then((res) {
log(res.body.toString());
List<dynamic> dynamicList = jsonDecode(res.body);
var student = Student.fromJson(dynamicList[0]);
});
This is all well and good, but if you have more than a few model classes you will quickly realize that adding this boilerplate code to every model class gets really tedious really quick. That's why various packages exist to try and alleviate the burden with automatic code generation, such as json_serializable and freezed.
TL;DR: What you are asking for is not currently possible in Flutter Dart, and that isn't likely to change any time soon. You need to either write a manual factory constructor to make the conversion or use a code-gen package that generates the constructors for you.
Have a look at https://pub.dev/packages/json_serializable which, through code generation, will give you methods like
#JsonSerializable()
class Student {
String photo;
String upn;
String fullName;
String studentClass;
String studentYear;
Student({this.photo, this.upn, this.fullName, this.studentClass, this.studentYear});
factory Student.fromJson(Map<String, dynamic> json) => _$StudentFromJson(json);
Map<String, dynamic> toJson() => _$StudentToJson(this);
}
Then you can do:
var list = jsonDecode(res.body);
for (var map in list) {
print(Student.fromJson(map)); // Student class
}

Cannot access map object with []. operator [] is not defined

I am working on a mobile application using Flutter 1.7.8.
I collected data from the server in a form of a nested json object, e.g.
class Obj {
final String obj_to_access;
const Obj({this.obj_to_access});
factory Obj.fromJson(Map<String, dynamic> json) {
return Obj(
obj_to_access: json['item']
);
}
some_obj =
{
"id": some_id,
"nested_obj": Obj.fromJson(json["obj_to_access"])
}
However, I can't seem to access the nested Obj with '[]' and it always give me this error: The operator '[]' isn't defined for the class 'Obj'. Try defining the operator.
I saw that I should try defining the '[]' but I am not sure how. Please help thanks
The reason for the error is that you're passing a dynamic value to Obj.fromJson() instead of a Map - which it expects. To solve this issue, you can add cast as <Map<String, dynamic>> to the value to let the compiler know that it's a Map.