Как сделать два зависимых асин c запросов в флаттер? - PullRequest
0 голосов
/ 08 апреля 2020

Вопрос, кажется, немного тупой, но как человек, который никогда не работал с asyn c, до того как он функционировал, это не так тривиально.

Я получаю json данные из http-запроса и строю список. Например, скажем, ID пользователя и имя пользователя.

[
  {"userid":1,"username":"JohnDoe"},
  {"userid":2,"username":"SamSmith"}
]

Код:

Future<UsersList> fetchUsers() async {
  final response = await http.get(
      Uri.encodeFull('https://www.myurl.com/users'),
      headers: {'Accept': 'application/json'});

  if (response.statusCode == 200) {
    return UsersList.fromJson(json.decode(response.body));
  } else {
    throw Exception('Failed to load users');
  }
}

class User {
  final String userid;
  final String username;
  String tag;

  User({this.userid, this.username});

  factory User.fromJson(Map<String, dynamic> json){
    return User(
      userid: json['userid'],
      username: json['username'],
    );
  }
}

class UsersList {
  final List<User> Users;

  UsersList({this.Users});

  factory UsersList.fromJson(List<dynamic> parsedJson) {
    List<User> Users = new List<User>();
    Users = parsedJson.map((i) => User.fromJson(i)).toList();
    return new UsersList(Users: Users);
  }
}

class UsersTab extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return UsersTabState();
  }
}

class UsersTabState extends State<UsersTab> {
  Future<UsersList> Users;

  @override
  void initState() {
    super.initState();
    Users = fetchUsers();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('users'), backgroundColor: Colors.blue),
      body: Center(
        child: FutureBuilder<usersList>(
          future: users,
          builder: (context, snapshot) {
            if (snapshot.hasData) {
              return ListView.builder(
                itemCount: snapshot.data.users.length,
                itemBuilder: (BuildContext context, int index) {
                  return Container(
                    child: Text('User: ' +
                        snapshot.data.users[index].username +
                        '\nTag: ' + 
                        snapshot.data.users[index].tag),
                  );
                },
              );
            } else if (snapshot.hasError) {
              return Text('${snapshot.error}');
            }

            return CircularProgressIndicator();
          },
        ),
      ),
    );
  }
}

Теперь у меня также есть локальные данные из shared_preferences, где я могу пометить пользователей по id. Итак, у меня есть такая функция, как

Future<String> getTag(String id) async {
  final prefs = await SharedPreferences.getInstance();
  return prefs.getString(id) ?? "none";
}

Мой вопрос: где я могу вызвать эту функцию? Очевидно, это должно быть до того, как FutureBuilder создаст список, но после завершения http-запроса. У меня были некоторые идеи, такие как initState of UsersTabState или конструктор класса User, но он всегда заканчивается в будущем, где мне понадобится строка. Каков наилучший способ получить локально сохраненный тег в класс User?

1 Ответ

0 голосов
/ 08 апреля 2020

Таким образом, мое решение состоит в том, чтобы поместить метод getTag в класс User и преобразовать User.fromJson и UsersList.fromJson в методы stati c, которые возвращают Future<User> и Future<UsersList>. Сделав это, мы можем поместить все ожидающие в fetchUsers, так что этот метод в конечном итоге вернет объект UsersList, выполненный после его ожидания.

Future<UsersList> fetchUsers() async {
  final response = await http.get(
      Uri.encodeFull('https://www.myurl.com/users'),
      headers: {'Accept': 'application/json'});

  if (response.statusCode == 200) {
    return await UsersList.fromJson(json.decode(response.body));
  } else {
    throw Exception('Failed to load users');
  }
}

class User {
  final String userid;
  final String username;
  final String tag;

  User({this.userid, this.username, this.tag});

  static Future<User> fromJson(Map<String, dynamic> json) async {
    final userId = json['userid'];
    final tag = await _getTag(userId);

    return User(
      userid: json['userid'],
      username: json['username'],
      tag: tag
    );
  }

  static Future<String> _getTag(String id) async {
    final prefs = await SharedPreferences.getInstance();
    return prefs.getString(id) ?? "none";
  }
}

class UsersList {
  final List<User> Users;

  UsersList({this.Users});

  static fromJson(List<dynamic> parsedJson) async {
    List<User> Users = new List<User>();
    Users = await Future.wait(parsedJson.map((i) => User.fromJson(i));
    return new UsersList(Users: Users);
  }
}

class UsersTab extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return UsersTabState();
  }
}

class UsersTabState extends State<UsersTab> {
  Future<UsersList> Users;

  @override
  void initState() {
    super.initState();
    Users = fetchUsers();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('users'), backgroundColor: Colors.blue),
      body: Center(
        child: FutureBuilder<usersList>(
          future: users,
          builder: (context, snapshot) {
            if (snapshot.hasData) {
              return ListView.builder(
                itemCount: snapshot.data.users.length,
                itemBuilder: (BuildContext context, int index) {
                  return Container(
                    child: Text('User: ' +
                        snapshot.data.users[index].username +
                        '\nTag: ' +
                        snapshot.data.users[index].tag),
                  );
                },
              );
            } else if (snapshot.hasError) {
              return Text('${snapshot.error}');
            }

            return CircularProgressIndicator();
          },
        ),
      ),
    );
  }
}

Я использовал один трюк для ожидания на несколько Future, которые будут возвращать список:

    Users = await Future.wait(parsedJson.map((i) => User.fromJson(i));

Вы можете прочитать об этом здесь: https://api.dart.dev/stable/2.7.2/dart-async/Future/wait.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...