我的屏幕上有一个listView。我已经连接了一个控制器。我可以调用我的端点,接收响应,解析它并插入行中。 ListView应该自动滚动。确实可以,但是不是完美的方式。我总是落后。这是我的代码:
@override
Widget build(BuildContext context) {
// Scroll to the most recent item
if (equationList.length > 0) {
_toEnd();
}
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: EquList(equationList, _scrollController),
floatingActionButton: new FloatingActionButton(
onPressed: onFabClick,
tooltip: 'Fetch Post',
child: new Icon(isLoading ? Icons.pause : Icons.play_arrow),
),
);
}
void _toEnd() {
_scrollController.animateTo(
_scrollController.position.maxScrollExtent,
duration: const Duration(milliseconds: 250),
curve: Curves.ease,
);
}
问题是,我要在最后一项插入列表之前调用_toEnd()
函数。因此,我正在寻找一个告诉我build()
完成的回调(如果有)。然后调用我的_toEnd()
函数。
在这种情况下,最佳做法是什么?
答案 0 :(得分:21)
从@creativecreatorormaybenot开始的异步方式足以回答大多数情况下的问题。
但是,如果您要setState()
或执行某些操作以在构建窗口小部件后立即更改树中的窗口小部件,则不能使用异步方式。因为回调将在小部件树的构建过程中触发。它将引发异常:
Dart Error: Unhandled exception:
E/flutter (25259): setState() or markNeedsBuild() called during build.
在这种情况下,您可以注册后框架回调以修改小部件:
@override
Widget build(BuildContext context) {
WidgetsBinding.instance
.addPostFrameCallback((_) => executeAfterWholeBuildProcess(context));
答案 1 :(得分:5)
只是为了解决问题,我没想到这个问题会引起如此多的关注。因此,我只针对这种非常具体的情况进行回答。
由于explained in another answer WidgetsBinding
提供了一种添加一次 后帧回调 的方法。
WidgetsBinding.addPostFrameCallback(() {
// executes after build
})
由于此回调将only be called a single time,因此您希望在每次构建时都添加它:
@override
Widget build(BuildContext context) {
WidgetsBinding.addPostFrameCallback(afterBuild);
return Container(); // widget tree
}
void afterBuild() {
// executes after build is done
}
详细介绍Günter's条评论:
@override
Widget build(BuildContext context) {
executeAfterBuild();
return Container();
}
Future<void> executeAfterBuild() async {
// this code will get executed after the build method
// because of the way async functions are scheduled
}
有一个很好的例子illustrating that effect here。
有关在Dart中调度 can be found here的广泛信息。
答案 2 :(得分:1)
使用Future.delayed将是一种更好的方法,以确保在构建后执行代码。
@override
Widget build(BuildContext context) {
Future.delayed(Duration.zero, () => executeAfterBuild());
return Container();
}
Future<void> executeAfterBuild() async {
// this code will get executed after the build method
}