[英]Flutter ListView.builder Stutters and Jumps to Top on Scroll
[英]Flutter ListView Jumps To Top
我正在为我的应用创建一个新闻提要,当我向下滚动时,数据是从 Firestore 获取的。 一旦我向上滚动,列表视图就会直接捕捉到最顶部,跳过中间的所有其他项目。 如果我使用静态数据加载列表视图,列表视图工作正常,但是当我从 Firestore 提取数据时,问题再次出现。 我不确定是什么导致了这种行为。
这是我的代码
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]),);
});
},
);
刚刚在使用 Flutter 1.17.3 时遇到了同样的问题。 基于@Ashton-Thomas 的评论: Flutter ListView Jumps To Top我补充说:
cacheExtent: estimatedCellHeight * numberOfItems,
到我的ListView
构造函数,问题就消失了。 它也大大提高了整个列表的响应能力。
注意:我的列表中只有大约 50 个项目,所以cacheExtent
不是很高。 如果您有数百/数千个项目,则可能需要进行调整。
问题原因:
ListView
和一般的ScrollViews
倾向于处理当前在屏幕上不可见的子项。 当我们尝试回滚到孩子时,孩子会从头开始重新初始化。 但在这种情况下,我们的孩子是一个 FutureBuilder; 重新初始化它会在一秒钟内再次创建一个进度指示器,然后再次创建页面。 这混淆了滚动机制,让我们以不确定的方式四处游荡。
解决方案:
解决此问题的一种方法是确保进度指示器具有与页面完全相同的大小,但在大多数情况下,这不太实用。 因此,我们将求助于一种效率较低的方法,但这将解决我们的问题; 我们将阻止 ListView 处理孩子。 为了做到这一点,我们需要用
AutomaticKeepAliveClientMixin
包装每个孩子——也就是每个FutureBuilder
。 这个mixin让孩子们要求他们的父母即使在屏幕外也让他们活着,这将解决我们的问题。 所以:
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() 中。
这个问题有2个解决方案:
解决方案1:(如果ListView的高度是动态的)
SingleChildScrollView
包裹ListView
physics: const NeverScrollableScrollPhysics()
的 ListView 的physics: const NeverScrollableScrollPhysics()
属性shrinkWrap: true
以避免渲染问题或者
解决方案 2:(如果 ListView 的高度已知或可预测)
cacheExtent:
设置为 ListView 的高度 - 它是一个double
cacheExtent:
值。cacheExtent: 200.0
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.