虽然所有答案都产生了预期的效果,但我们应该在这里做一些改进。
首先,在大多数情况下(谈到自动滚动)使用 postFrameCallbacks 是没有用的,因为在 ScrollController 附件(由该attach
方法产生)之后可以呈现一些东西,控制器将滚动到他知道的最后一个位置并且该位置不可能您认为最新的。
使用reverse:true
应该是“尾随”内容的好技巧,但物理会反转,因此当您尝试手动移动滚动条时,您必须将其移动到另一侧 - > BAD UX。
在设计图形界面时使用计时器是一种非常糟糕的做法 -> 计时器在用于更新/生成图形工件时是一种病毒。
无论如何,谈到这个问题,完成任务的正确方法是使用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()
并将其附加到_scrollController
- initState
> 将在第一次滚动条移动后触发。在这个监听器中,我们更新 bool 值_shouldAutoscroll
,以了解滚动条是否位于屏幕底部。
- 我们删除了监听器,
dispose
只是为了确保在小部件处理后不会做无用的事情。
- 在我们
setState
确定_scrollController
已附加并且位于底部(检查 的值shouldAutoscroll
)时,我们可以调用_scrollToBottom()
.
同时,仅在第一次执行时,我们强制_scrollToBottom()
短路_firstAutoscrollExecuted
.