7

在使用 firestore 查询流从 streambuilder 呈现数据后,在列表中使用 ScrollController 滚动到列表底部的最佳方法是什么?

使用 scrollcontroller.jumpto 方法的最佳位置是什么?

// initialisation
ScrollController scrollController=ScrollController();

// inside initstate() and build method with 1000ms delay - not working
scrollController.jumpTo(scrollController.position.maxScrollExtent);

// widget inside build method
Expanded(
                child: StreamBuilder<DocumentSnapshot>(
                    stream: Firestore.instance
                        .collection('Data')
                        .document(widget.dataID)
                        .snapshots(),
                    builder: (context, AsyncSnapshot<DocumentSnapshot> snapshot) {
                      print("Called");
                      if (!snapshot.hasData) {
                        return Text("Loading");
                      }
                      var info= snapshot.data.data;
                      if (info!= null &&
                          info["messages"] != null &&
                          info["messages"].length > 0 &&
                          userList.length > 0) {
                        return ListView.builder(
                          controller: scrollController,
                          itemCount: info["challengeReplies"].length,
                          itemBuilder: (BuildContext context, int index) {

                            return ChallengeReplyListItem(
                                currentUserID: widget.currentUserID,
                                messagesType: info["messages"][index]["messagesType"],
                                messagesContent: info["messages"][index]["messagesContent"],
                          },
                        );
                      } else if (isLoading) {
                        return Center(
                          child: CircularProgressIndicator(),
                        );
                      } else {
                        return Center(child: Text("No Data Found",style: TextStyle(color: Colors.blueGrey,fontSize: 22,fontWeight: FontWeight.w500),));
                      }
                    }),
              ),

任何人都可以提出适当的解决方案来在数据正确呈现后处理滚动到页面底部。

谢谢。

4

4 回答 4

2

当然,滚动控制器是让聊天一直到发送的最后一条消息的第一件事。但这不是唯一的方法。

我提供了一个我在制作聊天系统时发现的解决方案:

StreamBuilder(
      stream: FirebaseFirestore.instance
      .collection('<MessagesColection>')
      .orderBy('<Time field>',descending: true)
      .snapshots(),
      builder: (context,snapshot) {                    
          return ListView.builder(
            //The reversed list will put the list backwards. 
            //The start of the list will start at the bottom.
            reverse: true, 
            controller: scrollController,      
            itemCount: snapshot.data.size,
            itemBuilder: (context, index) {                                                    
              return ChatMessage(snapshot);          
            },
          );
        }
    ),

在前面的代码中,所做的是将列表倒置,最近的消息将在底部,并按降序排列记录,即从最近到最近。这样,最新消息将位于列表的开头(在本例中位于底部),而最少的消息将位于列表底部(在本例中位于顶部)。

于 2020-11-25T00:57:31.570 回答
0
scrollController.jumpTo(scrollController.position.maxScrollExtent);

只会将列表视图滚动到maxScrollValue,即一次可能的最大滚动。你将不得不使用

scrollController.jumpTo(info["challengeReplies"].length*1.0)

实现你正在尝试的东西。

info["challengeReplies"].length*1.0 的原因:因为,JumpTo 需要一个双精度值。

于 2020-04-12T20:22:55.627 回答
0

这可能有点hacky,但我找不到其他方法。

就我而言(我在 StreamBuilder 中使用 PageView,但应该与您的类似),

我将它提取到单独的有状态小部件中,给它一个UniqueKey()并将其移动ScrollController到它的状态,例如(请阅读代码中的注释):

  //The extracted scrolling widget 
  class ScrollHeader extends StatefulWidget {
      const ScrollHeader({
        Key key,
        @required this.slides,
        @required this.currentSlide,
      }) : super(key: key);
    
      final List<Widget> slides;
      final int currentSlide;
    
      @override
      _ScrollHeaderState createState() => _ScrollHeaderState();
    }
    
    class _ScrollHeaderState extends State<ScrollHeader> {
      PageController headerController;
    
      @override
      void initState() {
        super.initState();
    
        //Assign initialPage(Or newly received page) in initState.
        headerController = PageController(initialPage: widget.currentSlide);
      }
    
      @override
      Widget build(BuildContext context) {

        //Since PageView doesn't exist on the first render yet, 
        //ScrollController will throw an error, so we need to escape it using IF:
        if (headerController.hasClients) {
          headerController.animateToPage(
            widget.currentSlide,
            duration: Duration(milliseconds: 350),
            curve: Curves.easeIn,
          );
        }

        //If you don't need animation, you can just do this without IFs:
        headerController.jumpToPage(index);
    
        return PageView(
          scrollDirection: Axis.horizontal,
          children: widget.slides,
          controller: headerController,
        );
      }
    }

StreamBuilder本身将为这个 Scrolling 小部件提供initialPage子元素和子元素:

Widget build(BuildContext context) {

    Key appbarKey = UniqueKey(); //We need Key to not destroy State between StreamBuilder's updates 

    StreamBuilder(
                    stream: appBarState.stream$, //Could be any stream(Like Firestore for example).
                    builder: (context, snap) {
                      if (snap.hasData) {
                        //Just generated children elements
                        List<Widget> slides = List<Widget>.from(
                          List<String>.from(appBarState.current["slides"]).map(
                            (e) => Center(
                              child: Text(
                                e,
                                style: TextStyle(
                                  color: theme.accentColor,
                                  fontSize: 20,
                                ),
                              ),
                            ),
                          ),
                        );
                        print(snap.data);
                        return ScrollHeader(
                          key: appbarKey,
                          slides: slides,
                          currentSlide: snap.data["currentSlide"], //This is the page that pageView will snap/animate to after receiving new data
                        );
                      }
                      return Container();
                    },
                  ),

这允许您随时更新和动画/捕捉滚动小部件的页面,以及在使用流时更新其子级。

希望这会有所帮助,我希望有人会发布一个更好、更优雅的解决方案,但是这个可行(到目前为止)。

于 2021-02-25T09:50:06.660 回答
0

试试这个它会工作:

StreamBuilder(
  stream: ...,
  builder: (BuildContext context, AsyncSnapshot snapshot) {
    // Like this:
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (_controller.hasClients) {
        _controller.jumpTo(_controller.position.maxScrollExtent);
      } else {
        setState(() => null);
      }
     });

     return PutYourListViewHere
}),
于 2021-12-06T06:17:23.170 回答