Retour à l'archive technique

Article d'archive

Relu 2023-03-30

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

Supposez que vous ayez un 'Dialog' comportant des Widgets tels que RadioListTile, DropdownButton... ou n'importe quoi d'autre qui devrait être mis à jour PENDANT que le 'Dialog' reste visible... Comment faire?

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

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:



import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

class Sample extends StatefulWidget {
  const Sample({super.key});

  @override
  State<Sample> createState() => _SampleState();
}

class _SampleState extends State<Sample> {
  final List<String> countries = <String>[
    'Belgium',
    'France',
    'Italy',
    'Germany',
    'Spain',
    'Portugal'
  ];
  int _selectedCountryIndex = 0;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _showDialog();
    });
  }

  _buildList() {
    if (countries.isEmpty) {
      return const SizedBox.shrink();
    }

    return Column(
        children:
            List<RadioListTile<int>>.generate(countries.length, (int index) {
      return RadioListTile<int>(
        value: index,
        groupValue: _selectedCountryIndex,
        title: Text(countries[index]),
        onChanged: (int? value) {
          if (mounted) {
            setState(() {
              _selectedCountryIndex = value!;
            });
          }
        },
      );
    }));
  }

  _showDialog() async {
    await showDialog<String>(
      context: context,
      builder: (BuildContext context) {
        return CupertinoAlertDialog(
          title: const Text('Please select'),
          actions: <Widget>[
            CupertinoDialogAction(
              isDestructiveAction: true,
              onPressed: () {
                Navigator.of(context).pop('Cancel');
              },
              child: const Text('Cancel'),
            ),
            CupertinoDialogAction(
              isDestructiveAction: true,
              onPressed: () {
                Navigator.of(context).pop('Accept');
              },
              child: const Text('Accept'),
            ),
          ],
          content: SingleChildScrollView(
            child: Material(
              child: _buildList(),
            ),
          ),
        );
      },
      barrierDismissible: false,
    );
  }

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}


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.



import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

class Sample extends StatefulWidget {
  const Sample({super.key});

  @override
  State<Sample> createState() => _SampleState();
}

class _SampleState extends State<Sample> {
  final List<String> countries = <String>[
    'Belgium',
    'France',
    'Italy',
    'Germany',
    'Spain',
    'Portugal'
  ];

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _showDialog();
    });
  }

  _showDialog() async {
    await showDialog<String>(
      context: context,
      builder: (BuildContext context) {
        return CupertinoAlertDialog(
          title: const Text('Please select'),
          actions: <Widget>[
            CupertinoDialogAction(
              isDestructiveAction: true,
              onPressed: () {
                Navigator.of(context).pop('Cancel');
              },
              child: const Text('Cancel'),
            ),
            CupertinoDialogAction(
              isDestructiveAction: true,
              onPressed: () {
                Navigator.of(context).pop('Accept');
              },
              child: const Text('Accept'),
            ),
          ],
          content: SingleChildScrollView(
            child: Material(
              child: MyDialogContent(countries: countries),
            ),
          ),
        );
      },
      barrierDismissible: false,
    );
  }

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

class MyDialogContent extends StatefulWidget {
  const MyDialogContent({
    super.key,
    required this.countries,
  });

  final List<String> countries;

  @override
  State<MyDialogContent> createState() => _MyDialogContentState();
}

class _MyDialogContentState extends State<MyDialogContent> {
  int _selectedIndex = 0;

  Widget _getContent() {
    if (widget.countries.isEmpty) {
      return const SizedBox.shrink();
    }

    return Column(
      children: List<RadioListTile<int>>.generate(
        widget.countries.length,
        (int index) {
          return RadioListTile<int>(
            value: index,
            groupValue: _selectedIndex,
            title: Text(widget.countries[index]),
            onChanged: (int? value) {
              if (mounted) {
                setState(() {
                  _selectedIndex = value!;
                });
              }
            },
          );
        },
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return _getContent();
  }
}

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.