虽然所有答案都产生了预期的效果,但我们应该在这里做一些改进。
-
首先在大多数情况下(谈到自动滚动)使用 postFrameCallbacks 是无用的,因为可以在 ScrollController 附件(由attach 方法产生)之后呈现一些东西,控制器将滚动到他知道的最后一个位置在您看来,该职位不可能是最新的。
-
使用reverse:true 应该是“尾随”内容的一个好技巧,但物理会颠倒,所以当您尝试手动移动滚动条时,您必须将其移动到另一侧 -> 糟糕的用户体验。
-
在设计图形界面时使用计时器是一种非常糟糕的做法 -> 在用于更新/生成图形工件时,计时器是一种病毒。
无论如何,说到问题,完成任务的正确方法是使用jumpTo 方法和hasClients 方法作为警卫。
是否有任何 ScrollPosition 对象已使用 attach 方法将自己附加到 ScrollController。
如果为 false,则不得调用与 ScrollPosition 交互的成员,例如 position、offset、animateTo 和 jumpTo。
用代码说话只需做这样的事情:
if (_scrollController.hasClients) {
_scrollController.jumpTo(_scrollController.position.maxScrollExtent);
}
无论如何,这段代码还不够,即使滚动条不在屏幕末尾,也会触发该方法,因此如果您手动移动栏,该方法将触发并执行自动滚动。
我们可以做得更好,在听众的帮助下,几个 bool 就可以了。
我正在使用这种技术在 SelectableText 中可视化大小为 100000 的 CircularBuffer 的值,并且内容不断正确更新,自动滚动非常流畅,即使对于非常非常长的内容也没有性能问题。也许正如有人在其他答案中所说的那样,animateTo 方法可能更流畅,更可定制,所以请随意尝试。
ScrollController _scrollController = new ScrollController();
bool _firstAutoscrollExecuted = false;
bool _shouldAutoscroll = false;
void _scrollToBottom() {
_scrollController.jumpTo(_scrollController.position.maxScrollExtent);
}
void _scrollListener() {
_firstAutoscrollExecuted = true;
if (_scrollController.hasClients && _scrollController.position.pixels == _scrollController.position.maxScrollExtent) {
_shouldAutoscroll = true;
} else {
_shouldAutoscroll = false;
}
}
@override
void initState() {
super.initState();
_scrollController.addListener(_scrollListener);
}
@override
void dispose() {
_scrollController.removeListener(_scrollListener);
super.dispose();
}
- 然后触发
_scrollToBottom,根据您的逻辑和需求,在您的setState:
setState(() {
if (_scrollController.hasClients && _shouldAutoscroll) {
_scrollToBottom();
}
if (!_firstAutoscrollExecuted && _scrollController.hasClients) {
_scrollToBottom();
}
});
解释
- 为了避免代码重复,我们做了一个简单的方法:
_scrollToBottom();
- 我们创建了一个
_scrollListener() 并将它附加到initState 中的_scrollController -> 将在滚动条第一次移动后触发。在这个监听器中,我们更新布尔值_shouldAutoscroll 的值,以了解滚动条是否位于屏幕底部。
- 我们删除了
dispose 中的侦听器,只是为了确保在小部件处置后不会做无用的事情。
- 在我们的
setState 中,当我们确定_scrollController 已连接并且位于底部(检查shouldAutoscroll 的值)时,我们可以调用_scrollToBottom()。
同时,仅在第一次执行时,我们强制 _scrollToBottom() 短路 _firstAutoscrollExecuted 的值。