Comment rafraîchir le contenu d'un Dialog via setState?

Compatibilité
Date révision
30 mars 2023
Publié le
09 mai 2018
Flutter
v 3.13.x
Dart
v 3.1.x

Contexte

Dernièrement, j'ai dû afficher un Dialog pour permettre à l'utilisateur de sélectionner un élément dans une liste et je voulais afficher une liste de RadioListTile.

Je n'ai eu aucun problème pour afficher le Dialog et la liste, via le code source suivant:


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

J'ai été surpris de voir que malgré le setState en lignes #44-48, le RadioListTile sélectionné n'était pas actualisé lorsque l'utilisateur appuyait sur l'un des éléments.

Explication

Après quelques recherches, j'ai réalisé que setState() fait référence au Widget "stateful" dans lequel setState est invoqué.

Dans cet exemple, tout appel à setState() reconstruit la vue du Sample Widget, et non celle du contenu de la boîte de dialogue. Par conséquent, comment faire?

Solution

Une solution très simple consiste à créer un autre Widget avec état(Stateful) qui restitue le contenu de la boîte de dialogue. Ensuite, toute invocation de setState reconstruira le contenu de la boîte de dialogue.


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

Parfois, certaines notions de base sont difficiles et setState en fait partie.

Comme la documentation officielle ne l'explique pas encore, je voulais partager cela avec vous.

Restez à l'écoute pour d'autres conseils et bon codage.

0 Commentaires
Soyez le premier à faire un commentaire...
© 2024 - Flutteris
email: info@flutteris.com

Flutteris



Quand l'excellence rencontre l'innovation
"votre satisfaction est notre priorité"

© 2024 - Flutteris
email: info@flutteris.com