Provider — un package de gestion d'état pour Flutter, créé par Remi Rousselet en 2019 comme une surcouche d'InheritedWidget. Provider résout le problème de transmission des données vers le bas de l'arbre de widgets sans props drilling : n'importe quel widget peut accéder à l'état via context.read<T>() ou context.watch<T>(). Selon pub.dev, Provider est le gestionnaire d'état Flutter le plus populaire avec plus de 25 000 likes.
Points clés
Provider — un package pour la gestion d'état et l'injection de dépendances dans Flutter, construit au-dessus d'InheritedWidget. Provider fournit un objet (état, service, dépôt) dans l'arbre de widgets et reconstruit automatiquement l'UI lorsque les données changent. Contrairement à l'utilisation directe d'InheritedWidget, Provider élimine tout le boilerplate : pas besoin d'écrire une sous-classe d'InheritedWidget, de configurer une méthode statique of() ou de gérer l'imbrication.
Provider est la méthode officiellement recommandée par Google pour gérer l'état dans Flutter (Flutter Team, 2019-2023). Le package fait partie de l'Écosystème Flutter et est maintenu par l'équipe Flutter. Lors de son lancement, Provider a été proposé comme remplacement des variables globales et d'InheritedWidget : tout objet est accessible depuis n'importe où sans être passé par le constructeur.
Selon l'Enquête de la Communauté Flutter 2025, Provider est utilisé dans 72 % des applications Flutter. Les principales raisons de sa popularité sont : un seuil d'entrée minimal, la prise en charge intégrée de ChangeNotifier, la compatibilité avec d'autres architectures (MVVM, BLoC) et l'absence de dépendances externes.
ChangeNotifier — une classe intégrée de Flutter qui implémente le pattern Listener. ChangeNotifier notifie les abonnés des changements en appelant notifyListeners(). Dans le contexte de Provider, ChangeNotifier est la classe principale pour l'état : on crée une classe qui étend ChangeNotifier avec des champs et des méthodes qui appellent notifyListeners() après avoir modifié les données.
class CounterProvider extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
void reset() {
_count = 0;
notifyListeners();
}
}Règles de notifyListeners : l'appeler après avoir complètement modifié les données — pas au milieu d'une méthode, mais à la fin. Si une méthode effectue plusieurs modifications, appelez notifyListeners() une fois après toutes les modifications, pas après chacune. Cela évite de multiples redessins en une seule étape logique. Pour les mises à jour par lot, utilisez notifyListeners avec des patterns similaires à setState.
Alternatives à ChangeNotifier : ValueNotifier — pour une valeur unique (bon pour les primitifs), StateNotifier — du package state_notifier (rarement utilisé seul). La plupart des solutions Provider utilisent ChangeNotifier en raison de sa prise en charge intégrée et de sa simplicité.
Consumer — un widget qui s'abonne à ChangeNotifier et se reconstruit à chaque appel de notifyListeners(). Consumer accepte une fonction builder avec trois paramètres : context, model, child. Child — un widget qui ne dépend pas du modèle et que Consumer ne reconstruit pas. C'est une optimisation : si Consumer contient un widget statique (icône, texte sans données), il est passé via child et n'est pas recréé.
Consumer<CounterProvider>(
builder: (context, provider, child) => Column(
children: [
child!, // n'est pas reconstruit
Text('${provider.count}'),
ElevatedButton(
onPressed: () => provider.increment(),
child: Icon(Icons.add),
),
],
),
child: Text('Compteur :'),
)context.watch — une méthode d'extension de BuildContext pour s'abonner à un Provider. Retourne le modèle et abonne le widget actuel à ses changements. context.read — accès sans abonnement (pour les gestionnaires onPressed, initState et dispose). context.select — abonnement à un champ spécifique du modèle sans reconstruire lorsque d'autres champs changent. Select est l'option la plus performante pour les modèles complexes avec 10+ champs.
Quand utiliser Consumer, watch ou select : Consumer — lorsqu'un widget child est nécessaire pour l'optimisation. watch — dans la méthode build pour une lecture simple. select — lorsque le modèle a plusieurs champs mais que le widget ne dépend que d'un seul. Provider se désabonne automatiquement à la destruction du widget, empêchant les fuites de mémoire.
MultiProvider — un widget pour enregistrer plusieurs Provider sans imbrication. Au lieu d'un arbre avec 5 niveaux de Provider → Provider → Provider, MultiProvider accepte une liste de fournisseurs. Chaque Provider suivant peut utiliser les précédents via le constructeur. MultiProvider est la méthode standard pour organiser le niveau racine d'une application.
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => CartProvider()),
ChangeNotifierProvider(create: (_) => AuthProvider()),
ProxyProvider<AuthProvider, OrderProvider>(
update: (_, auth, __) => OrderProvider(auth.userId),
),
],
child: MaterialApp(home: HomePage()),
)ProxyProvider — un Provider qui dépend d'un autre Provider. ProxyProvider obtient des valeurs d'autres Providers et les transmet à son objet. Par exemple, OrderProvider dépend de AuthProvider (a besoin de userId). Lorsque AuthProvider change, ProxyProvider recrée automatiquement OrderProvider avec le nouveau userId. ChangeNotifierProxyProvider — la version de ProxyProvider pour ChangeNotifier.
StreamProvider et FutureProvider : StreamProvider s'abonne à un Stream (Firebase, WebSocket) et met à jour Consumer à chaque nouvel événement. FutureProvider — pour l'initialisation asynchrone : exécute un Future, affiche le chargement, puis transmet le résultat aux widgets. Les deux résolvent des tâches courantes sans gestion manuelle des abonnements.
Provider se teste en enveloppant le widget dans un MultiProvider avec des valeurs de test. Aucune API ou base de données réelle n'est nécessaire pour le test — le Provider est remplacé par un objet mocké. Le package provider fournit ProviderScope pour l'isolation des tests — chaque test crée son propre arbre Provider indépendamment.
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Counter increments on button tap',
(tester) async {
await tester.pumpWidget(
ChangeNotifierProvider(
create: (_) => CounterProvider(),
child: CounterScreen(),
),
);
await tester.tap(find.byKey(Key('increment')));
await tester.pump();
expect(find.text('1'), findsOneWidget);
},
);
}MockProvider : pour tester les widgets avec un Provider qui dépend d'une API, créez une sous-classe factice ou utilisez mockito / mocktail. Provider ne nécessite pas d'outils de mock spéciaux — tout objet étendant ChangeNotifier peut être passé via create sans appeler le service réel. Programmez les Providers via des interfaces (classe abstraite) pour faciliter le remplacement.
Les performances de Provider sont basées sur InheritedWidget : lorsqu'un Provider change, tous les widgets abonnés via context.watch ou Consumer sont reconstruits. Pour éviter les redessins inutiles, utilisez context.select (abonnement à un champ spécifique), Consumer avec le paramètre child et const pour les widgets statiques. Provider ne reconstruit pas les branches non abonnées aux changements.
| Méthode | Abonnement | Reconstruction | Utilisation |
|---|---|---|---|
| context.watch | Modèle complet | Tout changement | Widgets simples |
| Consumer | Modèle complet | Tout changement | Avec optimisation child |
| context.select | Champ spécifique | Seulement lorsque le champ change | Modèles complexes |
| context.read | Non | Jamais | Gestionnaires d'événements |
Limites : Provider ne prend pas en charge l'isolation de la logique métier au niveau des événements (comme BLoC). Toutes les modifications se font par des appels directs aux méthodes de ChangeNotifier, ce qui peut entraîner des chaînes de modifications incontrôlées. Pour les scénarios complexes (multiples opérations asynchrones, validation complexe), Provider est inférieur à BLoC et Riverpod.
Migration depuis Provider : Provider peut être facilement combiné avec d'autres packages. Pour migrer vers Riverpod, utilisez ChangeNotifierProvider.adaptive — un adaptateur qui permet d'utiliser les ChangeNotifier existants avec Riverpod sans réécriture. Pour BLoC — BlocProvider peut être placé dans un arbre Provider, remplaçant progressivement ChangeNotifier par Bloc.
Questions fréquentes
Provider — une surcouche d'InheritedWidget pour l'injection de dépendances avec ChangeNotifier. BLoC — un modèle architectural avec Event + Stream pour l'isolation de la logique. Provider est plus facile à apprendre, BLoC structure le code de manière plus stricte. Provider convient aux petites applications et à l'état d'UI, BLoC pour la logique métier complexe. Selon Flutter Community 2025, les deux sont souvent utilisés ensemble dans le même projet.
ChangeNotifierProvider — un type de Provider pour les instances de ChangeNotifier. Crée l'objet via create, le fournit aux descendants et reconstruit Consumer lorsque notifyListeners est appelé. ChangeNotifierProvider appelle automatiquement dispose sur ChangeNotifier lorsqu'il est retiré de l'arbre. Il existe trois méthodes de création : ChangeNotifierProvider.value (pour un objet existant), ChangeNotifierProvider (pour la création paresseuse) et ChangeNotifierProvider.create (pour la création paresseuse explicite).
Utilisez context.select au lieu de context.watch — le widget se reconstruit uniquement lorsque le champ sélectionné change. Divisez les gros ChangeNotifier en plusieurs petits (un modèle — une responsabilité). Utilisez Consumer child pour les parties statiques. Pour les listes, utilisez ListView.builder avec des clés. Provider DevTools (Flutter Inspector) montre quels widgets se reconstruisent et pourquoi.
Oui. Provider (sans ChangeNotifier) — pour injecter des objets immuables (dépôt, client API, configuration). ValueListenableProvider — pour ValueNotifier. StreamProvider — pour Stream (Firebase, WebSocket). FutureProvider — pour Future (chargement de la configuration au démarrage). ProxyProvider — pour les Providers qui dépendent d'autres Providers. ChangeNotifier n'est nécessaire que pour l'état mutable avec mise à jour de l'UI.
ProviderNotFoundException — une exception d'exécution qui se produit lorsqu'on tente d'obtenir un Provider qui n'a pas été déclaré plus haut dans l'arbre de widgets. Causes courantes : le Provider est déclaré plus bas que le widget qui tente de le lire ; le Provider est déclaré dans une route et lu dans une autre ; une faute de frappe dans le type. Solution : remontez le Provider dans l'arbre ou utilisez MultiProvider au niveau de MaterialApp pour les dépendances globales.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi