Widget — qu'est-ce que c'est, types et composition dans Flutter

Auteur : IT Sectr Publié le : 2026-06-30 Temps de lecture : 10 min

Widget est le concept central du framework Flutter, décrivant la configuration d'un élément d'interface utilisateur. Chaque composant visuel, d'un bouton à une animation complexe, est un Widget. Contrairement à d'autres frameworks où l'UI est décrite par des fichiers XML séparés ou peinte de manière impérative, Flutter construit l'interface via la composition de Widgets — la combinaison de petits éléments indivisibles en un arbre hiérarchique. Selon la Documentation Flutter (2025), la bibliothèque Flutter SDK comprend plus de 260 Widgets intégrés couvrant Material Design, Cupertino et les styles personnalisés.

Points Clés

  • Widget — le bloc de construction de base de l'UI dans Flutter, décrivant la configuration d'un élément.
  • Composition — l'UI est construite en imbriquant des Widgets les uns dans les autres, non par héritage.
  • StatelessWidget — un widget qui ne change pas après le rendu (texte, icône, padding).
  • StatefulWidget — un widget avec un état mutable (formulaires, animations, listes de données).
  • Element tree — Flutter maintient trois arbres : Widget, Element et RenderObject.

Qu'est-ce qu'un Widget dans Flutter

Un Widget dans Flutter est une description immuable (immutable) d'une partie de l'interface utilisateur. Chaque Widget contient des propriétés de configuration : taille, couleur, position, texte, gestionnaires d'événements et Widgets enfants. Les Widgets ne sont pas rendus directement — ce sont des plans (blueprints) à partir desquels le Flutter Engine crée un RenderObject, l'objet graphique réel à l'écran.

La philosophie de Flutter énonce : « Everything is a Widget ». Cela signifie que non seulement les éléments visibles (Text, Image, Button) sont des Widgets, mais aussi les blocs structurels (Padding, Center, Column, Stack), les blocs comportementaux (GestureDetector, AnimatedBuilder) et même l'application elle-même (MaterialApp, CupertinoApp). Cette approche garantit l'uniformité : tout élément d'écran peut être combiné avec n'importe quel autre par simple imbrication.

Selon Google I/O 2024 — Flutter Widgets Deep Dive, l'application moyenne sur Flutter contient entre 200 et 1500 Widgets à tout moment. Malgré cette quantité, Flutter maintient 60 FPS même sur les appareils économiques grâce aux optimisations au niveau du moteur C++ Skia/Impeller. Les Widgets sont des objets légers (40–80 octets chacun), donc leur création n'est pas un goulot d'étranglement de performance.

dart
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: Scaffold(
        body: Center(
          child: Text('Bonjour, Flutter !'),
        ),
      ),
    );
  }
}

Les trois arbres de Flutter : Widget, Element et RenderObject

Pour comprendre comment Widget fonctionne, il faut comprendre l'architecture de Flutter, qui se compose de trois arbres interconnectés. Le premier — l'arbre Widget — décrit la configuration de l'UI. C'est un arbre léger qui peut être complètement reconstruit à chaque image (le ramasse-miettes supprime les anciens Widgets et en crée de nouveaux). Les Widgets sont immuables : si la couleur du texte change, un nouveau Widget Text avec la nouvelle couleur est créé, l'ancien est jeté.

Le deuxième arbre — l'arbre Element — est le lien entre Widget et RenderObject. Element contient une référence au Widget (configuration) et au RenderObject (rendu). Lorsqu'un Widget change, Flutter compare le nouveau Widget avec l'ancien Element et décide : mettre à jour le RenderObject existant (si le Widget est du même type) ou en créer un nouveau (si le type de Widget a changé). Ce processus s'appelle Reconciliation et est analogue au Virtual DOM de React.

Le troisième arbre — l'arbre RenderObject — est responsable du rendu réel à l'écran. RenderObject contient des tailles, positions et méthodes de peinture spécifiques. Le Flutter Engine (C++ Skia ou Impeller) parcourt l'arbre RenderObject et rend chaque nœud. L'arbre RenderObject est le plus lourd, donc Flutter minimise ses changements en réutilisant les RenderObjects lors du passage à des Widgets du même type.

ArbreObjectifImmuable ?Cycle de vie
WidgetConfiguration UI (plan)OuiRecréé à chaque build
ElementLien Widget ↔ RenderObjectNonExiste tant que le widget est dans l'arbre
RenderObjectRendu et layoutNonLourd, réutilisé quand possible

StatelessWidget vs StatefulWidget

Flutter divise les Widgets en deux types fondamentaux : StatelessWidget et StatefulWidget. StatelessWidget est un widget qui ne contient pas d'état mutable. L'apparence d'un StatelessWidget est entièrement déterminée par son constructeur et ne peut pas changer après le rendu. Exemples : Text, Icon, Divider, Padding. Toutes les propriétés d'un StatelessWidget sont déclarées comme final dans le constructeur et sont en lecture seule.

Un StatefulWidget est un widget avec un état mutable. Il se compose de deux classes : le Widget lui-même (configuration immuable, comme StatelessWidget) et State (état mutable). Séparer Widget de State est une décision architecturale clé dans Flutter. Le Widget est recréé à chaque build, mais l'objet State continue d'exister pendant tout le cycle de vie du widget dans l'arbre, préservant son état.

Lorsque setState() est appelé, Flutter marque le State comme « sale » et dans l'image suivante appelle la méthode build() pour reconstruire le sous-arbre. Important : setState() ne recrée pas le Widget lui-même — il déclenche seulement l'appel build() sur le State existant. Cela signifie que StatefulWidget peut mettre à jour l'UI sans perdre l'état des Widgets enfants, tant que les clés (Key) des éléments enfants restent stables.

dart
// StatelessWidget — l'apparence ne change jamais
class GreetingWidget extends StatelessWidget {
  const GreetingWidget({super.key, required this.name});
  final String name;

  @override
  Widget build(BuildContext context) {
    return Text('Bonjour, $name');
  }
}

// StatefulWidget — compteur avec état mutable
class CounterWidget extends StatefulWidget {
  const CounterWidget({super.key});

  @override
  State<CounterWidget> createState() => _CounterWidgetState();
}

class _CounterWidgetState extends State<CounterWidget> {
  int _count = 0;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Comptage : $_count'),
        ElevatedButton(
          onPressed: () => setState(() => _count++),
          child: const Text('Incrémenter'),
        ),
      ],
    );
  }
}

Types de Widget : layout, painting, interactive

Tous les Widgets dans Flutter peuvent être divisés en trois catégories principales par objectif fonctionnel. Layout Widgets — responsables du positionnement des éléments enfants à l'écran. Row et Column disposent les enfants en ligne, Stack superpose l'un sur l'autre, Expanded et Flexible distribuent l'espace disponible. Les Layout Widgets n'ont pas de représentation visuelle propre — ils gèrent la position et la taille des widgets enfants.

Painting Widgets — responsables du style visuel. Container combine des décorations (couleur, dégradé, ombre, bordure) avec des propriétés de layout. Padding ajoute de l'espacement, DecoratedBox dessine un fond, Transform applique des transformations (rotation, échelle). Les Painting Widgets sont les blocs de construction du style visuel et sont souvent utilisés avec les Layout Widgets pour obtenir l'apparence souhaitée.

Interactive Widgets — gèrent les entrées utilisateur. GestureDetector détecte les gestes (tap, swipe, pinch), InkWell ajoute l'effet ripple de Material, TextField accepte la saisie de texte, Slider et Switch fournissent des éléments de contrôle standard. Les Interactive Widgets déclenchent des événements via des fonctions callback passées au constructeur ou traitées via des fournisseurs d'état.

CatégorieExemples de WidgetObjectif
LayoutRow, Column, Stack, Expanded, Flexible, AlignPositionnement et taille des éléments enfants
PaintingContainer, Padding, DecoratedBox, RotatedBoxCouleur, fond, bordures, ombres, transformations
InteractiveGestureDetector, InkWell, TextField, SliderGestion des touches, saisie, gestes
PlatformMaterialApp, CupertinoApp, Theme, MediaQueryIntégration plateforme, thèmes, adaptation
AsyncFutureBuilder, StreamBuilder, ValueListenableBuilderMise à jour réactive depuis des données asynchrones

Selon Flutter Widget of the Week (Google, 2025), la communauté Flutter utilise activement la combinaison de layout + painting + interactive Widgets pour construire pratiquement n'importe quelle interface. Par exemple, un bouton : InkWell (interactive) + Container (painting) + Text (static) + Padding (layout). Cette modularité permet de réutiliser des blocs standard dans différents contextes sans duplication de code.

Composition de Widgets et BuildContext

La composition de Widgets est le processus de construction de l'UI en imbriquant certains Widgets dans d'autres. Contrairement à l'héritage classique (extends) où une classe enfant hérite du comportement du parent, Flutter utilise l'agrégation : chaque Widget contient d'autres Widgets via le paramètre child (pour un) ou children (pour plusieurs). Cette approche offre plus de flexibilité et de réutilisabilité.

BuildContext est le deuxième concept le plus important après Widget. BuildContext est un descripteur de la position d'un Widget dans l'arbre d'éléments. Via BuildContext, un Widget peut accéder aux widgets ancêtres (Theme.of(context), MediaQuery.of(context), Navigator.of(context)). BuildContext est passé à la méthode build() et est utilisé pour interagir avec les éléments parents et enfants. Chaque Widget a exactement un BuildContext, qui identifie de manière unique sa position dans l'arbre.

Selon l'Aperçu Architectural de Flutter (Google, 2025), BuildContext est le fondement d'InheritedWidget — un mécanisme qui permet de passer des données vers le bas dans l'arbre sans passage explicite via des constructeurs. Theme, MediaQuery, Navigator et Provider utilisent InheritedWidget en interne. Tout Widget profondément imbriqué peut accéder aux données d'un ancêtre via BuildContext.dependOnInheritedWidgetOfExactType, faisant de BuildContext la clé de l'architecture réactive de Flutter.

dart
// Composition de widgets par imbrication
Scaffold(
  appBar: AppBar(title: const Text('Mon App')),
  body: Center(
    child: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        const Text('Bienvenue sur Flutter',
          style: TextStyle(fontSize: 24)),
        const SizedBox(height: 16),
        ElevatedButton(
          onPressed: () { /* nav */ },
          child: const Text('Commencer'),
        ),
      ],
    ),
  ),
)

// Accéder aux thèmes via BuildContext
Text(
  'Texte stylisé',
  style: Theme.of(context).textTheme.headlineMedium,
)

Erreurs courantes avec les Widgets

La première erreur et la plus fréquente est d'utiliser StatefulWidget là où StatelessWidget suffit. De nombreux développeurs Flutter débutants créent StatefulWidget pour tous les widgets, même lorsque l'état est stocké dans un fournisseur externe (Provider, Riverpod, BLoC). C'est excessif et nuit aux performances. Règle : utilisez StatelessWidget si l'état est géré de l'extérieur ou si le widget n'a pas son propre état mutable.

La deuxième erreur est de créer des Widgets à l'intérieur de la méthode build sans constructeur const. Chaque Widget créé sans const est réalloué à chaque build. Si vous créez des Widgets avec un constructeur const dans build(), Flutter peut réutiliser la même instance, réduisant la charge du ramasse-miettes. Ajoutez const partout où c'est possible — surtout pour Text, Icon, SizedBox, Padding et autres Widgets stateless.

La troisième erreur est l'utilisation incorrecte des clés (Key). Flutter utilise Key pour identifier les Widgets lors de la reconstruction de l'arbre. Si une liste de Widgets est reconstruite sans Keys, Flutter peut confondre l'ordre des éléments, entraînant des animations incorrectes ou une perte d'état. Ajoutez toujours une Key (par exemple, ValueKey ou ObjectKey) pour les éléments dans les listes, surtout lors de l'utilisation de ListView.builder avec des données dynamiques.

Foire Aux Questions

Quelle est la différence entre StatelessWidget et StatefulWidget ?

StatelessWidget est un widget sans état mutable ; son apparence est entièrement déterminée par le constructeur. StatefulWidget est un widget avec un état mutable, stocké dans un objet State séparé et pouvant être mis à jour via setState() sans recréer le widget lui-même. Utilisez StatelessWidget autant que possible, StatefulWidget — lorsqu'un état local est requis.

Pourquoi les Widgets sont-ils appelés immuables ?

L'immuabilité des Widgets est une décision architecturale de Flutter pour la performance. Si les Widgets étaient mutables, Flutter ne pourrait pas comparer en toute sécurité les anciennes et nouvelles configurations à chaque build. L'immuabilité permet à Flutter de déterminer rapidement si un Widget a changé (via l'opérateur ==) et de réutiliser le RenderObject existant, minimisant les opérations de rendu coûteuses.

Qu'est-ce que BuildContext et à quoi sert-il ?

BuildContext est un descripteur de la position d'un Widget dans l'arbre d'éléments. Grâce à lui, un Widget accède aux widgets ancêtres (Theme, MediaQuery, Navigator) et à InheritedWidget. BuildContext est également utilisé pour la navigation (Navigator.of(context)), l'affichage de SnackBar et l'interaction avec Provider. Chaque Widget reçoit BuildContext via la méthode build() et le transmet aux descendants.

Comment choisir entre Row, Column et Stack pour le layout ?

Utilisez Row pour la disposition horizontale des éléments, Column pour la disposition verticale, Stack pour superposer les éléments les uns sur les autres. Row et Column fonctionnent sur le principe flexbox : les enfants occupent l'espace selon mainAxisSize, mainAxisAlignment et crossAxisAlignment. Stack utilise des enfants positionnés pour un placement précis par rapport aux bords ou au centre.

Comment Flutter maintient-il 60 FPS avec des milliers de Widgets ?

Flutter atteint des performances élevées grâce à trois mécanismes : (1) Les Widgets sont légers — objets immuables légers (40–80 octets), leur création ne sollicite pas le GC. (2) Réutilisation de RenderObject — lors du passage à un Widget du même type, le RenderObject est réutilisé, évitant une recréation coûteuse. (3) Moteur Skia/Impeller — rendu au niveau C++ avec des appels de dessin minimisés via des repaint boundaries.

Résumé

  • Widget — configuration immuable de l'UI dans Flutter, le bloc de construction de base décrivant l'apparence et le comportement.
  • Trois arbres — Flutter utilise l'arbre Widget (configuration), l'arbre Element (lien), l'arbre RenderObject (rendu) pour un rendu optimal.
  • Stateless vs Stateful — StatelessWidget sans état, StatefulWidget avec State mutable et méthode setState() pour les mises à jour de l'UI.
  • Composition — l'UI est construite en imbriquant les Widgets via child/children, sans héritage, offrant flexibilité et réutilisabilité.
  • BuildContext — descripteur de la position du Widget dans l'arbre pour accéder à Theme, Navigator, MediaQuery et InheritedWidget.
  • Performance — les Widgets sont recréés à chaque build (60 FPS), mais RenderObject est réutilisé si le type correspond.
  • Catégories — les Widgets sont divisés en layout (Row, Column, Stack), painting (Container, Padding), interactive (GestureDetector, TextField) et platform (MaterialApp, Theme).

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