Qu’est-ce que Provider et pourquoi l’utiliser à la place de setState ?
Dans un précédent article, nous avons exploré la gestion d’état avec setState, une méthode intégrée à Flutter qui convient parfaitement pour des applications simples ou des changements d’état locaux. Cependant, lorsque votre application commence à grandir – avec plusieurs écrans, des widgets imbriqués et des données partagées entre différentes parties de l’interface – setState montre vite ses limites. La gestion manuelle des mises à jour devient lourde, le code perd en lisibilité et les performances peuvent en souffrir à cause des reconstructions inutiles.
C’est là qu’intervient Provider, une solution populaire et légère pour la gestion d’état dans Flutter. Recommandé par l’équipe Flutter elle-même, Provider est une bibliothèque qui simplifie le partage et la mise à jour des données à travers une application, tout en restant efficace et facile à comprendre. Contrairement à setState, qui oblige à reconstruire manuellement des widgets, Provider adopte une approche réactive : il notifie automatiquement les widgets concernés lorsqu’un état change, sans toucher aux autres. Pour des applications complexes, cela signifie un code plus propre, une meilleure scalabilité et une maintenance simplifiée.
En quoi Provider est-il plus efficace que setState ?
Prenons un exemple tout simple pour voir en quoi Provider est plus efficace que setState pour conserver les valeurs de vos variables à travers votre application.
Admettons que vous ayez un bouton qui compte combien de fois, vous cliquez dessus et qui utilise setState pour mettre à jour le nombre, après chaque clic. Cette méthode marche bien sur une seule page, mais devient compliquée si vous souhaitez afficher ce nombre sur une autre page. Il va vous falloir trouver un moyen de le transmettre à travers vos widgets, ce qui peut vite être compliqué.
class Compteur extends StatefulWidget {
@override
_CompteurState createState() => _CompteurState();
}
class _CompteurState extends State<Compteur> {
int nombre = 0;
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Nombre : $nombre'),
ElevatedButton(
onPressed: () {
setState(() {
nombre++;
});
},
child: Text('Cliquez-moi'),
),
],
);
}
}
Maintenant, faisons la même chose, mais avec Provider. À la place de garder la valeur de votre nombre dans un widget, vous allez le placer dans une classe spéciale (comme une boîte à outils) pourque tous vos widgets puissent y accéder. Quand le nombre change, Provider prévient alors automatiquement les widgets qui l’utilisent et le met à jour.
// La boîte à outils
class CompteurManager extends ChangeNotifier {
int nombre = 0;
void incrementer() {
nombre++;
notifyListeners(); // Dit aux widgets : "Hey, j’ai changé !"
}
}
// Dans votre application
ElevatedButton(
onPressed: () {
Provider.of<CompteurManager>(context, listen: false).incrementer();
},
child: Text('Cliquez-moi'),
)
// Et ailleurs, pour afficher
Consumer<CompteurManager>(
builder: (context, compteur, child) {
return Text('Nombre : ${compteur.nombre}');
},
)
Comment mettre en place Provider pour votre application Flutter ?
Pour comprendre comment mettre en place Provider, imaginons une application ayant une page avec un champ de texte où l’utilisateur tape un mot, et une liste qui se met à jour en temps réel avec les mots soumis. Avec setState, cela nécessiterait de passer des callbacks ou de gérer manuellement les états à travers les widgets. Avec Provider, tout devient plus fluide. Voici comment procéder, étape par étape.
Étape 1 : Ajouter la dépendance Provider
Tout d’abord, assurez-vous d’ajouter le package provider (6.1.2) à votre projet. Dans votre fichier pubspec.yaml, incluez :
dependencies:
provider: ^6.1.2
Exécutez ensuite flutter pub get pour installer la dépendance.
Étape 2 : Créer une classe de gestion d’état
Provider repose sur une classe qui contient les données et la logique de votre application. Appelons cette classe WordManager. Elle hérite de ChangeNotifier, ce qui lui permet de notifier les widgets lorsqu’un changement survient.
import 'package:flutter/material.dart';
class WordManager extends ChangeNotifier {
String _currentWord = '';
List<String> _wordList = [];
String get currentWord => _currentWord;
List<String> get wordList => _wordList;
void updateWord(String newWord) {
_currentWord = newWord;
notifyListeners(); // Notifie les widgets abonnés au changement
}
void addWord() {
if (_currentWord.isNotEmpty) {
_wordList.add(_currentWord);
_currentWord = ''; // Réinitialise le champ après ajout
notifyListeners();
}
}
}
Ici, WordManager gère deux variables : _currentWord (le mot en cours de saisie) et _wordList (la liste des mots). Chaque fois que ces données changent, notifyListeners() informe les widgets qui dépendent de cet état.
Étape 3 : Intégrer Provider dans l’arbre des widgets
Pour rendre cet état accessible partout dans l’application, placez un ChangeNotifierProvider au sommet de votre arbre de widgets, généralement dans main.dart.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'word_manager.dart';
import 'home_page.dart';
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => WordManager(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Demo Provider',
home: HomePage(),
);
}
}
Le ChangeNotifierProvider encapsule votre application et fournit une instance de WordManager à tous les widgets descendants.
Étape 4 : Construire l’interface avec Provider
Ne reste plus qu’à créer une page (HomePage) avec un champ de texte et une liste. Les widgets écouteront les changements via Provider.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'word_manager.dart';
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Gestion avec Provider')),
body: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
children: [
// Champ de texte
TextField(
onChanged: (value) {
Provider.of<WordManager>(context, listen: false).updateWord(value);
},
decoration: InputDecoration(labelText: 'Entrez un mot'),
),
SizedBox(height: 20),
// Affichage du mot en temps réel
Consumer<WordManager>(
builder: (context, wordManager, child) {
return Text('Mot actuel : ${wordManager.currentWord}');
},
),
SizedBox(height: 20),
// Bouton pour ajouter à la liste
ElevatedButton(
onPressed: () {
Provider.of<WordManager>(context, listen: false).addWord();
},
child: Text('Ajouter à la liste'),
),
SizedBox(height: 20),
// Liste des mots
Expanded(
child: Consumer<WordManager>(
builder: (context, wordManager, child) {
return ListView.builder(
itemCount: wordManager.wordList.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(wordManager.wordList[index]),
);
},
);
},
),
),
],
),
),
);
}
}
Explications du code
-
Provider.of: Utilisé pour accéder auWordManageret appeler ses méthodes (commeupdateWordouaddWord). L’optionlisten: falseindique qu’on ne veut pas reconstruire le widget à cet endroit, seulement agir sur l’état. -
Consumer: Ce widget écoute les changements dansWordManageret reconstruit uniquement les parties concernées (par exemple, le texte ou la liste) quandnotifyListeners()est appelé.
Modifier une variable avec Provider
Avec Provider, modifier l’état reste aussi simple que de changer des variables dans une classe, comme vous le feriez avec setState. La différence principale est que tout est centralisé dans votre ChangeNotifier. Voici quelques opérations de base :
Modifier une valeur simple
Voici comment modifier un compte, avec provider :
class CounterManager extends ChangeNotifier {
int count = 0;
void increment() {
count++; // Change la valeur
notifyListeners(); // Préviens les widgets
}
}
On peut nesuite appeler la fonction avec :
Provider.of<CounterManager>(context, listen: false).increment();
Ajouter un élément à une liste
class ListManager extends ChangeNotifier {
List<String> items = [];
void addItem(String item) {
items.add(item); // Ajoute à la liste
notifyListeners(); // Met à jour l’écran
}
}
Supprimer un élément d’une liste
void removeItem(String item) {
items.remove(item); // Supprime l’élément
notifyListeners(); // Préviens les widgets
}
Multiplier une valeur
void multiplyCount(int factor) {
count *= factor; // Multiplie
notifyListeners(); // Met à jour
}
Conclusion
Provider est donc une manière bien plus efficace de gérer toutes les variables dont votre application a besoin de fonctionner et est une méthode mise en avant par l’équipe Flutter. Toutefois, une nouvelle méthode, encore plus efficace tend à émerger : Riverpod.

