State : qu'est-ce que c'est, gestion d'état et principe de fonctionnement

Auteur : IT Sectr Publié le : 2026-07-01 Temps de lecture : 9 min

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 — un objet qui stocke les données mutables de StatefulWidget et gère sa reconstruction via setState
  • Cycle de vie — State passe par initState, didChangeDependencies, build, didUpdateWidget et dispose, chaque étape avec un objectif clair
  • mounted — un indicateur signalant que State est toujours dans l'arbre de widgets et peut appeler setState en toute sécurité
  • widget — une référence au StatefulWidget associé, accessible via la propriété State pour lire les paramètres du parent
  • Isolation — State est isolé des autres State ; pour l'échange de données, on utilise InheritedWidget ou des outils externes de gestion d'état

Qu'est-ce que State dans Flutter ?

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.

Où State est-il stocké ?

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.

Cycle de vie de State

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 — initialisation

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

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 — construction de l'UI

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

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 — libération des ressources

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éthodeQuand est-elle appeléesuper obligatoire
initStateLors de la création de StateOui, à la première ligne
didChangeDependenciesAprès initState et lors du changement d'InheritedWidgetOui
buildAprès initState, didChangeDependencies, setStateNon
didUpdateWidgetLors d'un nouveau widget du parentOui
setStateSur appel du développeurNon
disposeLors du retrait de l'arbreOui, à la dernière ligne

Comment fonctionne State ?

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.

Exemples de code Dart

Exemple de base de State avec un champ modifié par un temporisateur. Montre initState, setState et dispose :

dart
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 :

dart
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 vs StatefulWidget

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.

Pourquoi StatefulWidget ne peut-il pas être 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.

Gestion d'état entre widgets

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.

État local vs global

  • Local — dans le State d'un widget spécifique (position de défilement, état de focus)
  • Global — dans un stockage externe (données utilisateur, paramètres, cache)
  • Règle : si les données sont utilisées par un seul widget — stockez-les dans State
  • Si les données sont utilisées par 2+ widgets — déplacez-les vers Riverpod/Bloc/Provider

Erreurs courantes

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.

Vérification de mounted avant setState

Un modèle de sécurité pour les opérations asynchrones dans State :

dart
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

En quoi State diffère-t-il de StatefulWidget ?

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.

Combien d'objets State sont créés pour un StatefulWidget ?

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.

Qu'est-ce que mounted dans State ?

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.

Peut-on utiliser State sans StatefulWidget ?

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.

Que se passe-t-il en appelant setState dans dispose ?

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é

  • State — objet de gestion de données de StatefulWidget qui stocke des champs mutables et déclenche la reconstruction de l'interface utilisateur via setState
  • Cycle de vie comprend les méthodes obligatoires initState, didChangeDependencies, build, didUpdateWidget et dispose, chacune avec son objectif
  • mounted — un indicateur de sécurité critique empêchant les appels setState après le retrait du widget de l'arbre
  • widget — propriété de State pour accéder aux paramètres du StatefulWidget associé, mise à jour via didUpdateWidget
  • Isolation — State n'a pas accès aux autres State ; la communication inter-widgets est implémentée via InheritedWidget ou des outils externes
  • setState — n'appelle pas build immédiatement, mais marque seulement State comme sale pour une reconstruction à l'image suivante
  • Règle — utilisez State pour les données locales du widget ; déplacez l'état global vers des couches externes (Riverpod, Bloc)

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.

Discuter du projet

Lisez aussi