是否有任何回调告诉我什么时候“构建”函数完成在扑动?

我的屏幕上有一个 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()函数。

在这种情况下,最佳实践是什么?

61426 次浏览

General solution

Just to clear things up, I did not expect this question to attract so much attention. Hence, I only answered for this very specific case.
As explained in another answer WidgetsBinding offers a way to add a one time post frame callback.

WidgetsBinding.instance!.addPostFrameCallback((_) {
// executes after build
})

As this callback will only be called a single time, you will want to add it every time you build:

@override
Widget build(BuildContext context) {
WidgetsBinding.instance!.addPostFrameCallback((_) => afterBuild);
return Container(); // widget tree
}


void afterBuild() {
// executes after build is done
}

Specific (async)

Elaborating on Günter's comment:

@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
}

There is a nice example illustrating that effect here.
Extensive information about scheduling in Dart can be found here.

The async way from @creativecreatorormaybenot is enough to answer the question for most situations.

But if you want to setState() or do something that will change widgets in the tree right after building the widget, you cannot use the async way. Because the callback will be fired during the build process of the widget tree. It will throw an exception :

Dart Error: Unhandled exception:
E/flutter (25259): setState() or markNeedsBuild() called during build.

For this situation, you can register a post frame callback to modify the widget:

@override
Widget build(BuildContext context) {
WidgetsBinding.instance
.addPostFrameCallback((_) => executeAfterWholeBuildProcess(context));

If you don't want to use WidgetsBinding:

  • Use Future or Timer

    @override
    Widget build(BuildContext context) {
    Timer(_runsAfterBuild); // <-- Just add this line
    return Container();
    }
    
    
    Future<void> _runsAfterBuild() async {
    // This code runs after build ...
    }
    
  • Add a dummy wait (fixes @creativecreatureormaybenot problem)

    @override
    Widget build(BuildContext context) {
    _runsAfterBuild();
    return Container();
    }
    
    
    Future<void> _runsAfterBuild() async {
    await Future.delayed(Duration.zero); // <-- Add a 0 dummy waiting time
    // This code runs after build ...
    }