【Flutter】showDialog内でsetStateを使っても反映されない時の対処法

2025/03/07に公開

こんにちは、laughtaoneです。
Flutter学習中に備忘録として書いていますので、正確な情報でない可能性もありますのでご了承ください。

修正前の状況

showDialog 内の builder: (BuildContext context) {}で、次のようにテキストを変更したら、下の「保存」ボタンが活性化し、押せるようにしたいのですが押せたい状態となってしまっています。

これを押せるように修正していきます!
showDialogの元コードは、次のトグル内のコードのとおりです。

元コード (showDialog部分)

※一部に別ファイルのコンポーネントを使用しているため、そのままコピペしても使えませんので、ご了承ください。

showDialog(
  context: context,
  barrierDismissible: false,
  builder: (BuildContext context) {
    String newName = widget.title;
    return AlertDialog(
      backgroundColor: Colors.white,
      title: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Text("編集"),
              CompCloseCircleButton(onPressed: () => Navigator.pop(context))
            ],
          ),
          SizedBox(height: 6),
          Text(
            '元の名前:${widget.title}',
            style: TextStyle(fontSize: 12),
          ),
        ],
      ),
      content: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          TextFormField(
            style: TextStyle(fontSize: 23),
            initialValue: widget.title,
            decoration: InputDecoration(labelText: '変更後の名前を入力'),
            onChanged: (value) {
              setState(() {
                newName = value; // テキストフィールドの変更を反映
              });
            },
          ),
          SizedBox(height: 30),
          SizedBox(
            width: double.infinity,
            height: 55,
            child: TextButton(
              onPressed: (newName.isNotEmpty && widget.title != newName)
                ? () => Navigator.pop(context, newName)
                : null,
              style: TextButton.styleFrom(
                disabledBackgroundColor: Color.fromRGBO(140, 140, 140, 0.6),
                backgroundColor: Color.fromRGBO(140, 140, 140, 1),
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(15),
                ),
              ),
              child: Text(
                '保存',
                style: TextStyle(
                  fontSize: 15.0,
                  fontWeight: FontWeight.bold,
                  color: Colors.white,
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }).then((newName) {
  if (newName != null && newName.isNotEmpty) {
    widget.onNameChanged(newName);
  }
});

修正方法

return AlertDialogreturn StatefulBuilder(builder: (context, setState) {}) で囲みます。
この StatefulBuilder で囲むことで、ダイアログ内の setState を有効化することができます。
修正箇所部分を切り抜いて、具体的なコードで紹介します。

修正前

showDialog(
  context: context,
  barrierDismissible: false,
  builder: (BuildContext context) {
    String newName = widget.title;
    return AlertDialog(
\downarrow

修正後

showDialog(
  context: context,
  barrierDismissible: false,
  builder: (BuildContext context) {
    String newName = widget.title;
+    return StatefulBuilder(
+      builder: (context, setState) {
        return AlertDialog(
showDialog 全コード
dart
showDialog(
  context: context,
  barrierDismissible: false,
  builder: (BuildContext context) {
    String newName = widget.title;
+    return StatefulBuilder(
+      builder: (context, setState) {
        return AlertDialog(
          backgroundColor: Colors.white,
          title: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Text("編集"),
                  CompCloseCircleButton(onPressed: () => Navigator.pop(context))
                ],
              ),
              SizedBox(height: 6),
              Text(
                '元の名前:${widget.title}',
                style: TextStyle(fontSize: 12),
              ),
            ],
          ),
          content: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              TextFormField(
                style: TextStyle(fontSize: 23),
                initialValue: widget.title,
                decoration: InputDecoration(labelText: '変更後の名前を入力'),
                onChanged: (value) {
                  setState(() {
                    newName = value;
                  });
                },
              ),
              SizedBox(height: 30),
              SizedBox(
                width: double.infinity,
                height: 55,
                child: TextButton(
                  onPressed: (newName.isNotEmpty && widget.title != newName)
                      ? () => Navigator.pop(context, newName)
                      : null,
                  style: TextButton.styleFrom(
                    disabledBackgroundColor: Color.fromRGBO(140, 140, 140, 0.6),
                    backgroundColor: (newName.isNotEmpty && widget.title != newName)
                        ? Color.fromRGBO(140, 140, 140, 1)
                        : Color.fromRGBO(140, 140, 140, 0.6),
                    shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(15),
                    ),
                  ),
                  child: Text(
                    '保存',
                    style: TextStyle(
                      fontSize: 15.0,
                      fontWeight: FontWeight.bold,
                      color: Colors.white,
                    ),
                  ),
                ),
              ),
            ],
          ),
        );
      },
    );
  },
).then((newName) {
  if (newName != null && newName.isNotEmpty) {
    widget.onNameChanged(newName);
  }
});

このように AlertDialogStatefulBuilder で囲んであげるだけで、setState が正常に働いてくれて、次のように「保存」ボタンが状況によって押せるようになります。(UIを変える前に撮るのを忘れてたので、見た目は変わってます)

なんでこれで解決するの?

ChatGPT様に聞いてみたところ、次のように回答してくれました。

showDialog で表示される AlertDialogStatelessWidget のように振る舞うため、内部の状態を更新するには StatefulBuilder を使って setState を適用する必要があります。

つまり「 showDialog は表示されたら、その後は内部の状態が変わらないことを前提としているから、その中で setState を使ってもその変更は反映されない」ってことみたいです!

まとめ

このようにして、showDialog内でsetStateを使っても反映されない問題を解決することができました。ご参考になれば幸いです!

Discussion