Utilisation de ChangeNotifier dans Flutter
Utilisation de ChangeNotifier dans Flutter
-
Objectif
- Comprendre le concept de ChangeNotifier
- Explorer les approches de consommation de ChangeNotifier
- Illustrer avec des exemples pratiques
- Acquérir des Pratiques pour des Applications de Grande Échelle
-
Présentation
- La gestion d’état est cruciale dans le développement d’applications modernes. Elle permet de synchroniser l’interface utilisateur avec les données sous-jacentes, garantissant que les modifications apportées aux données se reflètent immédiatement dans l’interface.
ChangeNotifier
est un outil essentiel dans cette gestion, permettant aux widgets d’écouter les changements d’état et de se reconstruire en conséquence. - En ce qui concerne la gestion des états dans le SDK Flutter, l’une des classes incontournables est la classe
ChangeNotifier
, une classe simple qui expose une partie cruciale de l’API de notification des modifications. FlutterChangeNotifier
fait partie du package Flutter Provider, qui permet une gestion efficace de l’état d’une application. - Ce tutoriel vise à fournir une compréhension approfondie de l’utilisation de ChangeNotifier dans Flutter, en abordant ses concepts fondamentaux et en présentant des exemples pratiques pour aider les développeurs à intégrer cette fonctionnalité dans leurs projets.
-
Importance de la gestion d’état
- La gestion d’état est cruciale dans le développement d’applications modernes. Elle permet de synchroniser l’interface utilisateur avec les données sous-jacentes, garantissant que les modifications apportées aux données se reflètent immédiatement dans l’interface. ChangeNotifier est un outil essentiel dans cette gestion, permettant aux widgets d’écouter les changements d’état et de se reconstruire en conséquence.
- Ce tutoriel vise à fournir une compréhension approfondie de l’utilisation de ChangeNotifier dans Flutter, en abordant ses concepts fondamentaux et en présentant des exemples pratiques pour aider les développeurs à intégrer cette fonctionnalité dans leurs projets.
-
Utilisation directe de
.addListener
- Fonctionnement
- Étape 1 : Créez une classe qui étend ChangeNotifier et gère l’état.
- Pour utiliser ChangeNotifier, vous devez créer une classe qui étend ChangeNotifier. Voici un exemple simple :
- Dans cet exemple, la classe Counter maintient un compteur _count. La méthode
increment()
augmente la valeur du compteur et appellenotifyListeners()
, ce qui informe tous les widgets qui écoutent ce modèle qu’ils doivent se reconstruire. - Étape 2 : Ajoutez un auditeur à l’instance de la classe.
- Pour utiliser ChangeNotifier dans votre application, vous devez envelopper vos widgets avec un ChangeNotifierProvider. Cela fournit une instance de votre ChangeNotifier à l’arborescence des widgets.
- Étape 3 : Appelez
notifyListeners()
dans votre classe pour déclencher les auditeurs. - Exemple : Un compteur simple
- Avantages :
- Simplicité: Méthode directe et sans dépendances externes.
- Contrôle manuel : Vous contrôlez exactement comment et où les auditeurs sont déclenchés.
- Inconvénients :
- Gestion manuelle : Vous devez ajouter et supprimer manuellement les auditeurs, ce qui peut entraîner des fuites de mémoire si les auditeurs ne sont pas correctement nettoyés.
- Difficile à maintenir : Complexe lorsque plusieurs widgets dépendent du même état.
-
Utilisation avec ChangeNotifierProvider, Consumer, et Provider
- La deuxième approche consiste à utiliser le package Provider, qui offre une manière plus déclarative et réactive de gérer l’état avec ChangeNotifier.
- Fonctionnement
- Le package Provider facilite la gestion d’état en :
- Enveloppant l’arbre des widgets avec un ChangeNotifierProvider pour fournir l’état.
- Utilisant un widget Consumer ou la méthode Provider.of pour accéder à l’état et écouter les changements.
-
Étape 1 : Créez une classe avec ChangeNotifier
-
Étape 2 : Fournir l’état avec ChangeNotifierProvider
- Le ChangeNotifierProvider enveloppe votre application ou une partie de votre arbre de widgets pour fournir l’état à ses descendants.
-
Étape 3 : Consommer l’état avec Consumer ou Provider.of
- Avec
Consumer
: Recommandé pour un usage local et granulaire. - Avec
Provider.of
: Utile pour des cas d’utilisation simples. -
Comparaison entre
Consumer
etProvider.of
- Avantages :
- Réactif et Automatique : Les widgets se reconstruisent automatiquement lorsque l’état change.
- Lisibilité : Favorise une séparation claire entre l’état et l’interface utilisateur.
- Efficace : Consumer permet de reconstruire uniquement les widgets nécessaires.
- Inconvénients :
- Complexité initiale : Peut être intimidant pour les débutants.
- Dépendance externe : Nécessite d’ajouter le package Provider.
- Résumé Comparatif
Le ChangeNotifier
peut être utilisé directement grâce à la méthode .addListener
, car il est un sous-type de Listenable. Cela permet d’ajouter des auditeurs (callbacks) pour réagir aux modifications de l’état.
class Counter extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners(); // Notifie les auditeurs du changement
}
}
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => Counter(),
child: MyApp(),
),
);
}
import 'package:flutter/material.dart';
class Counter extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners(); // Notifie tous les auditeurs.
}
}
void main() {
final counter = Counter();
counter.addListener(() {
print('Le compteur a changé : ${counter.count}');
});
counter.increment(); // Affiche : "Le compteur a changé : 1".
counter.increment(); // Affiche : "Le compteur a changé : 2".
}
class Counter extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners(); // Notifie tous les widgets consommateurs.
}
}
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => Counter(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: CounterScreen(),
);
}
}
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
// Modèle ChangeNotifier pour gérer l'état du compteur
class Counter extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners(); // Notifie les widgets écoutant ce modèle
}
}
// Écran principal utilisant le modèle Counter
class CounterScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Compteur')),
body: Center(
child: Consumer( // Consomme le modèle Counter
builder: (context, counter, child) {
return Text(
'Compteur : ${counter.count}',
style: TextStyle(fontSize: 24),
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => context.read().increment(), // Incrémente le compteur
child: Icon(Icons.add),
),
);
}
}
void main() {
runApp(
ChangeNotifierProvider( // Fournit le modèle Counter à l'application
create: (context) => Counter(),
child: MaterialApp(
home: CounterScreen(),
),
),
);
}
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
// Modèle ChangeNotifier pour gérer l'état du compteur
class Counter extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners(); // Notifie les widgets écoutant ce modèle
}
}
// Écran principal utilisant le modèle Counter
class CounterScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final counter = Provider.of(context); // Récupère le modèle Counter
return Scaffold(
appBar: AppBar(title: Text('Compteur')),
body: Center(
child: Text(
'Compteur : ${counter.count}', // Affiche la valeur actuelle du compteur
style: TextStyle(fontSize: 24),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => counter.increment(), // Incrémente le compteur lorsqu'on appuie sur le bouton
child: Icon(Icons.add),
),
);
}
}
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => Counter(), // Crée une instance de Counter
child: MaterialApp(
home: CounterScreen(), // Définit l'écran principal
),
),
);
}
Aspect Consumer Provider.of
Performance Optimisé : reconstruit uniquement le widget cible. Moins optimisé : reconstruit tout le widget parent.
Lisibilité Plus clair pour les grands arbres de widgets. Plus compact mais moins lisible.
Usage recommandé Lorsque vous voulez limiter la reconstruction. Pour des usages rapides ou ponctuels.
Critère | Avec .addListener |
Avec Provider |
---|---|---|
Complexité | Simple pour des cas isolés. | Meilleur pour des projets de grande envergure. |
Maintenance | Moins maintenable dans des scénarios complexes. | Plus maintenable grâce à une gestion d’état centralisée. |
Gestion des auditeurs | Manuelle (risque de fuites mémoire). | Automatique grâce à ChangeNotifierProvider . |
Performances | Direct et léger. | Optimisé pour des applications plus grandes. |
Exemple d’usage | Projets simples ou expérimentations. | Applications professionnelles ou complexes. |
Étude de cas : ChangeNotifier dans une application Flutter réelle
- Considérez une application dont la fonction principale est d’afficher une liste de notes. Les informations des notes sont dynamiques et peuvent être mises à jour à tout moment, ce qui nécessite une gestion d’état. Voici comment nous pouvons implémenter cela à l’aide de Flutter ChangeNotifier :
- Tout d’abord, nous définissons une classe Note simple et notre NoteProvider, qui étend ChangeNotifier.
- Notre ChangeNotifierProvider serait alors :
- Nous pouvons ensuite accéder à nos notes pour les afficher via un consommateur dans notre interface utilisateur :
// Classe représentant une note avec un contenu textuel
class Note {
String content; // Propriété pour stocker le contenu de la note
// Constructeur qui initialise le contenu de la note
Note(this.content);
}
// Classe qui étend ChangeNotifier pour gérer une liste de notes
class NoteProvider extends ChangeNotifier {
List _notes = []; // Liste privée pour stocker les notes
// Méthode pour ajouter une nouvelle note à la liste
void addNote(String content) {
_notes.add(Note(content)); // Crée une nouvelle instance de Note et l'ajoute à la liste
notifyListeners(); // Notifie tous les auditeurs que l'état a changé
}
// Méthode pour obtenir la liste des notes (facultatif)
List get notes => _notes; // Getter pour accéder à la liste des notes
}
vvoid main() {
// La fonction runApp() est le point d'entrée de l'application Flutter.
runApp(
// ChangeNotifierProvider est un widget qui fournit une instance de NoteProvider
// à tous les widgets descendants dans l'arborescence.
ChangeNotifierProvider(
// La propriété create est une fonction qui crée une instance de NoteProvider
// lorsque le ChangeNotifierProvider est inséré dans l'arborescence des widgets.
create: (context) => NoteProvider(),
// child représente l'application principale qui sera construite
// et qui peut accéder à l'instance de NoteProvider.
child: MyApp(),
),
);
}
// Classe NoteList qui étend StatelessWidget pour afficher une liste de notes
class NoteList extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Utilise context.watch() pour écouter les changements dans NoteProvider
// et récupérer la liste des notes.
final notes = context.watch().notes;
// Retourne un widget ListView.builder pour afficher la liste des notes.
return ListView.builder(
// itemCount définit le nombre d'éléments dans la liste, ici la longueur de notes.
itemCount: notes.length,
// itemBuilder est une fonction qui construit chaque élément de la liste.
itemBuilder: (context, index) => Text(
// Récupère le contenu de la note à l'index donné et l'affiche dans un widget Text.
notes[index].content,
),
);
}
}