FutureBuilder 中的双重更新?
Double update in FutureBuilder?
我有一个 FutureBuilder,它正在从我的 firebase 实时数据库中获取数据。结果应该是所有帖子的列表,它也可以工作,但只能持续一秒钟左右,无论出于何种原因,它都会更新两次,然后第二次数据又消失了。这是我的请求代码:
Future<List<Posts>> getpostsOfThread() async {
List<String> threads = [];
await database
.ref()
.child("users/")
.child(user!.uid)
.child("threadsFollowing")
.once()
.then((value) => {
value.snapshot.children.forEach((element) {
threads.add(element.value.toString());
})
});
List<Posts> posts = [];
threads.forEach((element) async {
print("?");
await database
.ref()
.child("SecretChat")
.child("threads")
.child(element)
.child("posts")
.once()
.then((value) => {
value.snapshot.children.forEach((element) async {
posts.add(Posts(
postID: element.key.toString(),
title: element.child("title").value.toString(),
likes: int.parse(element.child("likes").value.toString()),
picturePath:
element.child("picturePath").value.toString()));
})
});
});
return posts;
}
这里是 FutureBuilder 的代码:
Container(
decoration: BoxDecoration(
color: Color.fromRGBO(247, 241, 236, 1),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30),
topRight: Radius.circular(30))),
height: height * 0.7379,
child: SafeArea(
minimum: EdgeInsets.all(20),
child: (Container(
child: Center(
child: Column(children: [
//Text(massages.toString()),
Flexible(
child: FutureBuilder(
future: getpostsOfThread(),
builder: (BuildContextcontext,
AsyncSnapshot<List<Posts>> snapshot) {
print(snapshot.data);
if (snapshot.hasData) {
int lenght = snapshot.data!.length;
print(lenght);
return Container(
child: ListView.builder(
itemCount: lenght,
itemBuilder: (context, index) {
return Container(
child: Text(
snapshot.data![index].likes.toString()),
);
},
));
}
return Container();
}))
]))))))
等待Future结果的方式一般有2种。
(1) myFuture.then((值) {});
(2) 等待我的未来();
使用方法 (1) 时,代码序列将在调用后继续,当 Future 完成时,then()
方法触发并运行该代码。
例如:
myMethod() {
myFuture.then((value) { console.log("myFuture Finished"); });
console.log("myMethod Finished");
}
在这里,您应该会在“myFuture Finished”之前看到“myMethod Finished”打印到控制台。
如果需要等待 future 完成才能继续下一个代码序列,请使用 await
关键字。
myMethod() async {
await myFuture();
console.log("myMethod Finished");
}
以这个为例。 “myMethod Finished”只会在 Future 方法完成后出现在控制台中。
您还应该知道,使 List 的 forEach
方法与调用 Future 方法非常相似,因为后面的代码将在 forEach 循环迭代时继续执行。当需要为循环的每次迭代等待来自 Future 的数据时,您将需要使用不同的语法。我经常回退到 for(int i = 0; i < list.length; i++) {}
因为它一直对我有效。
根据这些信息,我修改了您的代码以利用 await
关键字并确保 getpostsOfThread()
方法仅在所有代码完成后才 return处理应确保线程和帖子被 returned。这是我想出的:
Future<List<Posts>> getpostsOfThread() async {
// assign the result to a variable
var value = await database
.ref()
.child("users/")
.child(user!.uid)
.child("threadsFollowing")
.once();
// simplify mapping the results to the threads
List<String> threads = value.snapshot.children.map((element) => element.value.toString()).toList();
List<Posts> posts = [];
// don't use .forEeach with an async
// instead use a loop that can take advantage of the existing async
for(int i = 0; i < threads.length; i++) {
// assign the result for this thread to a variable
var value2 = await database
.ref()
.child("SecretChat")
.child("threads")
.child(threads[i])
.child("posts")
.once();
// without async, loop results and create Posts
value2.snapshot.children.forEach((element) {
posts.add(Posts(
postID: element.key.toString(),
title: element.child("title").value.toString(),
likes: int.parse(element.child("likes").value.toString()),
picturePath:
element.child("picturePath").value.toString()));
});
}
// finally return the list of Posts
return posts;
}
请记住,这段代码做了很多假设,并且没有进行任何错误检查。我建议在适当的地方添加错误检查/捕获以避免意外崩溃。
我有一个 FutureBuilder,它正在从我的 firebase 实时数据库中获取数据。结果应该是所有帖子的列表,它也可以工作,但只能持续一秒钟左右,无论出于何种原因,它都会更新两次,然后第二次数据又消失了。这是我的请求代码:
Future<List<Posts>> getpostsOfThread() async {
List<String> threads = [];
await database
.ref()
.child("users/")
.child(user!.uid)
.child("threadsFollowing")
.once()
.then((value) => {
value.snapshot.children.forEach((element) {
threads.add(element.value.toString());
})
});
List<Posts> posts = [];
threads.forEach((element) async {
print("?");
await database
.ref()
.child("SecretChat")
.child("threads")
.child(element)
.child("posts")
.once()
.then((value) => {
value.snapshot.children.forEach((element) async {
posts.add(Posts(
postID: element.key.toString(),
title: element.child("title").value.toString(),
likes: int.parse(element.child("likes").value.toString()),
picturePath:
element.child("picturePath").value.toString()));
})
});
});
return posts;
}
这里是 FutureBuilder 的代码:
Container(
decoration: BoxDecoration(
color: Color.fromRGBO(247, 241, 236, 1),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30),
topRight: Radius.circular(30))),
height: height * 0.7379,
child: SafeArea(
minimum: EdgeInsets.all(20),
child: (Container(
child: Center(
child: Column(children: [
//Text(massages.toString()),
Flexible(
child: FutureBuilder(
future: getpostsOfThread(),
builder: (BuildContextcontext,
AsyncSnapshot<List<Posts>> snapshot) {
print(snapshot.data);
if (snapshot.hasData) {
int lenght = snapshot.data!.length;
print(lenght);
return Container(
child: ListView.builder(
itemCount: lenght,
itemBuilder: (context, index) {
return Container(
child: Text(
snapshot.data![index].likes.toString()),
);
},
));
}
return Container();
}))
]))))))
等待Future结果的方式一般有2种。
(1) myFuture.then((值) {});
(2) 等待我的未来();
使用方法 (1) 时,代码序列将在调用后继续,当 Future 完成时,then()
方法触发并运行该代码。
例如:
myMethod() {
myFuture.then((value) { console.log("myFuture Finished"); });
console.log("myMethod Finished");
}
在这里,您应该会在“myFuture Finished”之前看到“myMethod Finished”打印到控制台。
如果需要等待 future 完成才能继续下一个代码序列,请使用 await
关键字。
myMethod() async {
await myFuture();
console.log("myMethod Finished");
}
以这个为例。 “myMethod Finished”只会在 Future 方法完成后出现在控制台中。
您还应该知道,使 List 的 forEach
方法与调用 Future 方法非常相似,因为后面的代码将在 forEach 循环迭代时继续执行。当需要为循环的每次迭代等待来自 Future 的数据时,您将需要使用不同的语法。我经常回退到 for(int i = 0; i < list.length; i++) {}
因为它一直对我有效。
根据这些信息,我修改了您的代码以利用 await
关键字并确保 getpostsOfThread()
方法仅在所有代码完成后才 return处理应确保线程和帖子被 returned。这是我想出的:
Future<List<Posts>> getpostsOfThread() async {
// assign the result to a variable
var value = await database
.ref()
.child("users/")
.child(user!.uid)
.child("threadsFollowing")
.once();
// simplify mapping the results to the threads
List<String> threads = value.snapshot.children.map((element) => element.value.toString()).toList();
List<Posts> posts = [];
// don't use .forEeach with an async
// instead use a loop that can take advantage of the existing async
for(int i = 0; i < threads.length; i++) {
// assign the result for this thread to a variable
var value2 = await database
.ref()
.child("SecretChat")
.child("threads")
.child(threads[i])
.child("posts")
.once();
// without async, loop results and create Posts
value2.snapshot.children.forEach((element) {
posts.add(Posts(
postID: element.key.toString(),
title: element.child("title").value.toString(),
likes: int.parse(element.child("likes").value.toString()),
picturePath:
element.child("picturePath").value.toString()));
});
}
// finally return the list of Posts
return posts;
}
请记住,这段代码做了很多假设,并且没有进行任何错误检查。我建议在适当的地方添加错误检查/捕获以避免意外崩溃。