在Flutter中使用SetState无效?可能是忽略了这个!

这次是Flutter开发技术分享,解决的问题点来自本人实际的开发经历。
首先描述一下问题:在某个组件中调用setState()方法更新该组件状态,结果是无法做到更新效果,组件仍然维持原状。
下面我们用代码示例还原问题场景:

class _MyHomePageState extends State<MyHomePage> {
  bool isChecked = false;

  showTestDialog() {
    showDialog(
      context: context,
      builder: (BuildContext context) {
        return SimpleDialog(title: Text("测试对话框标题"), children: <Widget>[
          Row(children: <Widget>[
            Checkbox(
                value: this.isChecked,
                onChanged: (bool val) {
                  setState(() {
                    this.isChecked = !this.isChecked;
                  });
                  debugPrint(this.isChecked.toString());
                }),
            Text("测试复选框")
          ])
        ]);
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
          child: RaisedButton(
        child: Text("点击出现弹窗"),
        onPressed: () {
          showTestDialog();
        },
      )),
    );
  }
}

为了突出问题点,减少不必要的干扰,我简化了原有代码内容。通过阅读上述代码,我们得知整个Demo的界面有一个按钮构成,当按钮被点击时,showTestDialog()方法被执行。界面将显示一个小窗口,里面有一个复选框。
我们要实现的效果当然是用户点击复选框的时候,改变复选框的状态。因此,在复选框的onChanged()方法中改变了决定复选框状态的布尔值,并setState()。
然而真实的运行结果并非像预期那样产生效果。
究其原因,我们还需从setState()说起。
顾名思义,setState()要求其作用对象必须是一个有状态的组件。如果作用对象本身无状态,那么setState()将无法起作用。
因此,我们找到原因:SimpleDialog()中的子组件默认是无状态的。
接下来的解决办法就简单了,只需要在SimpleDialog组件外部“套”一个StatefulBuilder组件即可。参考下面的代码:

class _MyHomePageState extends State<MyHomePage> {
  bool isChecked = false;

  showTestDialog() {
    showDialog(
      context: context,
      builder: (BuildContext context) {
        return StatefulBuilder(
          builder:
              (BuildContext context, void Function(void Function()) setState) {
            return SimpleDialog(title: Text("测试对话框标题"), children: <Widget>[
              Row(children: <Widget>[
                Checkbox(
                    value: this.isChecked,
                    onChanged: (bool val) {
                      setState(() {
                        this.isChecked = !this.isChecked;
                      });
                      debugPrint(this.isChecked.toString());
                    }),
                Text("测试复选框")
              ])
            ]);
          },
        );
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
          child: RaisedButton(
        child: Text("点击出现弹窗"),
        onPressed: () {
          showTestDialog();
        },
      )),
    );
  }
}

再次运行,对话框中的复选框可以正常响应。至此,问题解决。

上一篇:奇怪设置了enable.auto.commit=false不生效?!


下一篇:文件路径选择中的三态逻辑