Flutter Deep Dive
Flutter Deep Dive
</>
■ ■ ■ ■
Arbre des Premiers Row & State vs
Widgets Widgets Column Stateless
SOMMAIRE DE CE GUIDE
expliquée · Le cycle de vie d'un widget · Lire et comprendre un arbre de widgets · child vs children
Stateless vs Stateful
La notion d'état expliquée simplement · StatelessWidget — immuable et performant · StatefulWidget —
Chapitre 4
dynamique et réactif · La méthode setState() en détail · Le cycle de vie du State · Quand utiliser lequel ?
· Erreurs fréquentes et comment les éviter
Si tu ne retiens qu'une seule chose de toute cette formation, que ce soit celle-ci : en
Flutter, TOUT est un widget. Cette phrase n'est pas un raccourci — c'est la vérité
absolue. Comprendre profondément l'arbre des widgets, c'est comprendre comment Flutter
pense, construit et met à jour ton interface.
Un widget est la description d'une partie de l'interface. C'est une classe Dart qui dit à Flutter : "Voici ce que
je veux afficher, avec ces propriétés, dans cet espace." Flutter lit cette description et se charge de tout
dessiner sur l'écran réel.
■ ANALOGIE
Imagine que tu es architecte. Tu dessines des plans (les widgets), et les ouvriers (Flutter)
construisent.
Tu ne touches pas aux briques — tu décris. Flutter construit.
Chaque widget est un PLAN, pas le bâtiment réel. Flutter transforme tes plans en pixels.
Ce concept est fondamental car il explique pourquoi Flutter est si performant et si facile à raisonner. Quand
quelque chose change dans ton app, tu redécris juste la partie qui a changé, et Flutter se charge de mettre à
jour l'écran intelligemment.
Voici ce qui se passe en coulisses quand tu lances une app Flutter. C'est important de comprendre ce
processus, car ça t'explique pourquoi tu codes d'une certaine façon.
■ ASTUCE
Tu n'as besoin de connaître que le Widget Tree (étape 1 et 2).
Les étapes 3, 4 et 5 sont gérées AUTOMATIQUEMENT par Flutter.
Ton seul travail : bien structurer tes widgets. Flutter fait le reste.
Dans l'arbre de widgets, chaque widget a exactement un parent (sauf la racine) et peut avoir zéro, un, ou
plusieurs enfants. Cette relation est définie par les propriétés child et children.
Chaque indentation représente un niveau de profondeur dans l'arbre. Le widget Scaffold contient l'AppBar
et la Column. La Column contient le Container, le SizedBox et le Button. C'est toujours cette logique
d'emboîtement.
C'est l erreur numéro 1 des débutants Flutter. Certains widgets acceptent child (UN seul enfant), d'autres
children (UNE LISTE d'enfants). Confondre les deux = erreur immédiate.
● ● ● Dart / Flutter
3 width: 200,
4 child: Text('Je suis l'enfant unique'), // UN seul enfant
5)
6
13 ],
14 )
15
■ POINT
TRUC CLÉ
MNÉMOTECHNIQUE :
→ Les widgets qui ORGANISENT (Column, Row, Stack) = children (liste) car ils gèrent PLUSIEURS éléments
→ Les widgets qui DÉCORENT (Container, Center, Padding) = child (1 seul) car ils habillent UN élément
→ Si un widget existe pour 'envelopper' quelque chose → child
→ Si un widget existe pour 'lister' quelque chose → children
Voici un exercice concret. Regarde ce code et trace l'arbre mentalement avant de regarder le schéma
ci-dessous. C'est l'exercice le plus formateur !
14 mainAxisAlignment: [Link],
15 children: [
16 Column(children: [Text('42'), Text('Posts')]),
17 Column(children: [Text('1.2k'), Text('Abonnés')]),
18 Column(children: [Text('180'), Text('Abonnements')]),
19 ],
20 ),
21 ],
22 ),
23 ),
24 )
■
TuBONNE PRATIQUE
vois comment le code et l'arbre se correspondent exactement ?
Chaque parenthèse ouvrante '(' crée un nouveau niveau de profondeur dans l'arbre.
Chaque 'child:' ou ligne dans 'children: [...]' crée un branche descendante.
ENTRAÎNE-TOI : Pour chaque code Flutter que tu vois, trace toujours l'arbre mentalement.
En 2 semaines, ça deviendra instinctif et tu liras/écriras du Flutter 3x plus vite.
Ces widgets, tu les utiliseras dans absolument CHAQUE application que tu créeras. Les
connaître sur le bout des doigts — leurs propriétés, leurs subtilités, leurs pièges — est ce
qui distingue un débutant qui galère d'un développeur fluide.
Le widget Text est le plus simple, mais il a bien plus d'options que tu ne le penses. Maîtriser le style du texte
est fondamental car c'est 60% du contenu visuel de toute application.
6 'Titre de ma page',
7 style: TextStyle(
8 fontSize: 24, // Taille en pixels logiques
9 fontWeight: [Link], // Épaisseur : w100 à w900, ou bold/normal
10 color: [Link], // Couleur du texte
11 ),
12 )
13
39 text: TextSpan(
40 style: TextStyle(fontSize: 16, color: [Link]),
41 children: [
42 TextSpan(text: 'Bonjour '),
43 TextSpan(text: 'Alice', style: TextStyle(fontWeight: [Link], color:
[Link])),
44 TextSpan(text: ' ! Tu as '),
45 TextSpan(text:'3 messages', style: TextStyle(color: [Link], decoration:
[Link])),
46 TextSpan(text: ' non lus.'),
47 ],
48 ),
49 )
■■ INFO IMPORTANTE
[Link][100] à [Link][900] → variations de teinte (100 = clair, 900 = foncé)
Color(0xFF1A73E8) → couleur hexadécimale (FF = opacité complète, puis RR GG BB)
[Link](0.5) → couleur avec 50% de transparence
[Link](context).[Link] → couleur du thème de l'app (recommandé !)
Le Container est sans doute le widget que tu utiliseras le plus. Il peut avoir une taille, une couleur, des
marges, des coins arrondis, une ombre, une image de fond, une transformation... C'est la boîte Swiss Army
Knife de Flutter.
■ POINT
SANS CLÉ
child : Container est juste un espace vide de la taille définie.
SANS taille : Container s'adapte à la taille de son enfant (child).
SANS les deux : Container prend TOUT l'espace disponible.
→ Comprendre ce comportement évite des bugs de mise en page frustrants !
10 width: 300,
11 height: 150,
12 decoration: BoxDecoration(
13 color: [Link], // Couleur de fond
14 borderRadius: [Link](16), // Coins arrondis
15 border: [Link](color: [Link], width: 2), // Bordure
16 boxShadow: [
17 BoxShadow(
18 color: [Link](0.3),
19 blurRadius: 12, // Flou de l'ombre
20 spreadRadius: 2, // Expansion de l'ombre
21 offset: Offset(0, 4), // Décalage X, Y
22 ),
23 ],
29 ),
38 color: [Link],
39 borderRadius: [Link](12),
40 boxShadow: [BoxShadow(color: Colors.black12, blurRadius: 8)],
41 ),
47 decoration: BoxDecoration(
48 image: DecorationImage(
49 image: NetworkImage('[Link]
50 fit: [Link], // cover | contain | fill | fitWidth | fitHeight
51 colorFilter: [Link](Colors.black38, [Link]),
52 ),
53 ),
■ ERREUR
PIÈGE FRÉQUENTE
FRÉQUENT : Ne jamais utiliser 'color' ET 'decoration' en même temps !
Container(color: X, decoration: BoxDecoration(...)) ← ERREUR Flutter
Solution : mettre la couleur DANS la BoxDecoration :
Container(decoration: BoxDecoration(color: X, ...)) ← CORRECT
■ ANALOGIE
margin vs padding — la confusion classique :
Flutter intègre plus de 1100 icônes Material Design prêtes à l'emploi, accessibles via la classe Icons. Tu
n'as besoin d'importer aucun fichier externe — tout est intégré dans Flutter.
1 // UTILISATION BASIQUE
2 Icon([Link]) // Icône simple avec taille/couleur par défaut
3
4 // UTILISATION COMPLÈTE
5 Icon(
20 )
21
■ ASTUCE
ASTUCE : Dans Android Studio / VS Code, tape Icons. puis Ctrl+Espace
Tu verras toutes les icônes disponibles avec aperçu visuel.
Tu peux aussi aller sur [Link]/icons pour chercher par nom ou catégorie.
Flutter permet d'afficher des images depuis plusieurs sources. Comprendre BoxFit est essentiel car c'est lui
qui décide comment ton image s'adapte à l'espace disponible.
3 '[Link]
4 width: 200,
5 height: 200,
6 fit: [Link], // Comment l'image remplit l'espace
7
18 : null,
19 ),
20 ),
21 );
22 },
23
31 },
32 )
33
41 [Link](
42 'assets/images/[Link]',
43 width: 100,
44 height: 100,
45 fit: [Link],
46 )
47
49 ClipRRect(
56 )
57
cover Remplit tout l'espace, PEUT être rogné Photos de fond, avatars, galeries
scaleDown Réduit si nécessaire, jamais agrandit Images qui ne doivent pas être floues
8 child: Padding(
9 padding: [Link](16),
10 child: Text('Contenu de la carte'),
11 ),
12 )
13
16 label: Text('Flutter'),
17 avatar: Icon([Link], size: 18),
18 backgroundColor: [Link][100],
19 deleteIcon: Icon([Link], size: 16),
20 onDeleted: () { print('Chip supprimé'); }, // Rend le chip supprimable
21 )
22
25 label: Text('Facile'),
26 selected: _estSelectionne,
27 onSelected: (bool valeur) {
28 setState(() { _estSelectionne = valeur; });
29 },
30 selectedColor: [Link],
31 labelStyle: TextStyle(
32 color: _estSelectionne ? [Link] : [Link],
33 ),
34 )
35
La mise en page est l art de placer les widgets exactement où tu veux. Flutter utilise un
système d'axes très logique. Une fois que tu comprends Row, Column, Expanded et Spacer, tu
peux recréer N'IMPORTE quelle interface que tu vois sur ton téléphone.
Avant de regarder les propriétés, il faut comprendre le concept des axes. Chaque widget de layout a deux
axes : un axe principal (mainAxis) et un axe secondaire (crossAxis).
Column Row
mainAxisAlignment = comment répartir les enfants mainAxisAlignment = comment répartir les enfants
verticalement dans la Column horizontalement dans la Row
■ ANALOGIE
TRUC ULTIME pour ne plus jamais confondre :
mainAxisAlignment = l'axe DANS LE SENS de la flèche du widget
→ Column descend ↓ donc mainAxis est vertical
→ Row avance → donc mainAxis est horizontal
crossAxisAlignment = l'axe PERPENDICULAIRE à la flèche
Ces 6 valeurs contrôlent comment les enfants sont répartis sur l'axe principal. Voici leur effet exact :
[Link] Espace ÉGAL entre les éléments. Premier et Navigation du bas, menu
tween dernier collés aux bords horizontal, tabs
[Link] Étire les enfants pour occuper TOUTE la Étire les enfants pour occuper
h largeur TOUTE la hauteur
13 )
14
34 ],
35 )
36
39 children: [
40 Expanded( // Expanded prend 1/3 de la place
41 child: Column(
42 children: [
43 Text('42', style: TextStyle(fontSize: 24, fontWeight: [Link])),
44 Text('Posts', style: TextStyle(color: [Link])),
45 ],
46 ),
47 ),
49 Expanded(
50 child: Column(
51 children: [
52 Text('1.2k', style: TextStyle(fontSize: 24, fontWeight: [Link])),
53 Text('Abonnés', style: TextStyle(color: [Link])),
54 ],
55 ),
56 ),
59 child: Column(
60 children: [
61 Text('300', style: TextStyle(fontSize: 24, fontWeight: [Link])),
62 Text('Abonnements', style: TextStyle(color: [Link])),
63 ],
64 ),
65 ),
66 ],
67 )
Expanded est l'un des widgets les plus importants de Flutter. Il resout le probleme suivant : comment un
widget peut-il prendre tout l'espace restant apres que les autres widgets ont pris le leur ?
■ ANALOGIE
ANALOGIE : Imagine 3 personnes dans une voiture.
→ Certains passagers ont une taille fixe (enfants, ou widgets avec width fixe)
→ Un passager Expanded va remplir TOUT l'espace qui reste dans la voiture
→ Si deux passagers sont Expanded, ils se partagent l'espace restant équitablement
→ Si l'un a flex:2 et l'autre flex:1, le premier prend le DOUBLE de la place
3 children: [
4 Icon([Link]),
5 SizedBox(width: 8),
6 Text('tres-long-email-qui-va-deborder@[Link]'), // ← OVERFLOW !
7 Icon([Link]),
8 ],
9)
10
13 children: [
14 Icon([Link]),
15 SizedBox(width: 8),
16 Expanded( // ← Prend TOUT l'espace restant
17 child: Text(
18 'tres-long-email-qui-va-deborder@[Link]',
21 ),
22 Icon([Link]),
23 ],
24 )
25
28 children: [
29 Expanded(flex: 2, child: Container(color: [Link], height: 50)), // 2/3
30 SizedBox(width: 8),
31 Expanded(flex: 1, child: Container(color: [Link], height: 50)), // 1/3
32 ],
33 )
34
35 // FLEXIBLE — comme Expanded mais peut être PLUS PETIT que l'espace dispo
36 Row(
37 children: [
38 Flexible( // Prend jusqu'à l'espace disponible, mais pas forcément tout
39 child: Container(
40 color: [Link],
41 height: 50,
42 width: 50, // S'arrête à 50px si y'a assez de place
43 ),
44 ),
45 Text('Reste à côté'),
46 ],
47 )
■ RÈGLE
Règle D'OR
simple :
→ Expanded = toujours aussi grand que possible (remplit l'espace)
→ Flexible = aussi grand que possible SANS dépasser son propre contenu
→ En pratique, tu utiliseras Expanded dans 95% des cas.
→ Expanded fonctionne UNIQUEMENT à l'intérieur d'une Row ou Column !
13 children: [
14 Icon([Link]),
19 ],
20 )
21
24 padding: [Link](16),
25 child: Text('Avec espace tout autour'),
26 )
27 Padding(
45 alignment: Alignment(0.5, -0.8), // x: -1.0 (gauche) à 1.0 (droite), y: -1.0 (haut) à 1.0
(bas)
46 child: Text('Positionné précisément'),
47 )
Le Stack est l'équivalent Flutter des calques dans Photoshop. Il te permet de placer des widgets les uns
{bold('par-dessus')} les autres. Indispensable pour les overlays, les badges, les images avec texte.
13 borderRadius: [Link](16),
14 ),
15 ),
16
27 ),
28 ),
29
43 ),
44 ),
45 ],
46 )
47
54 top: -4,
55 right: -4,
56 child: Container(
57 padding: [Link](4),
58 decoration: BoxDecoration(
59 color: [Link],
60 shape: [Link],
61 ),
65 ),
66 ],
67 )
Maintenant qu'on a vu chaque widget séparément, voici comment les combiner pour créer des interfaces que
tu vois tous les jours dans les vraies apps :
3 elevation: 3,
4 shape: RoundedRectangleBorder(borderRadius: [Link](12)),
5 child: Column(
6 crossAxisAlignment: [Link],
7 children: [
8 // Image produit avec badge promo
9 Stack(
10 children: [
11 ClipRRect(
12 borderRadius: [Link](
13 topLeft: [Link](12),
14 topRight: [Link](12),
15 ),
16 child: [Link]('[Link]
17 height: 160, width: [Link], fit: [Link]),
18 ),
19 Positioned(
20 top: 8, left: 8,
21 child: Container(
22 padding: [Link](horizontal: 8, vertical: 4),
23 decoration: BoxDecoration(
24 color: [Link], borderRadius: [Link](4)),
25 child: Text('-20%', style: TextStyle(color: [Link],
26 fontWeight: [Link], fontSize: 12)),
27 ),
28 ),
29 ],
30 ),
31
32 // Infos produit
33 Padding(
34 padding: [Link](12),
35 child: Column(
36 crossAxisAlignment: [Link],
37 children: [
38 Text('Nom du Produit', style: TextStyle(
39 fontWeight: [Link], fontSize: 15)),
40 SizedBox(height: 4),
41 Text('Marque • Catégorie',
42 style: TextStyle(color: [Link], fontSize: 12)),
43 SizedBox(height: 8),
44 Row(
45 children: [
52 Row(children: [
53 Icon([Link], color: [Link], size: 16),
54 Text(' 4.7', style: TextStyle(fontSize: 13)),
55 ]),
56 ],
57 ),
58 SizedBox(height: 12),
59 SizedBox(
60 width: [Link],
61 child: ElevatedButton(
62 onPressed: () {},
63 child: Text('Ajouter au panier'),
64 ),
65 ),
66 ],
67 ),
68 ),
69 ],
70 ),
71 )
Stateless vs Stateful
4 ■
Comprendre l'état — le concept qui change tout en Flutter
Voici LE concept qui fait la différence entre une app morte (qui affiche toujours la même
chose) et une app vivante (qui réagit à l'utilisateur). Comprendre la notion d'état, c'est
comprendre comment Flutter gère le dynamisme de ton interface.
L'état (state en anglais), c'est tout simplement l ensemble des données qui peuvent changer au cours du
temps dans ton widget et qui, quand elles changent, doivent provoquer une mise à jour de l'affichage.
■ ANALOGIE
ANALOGIE DU FEU TRICOLORE :
Un StatelessWidget ne garde aucun état interne qui peut changer. Une fois créé avec ses propriétés, il
s'affiche toujours pareil. Il n'a qu une seule méthode à implémenter : build(). C'est le widget le plus
simple et le plus performant.
4 import 'package:flutter/[Link]';
5
31 return Card(
32 child: Column(
33 crossAxisAlignment: [Link],
34 children: [
35 // On utilise les propriétés pour afficher le contenu
36 [Link](imageUrl, height: 150, width: [Link], fit: [Link]),
37 Padding(
38 padding: [Link](12),
39 child: Column(
40 crossAxisAlignment: [Link],
41 children: [
42 Text(titre, style: TextStyle(fontWeight: [Link], fontSize: 16)),
43 SizedBox(height: 4),
44 Row(
45 children: [
46 Icon([Link], size: 14, color: [Link]),
47 SizedBox(width: 4),
48 Text(auteur, style: TextStyle(color: [Link], fontSize: 12)),
49 Spacer(),
54 ),
55 ],
56 ),
57 ),
58 ],
59 ),
60 );
61 }
62 }
63
Un StatefulWidget est composé de deux classes qui travaillent ensemble. C'est la partie qui déroute le
plus les débutants — expliquons ça clairement.
■ POINT CLÉ
POURQUOI DEUX CLASSES ?
9 const CompteurAvecHistorique({
10 [Link],
11 required [Link],
12 [Link] = 0,
13 });
14
21
33 void initState() {
43 void dispose() {
44 // Ici : libérer les ressources (controllers, timers, streams...)
45 // Exemple : _monController.dispose();
46 [Link](); // TOUJOURS appeler [Link]() EN DERNIER
47 print('Widget détruit, ressources libérées');
48 }
49
55 });
56 }
57
58 void _decrementer() {
59 setState(() {
60 if (_valeur > 0) { // Logique métier avant de modifier l'état
61 _valeur--;
62 _historique.add(_valeur);
63 }
64 });
65 }
66
67 void _reinitialiser() {
68 setState(() {
69 _valeur = [Link]; // Réutilise la valeur initiale du widget parent
70 _historique.clear();
71 _historique.add(_valeur);
72 });
73 }
74
87 ],
88 ),
89 body: Column(
90 mainAxisAlignment: [Link],
91 children: [
92 Text('Valeur actuelle', style: TextStyle(color: [Link])),
93 Text('$_valeur', style: TextStyle(fontSize: 72, fontWeight: [Link],
94 color: _modeNuit ? [Link] : [Link])),
95 SizedBox(height: 24),
96 Row(
97 mainAxisAlignment: [Link],
98 children: [
99 FloatingActionButton(onPressed: _decrementer, child: Icon([Link])),
100 SizedBox(width: 24),
101 FloatingActionButton(onPressed: _reinitialiser, child: Icon([Link])),
102 SizedBox(width: 24),
103 FloatingActionButton(onPressed: _incrementer, child: Icon([Link])),
104 ],
105 ),
110 ),
111 );
112 }
113 }
C'est LA règle la plus importante de Flutter. Toute modification d'une variable d'état DOIT être faite à
l'intérieur de setState(() {{ ... }}): sinon Flutter ne sait pas qu'il doit redessiner.
1 // ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
2 // ■ ERREUR — Modifier une variable SANS setState()
3 // ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
4 void _incrementerMAL() {
5 _valeur++; // ← La variable change en mémoire...
6 // ...mais Flutter NE LE SAIT PAS !
7 // L'écran ne se met pas à jour.
8}
9
10 // ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
11 // ■ CORRECT — Toujours dans setState()
12 // ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
13 void _incrementerBIEN() {
14 setState(() { // ← "Flutter, tu dois redessiner !"
15 _valeur++; // Flutter voit le changement et appelle build()
16 });
17 }
18
19 // ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
20 // ■ STYLE INLINE — pour les changements simples
21 // ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
22 // Valide aussi, souvent utilisé pour les switches, checkboxes...
23 Switch(
24 value: _estActive,
25 onChanged: (bool nouvValeur) => setState(() => _estActive = nouvValeur),
26 )
27
28 // ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
29 // ■■ CE QUI VA DANS setState() et ce qui N'Y VA PAS
30 // ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
31 void _exemple() {
32 // LOGIQUE (vérifications, calculs) → AVANT setState()
33 if (_valeur >= 100) {
34 print('Valeur maximale atteinte');
35 return;
36 }
37
41 setState(() {
42 // UNIQUEMENT les modifications de variables d'état ici
43 _valeur = nouvelleValeur; // ■ dans setState
44 _historique.add(_valeur); // ■ dans setState
45 // print('...') → ici c'est ok mais inutile dans setState
46 });
47
53 }
54 }
■■ INFO
CE QUI SEIMPORTANTE
PASSE EXACTEMENT QUAND TU APPELLES setState() :
Un StatefulWidget passe par plusieurs étapes au cours de sa vie. Comprendre ces étapes te permet de
savoir exactement où mettre ton code. Voici le cycle complet :
createState()
1 Appelée une fois. Crée la classe _State. Ne rien mettre ici.
initState()
2 Première méthode du State. Initialiser variables, charger données, démarrer animations.
didChangeDependencies()
3 Quand les dépendances (Theme, MediaQuery...) changent. Rarement utilisé.
build()
4 Appelée à chaque setState(). Retourne le widget tree. Doit être PURE (pas de side effects)
didUpdateWidget()
5 Quand le widget parent est reconstruit avec de nouvelles props. Rarement utilisé.
setState()
6 Déclenche un nouveau build(). Toutes les modifications d'état passent par là.
deactivate()
7 Widget retiré de l'arbre temporairement. Très rarement utilisé.
dispose()
8 Widget détruit définitivement. LIBÉRER les ressources ici : controllers, timers, streams !
11 // Variables d'état
12 late TextEditingController _controller;
13 late AnimationController _animController;
14
15 @override
16 void initState() {
17 [Link](); // ← TOUJOURS en premier
18 // Initialiser les ressources qui ont besoin d'être 'disposées'
19 _controller = TextEditingController();
20 // Charger des données initiales
21 _chargerDonnees();
29 @override
37 }
38
39 @override
40 void dispose() {
41 // ← LIBÉRER toutes les ressources avant de détruire le widget
42 _controller.dispose(); // ← OBLIGATOIRE pour les TextEditingController
43 // _animController.dispose(); // ← OBLIGATOIRE pour AnimationController
44 [Link](); // ← TOUJOURS en dernier
45 print('■ Widget détruit, ressources libérées');
46 }
47 }
9 @override
15 }
16 }
17
24 @override
31 @override
38 onPressed: () {
39 setState(() {
40 _estFavori = !_estFavori; // Toggle l'état
41 });
44 );
45 }
46 }
■ RÈGLE
RÈGLE D'OR
PRATIQUE en 3 questions :
TOUT est un widget en Flutter ✓ Les widgets s'emboîtent comme des poupées
■ Arbre des
russes ✓ child (1 enfant) vs children (liste) ✓ Le cycle de rendu : Widget Tree →
Widgets Element Tree → Render Tree ✓ Savoir lire et tracer un arbre depuis le code