Flutter中setState调度build的机制及相关场景疑问
void main() { runApp(const MaterialApp(home: MyApp())); } class MyApp extends StatefulWidget { const MyApp({Key? key}) : super(key: key); @override State<MyApp> createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { @override Widget build(BuildContext context) { // calling setState intentionally ! setState(() { print('looping'); }); return Scaffold( body: Container() ); } }
问题解答
1. build内同步调用setState无异常/循环,异步调用却无限循环的原因
Flutter框架执行State的build方法时,会给当前State打上「正在构建」的标记。当你在build里同步调用setState时:
- 框架检测到当前处于构建阶段,只会把该State标记为「需要重建」,但不会立即触发新的build——当前build流程还没走完,不能中断重入。
- 等当前build执行完毕后,框架会检查待重建的State,但你的代码里setState并没有修改任何实际状态,加上该State刚完成构建,框架会跳过重复的build调度,所以既不会出现循环,也不会抛出「setState() called during build」的错误(这个错误通常是构建过程中触发其他组件重建时才会抛出,当前组件自身的同步setState不会触发该报错)。
而用Future.delayed异步调用setState时:
- 异步代码会在当前build完全执行结束后才运行,此时该State已经脱离「正在构建」状态。
- 调用setState会正常触发一次新的build调度,新的build里又会再次异步调用setState,如此往复就形成了无限重建循环。
2. initState/didChangeDependencies中调用setState不触发build的原因
在State生命周期中,initState和didChangeDependencies的执行时机早于第一次build的调度:
- 框架初始化State时,会依次执行
initState→didChangeDependencies,之后会自动调度第一次build。 - 如果在这两个方法里调用setState,框架只会记录该State的状态发生了变化,但因为本来就已经要执行build了,所以不会额外触发一次新的build——相当于把这次状态更新合并到了即将到来的第一次build中,所以你看不到单独的build触发,但后续的build会包含这次状态变化的结果。
官方文档原文翻译
调用setState会通知框架,该对象的内部状态已发生变化,可能会影响此子树中的用户界面,这会导致框架为此State对象调度一次构建。
内容的提问来源于stack exchange,提问作者Kaustubh Dixit
相关产品推荐
相关产品推荐

