Element Tree est une couche intermédiaire dans Flutter qui relie l'arbre Widget déclaratif à l'arbre RenderObject impératif. Contrairement aux widgets, qui sont recréés à chaque reconstruction, les éléments persistent entre les mises à jour et gèrent l'état, les clés et le cycle de vie. Selon la Flutter API Reference, 2025, comprendre Element Tree est essentiel pour travailler efficacement avec les clés, optimiser les performances et déboguer les comportements inattendus des widgets.
Points clés
Element Tree est une hiérarchie intermédiaire dans Flutter qui est créée à partir du Widget Tree et gère l'insertion des widgets dans l'application. Chaque instance d'élément correspond à un widget dans l'arbre et stocke une référence vers celui-ci. La principale différence entre un élément et un widget est que l'élément préserve sa position dans l'arbre entre les reconstructions, tandis que le widget peut être recréé à chaque appel de build.
Sans Element Tree, Flutter ne pourrait pas mettre à jour l'interface efficacement. Si chaque reconstruction recréait RenderObject Tree, les performances seraient inacceptablement faibles. Element Tree agit comme un stabilisateur : il conserve les références à RenderObject et State entre les mises à jour, permettant à Flutter d'appliquer uniquement les modifications minimales à l'arbre de rendu.
Flutter utilise trois types principaux d'éléments : StatelessElement pour StatelessWidget, StatefulElement pour StatefulWidget, et LeafRenderObjectElement, SingleChildRenderObjectElement, MultiChildRenderObjectElement pour RenderObjectWidget. Chaque type est spécialisé pour sa classe de widget et détermine comment l'élément interagit avec RenderObject.
L'architecture à trois couches de Flutter se compose de Widget Tree (configuration), Element Tree (gestion) et RenderObject Tree (rendu). Element Tree est le maillon de connexion : il lit la configuration du Widget et transmet des commandes à RenderObject. Sans Element Tree, le framework ne pourrait pas synchroniser efficacement la description déclarative avec le rendu réel.
Lorsqu'un élément est monté dans l'arbre, il vérifie le type du widget. Si le widget est un RenderObjectWidget, l'élément crée le RenderObject correspondant et l'ajoute à RenderObject Tree. Si le widget est un LeafRenderObjectWidget, l'élément crée un RenderObject feuille. Pour StatelessWidget et StatefulWidget, l'élément gère simplement le montage des éléments enfants.
abstract class Element {
Widget widget;
Element? parent;
List<Element>? children;
void mount(Element? parent, dynamic newSlot);
void update(Widget newWidget);
void unmount();
}
Ce code simplifié montre la structure de base de Element. Chaque élément stocke une référence au widget actuel, à l'élément parent et aux éléments enfants. Les méthodes mount, update et unmount gèrent le cycle de vie de l'élément et de son RenderObject associé.
Chaque élément dans Flutter traverse une séquence d'étapes du cycle de vie : création, montage, mise à jour et démontage. Comprendre ces étapes est nécessaire pour déboguer les comportements inattendus, en particulier lors du travail avec des animations, des opérations asynchrones et la gestion d'état.
Un élément est créé en appelant la méthode createElement du widget. Pour StatelessWidget, un StatelessElement est créé ; pour StatefulWidget, un StatefulElement est créé (qui crée également un objet State). Pour RenderObjectWidget, le RenderObjectElement correspondant est créé. La création de l'élément se produit lorsque le widget apparaît pour la première fois dans le Widget Tree.
Lors du montage, l'élément est ajouté à Element Tree et reçoit un élément parent. Pour RenderObjectElement, le montage crée également un RenderObject et l'insère dans RenderObject Tree. Si le widget est un StatefulWidget, la méthode initState de l'objet State est appelée à cette étape.
Lorsque le widget est reconstruit avec une nouvelle configuration, l'élément reçoit le nouveau widget via la méthode update. L'élément compare le type de l'ancien et du nouveau widget : si les types correspondent, l'élément met à jour sa configuration ; sinon, l'élément est démonté et un nouveau est créé. C'est ce qu'on appelle le « changement de widget » et c'est la cause de la perte d'état lors du changement de type.
Lorsqu'un widget est supprimé du Widget Tree, la méthode unmount de l'élément est appelée. L'élément est supprimé d'Element Tree, le RenderObject est supprimé de RenderObject Tree, et pour StatefulWidget, la méthode dispose de l'objet State est appelée. Après le démontage, l'élément peut être réutilisé si le widget réapparaît à la même position.
Les clés (Key) sont un mécanisme d'identification des éléments qui permet à Flutter de faire correspondre les widgets de l'ancien et du nouveau Widget Tree non pas par position, mais par un identifiant unique. Les clés sont cruciales lorsqu'on travaille avec des listes dynamiques où l'ordre des éléments peut changer : ajout, suppression ou réorganisation d'éléments.
Sans clé, Flutter fait correspondre les éléments par leur position dans l'arbre : l'élément à la position 0 de l'ancien arbre est remplacé par le widget à la position 0 du nouvel arbre. Si l'ordre a changé, les éléments se mélangent et l'état peut être perdu ou lié à des données incorrectes. Une clé force Flutter à rechercher un élément par identifiant plutôt que par position.
ValueKey utilise une valeur simple (chaîne, nombre) pour identifier un élément. ObjectKey utilise une référence d'objet — approprié lorsque l'élément n'a pas d'identifiant de chaîne stable. UniqueKey génère un identifiant unique à chaque création — utilisé lorsque chaque instance de widget doit être unique.
Column(
children: items.map((item) => TodoItem(
key: ValueKey(item.id),
title: item.title,
isDone: item.isDone,
)).toList(),
)
Dans cet exemple, ValueKey avec item.id garantit que chaque TodoItem conserve son état (par exemple, le focus du champ de saisie) lorsque l'ordre des éléments dans la liste change. Sans clé, l'élément à la première position recevrait l'état de l'élément précédent à la même position.
L'état (State) dans Flutter n'est pas stocké dans les widgets, mais dans les éléments. Lorsqu'un StatefulWidget est reconstruit et crée une nouvelle instance de widget, le StatefulElement correspondant conserve une référence à l'ancien objet State. Le nouveau widget est lié au State existant, permettant de préserver les données entre les reconstructions.
Lors de la reconstruction d'un widget, Flutter crée une nouvelle instance de StatefulWidget, mais le StatefulElement correspondant reste dans Element Tree. L'élément appelle la méthode update sur State, en passant le nouveau widget. Ainsi, l'objet State et ses données sont préservés. La perte d'état ne se produit que lorsque le type du widget change, la clé change ou l'élément est supprimé de l'arbre.
InheritedElement est un élément spécial qui permet aux éléments enfants de recevoir des données d'un InheritedWidget parent sans passage explicite via les constructeurs. Lorsque InheritedWidget change, InheritedElement notifie tous les éléments dépendants, qui sont reconstruits. Ce mécanisme est à la base de Theme, MediaQuery et Provider.
Element Tree consomme de la mémoire et affecte la vitesse du premier rendu. Chaque élément occupe une certaine quantité de mémoire : une référence au widget, une référence au parent, une liste d'éléments enfants, un emplacement et des champs supplémentaires pour RenderObjectElement. Optimiser Element Tree réduit le temps de démarrage et diminue la consommation de mémoire.
Flutter essaie de réutiliser les éléments lors de la reconstruction. Si le widget dans la nouvelle configuration a le même type et la même clé, l'élément n'est pas recréé — il est mis à jour. C'est considérablement plus rapide que de créer un nouvel élément avec un montage ultérieur. Cependant, lorsque le type ou la clé change, l'ancien élément est démonté et un nouveau est créé à partir de zéro.
RepaintBoundary crée un RenderRepaintBoundary séparé dans RenderObject Tree qui isole une partie de l'arbre. Au niveau d'Element Tree, RepaintBoundary ne crée pas d'élément spécial — il utilise SingleChildRenderObjectElement. La différence apparaît au niveau de RenderObject : lorsque le contenu de RepaintBoundary change, seule la zone isolée est repeinte.
| Opération | Sans RepaintBoundary | Avec RepaintBoundary |
|---|---|---|
| Repeinture | Écran entier | Zone isolée uniquement |
| Temps | ~16 ms à 60 FPS | ~2-5 ms |
| Mémoire | Minimale | + quelques kilo-octets par couche |
Comme le montre le tableau, RepaintBoundary réduit considérablement le temps de repeinture en isolant la zone changeante. Au niveau d'Element Tree, cela ne nécessite aucune configuration supplémentaire — il suffit d'envelopper le widget mutable dans RepaintBoundary.
Questions fréquentes
Widget Tree est une configuration qui est recréée à chaque reconstruction. Element Tree est une structure persistante qui reste entre les mises à jour et gère l'état, RenderObject et le cycle de vie des widgets.
Sans Element Tree, Flutter devrait recréer RenderObject Tree à chaque changement d'état, ce qui provoquerait des retards significatifs. Element Tree préserve RenderObject et State, permettant d'appliquer uniquement les modifications minimales.
Un élément est supprimé lorsque le widget correspondant disparaît du Widget Tree, ou lorsque le type de widget change (par exemple, Column remplacée par Row) ou la clé change. Lors du démontage, dispose est appelé sur State.
Les clés modifient l'algorithme de correspondance : au lieu de rechercher un élément par position, Flutter recherche un élément par la valeur de la clé. Cela permet de préserver l'état lorsque l'ordre ou la quantité des widgets change.
Oui, via BuildContext, qui est une abstraction d'un élément. Les méthodes findAncestorWidgetOfExactType et dependOnInheritedWidgetOfExactType travaillent avec Element Tree, en remontant l'arbre des éléments.
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