Partagez l'état applicatif avec ChangeNotifier et Provider en maîtrisant watch, read et select pour des rebuilds ciblés.
Ouvrir cette leçon dans KodokonsetState suffit tant que l'état vit et meurt avec un seul écran. Dès qu'une donnée est partagée - panier, session, préférences - la faire descendre de constructeur en constructeur devient ingérable. ChangeNotifier fournit l'observable minimal du SDK ; provider l'injecte dans l'arbre et reconstruit uniquement les widgets abonnés. Face à Riverpod ou Bloc, ce duo reste le plus simple à auditer : peu de magie, une seule dépendance, une mécanique que vous pouvez expliquer en une phrase.
flutter pub add providerUn notifier bien conçu encapsule : champs privés, getters en lecture seule, mutations uniquement via des méthodes qui se terminent par notifyListeners(). C'est ce contrat qui garantit qu'aucun changement d'état n'échappe à l'interface.
import 'package:flutter/foundation.dart';
class CartModel extends ChangeNotifier {
final List<String> _items = [];
List<String> get items => List.unmodifiable(_items);
int get count => _items.length;
void add(String item) {
_items.add(item);
notifyListeners();
}
void remove(String item) {
_items.remove(item);
notifyListeners();
}
}Trois portes d'entrée, trois usages. context.watch<T>() abonne le widget : il se reconstruit à chaque notifyListeners. context.read<T>() lit sans abonner : c'est l'outil des callbacks comme onPressed. context.select<T, R>() n'abonne qu'à une projection : le widget ne se reconstruit que si cette valeur précise change. Règle d'or : watch et select dans build uniquement, read dans les handlers - jamais l'inverse.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class CounterModel extends ChangeNotifier {
int _value = 0;
int get value => _value;
void increment() {
_value++;
notifyListeners();
}
}
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => CounterModel(),
child: const CounterApp(),
),
);
}
class CounterApp extends StatelessWidget {
const CounterApp({super.key});
@override
Widget build(BuildContext context) {
final value = context.watch<CounterModel>().value;
return MaterialApp(
home: Scaffold(
body: Center(child: Text('Count: $value')),
floatingActionButton: FloatingActionButton(
onPressed: () =>
context.read<CounterModel>().increment(),
child: const Icon(Icons.add),
),
),
);
}
}import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class CartModel extends ChangeNotifier {
final List<String> _items = [];
int get count => _items.length;
}
class CartBadge extends StatelessWidget {
const CartBadge({super.key});
@override
Widget build(BuildContext context) {
final count = context.select(
(CartModel cart) => cart.count,
);
return Badge(
label: Text('$count'),
child: const Icon(Icons.shopping_cart),
);
}
}
onPressed, comment déclencher cart.add(...) sans créer d'abonnement inutile ?context.watch<CartModel>().add(...)context.read<CartModel>().add(...)context.select((CartModel c) => c.add(...))notifyListeners() ?List.unmodifiable(_items) plutôt que _items directement ?notifyListeners, donc sans rebuild.