我有一个屏幕,其中显示一个按钮。如果按它,将启动异步作业。在这段时间内,我想显示一个带有转轮的AlertDialog。如果该工作完成,我将关闭该对话框或显示一些错误。这是我的(简化)代码:
Widget build(BuildContext context) {
if (otherClass.isTaskRunning()) {
showDialog( ... ); // Show spinning wheel
}
if (otherClass.hasErrors()) {
showDialog( ...); // Show errors
}
return Scaffold(
...
FlatButton(
onPress: otherClass.startJob
)
);
}
当作业状态更改或有错误时,将触发构建。到目前为止,还不错,但是如果我运行此代码,则会收到以下错误消息:
发生异常。 FlutterError(setState()或markNeedsBuild() 在构建期间调用。此叠加小部件无法标记为需要 之所以要构建,是因为该框架已经在构建过程中 小部件。可以将小部件标记为需要在 仅在其祖先之一当前正在构建时才进入构建阶段。这个 允许例外,因为框架会构建父窗口小部件 在孩子之前,这意味着将永远建造一个肮脏的后代。 否则,框架在此构建期间可能不会访问此小部件 相。调用了setState()或markNeedsBuild()的小部件 原为:Overlay- [LabeledGlobalKey#357d8]该小部件 拨打违规电话时当前正在构建的是:
设置屏幕)
因此,屏幕的重绘将以某种方式重叠。我不确定如何解决此问题。感觉像我在使用这个完全错误的方法。处理这种交互(触发“长时间”运行的任务,显示进度指示器和可能的错误)的首选方式是什么?
答案 0 :(得分:0)
问题是在构建方法尚未完成时将显示对话框。因此,如果要显示对话框,则应在build方法完成后执行。为此,可以使用以下命令:WidgetsBinding.instance.addPostFrameCallback()
,它将在构建最后一帧之后(恰好在build方法结束之后)调用一个函数。
您可以做的另一件事是使用三元运算符显示加载小部件,如下所示:
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: otherClass.isTaskRunning()
? CircularProgressIndicator()
: FlatButton(
onPressed: () {},
),
);
}
答案 1 :(得分:0)
如评论中所述,调用showDialog
和类似的内部构造是反模式的。这是详细的说明。
尽管有些晚,但重要的是要注意showDialog
确实是异步方法,在对话框关闭时返回,而build
本质上是同步。在幕后,它使用Navigator.of(context).push
。
build方法的设计方式应使其纯净/无副作用。这是因为许多外部因素可以触发新的小部件构建,例如: Route pop / push
因此,这直接导致抖动抱怨setState
在构建期间被调用。您可以只在对话框中使用FutureBuilder
。
很明显,您的SettingsScreen
不需要获取任何东西,因此您不应与其中任何一个结为兄弟。当您推迟执行此操作时,应该在对话框中进行操作。
Widget build(BuildContext context) {
return Scaffold(
body: FlatButton(
onPress: () async {
await showDialog(
context: context,
builder: (BuildContext context) {
return FutureBuilder(
future: otherClass.startJob(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasError) // Show error
if (snapshot.hasData) // Show data, or you can just close it by Navigator.pop
else // show spinning wheel
}
}
);
}
);
}
)
);
}