setState() or markNeedsBuild called during build

后端 未结 6 1673
自闭症患者
自闭症患者 2020-12-04 16:14
class MyHome extends StatefulWidget {
  @override
  State createState() => new MyHomePage2();
}

class MyHomePage2 extends State

        
6条回答
  •  孤街浪徒
    2020-12-04 17:02

    The problem with WidgetsBinding.instance.addPostFrameCallback is that, it isn't an all encompassing solution.

    As per the contract of addPostFrameCallback -

    Schedule a callback for the end of this frame. [...] This callback is run during a frame, just after the persistent frame callbacks [...]. If a frame is in progress and post-frame callbacks haven't been executed yet, then the registered callback is still executed during the frame. Otherwise, the registered callback is executed during the next frame.

    That last line sounds like a deal-breaker to me.

    This method isn't equipped to handle the case where there is no "current frame", and the flutter engine is idle. Of course, in that case, one can invoke setState() directly, but again, that won't always work - sometimes there just is a current frame.


    Thankfully, in SchedulerBinding, there also exists a solution to this little wart - SchedulerPhase

    Let's build a better setState, one that doesn't complain.

    (endOfFrame does basically the same thing as addPostFrameCallback, except it tries to schedule a new frame at SchedulerPhase.idle, and it uses async-await instead of callbacks)

    Future rebuild() async {
      if (!mounted) return false;
    
      // if there's a current frame,
      if (SchedulerBinding.instance.schedulerPhase != SchedulerPhase.idle) {
        // wait for the end of that frame.
        await SchedulerBinding.instance.endOfFrame;
        if (!mounted) return false;
      }
    
      setState(() {});
      return true;
    }
    

    This also makes for nicer control flows, that frankly, just work.

    await someTask();
    
    if (!await rebuild()) return;
    
    await someOtherTask();
    

提交回复
热议问题