How to refresh the content of a Dialog via setState?

Compatibility
Last Reviewed
Mar 30, 2023
Published on
May 09, 2018
Flutter
v 3.13.x
Dart
v 3.1.x

Background

Lately I had to display a Dialog to let the user select an item from a list and I wanted to display a list of RadioListTile.

I had no problem to show the Dialog and display the list, via the following source code:


1
2import 'package:flutter/cupertino.dart';
3import 'package:flutter/material.dart';
4
5class Sample extends StatefulWidget {
6  const Sample({super.key});
7
8  
9  State<Sample> createState() => _SampleState();
10}
11
12class _SampleState extends State<Sample> {
13  final List<String> countries = <String>[
14    'Belgium',
15    'France',
16    'Italy',
17    'Germany',
18    'Spain',
19    'Portugal'
20  ];
21  int _selectedCountryIndex = 0;
22
23  
24  void initState() {
25    super.initState();
26    WidgetsBinding.instance.addPostFrameCallback((_) {
27      _showDialog();
28    });
29  }
30
31  _buildList() {
32    if (countries.isEmpty) {
33      return const SizedBox.shrink();
34    }
35
36    return Column(
37        children:
38            List<RadioListTile<int>>.generate(countries.length, (int index) {
39      return RadioListTile<int>(
40        value: index,
41        groupValue: _selectedCountryIndex,
42        title: Text(countries[index]),
43        onChanged: (int? value) {
44          if (mounted) {
45            setState(() {
46              _selectedCountryIndex = value!;
47            });
48          }
49        },
50      );
51    }));
52  }
53
54  _showDialog() async {
55    await showDialog<String>(
56      context: context,
57      builder: (BuildContext context) {
58        return CupertinoAlertDialog(
59          title: const Text('Please select'),
60          actions: <Widget>[
61            CupertinoDialogAction(
62              isDestructiveAction: true,
63              onPressed: () {
64                Navigator.of(context).pop('Cancel');
65              },
66              child: const Text('Cancel'),
67            ),
68            CupertinoDialogAction(
69              isDestructiveAction: true,
70              onPressed: () {
71                Navigator.of(context).pop('Accept');
72              },
73              child: const Text('Accept'),
74            ),
75          ],
76          content: SingleChildScrollView(
77            child: Material(
78              child: _buildList(),
79            ),
80          ),
81        );
82      },
83      barrierDismissible: false,
84    );
85  }
86
87  
88  Widget build(BuildContext context) {
89    return Container();
90  }
91}
92
93

I was surprised to see that despite the setState in lines #44-48, the selected RadioListTile was not refreshed when the user tapped one of the items.

Explanation

After some investigation, I realized that the setState() refers to the stateful widget in which the setState is invoked. In this example, any call to the setState() rebuilds the view of the Sample Widget, and not the one of the content of the dialog. Therefore, how to do?

Solution

A very simple solution is to create another stateful widget that renders the content of the dialog. Then, any invocation of the setState will rebuild the content of the dialog.


1
2import 'package:flutter/cupertino.dart';
3import 'package:flutter/material.dart';
4
5class Sample extends StatefulWidget {
6  const Sample({super.key});
7
8  
9  State<Sample> createState() => _SampleState();
10}
11
12class _SampleState extends State<Sample> {
13  final List<String> countries = <String>[
14    'Belgium',
15    'France',
16    'Italy',
17    'Germany',
18    'Spain',
19    'Portugal'
20  ];
21
22  
23  void initState() {
24    super.initState();
25    WidgetsBinding.instance.addPostFrameCallback((_) {
26      _showDialog();
27    });
28  }
29
30  _showDialog() async {
31    await showDialog<String>(
32      context: context,
33      builder: (BuildContext context) {
34        return CupertinoAlertDialog(
35          title: const Text('Please select'),
36          actions: <Widget>[
37            CupertinoDialogAction(
38              isDestructiveAction: true,
39              onPressed: () {
40                Navigator.of(context).pop('Cancel');
41              },
42              child: const Text('Cancel'),
43            ),
44            CupertinoDialogAction(
45              isDestructiveAction: true,
46              onPressed: () {
47                Navigator.of(context).pop('Accept');
48              },
49              child: const Text('Accept'),
50            ),
51          ],
52          content: SingleChildScrollView(
53            child: Material(
54              child: MyDialogContent(countries: countries),
55            ),
56          ),
57        );
58      },
59      barrierDismissible: false,
60    );
61  }
62
63  
64  Widget build(BuildContext context) {
65    return Container();
66  }
67}
68
69class MyDialogContent extends StatefulWidget {
70  const MyDialogContent({
71    super.key,
72    required this.countries,
73  });
74
75  final List<String> countries;
76
77  
78  State<MyDialogContent> createState() => _MyDialogContentState();
79}
80
81class _MyDialogContentState extends State<MyDialogContent> {
82  int _selectedIndex = 0;
83
84  Widget _getContent() {
85    if (widget.countries.isEmpty) {
86      return const SizedBox.shrink();
87    }
88
89    return Column(
90      children: List<RadioListTile<int>>.generate(
91        widget.countries.length,
92        (int index) {
93          return RadioListTile<int>(
94            value: index,
95            groupValue: _selectedIndex,
96            title: Text(widget.countries[index]),
97            onChanged: (int? value) {
98              if (mounted) {
99                setState(() {
100                  _selectedIndex = value!;
101                });
102              }
103            },
104          );
105        },
106      ),
107    );
108  }
109
110  
111  Widget build(BuildContext context) {
112    return _getContent();
113  }
114}
115

Conclusion

Sometimes some basic notions are tricky and setState is one of them. As the official documentation does not yet explain this, I wanted to share this with you.

Stay tuned for other hints and happy coding.

0 Comments
Be the first to give a comment...
© 2024 - Flutteris
email: info@flutteris.com

Flutteris



Where excellence meets innovation
"your satisfaction is our priority"

© 2024 - Flutteris
email: info@flutteris.com