issue with initializing List in a Future builder flutter firebase: LateInitializationError: Field 'posts' has not been initialized

issue with initializing List in a Future builder flutter firebase: LateInitializationError: Field 'posts' has not been initialized

我在使用这个 future builder 时遇到了问题,似乎找不到解决方案。我正在尝试创建映射到我创建的自定义 class(“BlogPosts”)的文档列表(“帖子”),但是在尝试使用它时出现以下错误。

错误:

LateInitializationError: Field 'posts' has not been initialized.

堆栈跟踪告诉我生成器内部发生了错误,但我已经初始化了 'posts' 以后

未来的建设者:

late User user;
late Userdata userdata;
CollectionReference usersRef = FirebaseFirestore.instance.collection('users');
CollectionReference postsRef = FirebaseFirestore.instance.collection('posts');
late List<BlogPost> posts;

FutureBuilder(
          future: Future.wait([
            usersRef.doc(user.uid).get().then((doc) {
            userdata = Userdata.fromDocument(doc);
            }),
            postsRef.get().then((val) {
              posts = val.docs.map((doc) => BlogPost.fromDocument(doc)).toList();
            })
    ]),
          builder: (context, snap) {
            print(posts);
            if(snap.connectionState == ConnectionState.done) {
              return ListView.builder(
                physics: BouncingScrollPhysics(),
                  itemCount: 3,
                  itemBuilder: (context, index) {
                  return Text(userdata.displayName);
                  }
              );
            } else {
              return Center(
                child: Text('Loading')
              );
            }
          }
        )

完整代码:

late User user;
late Userdata userdata;
CollectionReference usersRef = FirebaseFirestore.instance.collection('users');
CollectionReference postsRef = FirebaseFirestore.instance.collection('posts');

class HomeScreen extends StatefulWidget {
  const HomeScreen({Key? key}) : super(key: key);

  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  final auth = FirebaseAuth.instance;
  late List<BlogPost> posts;

  @override
  Widget build(BuildContext context) {
    user = auth.currentUser!;
    Color getColor(Set<MaterialState> states) {
      const Set<MaterialState> interactiveStates = <MaterialState>{
        MaterialState.pressed,
        MaterialState.hovered,
        MaterialState.focused,
      };
      if (states.any(interactiveStates.contains)) {
        return Colors.blue.shade900;
      }
      return Colors.blue.shade600;
    }
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        backgroundColor: Colors.blue,
        child: Icon(
          Icons.add,
          color: Colors.white,
        ),
        onPressed: (){
          Navigator.push(context, MaterialPageRoute(builder: (_) => Upload()));
        },
      ),
        appBar: AppBar(
          backgroundColor: Colors.grey[600],
          actions: [
            TextButton(
              child: Text(
                'Logout',
                style: TextStyle(color: Colors.black),
              ),
              onPressed: () {
                auth.signOut();
                Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (context) => LoginScreen()));
              },
              style: ButtonStyle(
                  backgroundColor: MaterialStateProperty.resolveWith(getColor)
              ),
            ),
          ],
        ),
        // body: Center(
        //   child: Text(user.uid)
        // ),

        body: FutureBuilder(
          future: Future.wait([
            usersRef.doc(user.uid).get().then((doc) {
            userdata = Userdata.fromDocument(doc);
            }),
            postsRef.get().then((val) {
              posts = val.docs.map((doc) => BlogPost.fromDocument(doc)).toList();
            })
    ]),
          builder: (context, snap) {
            print(posts);
            if(snap.connectionState == ConnectionState.done) {
              return ListView.builder(
                physics: BouncingScrollPhysics(),
                  itemCount: 3,
                  itemBuilder: (context, index) {
                  return Text(userdata.displayName);
                  }
              );
            } else {
              return Center(
                child: Text('Loading')
              );
            }
          }
        )
    );
  }
}

class Userdata {

  final String uid;
  final String email;
  final String displayName;


  Userdata(this.uid, this.email,  this.displayName);

  factory Userdata.fromDocument(DocumentSnapshot doc) {
    return Userdata(
        doc['uid'],
        doc['email'],
        doc['displayName']
    );
  }
}

class BlogPost {
  String displayName;
  String postmsg;
  String postId;
  String UserId;

  BlogPost(this.displayName, this.postmsg, this.postId, this.UserId);

  factory BlogPost.fromDocument(DocumentSnapshot doc) {
    return BlogPost(
        doc['displayName'],
        doc['postmsg'],
        doc['postId'],
        doc['UserId']
    );
  }

我怀疑这里的 print 语句导致了错误:

builder: (context, snap) {
  print(posts);
  ...

您的构建器被调用了很多次,最初 Future 实际上还没有从 Firestore 中读取数据。

您只想在连接完成后打印:

builder: (context, snap) {
  if(snap.connectionState == ConnectionState.done) {
    print(posts);
    ...

但是您处理 Future 的方式似乎也很不对。只是在代码周围放置 Future.wait() 并不会突然让所有异步调用都工作。

您需要将 Future 的数组传递给 Future.wait(),然后在您的 builder正文:

FutureBuilder(
  future: Future.wait([
    usersRef.doc(user.uid).get().then((doc) => Userdata.fromDocument(doc)),
    postsRef.get().then((val) => val.docs.map((doc) => BlogPost.fromDocument(doc)).toList()))
  ]),
  builder: (context, snap) {
    if(snap.connectionState == ConnectionState.done) {
      if (snap.data[1] != null) print(snap.data[1]);
      ...

所以这里 builder 的 future 属性 得到了两个 futures,snap 保存了数据库中的两个结果。当 snap.connectionState 完成后,您可以访问 snap.data[0]snap.data[1] 以获取结果。

另见:

我发现了我的问题,因为我的代码一直都很好我只是在我的 fromDocument 中有一个拼写错误我的 userId 字符串是 doc['UserId'] 并且被上传为 'userId'只是一个错位的大写。

非常感谢您的帮助