ListView.builder() et statefullWidget

Bonjour à tous,

J’ai un problème que je pense avoir du mal à résoudre seul :
Je souhaite obtenir une page sur laquelle je peux ajouter ou enlever des statefull widget.
Pour cela j’ai utilisé un ListView.builder() piloté par un bouton + et - qui agissent sur l’itemcount :

Scaffold(
        body: ListView.builder(
          itemBuilder: _generateStatefullWidget,
          itemCount: _nbStatefullWidget,
        ),
// fonction ratachée aux boutons + et - qui pilote item count :
  void _addStafullWidget() {
    setState(() {
      _nbStatefullWidget++;
    });
  }

  void _deleteStatefullWidget() {
    setState(() {
      _nbStatefullWidget--;
    });
  }

Ceci fonctionne bien tant que je n’ai pas agit sur mes Statefull Widget dans l’app : les éléments s’ajouter et se retire sans problème.

Sauf que bien sur mon objectif et de pouvoir faire « vivre » ces statefull widget avec des fonction setstate qui modifie l’affichage de ce qu’ils contiennent de manière dynamique. Et ceci fonctionne très bien sur tous les staefull widget enfant créés.

Le problème est qu’une fois que j’ai utilisé un de ces statefullwidget « enfant » et bien au moment de supprimer un widget avec le bouton - : ca plante !
J’utilise dans ces statefullwidget enfant des timers pour mettre a jour automatiquement l’affichage dans des setstate : voici l’erreur :

Exception has occurred.
FlutterError (setState() called after dispose(): _StefullWidgetEnfant#5a7fe(lifecycle state: defunct, not mounted)
This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback.
The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.
This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose().)```

Je pense que j’aurai besoin d’un peu d’aide pour décrypter la solution proposer « check the mounted property » ou « cancel the timer ».

Merci d’avance !

J’auto incrémente ce sujet :

Mon idée est de passer par une liste de « StatefullWidgetEnfant ». Comme dans le cours de Maxime.

Par contre ce que je n’arrive pas à faire c’est appeler une fonction d’un de ces StatefullWidget : Ce que je voudrais :

  • Sélectionner le dernier objet de la liste.
  • manipuler cet objet dans ma fonction _deleteStatefullWidget :
List<StatefullWidget> _statefullwidgets = [StatefullWidgets()];

// et dans ma fonction _delete

  void _deleteStatefullWidget() {
    setState(() {
       _statefullwidgets.last.unObjetSpecifiqueduSTW.unefonctionspecifiquedelobjet();
       _statefullwidgets.last.remove();
    });

Bon aucune de mes deux lignes de code ne fonctionne xD

Tu ne peux pas contrôler directement les widgets une fois qu’ils sont affichés.
Il te faut modifier ton état (tes variables) et ensuite redessiner toute ta vue.
C’est ce que tu faisais dans ta première version.
Le seul soucis est le timer : si tu as un timer actif sur un objet qui n’existe plus alors ça pose problème. Il te faut arrêter ton timer dans les widgets enfants une fois qu’ils sont supprimés et c’est d’ailleurs ce qui est écrit dans le message d’erreur.
Dans les widgets enfant, tu peux redéfinir la fonction dispose() pour supprimer le timer et ça devrait fonctionner.

« Arf » Merci pour ta réponse.
Effectivement en arrêtant le timer dans le widget enfant il n’y a plus de problème.
Ca m’embête tout de même d’un point de vue UI et fonctionnalités : je voulais faire des boutons qui appliquent la même « commande » à un nombre variable de widgets « enfants ». Du coup je comprends que ce n’est pas possible, en tout cas pas comme je l’imaginais :frowning:

Je ne comprends pas vraiment ce que tu cherches à faire :
tes fonction d’ajout et de suppression de lignes sont appelées par les timers ?

Je génère dans un parent un nombre variable de WidgetEnfant. Ce WidgetEnfant a un timer et d’autres fonctions (Exemple : void fonctionEnfant(){})

Dans le widget Parent un bouton + et un bouton - me permet d’ajouter ou supprimer un widgetEnfant.
Pour l’ajout / suppression : Tout fonctionne bien si le trimer est arrêté.

Du coup mon besoin serait d’avoir un bouton dans mon widget parent qui me permettrait via une boucle d’appeler pour tous les widgets enfants du parent une fonction leur appartenant.

for each (widgetEnfant in widgetEnfantList){
widgetEnfant.fonctionEnfant()}

Si cette chose la été possible je l’aurai utilisé à la fois pour :

  • arrêter le timer dans mon bouton - du dernier widgetEnfant créé.
  • et surtout pour appliquer avec un seul bouton du widget parent la même commande (fonction) à tous mes widgets enfants créés.

Si tu as besoin de faire une tache recurrente avec un timer, tu peux créer ton timer au niveau de ta vue principale et non dans chacune les lignes. Comme ça tu pourrais le laisser allumé en permanence.

Ensuite il faut vraiment oublier l’idée d’appeler des fonctions sur tes widgets :grin: Chaque widget est censé représenter une donnée réelle (un objet que tu aurais dans un tableau par exemple).
Si tu veux faire quelque chose pour chaque enfant, alors tu dois parcourir le tableau d’objets (les vrais objets et non les widgets), faire ta modification sur ces objets. Puis redessiner ton écran (ce qui va fabriquer des nouveaux widgets pour afficher tes objets modifiés).

:joy: :joy: C’est bien noté merci ! Je dois effectivement avoir un problème de méthode pour coder. Je vais travailler :wink:

1 « J'aime »