Flutter-使用StreamBuilder从Firebase提取数据后,如何使用ScrollController跳到列表视图的底部?

时间:2019-11-01 05:48:28

标签: listview flutter scroll flutter-layout stream-builder

在使用Firestore查询流从streambuilder渲染数据列表视图后,使用列表中的ScrollController滚动到列表底部的最佳方法是什么?

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

git diff

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

谢谢。

4 个答案:

答案 0 :(得分:1)

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

我提供了我在创建聊天系统时发现的解决方案:

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);          
            },
          );
        }
    ),

在前面的代码中,所做的是反转列表,最新消息将在底部,并以降序对记录进行排序,即从最新到最近。这样,最新消息将位于列表的开头(在这种情况下位于底部),最少的消息位于列表的底部(在此情况下位于顶部)。

答案 1 :(得分:0)

scrollController.jumpTo(scrollController.position.maxScrollExtent);

只会将列表视图滚动到maxScrollValue,即,最大可滚动一次。 您将必须使用

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

实现您的尝试。

信息的理由[“ challengeReplies”]。length * 1.0: Coz,JumpTo需要一个双精度值。

答案 2 :(得分:0)

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

就我而言(我在 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 本身将为这个滚动小部件提供 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();
                    },
                  ),

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

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

答案 3 :(得分:0)

将 addListener 与 jumpTo 函数一起使用。你可以参考这个例子。

ScrollController _scrollController;
double _lastEndOfScroll = 0;

_scrollController.addListener(() {
  double maxScroll = _scrollController.position.maxScrollExtent;
  double currentScroll = _scrollController.position.pixels;
  if (maxScroll == currentScroll) {
    if (!isLoading) {
      _lastEndOfScroll = maxScroll;

      fetchData();
    }
  }
});

if (!isLoading) _scrollController.jumpTo(_lastEndOfScroll);

让我知道这是否有效。