【Flutter】showDialog内でsetStateを使っても反映されない時の対処法
こんにちは、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 AlertDialog
を return StatefulBuilder(builder: (context, setState) {})
で囲みます。
この StatefulBuilder
で囲むことで、ダイアログ内の setState
を有効化することができます。
修正箇所部分を切り抜いて、具体的なコードで紹介します。
修正前
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
String newName = widget.title;
return AlertDialog(
修正後
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
String newName = widget.title;
+ return StatefulBuilder(
+ builder: (context, setState) {
return AlertDialog(
showDialog 全コード
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);
}
});
このように AlertDialog
を StatefulBuilder
で囲んであげるだけで、setState
が正常に働いてくれて、次のように「保存」ボタンが状況によって押せるようになります。(UIを変える前に撮るのを忘れてたので、見た目は変わってます)
なんでこれで解決するの?
ChatGPT様に聞いてみたところ、次のように回答してくれました。
showDialog
で表示されるAlertDialog
はStatelessWidget
のように振る舞うため、内部の状態を更新するにはStatefulBuilder
を使ってsetState
を適用する必要があります。
つまり「 showDialog
は表示されたら、その後は内部の状態が変わらないことを前提としているから、その中で setState
を使ってもその変更は反映されない」ってことみたいです!
まとめ
このようにして、showDialog内でsetStateを使っても反映されない問題を解決することができました。ご参考になれば幸いです!
Discussion