Firestore 单个文档快照流未更新

时间:2021-06-17 17:02:49

标签: android firebase flutter google-cloud-firestore firebase-authentication

我的 Flutter 应用程序需要监听特定 Firestore 文档中的任何更改。

这个过程非常简单,可以在各种解决方案中找到:

  1. StackOverflow: flutter-firestore-how-to-listen-for-changes-to-one-document
  2. StackOverflow:can-i-listen-to-a-single-document-in-firestore-with-a-streambuilder
  3. StackOverflow:using-stream-building-with-a-specific-firestore-document
  4. StackOverflow: how-to-get-a-specific-firestore-document-with-a-streambuilder
  5. 中等: how-to-get-data-from-firestore-and-show-it-on-flutterbuilder-or-streambuilder-e05

这些都是使用 StreamStreamBuilder 并侦听任何更改的解决方案。

通常使用这种方法:

Stream<UserModel?> getFirestoreUser(String uid) {
  return FirebaseFirestore.instance.collection('users').doc(uid).snapshots().map((event) => event.data()).map((event) => event == null ? null : UserModel.fromJson(event));
}

其中:

  • UserModel 有一个 fromJson(Map<String, dynamic>) 工厂构造函数
  • 并且存在文档 ID 为 usersuid 集合。

Stream 返回一个 Stream<UserModel?>,稍后可以由 Provider.of<UserModel?>(context) 或在 StreamBuider<UserModel?> 中使用


问题:

TL;DR - 在更新 Firestore().getCurrentUser(uid) 流后(直接或手动使用 Firestore 模拟器并更改文档值),下面的 StreamBuilder<UserModel?> 不会更新。

在应用生命周期的所有时间点(无论是否经过身份验证),流始终返回 null(但是,使用 .first 手动调用流会返回用户内容,但 .last 只是挂起)。请参阅下文了解更多信息。

链接到 github project

开始:

  1. 将您自己的 gooogle-services.json 文件放入 android/app 文件夹
  2. com.flutterprojects.myapp 的所有引用更改为 ${your google-services.json package name}
  3. 使用本地模拟器更新 Firestore 更改

更多详情:

我的问题使用了类似的方法,但 Stream 不返回任何内容。我的 Flutter 小部件 LoggedIn()FirebaseAuth.instance.currentUser 有效(即非空)时使用

class LoggedIn extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return StreamBuilder<UserModel?>(
      builder: (context, snapshot) {
        print("Checking UserModel from stream provider");
        if (snapshot.data == null) {
          print("UserModel is null");
          return UiLogin();
        }

        print("UserModel is NOT null");
        return UiHome();
      },
    );
  }
}

验证成功后,将文本 Checking UserModel from stream provider 打印到控制台,然后是 UserModel is null。任何时候都不会显示 UserModel is NOT null

身份验证成功后(即用户注册和文档更新),我有一个按钮,我可以手动调用流并获取第一项:

var currentUser = Firestore().getCurrentUser(FirebaseAuth.instance.currentUser!.uid);
var first = await currentUser.first;
print(first);

根据新添加的用户信息返回 UserModel

当运行以下代码时,执行在 .last 处停止,没有错误,没有崩溃,没有应用退出,只是停止执行。

var currentUser = Firestore().getCurrentUser(FirebaseAuth.instance.currentUser!.uid);
var last = await currentUser.last;
print(last);

但是

添加以下内容时,任何更改都会更新并打印到控制台

Firestore().getCurrentUser(currentUid).listen((data) {
    print("Firestore Data Changed");
    print("Firestore change value: $data");
});

这意味着 StreamBuilder<UserModel?> 无法正常工作,因为 Stream<UserModel> 事件会listen() 更改并确认这些更改,但 StreamBuilder<UserModel?> 更改不会受到影响并始终返回 null

我做错了吗?

2 个答案:

答案 0 :(得分:0)

试试if (snapshot.hasData) { print("UserModel is null"); return UiLogin(); }

答案 1 :(得分:0)

这对我有用。也许缺少连接状态。

StreamBuilder(
        stream: FirebaseFirestore.instance
            .collection('users')
            .doc(widget.uid)
            .collection('contacts')
            .doc(widget.client)
            .collection('messages')
            .orderBy('timestamp', descending: true)
            .snapshots(),
        builder: (context, snapshot) {
          if (snapshot.hasError)
            return Expanded(
              child: Center(child: Text('Problem Getting Your Messages')),
            );

          switch (snapshot.connectionState) {
            case ConnectionState.none:
              return Text('waiting');
            case ConnectionState.waiting:
              return Center(child: Text('Waiting'));
            case ConnectionState.active:
              var snap;
              print('active');
              if (snapshot.data != null) {
                snap = snapshot.data;
              }
              return Expanded(
                child: ListView(
                  ...
              );

            case ConnectionState.done:
              print('done');
              return Text('Lost Connection');
          }

          return Text('NOPE');
        },
      ),