[英]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
, andScrollViews
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.这混淆了滚动机制,让我们以不确定的方式四处游荡。
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 anAutomaticKeepAliveClientMixin
.为了做到这一点,我们需要用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:所以:
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;
}
只需将最顶部的小部件包装在 SingleChildScrollView() 中。
This problem has 2 solutions:这个问题有2个解决方案:
Solution 1: (If height of ListView is dynamic)解决方案1:(如果ListView的高度是动态的)
ListView
with SingleChildScrollView
用SingleChildScrollView
包裹ListView
physics: const NeverScrollableScrollPhysics()
property of ListView as mentioned设置physics: const NeverScrollableScrollPhysics()
的 ListView 的physics: const NeverScrollableScrollPhysics()
属性shrinkWrap: true
to avoid render issue设置shrinkWrap: true
以避免渲染问题or或者
Solution 2: (If height of ListView is known or predictable)解决方案 2:(如果 ListView 的高度已知或可预测)
cacheExtent:
to the height of the ListView - It is a double
value.将cacheExtent:
设置为 ListView 的高度 - 它是一个double
cacheExtent:
值。cacheExtent: 200.0
例如 - 如果 ListView 中存在 10 个项目并且每个项目的高度为 20.0,则给cacheExtent: 200.0
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.