Les viewModels persistent en mémoire?

Bonjour à tous et merci à @mbritto pour la qualité du contenu que tu proposes.

Contexte
J’ai suivi le cours sur l’architecture et la navigation pour Flutter et j’ai voulu mettre en pratique les concepts abordés dans mon app.
Rapidement, j’ai compris que j’allais devoir mettre en cache mes données récupérées du backend pour éviter les appels inutiles à l’API.
Pour cela j’ai créé une classe Repository qui hérite de ChangeNotifier et qui contient les données en cache, dans le but que mes viewModels puissent écouter les changements sur le cache et être notifiés lorsqu’une donnée fraîche arrive (pour ensuite mettre à jour les vues correspondantes).

Problème
Pour faire cela, j’appelle la méthode monRepository.addListener(maFonctionPourUpdateLaVue) dans le constructeur de mon viewModel, et pour libérer les ressources une fois que la page est détruite j’ai overridé la fonction dispose() de mon viewModel dans laquelle j’ai mis monRepository.removeListener(maFonctionPourUpdateLaVue).
Le souci c’est que je me suis aperçu que la fonction dispose() n’est en fait jamais appelée, car visiblement ce n’est le cas que pour les classes héritant de StatefullWidgets. Cela semble vouloir dire que lorsqu’on fait _monViewModel = null; dans le RouterDelegate pour supprimer la vue (lorsqu’on revient en arrière par exemple), les ressources du viewModel ne sont pas libérées, est-ce que c’est correct ?

Solution envisagée
Pour résoudre ce problème j’envisage d’appeler manuellement la fonction .dispose() de chaque viewModel dans mon RouterDelegate avant chaque _monViewModel = null;.
Cela me paraît un peu rébarbatif et si j’en oublie j’ai un risque de fuite mémoire. Connaissez-vous une meilleure solution ?

Merci d’avance pour votre aide :slight_smile:

1 « J'aime »

Hello Paul,
Je pense avoir rencontré un problème similaire au tiens. Si j’ai bien compris ta demande du peux essayer le widget PopScope dans le code de ta vue.

Il permet de faire appel à une fonction lorsque tu fermes ta vue :

PopScope(
            onPopInvokedWithResult: (didPop, result) async {
              await widget.viewmodel.TAFONCTIONDISPOSE;
            },
child : LECODETAVUE }

Je pense que tu dois pouvoir mettre ainsi ta fonction removeListener dans ton viewmodel.

Salut Xavier, merci pour ta réponse et pour ta réactivité !
Je ne connaissais pas PopScope, effectivement ça m’a l’air déjà plus clean dans la manière de procéder.
Si je comprends bien, comme une grande majorité de mes viewModels écoutent les changements du cache, il faudrait que j’englobe le contenu de chaque page dans un PopScope dans ce cas ?
Peut-être que cela cache un problème d’archi plus profond dans mon app. J’ai le sentiment qu’il doit y avoir une solution plus efficace que ce que j’ai mis en place pour gérer la mise à jour des vues suite à un changement de cache, j’ai du mal à trouver la solution idéale sans utiliser de dépendance externe.

Oui c’est ce que j’ai fais a pas mal d’endroits. Je ne sais pas si c’est le mieux, mais avant de trouver ce truc je faisais des choses vraiment tordues dans le viewmodel, la view, et le navigationdelegate.
Ce qui commence a me poser problème c’est l’imbrication avec les FutureBuilder et AnimatedBuilder qui commencent à faire un peu lourd… Je crois que Maxime avait fait une bibliothèque pour les deux derniers points.