如何在颤动中发出两个依赖的异步请求?
How to make two dependent async requests in flutter?
这个问题似乎有点愚蠢,但作为一个从未使用过异步函数的人来说,它并不是那么微不足道。
我从 http 请求中获取了一些 json 数据并构建了一个列表。例如,假设用户 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 请求完成之后。我有一些想法,比如 UsersTabState 的 initState 或 User class 构造函数,但它总是在未来某个我需要 String 的地方结束。
将本地存储的标签获取到用户的最佳方法是什么 class?
所以我的解决方案是将 getTag
方法放在 User
class 中,并将 User.fromJson
和 UsersList.fromJson
都放入静态方法中 returns Future<User>
和 Future<UsersList>
。通过这样做,我们可以将所有等待放入 fetchUsers
,因此该方法将最终 returning 一个 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,这将 return 一个列表:
Users = await Future.wait(parsedJson.map((i) => User.fromJson(i));
您可以在这里阅读:https://api.dart.dev/stable/2.7.2/dart-async/Future/wait.html
这个问题似乎有点愚蠢,但作为一个从未使用过异步函数的人来说,它并不是那么微不足道。
我从 http 请求中获取了一些 json 数据并构建了一个列表。例如,假设用户 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 请求完成之后。我有一些想法,比如 UsersTabState 的 initState 或 User class 构造函数,但它总是在未来某个我需要 String 的地方结束。 将本地存储的标签获取到用户的最佳方法是什么 class?
所以我的解决方案是将 getTag
方法放在 User
class 中,并将 User.fromJson
和 UsersList.fromJson
都放入静态方法中 returns Future<User>
和 Future<UsersList>
。通过这样做,我们可以将所有等待放入 fetchUsers
,因此该方法将最终 returning 一个 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,这将 return 一个列表:
Users = await Future.wait(parsedJson.map((i) => User.fromJson(i));
您可以在这里阅读:https://api.dart.dev/stable/2.7.2/dart-async/Future/wait.html