简体   繁体   English

Flutter ListView 跳转到顶部

[英]Flutter ListView Jumps To Top

I am creating a news feed for my app and as I scroll down data is fetched from Firestore.我正在为我的应用创建一个新闻提要,当我向下滚动时,数据是从 Firestore 获取的。 As soon as I scroll up the listview literally snaps to the very top skipping all the other items in between.一旦我向上滚动,列表视图就会直接捕捉到最顶部,跳过中间的所有其他项目。 The listview works fine if I load it with static data but when i pull data from Firestore, the issue reemerges.如果我使用静态数据加载列表视图,列表视图工作正常,但是当我从 Firestore 提取数据时,问题再次出现。 I am not sure what's causing this behaviour.我不确定是什么导致了这种行为。

Video demonstrating the irregular scrolling behaviour 演示不规则滚动行为的视频

Here is my code这是我的代码

return StreamBuilder(
            stream: Firestore.instance.collection(Constants.NEWS_FEED_NODE)
                .document("m9yyOjsPxV06BrxUtHdp").
            collection(Constants.POSTS_NODE).orderBy("timestamp",descending: true).snapshots(),
            builder: (BuildContext context, AsyncSnapshot snapshot) {
              if (!snapshot.hasData)
                return Center(child: CircularProgressIndicator(),);

              if (snapshot.data.documents.length == 0)
                return Container();
              //after getting the post ids, we then make a call to FireStore again
              //to retrieve the details of the individual posts
              return ListView.builder(
                  itemCount: snapshot.data.documents.length,
                  itemBuilder: (_, int index) {
                    return FeedItem2(feed: Feed.fireStore(
                        snapshot: snapshot.data.documents[index]),);
                  });
            },
          );

Just had the same issue using Flutter 1.17.3.刚刚在使用 Flutter 1.17.3 时遇到了同样的问题。 Based on the comment from @Ashton-Thomas here: Flutter ListView Jumps To Top I added:基于@Ashton-Thomas 的评论: Flutter ListView Jumps To Top我补充说:

cacheExtent: estimatedCellHeight * numberOfItems,

to my ListView constructor and the problem went away.到我的ListView构造函数,问题就消失了。 It has drastically increased the responsiveness of the entire list as well.它也大大提高了整个列表的响应能力。

Note: my list only has about 50 items in it, so the cacheExtent isn't extremely high.注意:我的列表中只有大约 50 个项目,所以cacheExtent不是很高。 May need to adjust if you have hundreds/thousands of items.如果您有数百/数千个项目,则可能需要进行调整。

Reason for the problem:问题原因:

ListView , and ScrollViews in general, tend to dispose of the children that are not currently visible on the screen. ListView和一般的ScrollViews倾向于处理当前在屏幕上不可见的子项。 When we try to scroll back to the child, the child is reinitialized from scratch.当我们尝试回滚到孩子时,孩子会从头开始重新初始化。 But in this case, our child is a FutureBuilder;但在这种情况下,我们的孩子是一个 FutureBuilder; re-initializing it creates a progress indicator again just for a part of a second, then creates the page once again.重新初始化它会在一秒钟内再次创建一个进度指示器,然后再次创建页面。 This confuses the scrolling mechanism, throwing us around in non-deterministic ways.这混淆了滚动机制,让我们以不确定的方式四处游荡。

Solution : 解决方案

One way to solve this is to make sure that the progress indicator has the exact same size of the page, but in most cases, that is not too practical.解决此问题的一种方法是确保进度指示器具有与页面完全相同的大小,但在大多数情况下,这不太实用。 So, we will resort to a method that is less efficient, but that will solve our problems;因此,我们将求助于一种效率较低的方法,但这将解决我们的问题; we will prevent ListView from disposing of the children.我们将阻止 ListView 处理孩子。 In order to do that, we need to wrap each child — that is, each FutureBuilder , with an AutomaticKeepAliveClientMixin .为了做到这一点,我们需要用AutomaticKeepAliveClientMixin包装每个孩子——也就是每个FutureBuilder This mixin makes the children ask their parent to keep them alive even when off-screen, which will solve our problem.这个mixin让孩子们要求他们的父母即使在屏幕外也让他们活着,这将解决我们的问题。 So:所以:

  1. Replace the FutureBuilder in your code with KeepAliveFutureBuilder.将代码中的 FutureBuilder 替换为 KeepAliveFutureBuilder。
  2. Create the KeepAliveFutureBuilder widget:创建 KeepAliveFutureBuilder 小部件:
class KeepAliveFutureBuilder extends StatefulWidget {

  final Future future;
  final AsyncWidgetBuilder builder;

  KeepAliveFutureBuilder({
    this.future,
    this.builder
  });

  @override
  _KeepAliveFutureBuilderState createState() => _KeepAliveFutureBuilderState();
}

class _KeepAliveFutureBuilderState extends State<KeepAliveFutureBuilder> with AutomaticKeepAliveClientMixin {
  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: widget.future,
      builder: widget.builder,
    );
  }

  @override
  bool get wantKeepAlive => true;
}
  • This widget is just a wrapper around the FutureBuilder.这个小部件只是 FutureBuilder 的包装器。 It is a StatefulWidget whose State extends the State class with the AutomaticKeepAliveClientMixin.它是一个 StatefulWidget,其 State 使用 AutomaticKeepAliveClientMixin 扩展了 State 类。
  • It implements the wantKeppAlive getter, and makes it simply return true, to denote to the ListView that we want this child to be kept alive.它实现了 wantKeppAlive getter,并使其简单地返回 true,以向 ListView 表示我们希望这个孩子保持活动状态。

只需将最顶部的小部件包装在 SingleChildScrollView() 中。

This problem has 2 solutions:这个问题有2个解决方案:

Solution 1: (If height of ListView is dynamic)解决方案1:(如果ListView的高度是动态的)

  1. Wrap the ListView with SingleChildScrollViewSingleChildScrollView包裹ListView
  2. Set the physics: const NeverScrollableScrollPhysics() property of ListView as mentioned设置physics: const NeverScrollableScrollPhysics()的 ListView 的physics: const NeverScrollableScrollPhysics()属性
  3. Set shrinkWrap: true to avoid render issue设置shrinkWrap: true以避免渲染问题

or或者

Solution 2: (If height of ListView is known or predictable)解决方案 2:(如果 ListView 的高度已知或可预测)

  • set cacheExtent: to the height of the ListView - It is a double value.cacheExtent:设置为 ListView 的高度 - 它是一个double cacheExtent:值。
  • Eg - If 10 items are present in ListView & each item has 20.0 as height, then give cacheExtent: 200.0例如 - 如果 ListView 中存在 10 个项目并且每个项目的高度为 20.0,则给cacheExtent: 200.0

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM