Flutter Error: "Widget cannot build because is already in the process of building"
Flutter Error: "Widget cannot build because is already in the process of building"
我收到这个错误:
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)
在向您大量介绍代码之前,我想稍微解释一下背后的逻辑:
我正在尝试执行百家乐游戏规则,
为此,我设置了一个 Inherited 小部件,一些无状态小部件发送
存储在继承状态中的对象的输入,
根据此对象数据,其他小部件自行重建。
我做的最后一个改变是应用百家乐 "drawing rules",
可以使对象在需要时跳过输入,
但是当发生这种情况时,继承的小部件不喜欢它。
我将 post 代码的最后一部分,但如果您认为
有其他相关的问题,我会post。
提前感谢您的帮助
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;
}
}
您在 build()
中调用 state.skip()
并在 skip()
中调用 setState()
void skip() {
setState(() {
game.count = game.count + 1;
});
}
通常在 build()
中避免 "business logic" 是个好主意。
不需要计算您在 build()
中使用 InhState
所做的所有事情。 build()
可以在不同时间经常调用。
您应该根据用户交互事件 (onTap:
, ...) 或其他事件源(如指示异步操作的状态更改的流或期货)来更新状态。
我支持@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
来帮助您调整代码风格。
我收到这个错误:
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)
在向您大量介绍代码之前,我想稍微解释一下背后的逻辑: 我正在尝试执行百家乐游戏规则, 为此,我设置了一个 Inherited 小部件,一些无状态小部件发送 存储在继承状态中的对象的输入, 根据此对象数据,其他小部件自行重建。
我做的最后一个改变是应用百家乐 "drawing rules", 可以使对象在需要时跳过输入, 但是当发生这种情况时,继承的小部件不喜欢它。
我将 post 代码的最后一部分,但如果您认为 有其他相关的问题,我会post。
提前感谢您的帮助
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;
}
}
您在 build()
中调用 state.skip()
并在 skip()
setState()
void skip() {
setState(() {
game.count = game.count + 1;
});
}
通常在 build()
中避免 "business logic" 是个好主意。
不需要计算您在 build()
中使用 InhState
所做的所有事情。 build()
可以在不同时间经常调用。
您应该根据用户交互事件 (onTap:
, ...) 或其他事件源(如指示异步操作的状态更改的流或期货)来更新状态。
我支持@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
来帮助您调整代码风格。