在Flutter中使用SetState無效?可能是忽略了這個!

蕭文翰發表於2020-06-20

這次是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();
        },
      )),
    );
  }
}
複製程式碼

再次執行,對話方塊中的核取方塊可以正常響應。至此,問題解決。

相關文章