After generating part 'UserModel.g.dart'; successfully, I got the following error upon trying to do
if (user != null) {
var uid = user.providerData.first.uid;
var displayName = user.providerData.first.displayName;
var email = user.providerData.first.email;
var phoneNumber = user.providerData.first.phoneNumber;
var providerId = user.providerData.first.providerId;
var photoUrl = user.providerData.first.photoURL;
UserModel userModel = UserModel(phoneNumber,
uid: uid,
displayName: displayName,
email: email,
providerId: providerId,
photoUrl: photoUrl);
Query query = users.where('uid', isEqualTo: uid);
query.get().then((querySnapshot) => {
if (querySnapshot.size < 1) {addUser(userModel)}
});
}
}
And here is my UserModel.dart without any errors in the file.
import 'package:json_annotation/json_annotation.dart';
part 'UserModel.g.dart';
#JsonSerializable()
class UserModel {
late String? uid;
late String? displayName;
late String? email;
late String? phoneNumber;
late String providerId;
late String? photoUrl;
UserModel(this.phoneNumber, {required this.uid, required this.displayName, required this.email,
required this.providerId, required this.photoUrl});
factory UserModel.fromJson(Map<String, dynamic> json) => _$UserModelFromJson(json);
Map<String, dynamic> toJson() => _$UserModelToJson(this);
}
Offcourse, I could manually map each value. But as recommended here https://flutter.dev/docs/development/data-and-backend/json, to prevent
Manual decoding does not perform well when your project becomes bigger. Writing decoding logic by hand can become hard to manage and error-prone. If you have a typo when accessing a nonexistent JSON field, your code throws an error during runtime.
I used the plugin json_serializable to sort of automate it.
But I got the following error:
Expected a value of type 'Map<String, dynamic>', but got one of type 'UserModel$'
Is this error expected due to incompatibility or am I doing something wrong? Please respond. Thank you very much. :)
I'm guessing this is where your issue is.
query.get().then((querySnapshot) => {
if (querySnapshot.size < 1) {addUser(userModel)}
});
Use the toJson method you created in your model class to pass in a Map instead of your custom UserModel object.
{addUser(userModel.toJson())}
Related
I want to fetch and format json data from this as a trial in flutter. However, during the formatting process, an exception occurs: type 'Null' is not a subtype of type 'String'.
And these are my code:
user_model.dart
class User {
int id;
String email;
String firstName;
String lastName;
String avator;
User({
required this.id,
required this.email,
required this.firstName,
required this.lastName,
required this.avator
});
factory User.fromJson(Map<String, dynamic> json) => User(
id : json['id'],
email : json['email'],
firstName : json['first_name'],
lastName : json['last_name'],
avator : json['avator']
);
}
user_api.dart
...
class UserApi {
Future<List<User>?> getUsers() async {
final url = Uri.parse('https://reqres.in/api/users?page=2');
try {
final res = await http.get(url);
if (res.statusCode == 200) {
final Map<String, dynamic> body = jsonDecode(res.body);
final List<User> users = body['data'].map((dynamic userData) => {
print('userData : $userData');
User.fromJson(userData) // There seems to be an error here.
}).toList();
return users;
} else {
return null;
}
} catch (e) {
print(e.toString());
}
return null;
}
}
And userData seems like this in my console:
flutter: userData : {id: 7, email: michael.lawson#reqres.in, first_name: Michael, last_name: Lawson, avatar: https://reqres.in/img/faces/7-image.jpg}
I don't think userData is kind of Null, but why do I get the exception?
You need to use json['avatar'] instead of json['avator']
factory User.fromJson(Map<String, dynamic> json) => User(
id : json['id'],
email : json['email'],
firstName : json['first_name'],
lastName : json['last_name'],
avator : json['avatar'] //here `a` instead of `o`
);
I just checked the link you have mentioned for the json you are using. There is a typo at your end. In the json, avatar is the correct field spelling. You have mentioned avator in your class's factory constructor.
So, avator is Null and thus, String avator is assigned to a Null value.
FYI: The error type 'Null' is not a subtype of type 'String' means that you are trying to assign a Null value to a String type variable.
its a typo in the fromJson method : as mentioned by yeasin-sheikh (You need to use json['avatar'] instead of json['avator']),
Yeasin-sheikh's answer
there are some json parsing websites, using that we can easily generate model class and other methods related to it.
eg : app.quicktype.io
just input the json response and generate the model class in required language.
I am very confused about this problem, parsing JSON to model always fails with the message "null is not subtype of type string in type cast". I've made sure all values are not null. When manually initiating the model with the constructor, it works, but using the fromJson function always fails.
This is my model:
import 'package:freezed_annotation/freezed_annotation.dart';
part 'user_model.g.dart';
part 'user_model.freezed.dart';
#freezed
class UserModel with _$UserModel {
const factory UserModel({
required String id,
required String identity,
required String name,
required String email,
required String phone,
required String role,
required String? createdAt,
required String? updatedAt,
required String? accountVerifiedAt,
required String jsonWebToken,
}) = _UserModel;
const UserModel._();
factory UserModel.fromJson(Map<String, dynamic> json) =>
_$UserModelFromJson(json);
}
This is how I parse with fromJson, but it always throw exception (null is not a subtype of type string in type cast):
var user = UserModel.fromJson(object['data']);
But it works:
var user = UserModel(
id: object['data']['id'],
identity: object['data']['identity'],
name: object['data']['name'],
email: object['data']['email'],
phone: object['data']['phone'],
role: object['data']['role'],
createdAt: object['data']['createdAt'],
updatedAt: object['data']['updatedAt'],
accountVerifiedAt: object['data']['accountVerifiedAt'],
jsonWebToken: object['data']['jsonWebToken'],
);
Maybe your json has null value that you don't recognized. It happens to me all the times. By the way, I never use freezed_annotation because model is easy to write by hands, plus if there is is a bug, it would be easier to fix. You can do like this:
When back-end responds data:
if (json["error_code"] != 0) { // Back-end responds an error
// handle error
} else {
YourModel.fromJson(json);
}
And here is your model:
class YourModel {
String variable;
YourModel(this.variable = <default value>);
YourModel.fromJson(Map<String, dynamic> json) {
this.variable = json["blah"]["yolo"] ?? <value if json null>;
}
}
I just figured out, that it's caused by the generated freezed class, my JSON response use camelCase as keys, but the model generates with snake_case. So basically I convert my response keys from camelCase to snake_case before initiating the object using the fromJson function.
Map<String, dynamic> data = {};
for (var element in Map<String, dynamic>.from(object['data']).entries) {
data[StringUtils.camelCaseToLowerUnderscore(element.key)] =
element.value;
}
object['data'] = UserModel.fromJson(data);
If anyone facing the same problem, I hope it can help. And if anyone has a better solution, you guys can post it here too. Thank you.
NB: I use basic_utils: ^4.4.3
EDIT:
Following this article https://codewithandrea.com/articles/parse-json-dart-codegen-freezed/, we can add #JsonKey annotation to make sure the property name is correct, example:
#JsonKey(name: 'customerId') required String? customerId
Without giving #JsonKey annotation, customerId will be written as customer_id.
response body from the .NET Core 6 API :
[{"establishmentId":1,"establishmentName":"Secret","addressId":1,"documentationId":1,"address":null,"documentation":null,"associationsEts":[],"prices":[]},{"establishmentId":2,"establishmentName":"HRB","addressId":2,"documentationId":2,"address":null,"documentation":null,"associationsEts":[],"prices":[]}]
My model class :
class Establishment {
final int id;
final String name;
final int addressid;
final int documentationid;
Establishment(
{required this.id,
required this.name,
required this.addressid,
required this.documentationid});
factory Establishment.fromJson(Map<String, dynamic> json) {
return Establishment(
id: json['id'],
name: json['name'],
addressid: json['addressid'],
documentationid: json['documantationid']);
}
}
The problem is that snapshot got an error , I would like snapshot to accept null values, could someone help me to fix this ?
Thanks
UPDATE :
The problem doesn't occurs in the from json beacuase since I modified them, the probelm still occurs
class Establishment {
final int establishmentId;
final String establishmentName;
final int addressId;
final int documentationId;
Establishment(
{required this.establishmentId,
required this.establishmentName,
required this.addressId,
required this.documentationId});
factory Establishment.fromJson(Map<String, dynamic> json) {
return Establishment(
establishmentId: json['establishmentId'],
establishmentName: json['establishmentName'],
addressId: json['addressId'],
documentationId: json['documantationId']);
}
}
The error ocurrs in your "fromJson" parsing, replace your factory fromJson function to this:
factory Establishment.fromJson(Map<String, dynamic> json) {
return Establishment(
id: json['establishmentId'],
name: json['establishmentName'],
addressid: json['addressId'],
documentationid: json['documentationId']);
}
Tip: Check the camelCase name, the names must be equals in your parsing.
I am trying to upgrade my Flutter app to be Null Safe and I encountered a problem with the retrofit code generator.
So I have a RestAPI abstract class declared like this:
#RestApi(baseUrl: ApiConsts.authBaseURL)
abstract class IAuthApi {
factory IAuthApi(Dio dio) = _IAuthApi;
#POST(ApiConsts.verifyPath)
Future<AccessToken> verifyToken(#Body() VerifyBody body);
}
and the data classes are as follows:
#JsonSerializable(includeIfNull: false)
class VerifyBody {
#JsonKey(name: 'grant_type')
String grantType;
#JsonKey(name: 'client_id')
String clientId;
String username;
String otp;
String realm;
String audience;
String scope;
VerifyBody(this.username, this.otp,
{this.grantType = ApiConsts.GRANT_TYPE,
this.clientId = ApiConsts.CLIENT_ID,
this.realm = ApiConsts.SMS,
this.audience = ApiConsts.AUDIENCE,
this.scope = ApiConsts.AUTH_SCOPE});
factory VerifyBody.fromJson(Map<String, dynamic> json) => _$VerifyBodyFromJson(json);
Map<String, dynamic> toJson() => _$VerifyBodyToJson(this);
}
#JsonSerializable(includeIfNull: false)
class AccessToken {
#JsonKey(name: 'access_token')
String accessToken;
#JsonKey(name: 'refresh_token')
String refreshToken;
#JsonKey(name: 'id_token')
String idToken;
String scope;
#JsonKey(name: 'expires_in')
int expiresIn;
#JsonKey(name: 'token_type')
String tokenType;
AccessToken(this.accessToken, this.refreshToken, this.idToken, this.scope, this.expiresIn,
this.tokenType);
factory AccessToken.fromJson(Map<String, dynamic> json) => _$AccessTokenFromJson(json);
Map<String, dynamic> toJson() => _$AccessTokenToJson(this);
}
When I run the command to generate my retrofit code pub run build_runner build --delete-conflicting-outputs
I get the following error:
[SEVERE] retrofit_generator:retrofit on lib/services/api/AuthAPI.dart:
type 'ExpandIterable<InterfaceType, MethodElement>' is not a subtype of type 'Iterable<MethodElementImpl>' of 'iterable'
Has anyone encountered something like this?
This is a reported issue here.
It will be fixed in the next release.
For the temporary fix:
retrofit_generator:
git:
url: https://github.com/Chimerapps/retrofit.dart.git
ref: 9f90296751984b359937c38563da5b19db5550f5
path: generator
Using Flutter and Dart, lets say I have this class:
#JsonSerializable()
class User {
#JsonKey(nullable: true, required: false)
final String name;
#JsonKey(nullable: true, required: false)
final int age;
User({
this.name,
this.age,
});
factory User.fromJson(Map<String, dynamic> json) => _$AddressFromJson(json);
Map<String, dynamic> toJson() => _$AddressToJson(this);
#override
String toString() {
return 'User ${toJson().toString()}';
}
}
In my code I'm trying to create a new instance of this class to be sent to /update-user endpoint on the server. My goal is to send an object that contains just the properties I would like the server to update. let's say only update age.
Using
final dto = new UpdateUserRequest(
age: 34
);
results in this json representation: {name: null, age: 34}, which will override the already existing name on the server.
I also tried 'json merging' 2 objects, the one I already have with the name, and the new dto that updates age:
final combined = UpdateUserRequest.fromJson({
...(dtoWithAge.toJson()),
...(existingUserWithName.toJson()),
});
but no matter how I play around with these, they end up overriding each other.
So, is there anyway to get a json/DTO instance of the class, that only contains the properties and values I want the server to update? (trying to achieve something very similar to javascript)
I don't think there's a pre-implemented solution for that. Assuming I understood what you're trying to accomplish, how about adding a copyWith method to User?
User copyWith({
String name,
int age,
}) => User(
name: name ?? this.name,
age: age ?? this.age,
);
You'd use it like this:
final existingUser = User(name: 'John');
final updatedUser = existingUser.copyWith(age: 25);
sendUpdateRequestWith(updatedUser);
For you convenience, there's a plugin to generate it.
vscode: https://marketplace.visualstudio.com/items?itemName=BendixMa.dart-data-class-generator
IDEA/Android Studio: https://plugins.jetbrains.com/plugin/12429-dart-data-class