Flutter Flatter Firestore读取所有文档

Flutter Flatter Firestore读取所有文档,flutter,google-cloud-firestore,Flutter,Google Cloud Firestore,更新2 经过大量调试,我意识到在messages子集合中创建消息时会发生错误。在我写入Messages子集合(chats集合的子集合)之后,它将读取所有文档,无论我在查询中拥有什么。我尝试了多种类型的查询,这些查询在开始时可以工作,但当我写入messages子集合时,它会读取所有文档,甚至是我已经读取的文档 当我写到messages子集合时,我注释掉了批处理行,只在chats集合中显示最近的消息。同样,这只发生在发送消息的用户身上,现在是接收消息的用户。收到消息的用户将获得文档,当我重新进入聊天

更新2

经过大量调试,我意识到在messages子集合中创建消息时会发生错误。在我写入Messages子集合(chats集合的子集合)之后,它将读取所有文档,无论我在查询中拥有什么。我尝试了多种类型的查询,这些查询在开始时可以工作,但当我写入messages子集合时,它会读取所有文档,甚至是我已经读取的文档

当我写到messages子集合时,我注释掉了批处理行,只在chats集合中显示最近的消息。同样,这只发生在发送消息的用户身上,现在是接收消息的用户。收到消息的用户将获得文档,当我重新进入聊天时,它不会调用所有文档。但是写消息并发送消息的用户,当该用户重新进入聊天室时,所有的文档都被读取,除了我不写消息子集合,我只是更新聊天室集合

代码:

聊天室页面

  Chat _chat;

  @override
  void initState() {
    this._chat = widget.chat;

    super.initState();
  }


  Widget _column() => Column(
        children: <Widget>[
          this._chat != null
              ? MessagesList(
                  controller: this._scrollController,
                  isReversed: false,
                  chat: this._chat,
                  scrollToBtm: this._scrollToBtm,
                )
              : Expanded(
                  child: Container(),
                ),
          Container(
            color: Colors.black,
            height: 1.0,
          ),
          Container(
            height: 45.0,
            decoration: BoxDecoration(),
            margin: EdgeInsets.only(left: 15.0, right: 15.0, bottom: 15.0),
            child: Column(
              children: <Widget>[
                Expanded(
                  child: Row(
                    crossAxisAlignment: CrossAxisAlignment.center,
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: <Widget>[
                      Expanded(
                        child: Padding(
                          padding: EdgeInsets.only(right: 15.0),
                          child: Directionality(
                            textDirection: TextDirection.ltr,
                            child: TextField(
                              controller: this._msgTEC,
                              focusNode: this._msgFN,
                              decoration: InputDecoration.collapsed(
                                hintText: 'Enter Message',
                                fillColor: Colors.white,
                              ),
                              keyboardType: TextInputType.text,
                              textInputAction: TextInputAction.send,
                              textCapitalization: TextCapitalization.sentences,
                              onSubmitted: (s) => this._sendMessage(
                                auth: auth,
                                main: main,
                                type: 'Text',
                                msg: s,
                              ),
                            ),
                          ),
                        ),
                      ),
                      GestureDetector(
                        child: Icon(Icons.send),
                        onTap: () => this._sendMessage(
                          auth: auth,
                          main: main,
                          type: 'Text',
                          msg: this._msgTEC.text,
                        ),
                      ),
                    ],
                  ),
                ),
              ],
            ),
          ),
        ],
      );

  _scrollToBtm() {
    if (this._scrollController.hasClients)
      this._scrollController.animateTo(this._scrollController.position.maxScrollExtent,
          duration: const Duration(milliseconds: 500), curve: Curves.easeOut);
  }

  Future _sendMessage({AuthProvider auth, MainProvider main, String type, String msg}) async {
    if (msg.trim().isEmpty) return;

    var now = Services().values.now();

    Chat chat = Chat(
      chatID: this._chat == null ? APIs().chats.collection.doc().id : this._chat.chatID,
      users: [widget.user.userID, auth.user.userID],
      recentMessage: Message(
        owner: APIs().users.collection.doc(auth.user.userID),
        type: type,
        msg: msg,
        createdAt: now,
      ),
      createdAt: now,
      updatedAt: now,
    );
    await main
        .createChat(
      chat: chat,
      isCreating: this._chat == null,
    )
        .then((value) async {
      if (mounted)
        setState(() {
          if (this._chat == null) this._chat = chat;
          this._msgTEC.text = '';
        });

      this._scrollToBtm();

      Map<dynamic, dynamic> results = await Services().cloudFunction.notifyUserInChat(data: <String, dynamic>{
        'toUserID': widget.user.userID,
        'selfie': widget.user.profile.selfie,
        'name': widget.user.profile.name,
        'chatID': this._chat.chatID,
        'msg': msg,
        'tokens': widget.user.settings.tokens.fcm,
      });

      Funcs.log(results);
    });
  }
  // ignore: cancel_subscriptions
  StreamSubscription _streamSubscription;
  bool _isFetching = false;
  bool _didFetch = false;

  @override
  void initState() {
    Funcs.log(widget.chat.messages.map((e) => e.msg).toList());

    widget.chat.messages.isEmpty
        ? this._moreMessages().then((value) {
            this._initMessages();
            widget.scrollToBtm();
          })
        : this._initMessages();

    super.initState();
  }

  @override
  void dispose() {
    this._streamSubscription.cancel();
    super.dispose();
  }

  _initMessages() {
    Funcs.log('Init Live Messages...');

    var q;

    widget.chat.messages.isEmpty
        ? q = APIs().chats.collection.doc(widget.chat.chatID).collection('messages').orderBy('createdAt', descending: false)
        : q = APIs()
            .chats
            .collection
            .doc(widget.chat.chatID)
            .collection('messages')
            .orderBy('createdAt', descending: false)
            .startAfterDocument(widget.chat.messages.last.ds);

    this._streamSubscription = q.snapshots().listen((event) {
      event.docChanges.forEach((element) {
        Message m = Message.model(ds: element.doc, id: element.doc.id, map: element.doc.data());

        Funcs.log('READ ${m.msg}');

        if (mounted)
          setState(() {
            if (element.type == DocumentChangeType.added) {
              widget.chat.messages.add(m);
              widget.chat.messages.sort((a, b) => a.createdAt.compareTo(b.createdAt));
            }

            if (element.type == DocumentChangeType.modified) {
              int index = widget.chat.messages.indexOf(m);

              if (index >= 0) {
                widget.chat.messages[index] = m;
                widget.chat.messages.sort((a, b) => a.createdAt.compareTo(b.createdAt));
              }
            }

            if (element.type == DocumentChangeType.removed) {
              widget.chat.messages.remove(m);
              widget.chat.messages.sort((a, b) => a.createdAt.compareTo(b.createdAt));
            }
          });
        Funcs.log(widget.chat.messages.map((e) => e.msg).toList());
      });
    });
  }

  Future _moreMessages() async {
    if (!this._isFetching && !this._didFetch) {
      setState(() => this._isFetching = true);
      List<Message> messages;

      if (widget.chat.messages.isEmpty) {
        Funcs.log('Getting Messages...');
        messages = await APIs().chats.initMessages(chat: widget.chat);
        Funcs.log(messages.map((e) => e.msg).toList());
      } else {
        Funcs.log('Getting More Messages...');
        messages = await APIs().chats.moreMessages(chat: widget.chat);
        Funcs.log(messages.map((e) => e.msg).toList());
      }

      if (messages != null) {
        int oldCount = widget.chat.messages.length;
        widget.chat.messages.addAll(messages);
        widget.chat.messages.sort((a, b) => a.createdAt.compareTo(b.createdAt));
        int newCount = widget.chat.messages.length;

        if (oldCount != newCount) {
        } else {
          this._didFetch = true;
        }
        setState(() => this._isFetching = false);
      }

      Funcs.log('Getting Messages Complete.');
    }
  }
  Chat _chat;

  @override
  void initState() {
    this._chat = widget.chat;

    super.initState();
  }


  Widget _column() => Column(
        children: <Widget>[
          this._chat != null
              ? MessagesList(
                  controller: this._scrollController,
                  isReversed: false,
                  chat: this._chat,
                  scrollToBtm: this._scrollToBtm,
                )
              : Expanded(
                  child: Container(),
                ),
          Container(
            color: Colors.black,
            height: 1.0,
          ),
          Container(
            height: 45.0,
            decoration: BoxDecoration(),
            margin: EdgeInsets.only(left: 15.0, right: 15.0, bottom: 15.0),
            child: Column(
              children: <Widget>[
                Expanded(
                  child: Row(
                    crossAxisAlignment: CrossAxisAlignment.center,
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: <Widget>[
                      Expanded(
                        child: Padding(
                          padding: EdgeInsets.only(right: 15.0),
                          child: Directionality(
                            textDirection: TextDirection.ltr,
                            child: TextField(
                              controller: this._msgTEC,
                              focusNode: this._msgFN,
                              decoration: InputDecoration.collapsed(
                                hintText: 'Enter Message',
                                fillColor: Colors.white,
                              ),
                              keyboardType: TextInputType.text,
                              textInputAction: TextInputAction.send,
                              textCapitalization: TextCapitalization.sentences,
                              onSubmitted: (s) => this._sendMessage(
                                auth: auth,
                                main: main,
                                type: 'Text',
                                msg: s,
                              ),
                            ),
                          ),
                        ),
                      ),
                      GestureDetector(
                        child: Icon(Icons.send),
                        onTap: () => this._sendMessage(
                          auth: auth,
                          main: main,
                          type: 'Text',
                          msg: this._msgTEC.text,
                        ),
                      ),
                    ],
                  ),
                ),
              ],
            ),
          ),
        ],
      );

  _scrollToBtm() {
    if (this._scrollController.hasClients)
      this._scrollController.animateTo(this._scrollController.position.maxScrollExtent,
          duration: const Duration(milliseconds: 500), curve: Curves.easeOut);
  }

  Future _sendMessage({AuthProvider auth, MainProvider main, String type, String msg}) async {
    if (msg.trim().isEmpty) return;

    var now = Services().values.now();

    Chat chat = Chat(
      chatID: this._chat == null ? APIs().chats.collection.doc().id : this._chat.chatID,
      users: [widget.user.userID, auth.user.userID],
      recentMessage: Message(
        owner: APIs().users.collection.doc(auth.user.userID),
        type: type,
        msg: msg,
        createdAt: now,
      ),
      createdAt: now,
      updatedAt: now,
    );
    await main
        .createChat(
      chat: chat,
      isCreating: this._chat == null,
    )
        .then((value) async {
      if (mounted)
        setState(() {
          if (this._chat == null) this._chat = chat;
          this._msgTEC.text = '';
        });

      this._scrollToBtm();

      Map<dynamic, dynamic> results = await Services().cloudFunction.notifyUserInChat(data: <String, dynamic>{
        'toUserID': widget.user.userID,
        'selfie': widget.user.profile.selfie,
        'name': widget.user.profile.name,
        'chatID': this._chat.chatID,
        'msg': msg,
        'tokens': widget.user.settings.tokens.fcm,
      });

      Funcs.log(results);
    });
  }
  MainProvider.instance() {
    this.readChats();
  }

  readChats() {
    var query = APIs()
        .chats
        .collection
        .where('users', arrayContainsAny: [Services().auth.currentUser().uid])
        .orderBy('updatedAt', descending: true)
        .limit(10);

    if (this.lastChat != null) query = query.startAfterDocument(this.lastChat);

    this._chatsStream = Services().crud.readLive(
          stream: query.snapshots(),
          onEmpty: () {
            Funcs.log('EMP');
            // chats.clear();
          },
          onAdded: (c) {
            Funcs.log('ADD');
            Chat chat = Chat.model(id: c.id, map: c.data());

            this.chats.add(chat);
            this.lastChat = c;

            notifyListeners();
          },
          onModified: (c) {
            Funcs.log('MOD');
            Chat chat = Chat.model(id: c.id, map: c.data());

            int index = this.chats.indexWhere((element) => element.chatID == chat.chatID);

            chat.messages = this.chats[index].messages;

            this.chats[index] = chat;
            this.chats.sort((a, b) => b.updatedAt.compareTo(a.updatedAt));

            notifyListeners();
          },
          onRemoved: (c) {
            Funcs.log('REM');
            Chat chat = Chat.model(id: c.id, map: c.data());
            int index = this.chats.indexWhere((element) => element.chatID == chat.chatID);

            this.chats.removeAt(index);

            notifyListeners();
          },
          onFailure: (e) => print(e),
        );
  }