StreamBuilder Firestore分页 [英] StreamBuilder Firestore Pagination

查看:75
本文介绍了StreamBuilder Firestore分页的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是扑朔迷离的新手,并且我想在通过streambuilder滚动到顶部时对聊天进行分页.问题是:当我在scrollListener中进行查询时,streambuilder在scrollListener上方优先进行其查询,并返回旧响应.有什么办法吗?我在这里有什么选择?谢谢!

I m new to flutter, and I'm trying to paginate a chat when scroll reach top with streambuilder. The problem is: when i make the query in scrollListener streambuilder priorize his query above the scrollListener and returns de old response. Is there any way to do this? What are my options here? Thanks!

ChatScreenState类

在initState中,我创建滚动侦听器.

In initState I create the scroll listener.

  @override
void initState() {
 listScrollController = ScrollController();
 listScrollController.addListener(_scrollListener);
 super.initState();
}

在这里,我创建的StreamBuilder的查询限制为20条最后一条消息.使用_messagesSnapshots作为全局列表.

Here i create the StreamBuilder with the query limited to 20 last messages. Using the _messagesSnapshots as global List.

@override
Widget build(BuildContext context) {
 return Scaffold(
    key: key,
    appBar: AppBar(title: Text("Chat")),
    body: Container(
      child: Column(
        children: <Widget>[
          Flexible(
              child: StreamBuilder<QuerySnapshot>(
            stream: Firestore.instance
                .collection('messages')
                .where('room_id', isEqualTo: _roomID)
                .orderBy('timestamp', descending: true)
                .limit(20)
                .snapshots(),
            builder: (context, snapshot) {
              if (!snapshot.hasData) return LinearProgressIndicator();
              _messagesSnapshots = snapshot.data.documents;

              return _buildList(context, _messagesSnapshots);
            },
          )),
          Divider(height: 1.0),
          Container(
            decoration: BoxDecoration(color: Theme.of(context).cardColor),
            child: _buildTextComposer(),
          ),
        ],
      ),
    ));
}

Widget _buildList(BuildContext context, List<DocumentSnapshot> snapshot) {
 _messagesSnapshots = snapshot;

 return ListView.builder(
   controller: listScrollController,
   itemCount: _messagesSnapshots.length,
   reverse: true,
   itemBuilder: (context, index) {
     return _buildListItem(context, _messagesSnapshots[index]);
   },
 );
}

然后在_scollListener方法中查询接下来的20条消息,并将结果添加到全局"列表中.

And in the _scollListener method i query the next 20 messages and add the result to the Global list.

  _scrollListener() {

   // If reach top 
   if (listScrollController.offset >=
        listScrollController.position.maxScrollExtent &&
    !listScrollController.position.outOfRange) {

   // Then search last message
   final message = Message.fromSnapshot(
      _messagesSnapshots[_messagesSnapshots.length - 1]);

   // And get the next 20 messages from database
   Firestore.instance
      .collection('messages')
      .where('room_id', isEqualTo: _roomID)
      .where('timestamp', isLessThan: message.timestamp)
      .orderBy('timestamp', descending: true)
      .limit(20)
      .getDocuments()
      .then((snapshot) {

    // To save in the global list
    setState(() {
      _messagesSnapshots.addAll(snapshot.documents);
    });
  });

  // debug snackbar
  key.currentState.showSnackBar(new SnackBar(
    content: new Text("Top Reached"),
  ));
 }
}

推荐答案

我要发布我的代码,希望有人发布更好的解决方案,可能不是最好的解决方案,但是它可以工作.

I'm gonna post my code i hope someone post a better solution, probably is not the best but it works.

在我的应用中,实际的解决方案是在到达顶部时更改列表的状态,停止播放流并显示旧消息.

In my app the actual solution is change the state of the list when reach the top, stop stream and show old messages.

所有代码(州)

class _MessageListState extends State<MessageList> {
  List<DocumentSnapshot> _messagesSnapshots;
  bool _isLoading = false;

  final TextEditingController _textController = TextEditingController();
  ScrollController listScrollController;
  Message lastMessage;
  Room room;

  @override
  void initState() {
    listScrollController = ScrollController();
    listScrollController.addListener(_scrollListener);
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    room = widget.room;
    return Flexible(
      child: StreamBuilder<QuerySnapshot>(
        stream: _isLoading
            ? null
            : Firestore.instance
                .collection('rooms')
                .document(room.id)
                .collection('messages')
                .orderBy('timestamp', descending: true)
                .limit(20)
                .snapshots(),
        builder: (context, snapshot) {
          if (!snapshot.hasData) return LinearProgressIndicator();
          _messagesSnapshots = snapshot.data.documents;
          return _buildList(context, _messagesSnapshots);
        },
      ),
    );
  }

  Widget _buildList(BuildContext context, List<DocumentSnapshot> snapshot) {
    _messagesSnapshots = snapshot;

    if (snapshot.isNotEmpty) lastMessage = Message.fromSnapshot(snapshot[0]);

    return ListView.builder(
      padding: EdgeInsets.all(10),
      controller: listScrollController,
      itemCount: _messagesSnapshots.length,
      reverse: true,
      itemBuilder: (context, index) {
        return _buildListItem(context, _messagesSnapshots[index]);
      },
    );
  }

  Widget _buildListItem(BuildContext context, DocumentSnapshot data) {
    final message = Message.fromSnapshot(data);
    Widget chatMessage = message.sender != widget.me.id
        ? Bubble(
            message: message,
            isMe: false,
          )
        : Bubble(
            message: message,
            isMe: true,
          );
    return Column(
      children: <Widget>[chatMessage],
    );
  }

  loadToTrue() {
    _isLoading = true;
    Firestore.instance
        .collection('messages')
        .reference()
        .where('room_id', isEqualTo: widget.room.id)
        .orderBy('timestamp', descending: true)
        .limit(1)
        .snapshots()
        .listen((onData) {
      print("Something change");
      if (onData.documents[0] != null) {
        Message result = Message.fromSnapshot(onData.documents[0]);
        // Here i check if last array message is the last of the FireStore DB
        int equal = lastMessage?.compareTo(result) ?? 1;
        if (equal != 0) {
          setState(() {
            _isLoading = false;
          });
        }
      }
    });
  }

  _scrollListener() {
    // if _scroll reach top 
    if (listScrollController.offset >=
            listScrollController.position.maxScrollExtent &&
        !listScrollController.position.outOfRange) {
      final message = Message.fromSnapshot(
          _messagesSnapshots[_messagesSnapshots.length - 1]);
      // Query old messages
      Firestore.instance
          .collection('rooms')
          .document(widget.room.id)
          .collection('messages')
          .where('timestamp', isLessThan: message.timestamp)
          .orderBy('timestamp', descending: true)
          .limit(20)
          .getDocuments()
          .then((snapshot) {
        setState(() {
          loadToTrue();
          // And add to the list
          _messagesSnapshots.addAll(snapshot.documents);
        });
      });
      // For debug purposes
//      key.currentState.showSnackBar(new SnackBar(
//        content: new Text("Top reached"),
//      ));
    }
  }
}

最重要的方法是:

_scrollListener

到达顶部时,我查询旧消息,并在setState中将 isLoading var设置为true,并使用旧消息设置要显示的数组.

When reach the top i query old messages and in setState i set isLoading var to true and set with the old messages the array i m gonna show.

  _scrollListener() {
    // if _scroll reach top
    if (listScrollController.offset >=
            listScrollController.position.maxScrollExtent &&
        !listScrollController.position.outOfRange) {
      final message = Message.fromSnapshot(
          _messagesSnapshots[_messagesSnapshots.length - 1]);
      // Query old messages
      Firestore.instance
          .collection('rooms')
          .document(widget.room.id)
          .collection('messages')
          .where('timestamp', isLessThan: message.timestamp)
          .orderBy('timestamp', descending: true)
          .limit(20)
          .getDocuments()
          .then((snapshot) {
        setState(() {
          loadToTrue();
          // And add to the list
          _messagesSnapshots.addAll(snapshot.documents);
        });
      });
      // For debug purposes
//      key.currentState.showSnackBar(new SnackBar(
//        content: new Text("Top reached"),
//      ));
    }
  }

和loadToTrue在我们寻找旧消息时进行监听.如果有新消息,我们将重新激活流.

And loadToTrue that listen while we are looking for old messages. If there is a new message we re activate the stream.

loadToTrue

  loadToTrue() {
    _isLoading = true;
    Firestore.instance
        .collection('rooms')
        .document(widget.room.id)
        .collection('messages')
        .orderBy('timestamp', descending: true)
        .limit(1)
        .snapshots()
        .listen((onData) {
      print("Something change");
      if (onData.documents[0] != null) {
        Message result = Message.fromSnapshot(onData.documents[0]);
        // Here i check if last array message is the last of the FireStore DB
        int equal = lastMessage?.compareTo(result) ?? 1;
        if (equal != 0) {
          setState(() {
            _isLoading = false;
          });
        }
      }
    });
  }

我希望这对遇到相同问题的所有人(@Purus)有所帮助,等到有人为我们提供更好的解决方案!

I hope this helps anyone who have the same problem (@Purus) and wait until someone give us a better solution!

这篇关于StreamBuilder Firestore分页的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆