【问题标题】:Flutter StreamBuilder with Firestore PaginationFlutter StreamBuilder 与 Firestore 分页
【发布时间】:2020-05-28 20:36:54
【问题描述】:

我正在尝试构建一个基本的聊天功能,其中所有用户聊天消息都作为文档存储在“聊天”集合中。我已经成功实现了分页,以确保在用户滚动之前不会过度提取数据。

但是,即使我有一个 StreamBuilder,新的聊天文档也不会像往常一样自动出现。为什么流构建器没有注册和显示这些新消息?

这是我的代码:

class MotivatorChat extends StatefulWidget {
  @override
  _MotivatorChatState createState() => _MotivatorChatState();
}

class _MotivatorChatState extends State<MotivatorChat> {

  Firestore firestore = Firestore.instance;
  List<DocumentSnapshot> chats = [];
  bool isLoading = false;
  bool hasMore = true;
  int documentLimit = 10;
  DocumentSnapshot lastDocument;
  ScrollController _scrollController = ScrollController();

  StreamController<List<DocumentSnapshot>> _controller = StreamController<List<DocumentSnapshot>>();

  Stream<List<DocumentSnapshot>> get _streamController => _controller.stream;


  @override
  void initState() {
    super.initState();
    getChats();
    _scrollController.addListener(() {
      double maxScroll = _scrollController.position.maxScrollExtent;
      double currentScroll = _scrollController.position.pixels;
      double delta = MediaQuery.of(context).size.height * 0.20;
      if (maxScroll - currentScroll <= delta) {
        getChats();
      }


    });
  }

  getChats() async {
    if (!hasMore) {
      print('No More Chats');
      return;
    }
    if (isLoading) {
      return;
    }
    setState(() {
      isLoading = true;
    });
    QuerySnapshot querySnapshot;
    if (lastDocument == null) {
      querySnapshot = await firestore
          .collection('chats')
          .orderBy('timestamp', descending: true)
          .limit(documentLimit)
          .getDocuments();
    } else {
      querySnapshot = await firestore
          .collection('chats')
          .orderBy('timestamp', descending: true)
          .startAfterDocument(lastDocument)
          .limit(documentLimit)
          .getDocuments();
      print(1);
    }
    if (querySnapshot.documents.length < documentLimit) {
      hasMore = false;
    }

    lastDocument = querySnapshot.documents[querySnapshot.documents.length - 1];

    chats.addAll(querySnapshot.documents);
    _controller.sink.add(chats);

    setState(() {
      isLoading = false;
    });
  }

  @override
  Widget build(BuildContext context) {



    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Pagination with Firestore'),
      ),
      body: Column(children: [
        Expanded(
          child: StreamBuilder<List<DocumentSnapshot>>(
            stream: _streamController,
            builder: (sContext, snapshot) {
              print(snapshot.connectionState);
              if (snapshot.hasData && snapshot.data.length > 0) {
                return ListView.builder(
                  reverse: true,
                  controller: _scrollController,
                  itemCount: snapshot.data.length,
                  itemBuilder: (context, index) {
                    return Padding(
                      padding: EdgeInsets.only(top: 20),
                      child: Container(
                        height: 20,
                        child: Text(snapshot.data[index].data['text']),
                      ),
                    );
                  },
                );
              } else {
                return Center(
                  child: Text('No Data...'),
                );
              }
            },
          ),
        ),
        isLoading
            ? Container(
          width: MediaQuery
              .of(context)
              .size
              .width,
          padding: EdgeInsets.all(5),
          color: Colors.yellowAccent,
          child: Text(
            'Loading',
            textAlign: TextAlign.center,
            style: TextStyle(
              fontWeight: FontWeight.bold,
            ),
          ),
        )
            : Container(),

      ]),
    );
  }
}

更新了 StreamBuilder

StreamBuilder<List<DocumentSnapshot>>(
            stream: _streamController,
            builder: (sContext, snapshot) {
              if (snapshot.connectionState == ConnectionState.none) {
                return Text("None");
              } else if (snapshot.connectionState == ConnectionState.waiting) {
                return Text("Loading");
              } else if (snapshot.connectionState == ConnectionState.active) {
                if (snapshot.hasData && snapshot.data.length > 0) {
                  return ListView.builder(
                    reverse: true,
                    controller: _scrollController,
                    itemCount: snapshot.data.length,
                    itemBuilder: (context, index) {
                      return Padding(
                        padding: EdgeInsets.only(top: 20),
                        child: Container(
                          height: 20,
                          child: Text(snapshot.data[index].data['text']),
                        ),
                      );
                    },
                  );
                } else {
                  return Center(
                    child: Text('No Data...'),
                  );
                }
              } else {
                return Text("return list");
              }
            },
          ),

【问题讨论】:

  • 使用snapshot.connectionState查看。
  • @JohnJoe 连接状态是活动的,但仍未触发流添加新消息。
  • 请粘贴您的最新代码。
  • @JohnJoe 更新了!
  • 我没有看到你在某处使用connectionState.active

标签: flutter google-cloud-firestore


【解决方案1】:

看看这段代码是否有用:

class _MessagesState extends State<Messages> {
  ScrollController _scrollController = ScrollController();

  @override
  void initState() {
    super.initState();
    _scrollController.addListener(() {
      if (_scrollController.offset >=
              (_scrollController.position.maxScrollExtent) &&
          !_scrollController.position.outOfRange) {
        _getChats();
      }
    });
  }

  final StreamController<List<DocumentSnapshot>> _chatController =
      StreamController<List<DocumentSnapshot>>.broadcast();

  List<List<DocumentSnapshot>> _allPagedResults = [<DocumentSnapshot>[]];

  static const int chatLimit = 10;
  DocumentSnapshot? _lastDocument;
  bool _hasMoreData = true;

  Stream<List<DocumentSnapshot>> listenToChatsRealTime() {
    _getChats();
    return _chatController.stream;
  }

  void _getChats() {
    final CollectionReference _chatCollectionReference = FirebaseFirestore
        .instance
        .collection("ChatRoom")
        .doc(widget.chatRoomId)
        .collection("channel");
    var pagechatQuery = _chatCollectionReference
        .orderBy('createdAt', descending: true)
        .limit(chatLimit);

    if (_lastDocument != null) {
      pagechatQuery = pagechatQuery.startAfterDocument(_lastDocument!);
    }

    if (!_hasMoreData) return;

    var currentRequestIndex = _allPagedResults.length;
    pagechatQuery.snapshots().listen(
      (snapshot) {
        if (snapshot.docs.isNotEmpty) {
          var generalChats = snapshot.docs.toList();

          var pageExists = currentRequestIndex < _allPagedResults.length;

          if (pageExists) {
            _allPagedResults[currentRequestIndex] = generalChats;
          } else {
            _allPagedResults.add(generalChats);
          }

          var allChats = _allPagedResults.fold<List<DocumentSnapshot>>(
              <DocumentSnapshot>[],
              (initialValue, pageItems) => initialValue..addAll(pageItems));

          _chatController.add(allChats);

          if (currentRequestIndex == _allPagedResults.length - 1) {
            _lastDocument = snapshot.docs.last;
          }

          _hasMoreData = generalChats.length == chatLimit;
        }
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: StreamBuilder<List<DocumentSnapshot>>(
          stream: listenToChatsRealTime(),
          builder: (ctx, chatSnapshot) {
            if (chatSnapshot.connectionState == ConnectionState.waiting ||
                chatSnapshot.connectionState == ConnectionState.none) {
              return chatSnapshot.hasData
                  ? Center(
                      child: CircularProgressIndicator(),
                    )
                  : Center(
                      child: Text("Start a conversation."),
                    );
            } else {
              if (chatSnapshot.hasData) {
                final chatDocs = chatSnapshot.data!;
                final user = Provider.of<User?>(context);
                return ListView.builder(
                  controller: _scrollController,
                  reverse: true,
                  itemBuilder: (ctx, i) {
                    Map chatData = chatDocs[i].data() as Map;
                    return MessageBubble(
                        username: chatData['username'],
                        message: chatData['text'],
                        isMe: chatData['senderId'] == user!.uid,
                        key: ValueKey(chatDocs[i].id));
                  },
                  itemCount: chatDocs.length,
                );
              } else {
                return CircularProgressIndicator();
              }
            }
          }),
    );
  }
}

我参考了这个答案:Pagination in Flutter with Firebase Realtime Database

【讨论】:

    【解决方案2】:

    如果hasMore 为假,则流不会接收新数据。

    不要检查 hasMore

    //     if (!hasMore) {
    //       print('No More Chats');
    //       return;
    //     }
    

    并检查文件

    //     if (querySnapshot.documents.length < documentLimit) {
    //       hasMore = false;
    //     }
    
        if (querySnapshot.documents.isEmpty) {
          print('No More Chats');
          setLoading(false);
          return;
        }
    

    我在DartPad上测试,它每3秒发送一次数据。

    【讨论】:

    • 问题不在于滚动。即使不滚动,流构建器如何继续收听流并添加其他聊天?有没有办法收听流并添加到列表中?
    • 问题是你的滚动逻辑有错误。如果hasMore 为假,则流不会接收新数据。
    • 你可以在 DartPad 上试试这个例子。它每 3 秒接收一次数据。 dartpad.dartlang.org/965e9634d2a9fa2e64db0556d9c9bc8c
    【解决方案3】:

    我尝试了您的代码并将文档限制设置为 20,它工作正常。

    DartPad 上的示例

    【讨论】:

    • 检索数据不是问题。挑战在于它何时更新并将新文档添加到 Firestore。我希望它自动更新,但此解决方案不适用于此
    • 因为当一个新文档被添加到 Firestore 时,hasMore 总是 false ...
    • @PJQuakJag 你设法解决了吗?我有同样的问题
    猜你喜欢
    • 1970-01-01
    • 2019-05-29
    • 1970-01-01
    • 2020-10-01
    • 1970-01-01
    • 2020-06-07
    • 2020-03-30
    • 2019-10-01
    • 2021-02-26
    相关资源
    最近更新 更多