Home > other >  fetch user data from server flutter
fetch user data from server flutter

Time:05-04

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).

  • Related