如果ListView比较长的话,直接listScrollController.position.maxScrollExtent是只能滚到距离最底有一定距离的地方。
原理贴在最后。
方法0:加上额外的滚动距离
缺点:iOS和web上会有严重的反弹效果,并且效果期间无法操作。
方法1:Scrollable.ensureVisible(BuildContext)
缺点:根本不可行。item较多时,靠后的item没渲染过,没有对应的RenderObject。
方法2:第一次animateTo后,判断maxScrollExtent是否和第一次前的估算值一致,不一致的话,再滚动一次。
listScrollController.animateTo(maxScrollExtent, duration: Duration(milliseconds: 200), curve: Curves.linear)
.whenComplete(() {
if (!listScrollController.hasClients) return;
var after = listScrollController.position.maxScrollExtent;
if (after > maxScrollExtent)
listScrollController.animateTo(after, duration: Duration(milliseconds: 200), curve: Curves.linearToEaseOut);
});
缺点:由于是拼接两次滚动,只能使用Curves.linear,否则能看出来有明显的拼接感。并且依然无法滚动到指定item。
方法3:利用RectGetter组件获取控件位置尺寸实现的几个高级效果和功能(https://juejin.cn/post/6844903650737782792)
大概原理上是利用Rect记录所有item的位置,循环jumpTo,直到目标item出现在屏幕内。和方法2类似。
方法4:使用https://pub.dev/packages/scrollable_positioned_list
大概原理是使用一个辅助列表来记录所有widget的高度
源自大佬的博客 https://blog.bombox.org/2020-06-30/flutter-chat-listview/
缺点:没有使用原生ListView,侵入性强。
方法5:https://pub.dev/packages/indexed_list_view
这个只能适用于无限高度的列表,所以也就没有底部。
方法6:https://pub.dev/packages/scroll_to_index
缺点:卡顿。类似于循环滚动。没有计算padding。
========================================================================
ListView的滚动原理:
https://mp.weixin.qq.com/s/itsrBAry7cZKmLGmLym62g
https://juejin.cn/post/6844904008339947528
https://juejin.cn/post/6844904015994552333
//ListView滚动的最大值估算。可以直接在SDK的framework源码中打印调试。
static double _extrapolateMaxScrollOffset(
int firstIndex,
int lastIndex,
double leadingScrollOffset,
double trailingScrollOffset,
int childCount,
) {
if (lastIndex == childCount - 1)
return trailingScrollOffset;
final int reifiedCount = lastIndex - firstIndex + 1;
//算出平均值
final double averageExtent = (trailingScrollOffset - leadingScrollOffset) / reifiedCount;
//加上剩余估计值
final int remainingCount = childCount - lastIndex - 1;
return trailingScrollOffset + averageExtent * remainingCount;
}
我自己画的草图: