Search code examples
jsonflutterrestdartflutter-http

How to make a list from a class that the class need data from several database?


I have a problem to load a list from a class that the class need data from several database

The database:

A. userdata (_id, name, phone)

B. mariage status (_id, userID, mariageStatus)

--> "_id" in userdata & "userID" in marriage status are thing to be matched

read the userdata:

class User {
  final String idUser,
      name,
      phone;

  User(
      {this.idUser,
      this.name,
      this.phone});

  factory User.fromJson(Map<String, dynamic> json) {
    return User(
        idUser: json['_id'],
        name: json['name'],
        phone: json['phone']);
  }
}

List<User> userFromJson(jsonData) {
  List<User> result =
      List<User>.from(jsonData.map((item) => User.fromJson(item)));

  return result;
}

// index
Future<List<User>> fetchUser() async {
  String route = AppConfig.API_ENDPOINT + "userdata";
  final response = await http.get(route);
  if (response.statusCode == 200) {
    var jsonResp = json.decode(response.body);
    
    return userFromJson(jsonResp);
  } else {
    throw Exception('Failed load $route, status : ${response.statusCode}');
  }
}

while to read the marriage:

class Marriage{
  final String idUser, mariageStatus;

  Marriage(
      {this.idUser,
      this.mariageStatus});

  factory Marriage.fromJson(Map<String, dynamic> json) {
    return Marriage(
        idUser: json['userID'],
        name: json['mariageStatus']);
  }
}

List<Marriage> marriageFromJson(jsonData) {
  List<Marriage> result =
      List<Marriage>.from(jsonData.map((item) => Marriage.fromJson(item)));

  return result;
}

// index
Future<List<Marriage>> fetchMarriage() async {
  String route = AppConfig.API_ENDPOINT + "marriage";
  final response = await http.get(route);
  if (response.statusCode == 200) {
    var jsonResp = json.decode(response.body);
    
    return marriageFromJson(jsonResp);
  } else {
    throw Exception('Failed load $route, status : ${response.statusCode}');
  }
}

then how to make a list off combination class like this?

class User_Mariage {
  final String idUser,
      name,
      phone,
      mariageStatus;

  User(
      {this.idUser,
      this.name,
      this.phone,
      this.mariageStatus});

  factory User.fromJson(Map<String, dynamic> json) {
    return User(
        idUser: 
        name: 
        phone: 
        mariageStatus: 
  }
}

List<User> userFromJson(jsonData) {
  List<User> result =
      List<User>.from(jsonData.map((item) => User.fromJson(item)));

  return result;
}

of if there are another better way to make the list, please let me know, thank you very much


Solution

  • For me it looks like you are using the wrong database query. Maybe there is a possibility to use foreign keys (userId).

    There are different approaches to combine the data.

    Here is one example, where you could combine classes by reference and use getter

    Your original User class:

    import 'dart:convert';
    
    class User {
      final String id, name, phone;
    
      User({
        required this.id,
        required this.name,
        required this.phone,
      });
    
      factory User.fromJson(Map<String, dynamic> json) {
        return User(id: json['_id'], name: json['name'], phone: json['phone']);
      }
    }
    
    List<User> userFromJson(dynamic jsonData) {
      return List<User>.from(jsonData.map((item) => User.fromJson(item)));
    }
    
    Future<List<User>> fetchUser() async {
      String route = AppConfig.API_ENDPOINT + "userdata";
      final response = await http.get(route);
      if (response.statusCode == 200) {
        var jsonResp = json.decode(response.body);
        return userFromJson(jsonResp);
      } else {
        throw Exception('Failed load $route, status : ${response.statusCode}');
      }
    }
    

    Your original Mariage class

    import 'dart:convert';
    
    class Marriage {
      final String idUser, mariageStatus;
    
      Marriage({required this.idUser, required this.mariageStatus});
    
      factory Marriage.fromJson(Map<String, dynamic> json) {
        return Marriage(
            idUser: json['userID'], mariageStatus: json['mariageStatus']);
      }
    }
    
    List<Marriage> marriageFromJson(dynamic jsonData) {
      return List<Marriage>.from(jsonData.map((item) => Marriage.fromJson(item)));
    }
    
    // index
    Future<List<Marriage>> fetchMarriage() async {
      String route = AppConfig.API_ENDPOINT + "marriage";
      final response = await http.get(route);
      if (response.statusCode == 200) {
      var jsonResp = json.decode(response.body);
        //'[{"_id":"6194a2e65c504crb1d9af81d","userID":"6782452bd2ab63488c9f2663","mariageStatus":"Single"},{"_id":"6194a2959c534c01ft9rf24c","userID":"6785p58b41894b50b22db401","mariageStatus":"Married"}]'
      return marriageFromJson(jsonResp);
      } else {
        throw Exception('Failed load $route, status : ${response.statusCode}');
      }
    }
    

    A UserWithMariageStatus class, which will take User and Mariage Reference:

    import 'package:flutter_fetch_data/mariage.dart';
    import 'package:flutter_fetch_data/user.dart';
    
    class UserWithMariageStatus {
      final User user;
      final Marriage mariage;
    
      UserWithMariageStatus(this.user, this.mariage);
    
      String get userId => user.id;
      String get name => user.name;
      String get phone => user.phone;
      String get mariageStatus => mariage.mariageStatus;
    }
    
    List<UserWithMariageStatus> combineUserWithMarriageStatus(
        List<User> user, List<Marriage> userMariage) {
      List<UserWithMariageStatus> result = [];
      // if userId is unique, iterate the lists and combine
      for (var item in userMariage) {
        result.add(UserWithMariageStatus(
            user.singleWhere((element) => element.id == item.idUser), item));
      }
      return result;
    }
    

    Use it in your fetch calling function:

    import 'package:flutter_fetch_data/mariage.dart';
    import 'package:flutter_fetch_data/user.dart';
    import 'package:flutter_fetch_data/user_with_mariage.dart';
    
    void main(List<String> args) async {
      List<User> userList = await fetchUser();
    
      for (var user in userList) {
        print("id: ${user.id}, name: ${user.name}, phone: ${user.phone}");
      }
    
      List<Marriage> mariageList = await fetchMarriage();
      for (var mariage in mariageList) {
        print("id: ${mariage.idUser}, mariageStatus: ${mariage.mariageStatus}");
      }
    
      List<UserWithMariageStatus> userWithMariageList =
          combineUserWithMarriageStatus(userList, mariageList);
      for (var userWithMariage in userWithMariageList) {
        print(
            "id: ${userWithMariage.userId}, name: ${userWithMariage.name}, phone: ${userWithMariage.phone}, mariageStatus: ${userWithMariage.mariageStatus}");
      }
    }
    

    In a FutureBuilder, you could use it like that:

    class MyApp extends StatefulWidget {
      const MyApp({Key? key}) : super(key: key);
    
      @override
      State<MyApp> createState() => _MyAppState();
    }
    
    class _MyAppState extends State<MyApp> {
      Future<List<UserWithMariageStatus>>? getUserWithMarriageList() async {
        final user = await fetchUser();
        final marriage = await fetchMarriage();
        return combineUserWithMarriageStatus(user, marriage);
      }
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: "Test Fetch Future",
          home: Scaffold(
            body: FutureBuilder<List<UserWithMariageStatus>>(
              future: getUserWithMarriageList(),
              builder: (context, snapshot) {
                if (snapshot.hasData) {
                  return ListView(
                      children: snapshot.data!
                          .map((e) => ListTile(
                              title: Text(
                                  "id: ${e.userId}, name: ${e.name}, phone: ${e.phone}, marriage: ${e.mariageStatus}")))
                          .toList());
                }
                return const CircularProgressIndicator();
              },
            ),
          ),
        );
      }
    }