Une architecture Flutter testable avec Riverpod
setState mélange logique et UI dans la même classe ; InheritedWidget propage bien la donnée mais ne dit rien de sa création ni de son remplacement en test. Riverpod répond aux deux : un conteneur d'injection de dépendances qui produit des valeurs réactives, indépendantes de l'arbre de widgets.
Providers et notifiers
Provider expose une valeur ; un Notifier expose une valeur mutable assortie de la logique qui la fait évoluer. Avec la génération de code ( riverpod_generator ), on annote une classe et le build() renvoie l'état initial :
ref.watch à l'intérieur d'un provider crée une dépendance : si apiClientProvider change, currentUser se recalcule automatiquement. C'est le graphe de dépendances qui remplace les setState manuels en cascade.
Séparer l'UI de la logique
WidgetTester :
ref.watch(currentUserProvider) renvoie un AsyncValue , dont le .when(data:, loading:, error:) couvre les trois états sans booléen `isLoading` baladeur .
Injection de dépendances et overrides en test
InheritedWidget : chaque provider est remplaçable au montage du ProviderContainer . En test, on injecte un faux client API sans toucher au code de production :
addTearDown garantit qu'il est libéré. La documentation des tests Riverpod détaille les patterns de pump et de listeners.
Pourquoi pas setState ou InheritedWidget
setState reconstruit tout le State et garde la logique soudée à l'UI — impossible à tester sans rendre le widget. InheritedWidget partage une valeur mais impose d'écrire à la main le updateShouldNotify , ne gère ni l'asynchrone ni le remplacement, et fuit dès qu'on touche au BuildContext . Riverpod déplace l'état hors de l'arbre , le rend paresseux, mémoïsé et auto-disposé ( autoDispose ), et fait de l'override la voie normale du test.
Une bonne architecture Flutter ne se mesure pas au nombre de providers, mais à ceci : peut-on tester la logique sans jamais monter un widget ? Avec Riverpod, la réponse est oui par construction.