在 flutter 中使用 Firebase 实时数据库数据创建无限滚动效果

Moh*_*iem 2 chat infinite-scroll firebase firebase-realtime-database flutter

我正在我的移动应用程序中集成聊天功能,并决定使用 Firebase 实时数据库作为后端而不是 Firestore 作为降低成本的机制。然而,我遇到了一个问题。Realtime Database关于如何使用来自而不是的数据创建无限滚动的文档似乎非常稀疏Firestore

以下是我的聊天消息的组织。这是我想使用的查询:

FirebaseDatabase.instance
            .ref("messages/${widget.placeID}")
            .orderByChild("timeStamp")
Run Code Online (Sandbox Code Playgroud)

这是我想要为每个结果返回的小部件:

MessageWidget(
  message: message.text,
  id: message.uid,
  name: message.name,
  lastSender: message.lastSender,
  date: message.timeStamp,
  profilePicture: message.profilePicture,
);
Run Code Online (Sandbox Code Playgroud)

这是数据库结构
在此输入图像描述

该查询有效,并且我已经根据查询的 JSON 响应编写了 MessageWidget。我所需要的只是每当查询到达滚动顶部时就调用该查询,并加载更多MessageWdigets。另请注意,这是一个聊天应用程序,用户可以向上滚动以加载较旧的消息,并将其添加到上一条消息之上。

谢谢你!

编辑:这是我目前拥有的代码:

Flexible(
  child: StreamBuilder(
    stream: FirebaseDatabase.instance
        .ref("messages/${widget.placeID}")
        .orderByChild("timeStamp")
        .limitToLast(20)
        .onValue,
    builder:
        (context, AsyncSnapshot<DatabaseEvent> snapshot) {
      if (!snapshot.hasData) {
        return const CircularProgressIndicator();
      } else {
        Map<dynamic, dynamic> map =
            snapshot.data!.snapshot.value as dynamic;
        List<dynamic> list = [];
        list.clear();
        list = map.values.toList();
        return Align(
          alignment: Alignment.bottomCenter,
          child: Padding(
            padding: const EdgeInsets.only(bottom: 20),
            child: ListView.builder(
                controller: _scrollController,
                // shrinkWrap: true,
                itemCount: list.length,
                itemBuilder: (context, index) {
                  final json = list[index]
                      as Map<dynamic, dynamic>;
                  final message = Message.fromJson(json);
                  return MessageWidget(
                    message: message.text,
                    id: message.uid,
                    name: message.name,
                    lastSender: message.lastSender,
                    date: message.timeStamp,
                    profilePicture:
                        message.profilePicture,
                  );
                }),
          ),
        );
      }
    },
  ),
),
Run Code Online (Sandbox Code Playgroud)

我的初始化状态

void initState() {
   super.initState();

   _scrollController.addListener(() {
     if (_scrollController.position.atEdge) {
       bool isTop = _scrollController.position.pixels == 0;
       if (isTop) {
         //add more messages

       } else {
         print('At the bottom');
       }
     }
   });
 }
Run Code Online (Sandbox Code Playgroud)

Fra*_*len 5

您的代码已经加载了所有消息。


如果要加载最大数量的消息,则需要对加载的消息数量进行限制。如果您只想加载最新的消息,您可以limitToLast这样做 - 因为当您按其timeStamp值排序时,最新的消息位于最后。

例如,要仅加载 10 条最新消息,您可以使用:

FirebaseDatabase.instance
    .ref("messages/${widget.placeID}")
    .orderByChild("timeStamp")
    .limitToLast(10);
Run Code Online (Sandbox Code Playgroud)

这使得您最初在应用程序中显示的消息数量有限。


现在,当滚动到达屏幕顶部时,您需要加载之前的 10 条消息。为此,您需要知道timeStamp屏幕顶部消息的值和键 - 即迄今为止显示的最旧消息的值和键。

使用这两个值,您可以使用以下命令加载前 10 个值:

FirebaseDatabase.instance
    .ref("messages/${widget.placeID}")
    .orderByChild("timeStamp")
    .endBefore(timeStampValueOfOldestSeenItem, keyOfOldestSeenItem)
    .limitToLast(10);
Run Code Online (Sandbox Code Playgroud)

这里的数据库再次对它们的节点进行排序timeStamp,然后根据您给出的值找到屏幕顶部的节点,然后返回之前的 10 个节点。