State est l'objet central de gestion de données dans Flutter, associé à StatefulWidget et responsable du stockage des informations mutables et de la construction de l'interface. Selon la documentation officielle de Flutter (Flutter.dev, 2026), State existe pendant tout le cycle de vie du widget et survit à ses reconstructions, garantissant la cohérence des données entre les mises à jour de l'interface utilisateur. Contrairement au widget lui-même, State peut modifier ses champs et déclencher une reconstruction via l'appel setState.
Points clés
State est un objet dans l'architecture Flutter qui stocke les données mutables d'un StatefulWidget et détermine comment ces données sont affichées dans l'interface. Chaque StatefulWidget, lors de son insertion dans l'arbre, crée exactement un objet State via la méthode createState. State existe indépendamment du widget : si le parent reconstruit le StatefulWidget avec de nouveaux paramètres, State reste le même et reçoit le widget mis à jour via la propriété widget.
Selon l'Aperçu Architectural de Flutter (Google, 2026), la séparation de Widget et State est une décision architecturale délibérée qui permet au framework de réutiliser les éléments de l'arbre. Le widget (une description légère) peut être créé et détruit plusieurs fois, mais State (un objet lourd avec des données) reste en mémoire tant que l'élément est dans l'arbre. Cela évite la perte de données lors des reconstructions fréquentes des widgets parents.
State implémente l'interface StatefulWidget via des génériques : class _MyState extends State<MyWidget>. Le générique lie State à un type spécifique de StatefulWidget, fournissant un accès type-safe à ses champs via la propriété widget.
L'objet State est stocké dans StatefulElement — une couche intermédiaire entre Widget et RenderObject. StatefulElement crée State via createState, conserve une référence vers lui et passe State comme propriétaire. L'Element n'est détruit que lorsque le widget est retiré de l'arbre — jusqu'alors, State vit en mémoire.
Le cycle de vie de State est déterministe et consiste en une séquence stricte d'appels. Comprendre cette séquence est la base d'une gestion correcte des ressources et de la prévention des fuites mémoire.
initState est appelé en premier lors de la création de State. Dans cette méthode, les contrôleurs, les abonnements aux flux, les temporisateurs et les valeurs initiales des champs sont initialisés. L'appel à super.initState() à la première ligne est obligatoire. Au stade initState, l'arbre de widgets n'est pas encore complètement monté, donc des méthodes comme MediaQuery.of(context) peuvent ne pas fonctionner correctement.
didChangeDependencies est appelé après initState et à chaque changement des dépendances InheritedWidget. C'est ici, et non dans initState, qu'il faut appeler MediaQuery.of(context) ou Theme.of(context), car à ce moment l'arbre est déjà monté. Cette méthode est également appelée si le widget se déplace vers un contexte différent où InheritedWidget fournit d'autres valeurs.
build est la méthode principale de State qui retourne un arbre de widgets. Elle est appelée après initState, après didChangeDependencies et après chaque setState. La méthode build ne doit pas avoir d'effets secondaires — elle décrit seulement l'interface en fonction des valeurs actuelles des champs de State.
didUpdateWidget est appelé lorsque le parent reconstruit le StatefulWidget avec de nouveaux paramètres. State accède à l'ancien widget via oldWidget et peut le comparer au nouveau. Si les paramètres ont changé, on peut mettre à jour l'état, charger de nouvelles données ou redémarrer une animation.
dispose est la méthode finale où toutes les ressources sont libérées : contrôleurs, abonnements, temporisateurs. Après dispose, State est marqué comme mort : mounted retourne false, l'appel à setState lève une exception. L'appel à super.dispose() à la dernière ligne de la méthode est obligatoire.
| Méthode | Quand est-elle appelée | super obligatoire |
|---|---|---|
| initState | Lors de la création de State | Oui, à la première ligne |
| didChangeDependencies | Après initState et lors du changement d'InheritedWidget | Oui |
| build | Après initState, didChangeDependencies, setState | Non |
| didUpdateWidget | Lors d'un nouveau widget du parent | Oui |
| setState | Sur appel du développeur | Non |
| dispose | Lors du retrait de l'arbre | Oui, à la dernière ligne |
Le mécanisme de fonctionnement de State repose sur trois principes clés : l'association avec Element, la réactivité via setState et l'accès au parent via la propriété widget. Lorsque Flutter construit l'arbre d'éléments et rencontre un StatefulElement, il appelle createState du widget associé. Le State créé est stocké dans l'élément et existe jusqu'à ce que l'élément soit supprimé.
Lors de l'appel à setState, State se marque comme sale et planifie une reconstruction pour l'image suivante. Important : setState n'appelle pas build immédiatement — il enregistre seulement la nécessité d'une reconstruction. Flutter collecte tous les éléments sales de l'image actuelle et les reconstruit par lot, ce qui optimise les performances. Après l'appel à build, State retourne à l'état propre.
La propriété widget permet à State de lire les paramètres passés au constructeur de StatefulWidget. Comme StatefulWidget est immuable (comme StatelessWidget), ses champs ne changent pas — lorsque les paramètres changent, le parent crée un nouveau widget et State le reçoit via didUpdateWidget. Cela garantit que State travaille toujours avec les données actuelles du parent.
Exemple de base de State avec un champ modifié par un temporisateur. Montre initState, setState et dispose :
class _TimerWidgetState extends State<TimerWidget> {
int _seconds = 0;
Timer? _timer;
@override
void initState() {
super.initState();
_timer = Timer.periodic(
const Duration(seconds: 1),
(_) => setState(() => _seconds++),
);
}
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Text('$_seconds seconds elapsed');
}
}
Exemple utilisant la propriété widget pour accéder aux paramètres du parent et réagir à leurs changements via didUpdateWidget :
class _GreetingState extends State<GreetingWidget> {
String _displayName = '';
@override
void initState() {
super.initState();
_displayName = _formatName(widget.name);
}
@override
void didUpdateWidget(GreetingWidget oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.name != oldWidget.name) {
setState(() {
_displayName = _formatName(widget.name);
});
}
}
String _formatName(String name) => name.trim().isEmpty ? 'Guest' : name;
@override
Widget build(BuildContext context) {
return Text('Hello, $_displayName!');
}
}
Dans le second exemple, State suit les changements du paramètre d'entrée name et reformate l'affichage uniquement lorsqu'un changement réel se produit. Sans la vérification widget.name != oldWidget.name, la méthode serait appelée à chaque reconstruction du parent, même si le nom n'a pas changé — un travail inutile pour le framework.
State et StatefulWidget sont deux classes différentes dans l'architecture Flutter jouant des rôles distincts. StatefulWidget est une enveloppe immuable légère qui décrit la configuration du widget et crée State. State est un objet lourd qui stocke des données mutables, gère des abonnements et construit l'interface utilisateur. Cette séparation permet à Flutter de détruire et de créer des widgets sans perdre l'état.
Tous les champs de StatefulWidget doivent être final et définis dans le constructeur — ils ne changent pas après la création. State, en revanche, peut modifier ses champs à tout moment, mais toutes les modifications doivent être précédées d'un appel setState pour que Flutter soit informé de la nécessité d'une reconstruction. C'est la différence clé : StatefulWidget est « quoi montrer », State est « comment montrer et quelles données utiliser ».
Selon l'analyse du code source de Flutter (Flutter SDK, 2026), StatefulWidget ne contient qu'un seul champ obligatoire — createState, tandis que State a accès à BuildContext, peut s'abonner à des flux, gérer des animations et des contrôleurs. Il est recommandé de garder StatefulWidget aussi simple que possible, en transférant toute la logique dans State.
La séparation de Widget et State est une décision architecturale garantissant l'immuabilité de la configuration. Si StatefulWidget stockait lui-même l'état, l'état serait perdu à chaque reconstruction du parent. En déplaçant l'état dans un objet séparé, Flutter garantit que les données survivent aux reconstructions, tandis que les widgets restent légers et comparables.
L'objet State est isolé — il n'a pas d'accès direct à l'State des autres widgets. Pour l'échange de données entre widgets, on utilise InheritedWidget ou des outils externes de gestion d'état : Provider, Riverpod, Bloc, Redux. Chaque approche résout le problème différemment : InheritedWidget fonctionne via l'arbre de widgets, Provider via un conteneur DI, Bloc via des flux d'événements.
Le choix de l'outil dépend de l'échelle du projet. Pour une petite application, InheritedWidget et State local suffisent. Pour les projets moyens et grands, Riverpod ou Bloc sont recommandés — ils garantissent la testabilité, la prévisibilité et la séparation de la logique de l'interface utilisateur. State est alors utilisé uniquement pour les données locales du widget (focus, défilement, animation).
Selon l'Enquête de la Communauté Flutter 2025 (Flutter Foundation, décembre 2025), Riverpod est la solution de gestion d'état la plus populaire dans les nouveaux projets (38 %), suivi par Bloc (31 %) et Provider (22 %). Les trois outils sont compatibles avec State et ne nécessitent pas d'abandonner le cycle de vie standard.
La première erreur est d'oublier de vérifier mounted avant setState dans un callback asynchrone. Lorsqu'un widget est retiré de l'arbre (par exemple, l'utilisateur a quitté l'écran), mais qu'une opération asynchrone (requête HTTP) est encore en cours, après son achèvement State est déjà mort. Appeler setState dans un State mort lève une exception. La vérification if (mounted) setState(...) résout le problème.
La deuxième erreur est d'initialiser les dépendances InheritedWidget dans initState au lieu de didChangeDependencies. Dans initState, le contexte n'est pas encore monté, donc MediaQuery.of(context) lèvera une exception. Toutes les dépendances InheritedWidget doivent être configurées dans didChangeDependencies ou dans build.
La troisième erreur est de modifier des champs sans appeler setState. Si un développeur modifie un champ State sans setState, Flutter ne saura pas que le changement a eu lieu et l'interface utilisateur ne sera pas mise à jour. Par exemple : _list.add(item) sans setState((){}) ultérieur modifiera la liste, mais l'écran restera le même.
Un modèle de sécurité pour les opérations asynchrones dans State :
Future<void> _fetchData() async {
final data = await ApiService.fetch();
if (mounted) {
setState(() => _data = data);
}
}
Vérifier mounted garantit que setState n'est appelé que sur un State vivant, évitant l'exception « setState called after dispose ».
Foire aux questions
StatefulWidget est une configuration de widget immuable, tandis que State est un objet mutable qui stocke des données et gère le cycle de vie. Le widget peut être recréé, pas State. StatefulWidget crée State via createState.
Exactement un. La méthode createState est appelée une fois lors de la première insertion du StatefulWidget dans l'arbre. Même si le parent se reconstruit plusieurs fois, l'objet State reste le même jusqu'à ce que le type ou la Key du widget change.
mounted est un indicateur booléen montrant si State est dans l'arbre de widgets. Après l'appel à dispose, mounted devient false. Il est utilisé pour la vérification avant setState dans les callbacks asynchrones afin d'éviter les exceptions.
Non. State est toujours lié à un StatefulWidget spécifique via des génériques : State<T extends StatefulWidget>. Créer State directement, sans association avec un widget, est architecturalement impossible.
Une exception est levée : « setState called after dispose ». Après dispose, State est considéré comme mort, et toute tentative de reconstruire l'interface utilisateur via setState est interdite. La solution est de vérifier mounted avant chaque setState.
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