I'm following this tutorial from devs docs to fetch data from internet but I can't decode response to User
object.
Since I'm using Postman
to check API I can tell you that my request is successfully received and server responses me with 200
and a body full of data (name
, id
and token
) but when I try to call fromJson
method inside try-catch
block to create User
object it fails and this error is printed :
flutter: Response 200
flutter: type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'String'
This is my User class :
class User extends Equatable {
final String token;
final String name;
final String id;
const User({
required this.token,
required this.name,
required this.id,
});
static const User empty = User(token: "token", name: "name", id: "id");
Map<String, dynamic> toMap() {
return <String, String>{
'token': token,
'name': name,
'id': id,
};
}
factory User.fromMap(Map<String, dynamic> json) {
return User(
token: json['token'] ?? "token",
name: json['name'] ?? "name",
id: json['id'] ?? "id",
);
}
String toJson() => json.encode(toMap());
factory User.fromJson(Map<String, dynamic> jsonMap) => User.fromMap(jsonMap);
@override
String toString() {
return token ' ' name ' ' id '\n';
}
@override
List<Object?> get props => [token, name, id];
}
UserCredentials
that's just a wrapper for username and password :
class UserCredentials {
final String name;
final String email;
final String password;
UserCredentials(
{required this.name, required this.email, required this.password});
factory UserCredentials.fromJson(Map<String, dynamic> json) {
return UserCredentials(
name: json['name'] as String,
email: json['email'] as String,
password: json['password'] as String,
);
}
Map<String, String> toJsonRegistration() {
return {
"name": name,
"email": email,
"password": password,
};
}
Map<String, String> toJsonLogin() {
return {
"email": email,
"password": password,
};
}
@override
String toString() {
return name " " email " " password '\n';
}
}
And this is my user_repository
function used to get data from server :
Future<User> _login(UserCredentials user) async {
User? userFetched;
//print(user);
http.Response response = await http.post(
Uri.parse("http://link_to_repo"),
body: user.toJsonLogin(),
);
print("Inizio");
print((response.body));
print(jsonDecode(response.body));
print(User.fromJson(jsonDecode(response.body)));
print("fine");
if (response.statusCode == 200) {
print(response.statusCode);
try {
userFetched = User.fromJson(jsonDecode(response.body));
print(userFetched);
} catch (e) {
print(e);
}
print("End");
return userFetched!;
} else {
print("Login Failure from repository");
throw Exception("Email already taken");
}
}
As you seen above it's able to print "Response 200" but not to complete successfully the operation inside try-catch
block.
Edit :
This is the response.body
:
{"data" : {"token":"50|IUMNqKgc7Vffmz8elRd0MIZeSyuEgHL418KwQ0Jz","name":"test","id":1}}
And jsonDecode(response.body))
:
{data: {token: 50|IUMNqKgc7Vffmz8elRd0MIZeSyuEgHL418KwQ0Jz, name: test, id: 1}}
CodePudding user response:
Looking at your code it looks like you are doing the following
User.fromJson(jsonDecode(response.body));
and from there you are creating the User instance with the following factory
factory User.fromMap(Map<String, dynamic> json) {
return User(
token: json['token'] ?? "token",
name: json['name'] ?? "name",
id: json['id'] ?? "id",
);
The issue is that your Json is contained in another json called "data" as your print response show
{data: {token: 50|IUMNqKgc7Vffmz8elRd0MIZeSyuEgHL418KwQ0Jz, name: test, id: 1}}
in this case you have two solution, either you nest the keys inside your constructor (don't suggest you this approach)
factory User.fromMap(Map<String, dynamic> json) {
return User(
token: json['data']['token'] ?? "token",
name: json['data']['name'] ?? "name",
id: json['data']['id'] ?? "id",
);
Or where you parse the json you can extract directly the data
json (I suggest you this option)
User.fromJson(jsonDecode(response.body)['data']);
CodePudding user response:
jsonDecode(response.body)
will return a Map
. However your constructor User.fromJson
takes a String
as argument. Decoding it once is enough and from then on your can work with the Map
you already have. So no need to call jsonDecode
again in fromJson
, this should work fine:
factory User.fromJson(Map<String, dynamic> jsonMap) =>
User.fromMap(jsonMap);
If we look at your exception it tell us just that:
flutter: type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'String'
Your constructor expects a String
but received a Map (_InternalLinkedHashMap
).