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;
    
}

请记住,这段代码做了很多假设,并且没有进行任何错误检查。我建议在适当的地方添加错误检查/捕获以避免意外崩溃。