0% ont trouvé ce document utile (0 vote)
3 vues40 pages

Flutter Deep Dive

Le document présente un guide complet sur Flutter, abordant des concepts clés tels que l'arbre des widgets, la mise en page avec Row et Column, et la gestion de l'état avec Stateless et Stateful widgets. Chaque chapitre fournit des explications détaillées, des exemples de code et des conseils pratiques pour aider les débutants à maîtriser Flutter. L'accent est mis sur la compréhension des widgets et leur hiérarchie pour construire des interfaces utilisateur efficaces.

Transféré par

George Stéphane
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
3 vues40 pages

Flutter Deep Dive

Le document présente un guide complet sur Flutter, abordant des concepts clés tels que l'arbre des widgets, la mise en page avec Row et Column, et la gestion de l'état avec Stateless et Stateful widgets. Chaque chapitre fournit des explications détaillées, des exemples de code et des conseils pratiques pour aider les débutants à maîtriser Flutter. L'accent est mis sur la compréhension des widgets et leur hiérarchie pour construire des interfaces utilisateur efficaces.

Transféré par

George Stéphane
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Flutter Deep Dive Widgets • Layout • State

</>

Flutter Deep Dive


Le Guide Ultra-Complet du Débutant

■ ■ ■ ■
Arbre des Premiers Row & State vs
Widgets Widgets Column Stateless

Comprends chaque concept en profondeur — pas à pas, avec exemples visuels

Explications · Analogies · Schémas · Code commenté · Erreurs à éviter

Formation Flutter — Du Zéro à l'Application Réelle

4 Chapitres · Explications Détaillées · Code Prêt à l'Emploi

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 1


Flutter Deep Dive Widgets • Layout • State

SOMMAIRE DE CE GUIDE

L'Arbre des Widgets


Chapitre 1 Qu'est-ce qu'un widget exactement ? · Comment Flutter construit l'interface · La relation parent/enfant

expliquée · Le cycle de vie d'un widget · Lire et comprendre un arbre de widgets · child vs children

Tes Premiers Widgets


Widget Text — toutes les options de style · Widget Container — la boîte magique · Widget Icon — les
Chapitre 2
icônes Material Design · Widget Image — réseau, assets, erreurs · CircleAvatar · Card · Chip ·
Combinaisons fréquentes

Mise en Page : Row & Column


Column — empiler verticalement · Row — aligner horizontalement · MainAxisAlignment expliqué
Chapitre 3
visuellement · CrossAxisAlignment en détail · Expanded et Flexible · SizedBox, Padding, Center,

Spacer · Stack — superposer des widgets · Cas pratiques combinés

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

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 2


Flutter Deep Dive Widgets • Layout • State

L'Arbre des Widgets


1 Le concept fondamental de Flutter — comment il pense l'interface ■

Flutter Deep Dive · Formation Complète

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.

■ Qu'est-ce qu'un Widget exactement ?

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.

Les 3 grandes familles de widgets

Widgets Visuels Widgets Structurels Widgets Logiques

Ne s'affichent pas, gèrent le


Affichent quelque chose à l'écran. Organisent d'autres widgets.
comportement.

Text Column GestureDetector


Icon Row Navigator
Image Stack Builder
Container Scaffold Theme

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 3


Flutter Deep Dive Widgets • Layout • State

■■ Comment Flutter construit l'interface

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.

Tu écris des widgets


1 Tu crées des classes qui décrivent l'interface dans la méthode build()

Flutter construit le Widget Tree


2 Flutter assemble tous tes widgets en un arbre hiérarchique (parent → enfant)

Flutter crée l'Element Tree


3 Chaque widget crée un 'element' — l'instance active dans l'arbre réel

Flutter construit le Render Tree


4 Les éléments créent des objets de rendu qui savent comment se dessiner

Flutter dessine sur l'écran


5 Le moteur graphique (Skia/Impeller) traduit tout ça en pixels — 60 fois par seconde !

Quand setState() est appelé


6 Flutter ne recrée que les widgets modifiés, pas tout l'arbre — c'est ça la performance

■ 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.

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 4


Flutter Deep Dive Widgets • Layout • State

■■■ La relation Parent / Enfant — le coeur de l'arbre

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.

■ Arbre complet d'une page Flutter


MaterialApp
Scaffold
AppBar
Text('Titre')
body: Column
Container
Text('Bonjour')
SizedBox(h:20)
ElevatedButton
Text('Cliquer')
FloatingActionButton
Icon([Link])

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.

■ child vs children — La règle absolue

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.

child (singulier = 1 enfant) children (pluriel = liste [])

Container → child Column → children: [...]


Center → child Row → children: [...]
Padding → child Stack → children: [...]

● ● ● Dart / Flutter

1 // ■ CORRECT — Container avec child (singulier)


2 Container(

3 width: 200,
4 child: Text('Je suis l'enfant unique'), // UN seul enfant
5)
6

7 // ■ CORRECT — Column avec children (liste)


8 Column(

9 children: [ // LISTE d'enfants


10 Text('Enfant 1'),
11 Text('Enfant 2'),
12 Icon([Link]),

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 5


Flutter Deep Dive Widgets • Layout • State

13 ],

14 )
15

16 // ■ ERREUR — Column n'a PAS child !


17 // Column(child: Text('Erreur')) // Flutter va planter
18

19 // ■ ERREUR — Container n'a PAS children !


20 // Container(children: [...]) // Flutter va planter
21

22 // ■ ASTUCE : En cas de doute, appuie sur Ctrl+Espace dans ton éditeur


23 // Flutter t'affiche les propriétés disponibles automatiquement.

■ 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

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 6


Flutter Deep Dive Widgets • Layout • State

■■ Lire un Widget Tree — Exercice pratique

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 !

● ● ● Exercice — Trace l'arbre de ce code

1 // EXERCICE : Lis ce code et imagine l'interface


2 Scaffold(

3 appBar: AppBar(title: Text('Mon Profil')),


4 body: Padding(
5 padding: [Link](16),
6 child: Column(
7 children: [
8 CircleAvatar(radius: 50, backgroundImage: NetworkImage('...')),
9 SizedBox(height: 16),
10 Text('Alice Dupont', style: TextStyle(fontSize: 22, fontWeight: [Link])),
11 Text('alice@[Link]', style: TextStyle(color: [Link])),
12 SizedBox(height: 24),
13 Row(

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 )

■ Arbre correspondant à l'exercice ci-dessus


Scaffold
AppBar
Text('Mon Profil')
body: Padding
Column
CircleAvatar
SizedBox(h:16)
Text('Alice Dupont')
Text('alice@[Link]')
SizedBox(h:24)
Row
Column [Posts]
Column [Abonnés]
Column [Abonnements]

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 7


Flutter Deep Dive Widgets • Layout • State


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.

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 8


Flutter Deep Dive Widgets • Layout • State

Tes Premiers Widgets


2 ■
Text, Container, Icon, Image — maîtrise les blocs de base

Flutter Deep Dive · Formation Complète

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.

■ Widget Text — Afficher du texte

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.

Anatomie complète du widget Text

● ● ● Widget Text — du basique au professionnel

1 // NIVEAU 1 — Le plus basique


2 Text('Bonjour Flutter !')
3

4 // NIVEAU 2 — Avec style simple


5 Text(

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

14 // NIVEAU 3 — Style complet professionnel


15 Text(

16 'Texte richement stylisé',


17 style: TextStyle(
18 fontSize: 18,
19 fontWeight: FontWeight.w600, // Semi-gras
20 fontStyle: [Link], // Italique
21 color: Color(0xFF1A1A2E), // Couleur hexadécimale
22 letterSpacing: 1.5, // Espace entre les lettres
23 wordSpacing: 3.0, // Espace entre les mots
24 height: 1.5, // Hauteur de ligne (interligne)
25 decoration: [Link], // Soulignage
26 decorationColor: [Link],

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 9


Flutter Deep Dive Widgets • Layout • State

27 decorationStyle: [Link], // Style du soulignage


28 shadows: [Shadow(blurRadius: 4, color: Colors.black26, offset: Offset(2, 2))],
29 fontFamily: 'Roboto', // Police (si ajoutée dans [Link])
30 ),

31 textAlign: [Link], // left | right | center | justify | start | end


32 maxLines: 3, // Nombre maximum de lignes
33 overflow: [Link], // Que faire si le texte déborde : ellipsis | clip | fade
34 softWrap: true, // Retour à la ligne automatique
35 )
36

37 // NIVEAU 4 — RichText pour des styles MIXTES dans un même texte


38 RichText(

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 )

Propriété Type Description


fontSize double Taille en pixels logiques. Défaut: 14. Courant: 12, 14, 16, 18, 20, 24, 32
fontWeight FontWeight bold, w100→w900, normal. Plus le nombre est haut, plus c'est gras
fontStyle FontStyle [Link] ou [Link]
color Color [Link], [Link], Color(0xFFRRGGBB), [Link](...)
letterSpacing double Espace entre les lettres. Positif = écarté, négatif = serré
height double Multiplicateur de hauteur de ligne. 1.0 = normal, 1.5 = aéré
decoration TextDecoration underline, overline, lineThrough, none
textAlign TextAlign left, right, center, justify (sur le widget Text, pas TextStyle)
maxLines int Nombre max de lignes. Combiné avec overflow pour tronquer
overflow TextOverflow ellipsis (…), clip (coupe net), fade (disparaît progressivement)

■■ 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é !)

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 10


Flutter Deep Dive Widgets • Layout • State

■ Widget Container — La boîte tout-terrain

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 !

● ● ● Widget Container — 4 cas d'usage courants

1 // CAS 1 — Container simple avec taille et couleur


2 Container(

3 width: 200, // Largeur fixe en pixels logiques


4 height: 100, // Hauteur fixe
5 color: [Link], // Couleur simple (NE PAS combiner avec decoration !)
6)
7

8 // CAS 2 — Container avec décoration complète (BoxDecoration)


9 Container(

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 ],

24 gradient: LinearGradient( // Dégradé (remplace color)


25 colors: [[Link], [Link]],
26 begin: [Link],
27 end: [Link],
28 ),

29 ),

30 child: Center(child: Text('Contenu', style: TextStyle(color: [Link]))),


31 )
32

33 // CAS 3 — Container avec margin ET padding (très courant !)


34 Container(

35 margin: [Link](horizontal: 16, vertical: 8), // ESPACE EXTÉRIEUR


36 padding: [Link](20), // ESPACE INTÉRIEUR
37 decoration: BoxDecoration(

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 11


Flutter Deep Dive Widgets • Layout • State

38 color: [Link],
39 borderRadius: [Link](12),
40 boxShadow: [BoxShadow(color: Colors.black12, blurRadius: 8)],
41 ),

42 child: Text('Carte stylisée'),


43 )
44

45 // CAS 4 — Container avec image de fond


46 Container(

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 ),

54 child: Text('Texte sur image', style: TextStyle(color: [Link])),


55 )

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

Imagine une carte postale dans une enveloppe :


→ padding = l'espace blanc À L'INTÉRIEUR de la carte autour du texte
→ margin = l'espace entre l'enveloppe et les autres lettres dans la boîte aux lettres

[Link](16) → 16px de tous les côtés


[Link](top:8, left:16) → top=8, left=16, right=0, bottom=0
[Link](horizontal: 20, vertical: 10) → gauche+droite=20, haut+bas=10
[Link](10, 20, 10, 5) → left, top, right, bottom dans l'ordre

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 12


Flutter Deep Dive Widgets • Layout • State

■ Widget Icon — Les icônes Material Design

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.

● ● ● Widget Icon — Utilisation complète

1 // UTILISATION BASIQUE
2 Icon([Link]) // Icône simple avec taille/couleur par défaut
3

4 // UTILISATION COMPLÈTE
5 Icon(

6 [Link], // L'icône choisie


7 size: 48, // Taille en pixels logiques (défaut: 24)
8 color: [Link], // Couleur (défaut: couleur du thème)
9 semanticLabel: 'Favori', // Pour l'accessibilité (lecteurs d'écran)
10 )
11

12 // ICONBUTTON — Icône cliquable (très courant dans les AppBar)


13 IconButton(

14 icon: Icon([Link], color: [Link]),


15 iconSize: 28, // Taille de la zone cliquable
16 tooltip: 'Rechercher', // Bulle d'aide au survol
17 onPressed: () {
18 print('Recherche lancée');
19 },

20 )
21

22 // ICONES POPULAIRES PAR CATÉGORIE


23 // Navigation : [Link], [Link], [Link], [Link]
24 // Actions : [Link], [Link], [Link], [Link], Icons.more_vert
25 // Médias : Icons.play_arrow, [Link], Icons.volume_up, [Link]
26 // Communication : [Link], [Link], [Link], [Link]
27 // E-commerce : Icons.shopping_cart, [Link], [Link], Icons.local_offer
28 // UI : [Link], [Link], Icons.arrow_back, Icons.arrow_forward
29 // Fichiers : [Link], Icons.file_copy, [Link], [Link]
30 // Social : Icons.thumb_up, [Link], [Link], [Link]
31

32 // VARIANTES : _outlined, _rounded, _sharp pour beaucoup d'icônes


33 Icon([Link])

34 Icon(Icons.home_outlined) // Version contour


35 Icon(Icons.home_rounded) // Version arrondie

■ 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.

■■ Widget Image — Afficher des images

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 13


Flutter Deep Dive Widgets • Layout • State

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.

● ● ● Widget Image — Réseau, Assets, CircleAvatar

1 // SOURCE 1 : Image depuis Internet (URL)


2 [Link](

3 '[Link]

4 width: 200,
5 height: 200,
6 fit: [Link], // Comment l'image remplit l'espace
7

8 // Afficher quelque chose PENDANT le chargement


9 loadingBuilder: (context, child, loadingProgress) {
10 if (loadingProgress == null) return child; // Image chargée → afficher l'image
11 return Container(
12 width: 200, height: 200,
13 child: Center(
14 child: CircularProgressIndicator(
15 value: [Link] != null
16 ? [Link] /
17 [Link]!

18 : null,
19 ),

20 ),

21 );

22 },
23

24 // Afficher quelque chose en cas d'ERREUR


25 errorBuilder: (context, error, stackTrace) {
26 return Container(
27 width: 200, height: 200,
28 color: [Link][200],
29 child: Icon(Icons.broken_image, color: [Link], size: 48),
30 );

31 },

32 )
33

34 // SOURCE 2 : Image depuis les fichiers du projet (assets)


35 // D'abord, ajouter dans [Link] :
36 // flutter:
37 // assets:
38 // - assets/images/[Link]
39 // - assets/images/ (pour inclure tout un dossier)
40

41 [Link](

42 'assets/images/[Link]',

43 width: 100,
44 height: 100,
45 fit: [Link],
46 )
47

48 // DANS UN CONTENEUR CIRCULAIRE

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 14


Flutter Deep Dive Widgets • Layout • State

49 ClipRRect(

50 borderRadius: [Link](50), // Rend l'image circulaire


51 child: [Link](
52 '[Link]

53 width: 80, height: 80,


54 fit: [Link],
55 ),

56 )
57

58 // PLUS SIMPLE : CircleAvatar pour les avatars


59 CircleAvatar(

60 radius: 40, // Rayon du cercle


61 backgroundImage: NetworkImage('[Link]
62 backgroundColor: [Link], // Couleur si pas d'image
63 child: Text('AB'), // Initiales si pas d'image
64 )

BoxFit Comportement Quand utiliser

cover Remplit tout l'espace, PEUT être rogné Photos de fond, avatars, galeries

Image complète visible, PEUT avoir des bords


contain Logos, icônes, images techniques
vides

Rarement — seulement images


fill Remplit en s'étirant (peut déformer !)
conçues pour ça

fitWidth Ajuste à la largeur, hauteur proportionnelle Bannières horizontales

fitHeight Ajuste à la hauteur, largeur proportionnelle Images en format portrait

none Taille originale, pas d'adaptation Petites images pixel-art

scaleDown Réduit si nécessaire, jamais agrandit Images qui ne doivent pas être floues

Tableau BoxFit — Comment choisir le bon mode d'adaptation

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 15


Flutter Deep Dive Widgets • Layout • State

■ Card, Chip, Divider — Widgets utiles

● ● ● Card, Chip, Divider, ListTile

1 // CARD — Carte Material Design avec ombre


2 Card(

3 elevation: 4, // Hauteur de l'ombre (0 à 24)


4 color: [Link], // Couleur de la carte
5 shape: RoundedRectangleBorder(
6 borderRadius: [Link](12),
7 ),

8 child: Padding(
9 padding: [Link](16),
10 child: Text('Contenu de la carte'),
11 ),

12 )
13

14 // CHIP — Badge / étiquette compact


15 Chip(

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

23 // FilterChip — Chip sélectionnable (pour filtres)


24 FilterChip(

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

36 // DIVIDER — Ligne de séparation horizontale


37 Divider(height: 1, color: [Link][300], thickness: 1)
38

39 // LISTTILE — Widget tout-en-un pour les listes


40 ListTile(

41 leading: CircleAvatar(child: Icon([Link])), // Widget à gauche


42 title: Text('Nom Prénom'), // Titre principal
43 subtitle: Text('sous-titre ou description'), // Texte secondaire
44 trailing: Icon(Icons.arrow_forward_ios, size: 16), // Widget à droite
45 onTap: () { print('Ligne cliquée'); },
46 tileColor: [Link], // Couleur de fond de la tuile
47 contentPadding: [Link](horizontal: 16, vertical: 4),
48 )

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 16


Flutter Deep Dive Widgets • Layout • State

Mise en Page : Row & Column


3 Organise chaque pixel de ton interface avec précision ■

Flutter Deep Dive · Formation Complète

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.

■ Comprendre les Axes — la clé de tout

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

mainAxis = vertical (↕ de haut en bas) mainAxis = horizontal (↔ de gauche à droite)

crossAxis = horizontal (↔ gauche/droite) crossAxis = vertical (↕ haut/bas)

mainAxisAlignment = comment répartir les enfants mainAxisAlignment = comment répartir les enfants
verticalement dans la Column horizontalement dans la Row

crossAxisAlignment = comment aligner les crossAxisAlignment = comment aligner les enfants


enfants horizontalement dans la Column verticalement 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

Une autre façon de voir : 'main' = le sens où les éléments se placent.


Dans Column, tu empiles de haut en bas → main = vertical.
Dans Row, tu places de gauche à droite → main = horizontal.

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 17


Flutter Deep Dive Widgets • Layout • State

■■ MainAxisAlignment — 6 valeurs expliquées

Ces 6 valeurs contrôlent comment les enfants sont répartis sur l'axe principal. Voici leur effet exact :

Valeur Comportement Cas d'usage typique

Tous regroupés au DÉBUT (haut pour Column, Listes, formulaires —


[Link]
gauche pour Row) comportement par défaut

Boutons d'actions en bas


Tous regroupés à la FIN (bas pour Column, droite
[Link] de page, texte aligné à
pour Row)
droite

Écrans de login, pages


[Link] Tous regroupés au CENTRE de l'axe
vides, erreurs centrées

[Link] Espace ÉGAL entre les éléments. Premier et Navigation du bas, menu
tween dernier collés aux bords horizontal, tabs

[Link] Espace autour de chaque élément (bords ont la Grilles d'icônes,


ound moitié de l'espace) statistiques en Row

[Link] Espace PARFAITEMENT égal entre ET autour de Mise en page très


enly chaque élément équilibrée et symétrique

↔■ CrossAxisAlignment — 4 valeurs expliquées

Pour Row (axe croisé =


Valeur Pour Column (axe croisé = horizontal)
vertical)

Aligne TOUS les enfants en


[Link] Aligne TOUS les enfants à GAUCHE
HAUT

Aligne TOUS les enfants en


[Link] Aligne TOUS les enfants à DROITE
BAS

Centre tous les enfants


[Link] Centre tous les enfants horizontalement
verticalement

[Link] Étire les enfants pour occuper TOUTE la Étire les enfants pour occuper
h largeur TOUTE la hauteur

[Link] Aligne sur la ligne de base des


Aligne le bas des textes (rarement utilisé)
ne textes

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 18


Flutter Deep Dive Widgets • Layout • State

■ Column & Row — Exemples visuels détaillés

● ● ● Column & Row — Exemples concrets complets

1 // EXEMPLE 1 — Profil utilisateur avec Column


2 Column(

3 mainAxisAlignment: [Link], // Centré verticalement


4 crossAxisAlignment: [Link], // Centré horizontalement
5 children: [
6 CircleAvatar(radius: 50, backgroundImage: NetworkImage('...')),
7 SizedBox(height: 16),
8 Text('Marie Dupont', style: TextStyle(fontSize: 22, fontWeight: [Link])),
9 Text('@marie_dev', style: TextStyle(color: [Link])),
10 SizedBox(height: 20),
11 ElevatedButton(onPressed: () {}, child: Text('Suivre')),
12 ],

13 )
14

15 // EXEMPLE 2 — Barre d'infos avec Row


16 Row(

17 mainAxisAlignment: [Link], // Espace entre les éléments


18 crossAxisAlignment: [Link], // Centré verticalement
19 children: [
20 Row(children: [
21 Icon([Link], color: [Link], size: 18),
22 SizedBox(width: 4),
23 Text('4.8', style: TextStyle(fontWeight: [Link])),
24 ]),

25 Text('125 avis', style: TextStyle(color: [Link])),


26 Container(

27 padding: [Link](horizontal: 8, vertical: 4),


28 decoration: BoxDecoration(
29 color: [Link][100],
30 borderRadius: [Link](8),
31 ),

32 child: Text('Disponible', style: TextStyle(color: [Link][800], fontSize: 12)),


33 ),

34 ],

35 )
36

37 // EXEMPLE 3 — Statistiques côte à côte dans une Row


38 Row(

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 ),

48 Container(width: 1, height: 40, color: [Link][300]), // Séparateur

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 19


Flutter Deep Dive Widgets • Layout • State

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 ),

57 Container(width: 1, height: 40, color: [Link][300]),


58 Expanded(

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 )

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 20


Flutter Deep Dive Widgets • Layout • State

↔■ Expanded et Flexible — Gérer l'espace dynamiquement

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

● ● ● Expanded et Flexible — Gérer l'espace dynamiquement

1 // PROBLÈME COURANT sans Expanded — Row qui déborde


2 Row(

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

11 // SOLUTION avec Expanded — le Text prend tout l'espace disponible


12 Row(

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]',

19 overflow: [Link], // Coupe avec ...


20 ),

21 ),

22 Icon([Link]),

23 ],

24 )
25

26 // EXPANDED avec flex — partager l'espace proportionnellement


27 Row(

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

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 21


Flutter Deep Dive Widgets • Layout • State

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 !

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 22


Flutter Deep Dive Widgets • Layout • State

■ SizedBox, Spacer, Padding, Center — Contrôle fin

● ● ● SizedBox, Spacer, Padding, Center, Align

1 // SIZEDBOX — Espace fixe ou taille précise


2 SizedBox(height: 20) // Espace vertical de 20px
3 SizedBox(width: 10) // Espace horizontal de 10px
4 SizedBox( // Widget à taille précise
5 width: 200, height: 50,
6 child: ElevatedButton(child: Text('OK'), onPressed: () {}),
7)

8 [Link]() // Widget de taille 0 (pour cacher conditionnellement)


9 [Link]() // Prend tout l'espace disponible
10

11 // SPACER — Espace flexible (comme Expanded mais vide)


12 Row(

13 children: [
14 Icon([Link]),

15 Spacer(), // Pousse tout le reste à droite


16 Text('Accueil'),

17 Spacer(flex: 2), // Double espace par rapport à un Spacer normal


18 Icon([Link]),

19 ],

20 )
21

22 // PADDING — Espace intérieur autour d'un widget


23 Padding(

24 padding: [Link](16),
25 child: Text('Avec espace tout autour'),
26 )

27 Padding(

28 padding: [Link](top: 8, left: 16, right: 16),


29 child: Text('Espace sélectif'),
30 )
31

32 // CENTER — Centrer un widget dans son parent


33 Center(

34 widthFactor: 1.5, // Optionnel : facteur de taille


35 child: Text('Centré !'),
36 )
37

38 // ALIGN — Positionner précisément


39 Align(

40 alignment: [Link], // topLeft, topCenter, topRight


41 child: Icon([Link]), // centerLeft, center, centerRight
42 ) // bottomLeft, bottomCenter, bottomRight
43 // ou avec des coordonnées précises :
44 Align(

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 )

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 23


Flutter Deep Dive Widgets • Layout • State

■ Stack — Superposer des widgets (calques)

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.

● ● ● Stack et Positioned — Superposition et placement précis

1 // STACK basique — les widgets se superposent dans l'ordre


2 Stack(

3 alignment: [Link], // Alignement par défaut des enfants


4 children: [
5 // PREMIER = tout en bas (fond)
6 Container(

7 width: 300, height: 200,


8 decoration: BoxDecoration(
9 image: DecorationImage(
10 image: NetworkImage('[Link]
11 fit: [Link],
12 ),

13 borderRadius: [Link](16),
14 ),

15 ),
16

17 // DEUXIÈME = par-dessus l'image


18 Container(

19 width: 300, height: 200,


20 decoration: BoxDecoration(
21 borderRadius: [Link](16),
22 gradient: LinearGradient(
23 colors: [[Link], Colors.black87],
24 begin: [Link],
25 end: [Link],
26 ),

27 ),

28 ),
29

30 // TROISIÈME = texte par-dessus l'overlay


31 Positioned( // Positionner avec précision !
32 bottom: 16,
33 left: 16,
34 right: 16,
35 child: Column(
36 crossAxisAlignment: [Link],
37 children: [
38 Text('Titre de la photo',
39 style: TextStyle(color: [Link], fontSize: 18, fontWeight: [Link])),
40 Text('il y a 2 heures',
41 style: TextStyle(color: Colors.white70, fontSize: 12)),
42 ],

43 ),

44 ),

45 ],

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 24


Flutter Deep Dive Widgets • Layout • State

46 )
47

48 // BADGE avec Stack (notifications, panier, etc.)


49 Stack(

50 clipBehavior: [Link], // Permet au badge de dépasser


51 children: [
52 Icon(Icons.shopping_cart, size: 36), // L'icône principale
53 Positioned(

54 top: -4,
55 right: -4,
56 child: Container(
57 padding: [Link](4),
58 decoration: BoxDecoration(
59 color: [Link],
60 shape: [Link],
61 ),

62 child: Text('3', style: TextStyle(color: [Link], fontSize: 10,


63 fontWeight: [Link])),
64 ),

65 ),

66 ],

67 )

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 25


Flutter Deep Dive Widgets • Layout • State

■■ Combinaisons pratiques — Interfaces réelles

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 :

● ● ● Interface réelle — Carte produit e-commerce

1 // INTERFACE 1 — Carte produit e-commerce complète


2 Card(

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

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 26


Flutter Deep Dive Widgets • Layout • State

46 Text('19.99€', style: TextStyle(


47 fontSize: 18, fontWeight: [Link], color: [Link])),
48 SizedBox(width: 8),
49 Text('24.99€', style: TextStyle(
50 color: [Link], decoration: [Link], fontSize: 13)),
51 Spacer(),

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 )

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 27


Flutter Deep Dive Widgets • Layout • State

Stateless vs Stateful
4 ■
Comprendre l'état — le concept qui change tout en Flutter

Flutter Deep Dive · Formation Complète

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.

■ La notion d'État — Qu'est-ce que c'est ?

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 feu tricolore a un état : rouge, orange, ou vert.


Quand l'état change (rouge → vert), l'affichage change.
Si l'état ne changeait jamais → Stateless (widget figé)
Si l'état peut changer → Stateful (widget dynamique)

En Flutter, l'état = les variables de ton widget qui peuvent changer.


Exemples d'état :
• Le score dans un jeu : int _score = 0;
• Le texte saisi dans un champ : String _texte = '';
• Si un bouton toggle est activé : bool _actif = false;
• La page actuellement affichée : int _pageIndex = 0;

■ StatelessWidget — Immuable et performant

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.

● ● ● StatelessWidget — Structure complète commentée

1 // STRUCTURE COMPLÈTE d'un StatelessWidget


2 // Chaque élément est expliqué ligne par ligne
3

4 import 'package:flutter/[Link]';
5

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 28


Flutter Deep Dive Widgets • Layout • State

6 class CarteArticle extends StatelessWidget {


7 // ↑ nom de la classe ↑ hérite de StatelessWidget
8

9 // 1. LES PROPRIÉTÉS (passées de l'extérieur, ne changent jamais)


10 final String titre; // final = ne changera JAMAIS après init
11 final String auteur;
12 final String imageUrl;
13 final DateTime datePublication;
14 final int nombreLikes;
15

16 // 2. LE CONSTRUCTEUR (comment créer ce widget)


17 const CarteArticle({ // const = peut être optimisé par Flutter
18 [Link], // obligatoire pour identifier le widget dans l'arbre
19 required [Link], // required = doit être fourni obligatoirement
20 required [Link],
21 required [Link],
22 [Link] = 0, // Valeur par défaut si non fourni
23 required [Link],
24 });
25

26 // 3. LA MÉTHODE BUILD (la seule qui compte !)


27 @override // On redéfinit la méthode du parent
28 Widget build(BuildContext context) {
29 // ↑ retourne un Widget ↑ contexte = position du widget dans l'arbre
30

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(),

50 Icon(Icons.favorite_border, size: 14, color: [Link]),


51 SizedBox(width: 4),
52 Text('$nombreLikes', style: TextStyle(fontSize: 12)),
53 ],

54 ),

55 ],

56 ),

57 ),

58 ],

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 29


Flutter Deep Dive Widgets • Layout • State

59 ),

60 );

61 }

62 }
63

64 // UTILISATION — Comment passer des données au StatelessWidget


65 // Dans une autre page ou un autre widget :
66 CarteArticle(

67 titre: 'Comment apprendre Flutter rapidement',


68 auteur: 'Marie Dupont',
69 imageUrl: '[Link]
70 datePublication: [Link](),
71 nombreLikes: 42,
72 )

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 30


Flutter Deep Dive Widgets • Layout • State

■ StatefulWidget — Dynamique et réactif

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 ?

Class 1 (StatefulWidget) = le 'contrat' : définit les propriétés immuables du widget


Class 2 (_State) = le 'moteur' : contient l'état, la logique, et la méthode build()

Flutter sépare exprès les deux pour des raisons de performance :


→ Le widget peut être recréé sans perdre l'état
→ L'état persiste même si le widget est reconstruit par Flutter
→ Ça permet des animations et transitions fluides

● ● ● StatefulWidget complet — toutes les méthodes commentées

1 // STRUCTURE COMPLÈTE d'un StatefulWidget — tout commenté


2

3 class CompteurAvecHistorique extends StatefulWidget {


4 // ↑ CLASSE 1 : définit le widget (immuable, comme StatelessWidget)
5

6 final String titre; // Propriétés immuables ici


7 final int valeurInitiale;
8

9 const CompteurAvecHistorique({
10 [Link],

11 required [Link],
12 [Link] = 0,
13 });
14

15 // Cette méthode OBLIGATOIRE crée la classe State associée


16 @override

17 State<CompteurAvancé> createState() => _CompteurAvancéState();


18 // ↑ Par convention, nom avec _ (privé) et State
19 }
20

21

22 class _CompteurAvancéState extends State<CompteurAvancé> {


23 // ↑ CLASSE 2 : la classe State — contient TOUT ce qui peut changer
24

25 // ■■■ L'ÉTAT DU WIDGET ■■■


26 // Ces variables PEUVENT changer, et le feront via setState()
27 int _valeur = 0; // Le compteur actuel
28 List<int> _historique = []; // Historique des valeurs
29 bool _modeNuit = false; // Toggle mode sombre
30

31 // ■■■ INITSTATE — Méthode appelée UNE FOIS à la création ■■■


32 @override

33 void initState() {

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 31


Flutter Deep Dive Widgets • Layout • State

34 [Link](); // TOUJOURS appeler [Link]() en premier !


35 // Ici : initialiser avec les données du widget parent
36 _valeur = [Link]; // [Link] pour accéder aux propriétés
37 _historique.add(_valeur);

38 print('Widget créé avec valeur initiale: $_valeur');


39 }
40

41 // ■■■ DISPOSE — Méthode appelée quand le widget est détruit ■■■


42 @override

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

50 // ■■■ LES MÉTHODES DE LOGIQUE ■■■


51 void _incrementer() {
52 setState(() { // ← OBLIGATOIRE pour déclencher le redessin
53 _valeur++; // Toute modification d'état va ICI
54 _historique.add(_valeur);

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

75 // ■■■ BUILD — Reconstruite à chaque setState() ■■■


76 @override

77 Widget build(BuildContext context) {


78 return Scaffold(
79 appBar: AppBar(
80 title: Text([Link]), // [Link] pour accéder aux props du parent
81 backgroundColor: _modeNuit ? [Link] : [Link],
82 actions: [
83 IconButton(

84 icon: Icon(_modeNuit ? Icons.wb_sunny : Icons.nightlight_round),


85 onPressed: () => setState(() => _modeNuit = !_modeNuit),
86 ),

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 32


Flutter Deep Dive Widgets • Layout • State

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 ),

106 SizedBox(height: 32),


107 Text('Historique: ${_historique.join(' → ')}',
108 style: TextStyle(color: [Link], fontSize: 12)),
109 ],

110 ),

111 );

112 }

113 }

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 33


Flutter Deep Dive Widgets • Layout • State

■ setState() — La règle d'or absolue

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.

● ● ● setState() — La règle d'or avec exemples corrects et incorrects

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

38 // Calcul avant le setState


39 int nouvelleValeur = _valeur + 10;
40

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

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 34


Flutter Deep Dive Widgets • Layout • State

46 });
47

48 // Code après setState (navigation, notifications, etc.)


49 if (_valeur >= 100) {
50 [Link](context).showSnackBar(

51 SnackBar(content: Text('Objectif atteint !')),


52 );

53 }

54 }

■■ INFO
CE QUI SEIMPORTANTE
PASSE EXACTEMENT QUAND TU APPELLES setState() :

1. Flutter marque le widget comme 'dirty' (sale = doit être redessiné)


2. Au prochain frame (1/60 de seconde), Flutter appelle build() du widget
3. build() retourne un NOUVEAU widget tree avec les nouvelles valeurs
4. Flutter compare l'ancien et le nouveau tree (algorithme de diffing)
5. Flutter met à jour UNIQUEMENT ce qui a changé (très performant !)
6. Le résultat s'affiche à l'écran

→ Ça prend moins de 16ms (60fps) — l'utilisateur ne voit aucun délai !

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 35


Flutter Deep Dive Widgets • Layout • State

■ Cycle de vie complet du StatefulWidget

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 !

● ● ● Cycle de vie — initState, build, dispose

1 // CYCLE DE VIE — Résumé pratique dans le code


2

3 class MaPage extends StatefulWidget {


4 const MaPage({[Link]});
5 @override

6 State<MaPage> createState() => _MaPageState();


7}
8

9 class _MaPageState extends State<MaPage> {


10

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();

22 print('■ Widget créé');


23 }
24

25 Future<void> _chargerDonnees() async {


26 // Charger depuis API, base de données, etc.
27 }
28

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 36


Flutter Deep Dive Widgets • Layout • State

29 @override

30 Widget build(BuildContext context) {


31 // ← Appelé à chaque setState()
32 // IMPORTANT : Ne pas faire d'opérations lourdes ici
33 // build() doit être RAPIDE
34 return Scaffold(
35 body: TextField(controller: _controller),
36 );

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 }

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 37


Flutter Deep Dive Widgets • Layout • State

■ Stateless vs Stateful — Guide de décision

■ Utilise StatelessWidget si... ■ Utilise StatefulWidget si...

Le contenu ne change JAMAIS après Une variable peut changer ET doit


création changer l'affichage
Tu affiches juste des données passées en Compteur, score, toggle, sélection
paramètre Formulaire (TextField, Checkbox, Switch)
Carte d'article, bannière, titre de Navigation par onglets (currentIndex)
section Animation simple (visible/caché)

● ● ● Stateless vs Stateful — Exemple concret de choix

1 // EXEMPLE CONCRET — Quand basculer de Stateless à Stateful


2

3 // ======================== STATELESS ========================


4 // Parfait : bouton simple qui navigue (pas d'état à gérer)
5 class BoutonNavigation extends StatelessWidget {
6 final String destination;
7 const BoutonNavigation({[Link], required [Link]});
8

9 @override

10 Widget build(BuildContext context) {


11 return ElevatedButton(
12 onPressed: () => [Link](context, destination),
13 child: Text('Aller à $destination'),
14 );

15 }

16 }
17

18 // ======================== STATEFUL ========================


19 // Nécessaire : bouton favori qui change d'aspect (état à gérer)
20 class BoutonFavori extends StatefulWidget {
21 final String articleId;
22 const BoutonFavori({[Link], required [Link]});
23

24 @override

25 State<BoutonFavori> createState() => _BoutonFavoriState();


26 }
27

28 class _BoutonFavoriState extends State<BoutonFavori> {


29 bool _estFavori = false; // ← L'état qui change !
30

31 @override

32 Widget build(BuildContext context) {


33 return IconButton(
34 icon: Icon(
35 _estFavori ? [Link] : Icons.favorite_border, // Change selon l'état
36 color: _estFavori ? [Link] : [Link],
37 ),

38 onPressed: () {
39 setState(() {
40 _estFavori = !_estFavori; // Toggle l'état

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 38


Flutter Deep Dive Widgets • Layout • State

41 });

42 // Ici on pourrait aussi sauvegarder en base de données


43 },

44 );

45 }

46 }

■ RÈGLE
RÈGLE D'OR
PRATIQUE en 3 questions :

1. Est-ce que quelque chose dans ce widget va CHANGER au cours du temps ?


→ Non → StatelessWidget
→ Oui → Continuer à la question 2

2. Ce changement doit-il METTRE À JOUR l'affichage ?


→ Non → StatelessWidget (tu peux gérer le changement en dehors)
→ Oui → StatefulWidget

3. L'état appartient-il à CE widget ou à un widget PARENT ?


→ Ce widget → StatefulWidget ici
→ Widget parent → Passer une callback en prop et gérer l'état en haut

CONSEIL D'EXPERT : Commence TOUJOURS par StatelessWidget.


Si tu réalises que tu as besoin d'état → convertis en StatefulWidget.
Dans Android Studio : click droit sur le widget → Convert to StatefulWidget

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 39


Flutter Deep Dive Widgets • Layout • State

■ ■ Synthèse et Récapitulatif Final

Tu viens de parcourir les 4 piliers fondamentaux de Flutter en profondeur. Voici ce que tu


maîtrises maintenant :

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

Text avec TextStyle complet ✓ Container avec BoxDecoration (fond, arrondi,


■ Widgets
ombre, gradient) ✓ margin (extérieur) vs padding (intérieur) ✓ Icon, IconButton,
Essentiels [Link], [Link] ✓ CircleAvatar, Card, Chip, ListTile

Column (vertical) et Row (horizontal) ✓ mainAxis vs crossAxis — les axes de


chaque widget ✓ Les 6 valeurs de MainAxisAlignment ✓ Les 5 valeurs de
■ Layout CrossAxisAlignment ✓ Expanded (espace restant), Spacer, SizedBox, Padding ✓
Stack et Positioned pour les superpositions

StatelessWidget = immuable, rapide, pour contenu fixe ✓ StatefulWidget = 2


classes, état interne mutable ✓ setState() = obligatoire pour déclencher le
■ State redessin ✓ initState() = initialisation, dispose() = nettoyage ✓ Règle des 3
questions pour choisir le bon type

■ Tu as les bases. Maintenant — CONSTRUIS !


La meilleure façon de maîtriser Flutter : créer une vraie app.

Lance flutter create mon_app et commence à empiler des widgets aujourd'hui.

Flutter Deep Dive — Arbre · Widgets · Layout · State Page 40

Vous aimerez peut-être aussi