Erreur Riverpod ref.read() dans dispose()
Problème
Quand un ConsumerStatefulWidget doit appeler une méthode de service dans dispose() (par exemple pour annuler une opération quand l'utilisateur quitte un écran), utiliser ref.read() lève une erreur car le widget est déjà démonté.
Contexte / Conditions de déclenchement
- Message d'erreur :
Using ref when widget is unmounted is unsafe - Utilisation de
ConsumerStatefulWidgetavecConsumerState - Appel de
ref.read(someProvider)à l'intérieur dedispose() - Le widget se désaffiche (pop, push replacement, etc.)
- La stack trace montre l'erreur provenant de dispose()
Exemple de code problématique :
@override
void dispose() {
ref.read(authServiceProvider).cancelOperation(); // ERREUR!
super.dispose();
}
Solution
Étape 1 : Ajouter un champ late final pour le service
class _MyScreenState extends ConsumerState<MyScreen> {
late final MyService _myService; // Mise en cache ici
Étape 2 : Initialiser dans initState
@override
void initState() {
super.initState();
_myService = ref.read(myServiceProvider); // Sûr ici
}
Étape 3 : Utiliser la référence mise en cache dans dispose
@override
void dispose() {
_myService.cancelOperation(); // Utiliser la référence mise en cache
super.dispose();
}
Vérification
- Accéder à l'écran
- Se désafficher (bouton retour, push replacement, etc.)
- Aucune erreur "Using ref when widget is unmounted" dans la console
- L'action de nettoyage (cancel, close, etc.) s'exécute toujours
Exemple
Avant (cause une erreur) :
class _NostrConnectScreenState extends ConsumerState<NostrConnectScreen> {
@override
void dispose() {
// ERREUR : ref n'est pas sûr à utiliser ici
ref.read(authServiceProvider).cancelNostrConnect();
super.dispose();
}
}
Après (fonctionne correctement) :
class _NostrConnectScreenState extends ConsumerState<NostrConnectScreen> {
// Mise en cache d'AuthService pour utilisation dans dispose (impossible d'utiliser ref.read dans dispose)
late final AuthService _authService;
@override
void initState() {
super.initState();
_authService = ref.read(authServiceProvider);
}
@override
void dispose() {
// Annuler la session si l'utilisateur quitte l'écran
_authService.cancelNostrConnect();
super.dispose();
}
}
Notes
- Ce pattern est nécessaire car
refde Riverpod est lié au cycle de vie du widget ref.read()est sûr dansinitState()car le widget est en cours de montageref.read()n'est PAS sûr dansdispose()car le widget est en cours de démontage- Le pattern
late finalgarantit que le service est initialisé exactement une fois - Ceci s'applique à tout nettoyage nécessitant l'appel de méthodes sur des providers
- Pour les simples changements d'état de provider (sans appels de méthode), envisagez plutôt d'utiliser
deactivate() - Si le service lui-même peut être disposé, ajoutez des vérifications de null ou try-catch selon les besoins
Skills connexes
flutter-dispose-timer-test-failure: Pour les problèmes de dispose liés aux timersflutter-deactivate-setstate-during-build: Pour les problèmes de setState/build cycle dans deactivate