将showDialog与条件生成方法一起使用

时间:2019-08-05 19:11:54

标签: flutter

我有一个屏幕,其中显示一个按钮。如果按它,将启动异步作业。在这段时间内,我想显示一个带有转轮的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]该小部件   拨打违规电话时当前正在构建的是:
  设置屏幕)

因此,屏幕的重绘将以某种方式重叠。我不确定如何解决此问题。感觉像我在使用这个完全错误的方法。处理这种交互(触发“长时间”运行的任务,显示进度指示器和可能的错误)的首选方式是什么?

2 个答案:

答案 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

引用this question

  

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
                }
              }
            );
          }
        );
      }
    )
  );
}