颤振错误:“小部件无法构建,因为已经在构建过程中”

Posted

技术标签:

【中文标题】颤振错误:“小部件无法构建,因为已经在构建过程中”【英文标题】:Flutter Error: "Widget cannot build because is already in the process of building" 【发布时间】:2019-04-29 02:57:00 【问题描述】:

我收到了这个错误:

I/flutter (29346): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter (29346): The following assertion was thrown building MainLogic(dirty, state: _MainLogic#9c794):
I/flutter (29346): setState() or markNeedsBuild() called during build.
I/flutter (29346): This InhWidget widget cannot be marked as needing to build because the framework is already in the
I/flutter (29346): process of building widgets. A widget can be marked as needing to be built during the build phase
I/flutter (29346): only if one of its ancestors is currently building. This exception is allowed because the framework
I/flutter (29346): builds parent widgets before children, which means a dirty descendant will always be built.
I/flutter (29346): Otherwise, the framework might not visit this widget during this build phase.
I/flutter (29346): The widget on which setState() or markNeedsBuild() was called was:
I/flutter (29346):   InhWidget(state: InhState#3aaa8)
I/flutter (29346): The widget which was currently being built when the offending call was made was:
I/flutter (29346):   MainLogic(dirty, state: _MainLogic#9c794)

在向您介绍代码之前,我想先解释一下背后的逻辑: 我正在尝试执行百家乐游戏的规则, 为此,我设置了一个继承的小部件,一些无状态的小部件发送 存储在继承状态中的对象的输入, 根据此对象数据,其他小部件会自行重建。

我所做的最后一个更改是应用百家乐“绘图规则”, 可以使对象在需要时跳过输入, 但是当这种情况发生时,继承的小部件不喜欢它。

我将发布代码的最后一部分,但如果您认为 有其他相关的,请询问,我会发布。

提前感谢您的帮助

    class MainLogic extends StatefulWidget 
  @override
  State<StatefulWidget> createState() => new _MainLogic();

class _MainLogic extends State<MainLogic> 
  Widget cardGrid = CardGrid();
  Widget newGame = NewGame();
  Rules rules;
  bool gameOver;
  @override
  void initState()
    super.initState();
    gameOver = false;
  
  @override
  Widget build(BuildContext context) 
    final InhState state = InhWidget.of(context);
    rules = new Rules(game:state.game);
    if(gameOver==false && state.game.count>3 && rules.playerExtraCard()==false)
    state.skip();
    if(gameOver==false && state.game.count>4 && rules.bankerExtraCard()==false)
    state.skip();
    checkGame(state.game.count);
    return Container(child:status(gameOver));
  
  status(bool _gameOver) 
    Widget whichOne;
    if (_gameOver) whichOne=newGame;
    else whichOne=cardGrid;
    return whichOne;
  
  void checkGame(int cards) 
    if (cards >= 6) 
      gameOver = true;
     else gameOver = false;
  

...

   class Rules 
  final Game game;
  Rules(this.game);
  static Deck deck = new Deck();
  int get p1 => deck.getValue(game.p1);
  int get p2 => deck.getValue(game.p2);
  int get p3 => deck.getValue(game.p3);
  int get b1 => deck.getValue(game.b1);
  int get b2 => deck.getValue(game.b2);
  int get b3 => deck.getValue(game.b3);
  normalize(List<int> input) 
    int normal;
    int initial = 0;
    input.forEach((num e)initial += e;);
    if(initial>9) normal=initial-10;
    else normal=initial;
    return normal;
  
  playerExtraCard() 
    bool extraCard;
    int pInit = normalize([p1,p2]);
    int bInit = normalize([b1+b2]);
    if (pInit > 5) extraCard = false;
    else if (bInit > 7) extraCard = false;
    else extraCard = true;
    return extraCard;
  
  bankerExtraCard() 
    bool extraCard;
    int pInit = normalize([p1,p2]);
    int bInit = normalize([b1+b2]);
    int pWing = normalize([p3]);
    if (pInit>7) extraCard = false;
    else if (bInit>6) extraCard = false;
    else if (pInit>5 && bInit<6) extraCard = true;
    else if (pInit<6 && bInit==6 && pWing<8 && pWing>5) extraCard = true;
    else if (pInit<6 && bInit==5 && pWing<8 && pWing>3) extraCard = true;
    else if (pInit<6 && bInit==4 && pWing<8 && pWing>1) extraCard = true;
    else if (pInit<6 && bInit==3 && pWing==8) extraCard = true;
    else if (pInit<6 && bInit<3) extraCard = true;
    else extraCard = false;
    return extraCard;
  

...

   class InhState extends State<InhWidget> 
  Game game = new Game();
  @override
  void initState() 
    super.initState();
    game.blank();
  
  void deal(String card) 
    setState(() 
      game.cards[game.count] = card;
      game.count = game.count + 1;
    );
  
  void restart()
    setState(() 
      game.blank();
    );
  
  void skip() 
    setState(() 
      game.count = game.count + 1;
    );
  
  @override
  Widget build(BuildContext context) 
    return new InhCore(
      data: this,
      child: widget.child,
    );
  

...

   class InhWidget extends StatefulWidget 
  InhWidget(this.child);
  final Widget child;
  @override
  State<StatefulWidget> createState() => InhState();
  static InhState of(BuildContext context) 
    return (context.inheritFromWidgetOfExactType(InhCore) as InhCore).data;
  

【问题讨论】:

【参考方案1】:

您在build() 中调用state.skip() 和在skip() 中调用setState()

  void skip() 
    setState(() 
      game.count = game.count + 1;
    );
   

通常最好避免build() 中的“业务逻辑”。

应该不需要计算你在build() 中使用InhState 所做的所有事情。 build() 可以在不同时间经常调用。

您应该根据用户交互事件 (onTap:, ...) 或其他事件源(如流或期货)来更新状态,这些事件源指示异步操作的状态更改。

【讨论】:

我没有考虑过在继承的小部件“内部”构建流,老实说,我将继承的小部件描绘为流的替代品(我未能成功实现)。我明白你的意思,当 inhstate 变得非常拥挤时,我开始构建“规则”类,我将尝试实现一个流来处理它。谢谢你的帮助【参考方案2】:

我支持@Günter Zöchbauer 的回答并按照他的建议重构代码,但为了更快地修复,您可以尝试将state.skip(); 指令(build() 中的指令)包装到WidgetsBinding.instance.addPostFrameCallback 中。这样,您可以将状态更改操作推迟到构建之后立即发生。

@override
Widget build(BuildContext context) 
  final InhState state = InhWidget.of(context);
  rules = new Rules(game:state.game);
  if (!gameOver) 
    if (state.game.count > 3 && !rules.playerExtraCard() ||
        state.game.count > 4 && !rules.bankerExtraCard())
    
      WidgetsBinding.instance.addPostFrameCallback((_) 
        state.skip();
      );
    
  

  checkGame(state.game.count);
  return Container(child:status(gameOver));

不相关,但我也建议您在项目中使用 flutter format 来帮助您改进代码风格。

【讨论】:

以上是关于颤振错误:“小部件无法构建,因为已经在构建过程中”的主要内容,如果未能解决你的问题,请参考以下文章

颤振给我错误(不知道为啥)(使用颤振和Android Studio)

升级颤振后颤振标签栏错误“getter'key'被称为null”

当我对颤振项目进行颤振清理时出现错误

命令“颤振:新项目”导致错误(找不到命令“颤振.createProject”),我无法在 VSCODE 上调试颤振项目

颤振升级后出现错误'textBaseline!= null'

颤振:第一次运行我的颤振应用程序时出现 gradle 错误