Flutter错误:"Widget无法构建,因为它已经在构建过程中了"。

7

我遇到了这个错误:

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;
  }
}
2个回答

6

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

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

通常最好避免在build()中涉及"业务逻辑"。
build()中不应该需要计算使用InhState的所有内容。build()可能会在各种时间被经常调用。
相反,你应该根据用户交互事件(例如onTap:,...)或其他事件源,如流或未来(从异步操作指示状态更改)来更新状态。

说实话,我从未考虑过在继承的小部件中构建流,只是将继承的小部件视为流的替代品(但我并没有成功地实现它)。我理解你的观点,当inhstate变得非常拥挤时,我开始构建“规则”类,我会尝试实现一个流来处理它。感谢您的帮助。 - Francesco Iapicca

1
我会支持@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来帮助您处理代码风格。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接