Flutter Summary Chapter-3
Flutter Summary Chapter-3
interfaces
Explication
// Text simple
Text('Bonjour Flutter')
1/31
RichText(
text: TextSpan(
text: 'Texte normal ',
style: TextStyle(color: [Link]),
children: [
TextSpan(
text: 'en gras ',
style: TextStyle(fontWeight: [Link]),
),
TextSpan(
text: 'et en couleur',
style: TextStyle(color: [Link]),
),
],
),
)
// Icône simple
Icon([Link])
// Icône personnalisée
Icon(
[Link],
size: 50,
color: [Link],
)
2/31
IconButton(
icon: Icon(Icons.volume_up),
iconSize: 30,
color: [Link],
tooltip: 'Augmenter le volume',
onPressed: () {
print('Icône cliquée');
},
)
// Image simple
[Link]('assets/images/[Link]')
[Link](
'[Link]
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return CircularProgressIndicator(
value: [Link] != null
? [Link] /
[Link]!
: null,
);
},
errorBuilder: (context, error, stackTrace) {
return Icon([Link]);
},
)
3/31
// Depuis la mémoire
[Link](uint8List)
// Depuis un fichier
[Link](File('path/to/[Link]'))
[Link] : Étire l'image pour remplir la boîte (peut déformer) [Link] : Contient
l'image entière dans la boîte (peut laisser des espaces) [Link] : Remplit la boîte en
coupant l'image si nécessaire [Link] : Ajuste à la largeur [Link] : Ajuste à la
hauteur
[Link] : Taille originale
[Link] : Réduit si trop grande, sinon taille originale
CircleAvatar(
radius: 50,
backgroundImage: NetworkImage('[Link]
backgroundColor: [Link],
)
// Avec initiales
CircleAvatar(
radius: 50,
backgroundColor: [Link],
child: Text(
'AB',
style: TextStyle(fontSize: 24, color: [Link]),
),
)
AssetImage vs NetworkImage
4/31
Conteneur avec élévation et coins arrondis :
Card(
elevation: 5,
shape: RoundedRectangleBorder(
borderRadius: [Link](15),
),
child: Padding(
padding: [Link](16),
child: Column(
children: [
[Link]('assets/[Link]'),
SizedBox(height: 10),
Text('Produit'),
Text('Prix: 29.99€'),
],
),
),
)
ElevatedButton(
onPressed: () {
print('Bouton cliqué');
},
child: Text('Valider'),
style: [Link](
backgroundColor: [Link],
foregroundColor: [Link],
padding: [Link](horizontal: 30, vertical: 15),
shape: RoundedRectangleBorder(
borderRadius: [Link](10),
),
elevation: 5,
),
)
// Bouton désactivé
ElevatedButton(
onPressed: null, // null désactive le bouton
child: Text('Désactivé'),
)
5/31
TextButton(
onPressed: () {},
child: Text('Annuler'),
style: [Link](
foregroundColor: [Link],
),
)
OutlinedButton(
onPressed: () {},
child: Text('Plus d\'infos'),
style: [Link](
side: BorderSide(color: [Link], width: 2),
),
)
IconButton(
icon: Icon(Icons.favorite_border),
onPressed: () {},
iconSize: 30,
color: [Link],
)
FloatingActionButton(
onPressed: () {},
child: Icon([Link]),
backgroundColor: [Link],
tooltip: 'Ajouter',
)
// Version étendue
[Link](
onPressed: () {},
icon: Icon([Link]),
label: Text('Nouveau'),
)
6/31
GestureDetector(
onTap: () => print('Tap simple'),
onDoubleTap: () => print('Double tap'),
onLongPress: () => print('Appui long'),
onPanUpdate: (details) => print('Glissement'),
onScaleUpdate: (details) => print('Pincement'),
child: Container(
width: 200,
height: 200,
color: [Link],
child: Center(child: Text('Touchez-moi')),
),
)
InkWell(
onTap: () {},
onLongPress: () {},
splashColor: [Link](0.3),
highlightColor: [Link](0.1),
borderRadius: [Link](10),
child: Container(
padding: [Link](16),
child: Text('Cliquez pour effet d\'encre'),
),
)
StatefulWidget en détail
7/31
// Classe du Widget (immuable)
class Compteur extends StatefulWidget {
const Compteur({Key? key}) : super(key: key);
@override
State<Compteur> createState() => _CompteurState();
}
void _toggleVisibility() {
setState(() {
_isVisible = !_isVisible;
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Compteur : $_compteur'),
ElevatedButton(
onPressed: _incrementer,
child: Text('Incrémenter'),
),
if (_isVisible)
Text('Visible'),
IconButton(
icon: Icon(_isVisible ? [Link] : Icons.visibility_off),
onPressed: _toggleVisibility,
),
],
);
}
}
8/31
class MonWidget extends StatefulWidget {
@override
_MonWidgetState createState() => _MonWidgetState();
}
@override
void didChangeDependencies() {
[Link]();
// Appelé après initState et quand les dépendances changent
print('didChangeDependencies');
}
@override
Widget build(BuildContext context) {
// Appelé à chaque rebuild
print('build');
return Container();
}
@override
void didUpdateWidget(MonWidget oldWidget) {
[Link](oldWidget);
// Appelé quand le widget parent se reconstruit
print('didUpdateWidget');
}
@override
void dispose() {
// Nettoyage avant destruction
print('dispose');
[Link]();
}
}
9/31
class FormExample extends StatefulWidget {
@override
_FormExampleState createState() => _FormExampleState();
}
@override
void dispose() {
_controller.dispose(); // Important : libérer le contrôleur
[Link]();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
TextField(
controller: _controller,
decoration: InputDecoration(
labelText: 'Nom',
hintText: 'Entrez votre nom',
prefixIcon: Icon([Link]),
suffixIcon: IconButton(
icon: Icon([Link]),
onPressed: () => _controller.clear(),
),
border: OutlineInputBorder(),
),
onChanged: (value) {
setState(() {
_texte = value;
});
},
),
Text('Vous avez saisi : $_texte'),
],
);
}
}
10/31
int _selectedRadio 1;
bool _switched = false;
@override
Widget build(BuildContext context) {
return Column(
children: [
// Checkbox
Checkbox(
value: _checked,
onChanged: (value) {
setState(() {
_checked = value!;
});
},
),
// Radio buttons
RadioListTile(
title: Text('Option 1'),
value: 1,
groupValue: _selectedRadio,
onChanged: (value) {
setState(() {
_selectedRadio = value as int;
});
},
),
// Switch
Switch(
value: _switched,
onChanged: (value) {
setState(() {
_switched = value;
});
},
),
],
);
}
}
11/31
Sous-partie 3 : Positionnement avancé (Stack, Positioned, Wrap,
LayoutBuilder)
Stack permet de superposer des widgets les uns sur les autres :
Stack(
alignment: [Link], // Alignement par défaut
children: [
// Widget du fond
Container(
width: 300,
height: 300,
color: [Link],
),
// Widget au-dessus
Container(
width: 200,
height: 200,
color: [Link],
),
// Widget au premier plan
Text(
'Texte au premier plan',
style: TextStyle(color: [Link], fontSize: 20),
),
],
)
12/31
Stack(
children: [
Container(
width: 300,
height: 300,
color: [Link][300],
),
// Position en haut à gauche
Positioned(
top: 10,
left: 10,
child: Icon([Link], color: [Link], size: 40),
),
// Position en bas à droite
Positioned(
bottom: 10,
right: 10,
child: ElevatedButton(
onPressed: () {},
child: Text('Bouton'),
),
),
// Centré horizontalement en haut
Positioned(
top: 10,
left: 0,
right: 0,
child: Center(child: Text('Titre')),
),
// Étire sur toute la largeur
Positioned(
bottom: 0,
left: 0,
right: 0,
height: 50,
child: Container(color: [Link](0.5)),
),
],
)
Stack(
children: [
[Link](
child: [Link]('assets/[Link]', fit: [Link]),
),
[Link](
child: Container(color: [Link](0.3)),
),
],
)
13/31
Align - Alignement dans un conteneur
Container(
width: 200,
height: 200,
color: [Link],
child: Align(
alignment: [Link],
// [Link], topCenter, topRight
// [Link], center, centerRight
// [Link], bottomCenter, bottomRight
// Alignment(x, y) où x et y sont entre -1 et 1
child: Icon([Link]),
),
)
Wrap(
spacing: 8, // Espacement horizontal
runSpacing: 10, // Espacement vertical entre lignes
alignment: [Link],
children: [
Chip(label: Text('Flutter')),
Chip(label: Text('Dart')),
Chip(label: Text('Mobile')),
Chip(label: Text('Development')),
Chip(label: Text('UI/UX')),
Chip(label: Text('Material')),
],
)
14/31
Row(
children: [
// Prend 2 parts de l'espace disponible
Expanded(
flex: 2,
child: Container(color: [Link], height: 100),
),
// Prend 1 part de l'espace disponible
Expanded(
flex: 1,
child: Container(color: [Link], height: 100),
),
],
)
// Flexible vs Expanded
Column(
children: [
// Flexible : peut être plus petit que l'espace alloué
Flexible(
child: Container(color: [Link]),
),
// Expanded : doit remplir l'espace alloué
Expanded(
child: Container(color: [Link]),
),
],
)
15/31
LayoutBuilder(
builder: (context, constraints) {
// [Link] et [Link] disponibles
AspectRatio(
aspectRatio: 16 / 9, // Ratio largeur/hauteur
child: Container(
color: [Link],
child: Center(child: Text('16:9')),
),
)
FractionallySizedBox(
widthFactor: 0.5, // 50% de la largeur du parent
heightFactor: 0.3, // 30% de la hauteur du parent
child: Container(color: [Link]),
)
16/31
ConstrainedBox(
constraints: BoxConstraints(
minWidth: 100,
maxWidth: 300,
minHeight: 50,
maxHeight: 200,
),
child: Container(color: [Link]),
)
ListView simple :
ListView(
padding: [Link](8),
children: [
ListTile(
leading: Icon([Link]),
title: Text('Carte'),
subtitle: Text('Voir la carte'),
trailing: Icon(Icons.arrow_forward),
onTap: () {},
),
ListTile(
leading: Icon([Link]),
title: Text('Photos'),
),
ListTile(
leading: Icon([Link]),
title: Text('Téléphone'),
),
],
)
17/31
[Link](
itemCount: 100, // Nombre d'éléments
itemBuilder: (context, index) {
return ListTile(
leading: CircleAvatar(child: Text('${index + 1}')),
title: Text('Élément $index'),
subtitle: Text('Description de l\'élément $index'),
onTap: () {
print('Élément $index cliqué');
},
);
},
)
[Link](
itemCount: 20,
itemBuilder: (context, index) {
return ListTile(
title: Text('Item $index'),
);
},
separatorBuilder: (context, index) {
return Divider(thickness: 1); // Ligne de séparation
},
)
Liste horizontale :
ListView(
scrollDirection: [Link],
children: [
Container(width: 160, color: [Link]),
Container(width: 160, color: [Link]),
Container(width: 160, color: [Link]),
],
)
18/31
SingleChildScrollView(
child: Column(
children: [
Container(height: 300, color: [Link]),
Container(height: 300, color: [Link]),
Container(height: 300, color: [Link]),
],
),
)
[Link](
crossAxisCount: 2, // 2 colonnes
crossAxisSpacing: 10, // Espacement horizontal
mainAxisSpacing: 10, // Espacement vertical
padding: [Link](10),
children: [Link](20, (index) {
return Card(
child: Center(
child: Text('Item $index'),
),
);
}),
)
[Link](
maxCrossAxisExtent: 150, // Largeur max de chaque élément
crossAxisSpacing: 10,
mainAxisSpacing: 10,
children: [
Container(color: [Link]),
Container(color: [Link]),
Container(color: [Link]),
],
)
19/31
[Link](
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
childAspectRatio: 1, // Ratio largeur/hauteur
),
itemCount: 50,
itemBuilder: (context, index) {
return Card(
child: Center(child: Text('$index')),
);
},
)
@override
void initState() {
[Link]();
_scrollController.addListener(() {
// Détecte la position de défilement
if (_scrollController.offset > 200) {
setState(() {
_showBackToTop = true;
});
} else {
setState(() {
_showBackToTop = false;
});
}
});
}
@override
void dispose() {
_scrollController.dispose();
[Link]();
}
void _scrollToTop() {
_scrollController.animateTo(
0,
duration: Duration(milliseconds: 500),
20/31
duration: Duration(milliseconds: 500),
curve: [Link],
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: [Link](
controller: _scrollController,
itemCount: 100,
itemBuilder: (context, index) {
return ListTile(title: Text('Item $index'));
},
),
floatingActionButton: _showBackToTop
? FloatingActionButton(
onPressed: _scrollToTop,
child: Icon(Icons.arrow_upward),
)
: null,
);
}
}
21/31
RefreshIndicator(
onRefresh: () async {
// Logique de rafraîchissement
await [Link](Duration(seconds: 2));
// Recharger les données
},
child: [Link](
itemCount: 20,
itemBuilder: (context, index) {
return ListTile(title: Text('Item $index'));
},
),
)
Scrollbar - Barre de défilement
dartScrollbar(
thumbVisibility: true, // Toujours visible
child: [Link](
itemCount: 100,
itemBuilder: (context, index) {
return ListTile(title: Text('Item $index'));
},
),
)
ReorderableListView - Liste réorganisable
dartclass ReorderableExample extends StatefulWidget {
@override
_ReorderableExampleState createState() => _ReorderableExampleState();
}
@override
Widget build(BuildContext context) {
return ReorderableListView(
onReorder: (oldIndex, newIndex) {
setState(() {
if (newIndex > oldIndex) {
newIndex -= 1;
}
final item = _items.removeAt(oldIndex);
_items.insert(newIndex, item);
});
},
children: _items.map((item) {
return ListTile(
key: Key(item),
title: Text(item),
trailing: Icon(Icons.drag_handle),
);
}).toList(),
);
}
}
22/31
CustomScrollView avec Slivers - Défilement avancé
CustomScrollView(
slivers: [
// AppBar qui se rétracte
SliverAppBar(
expandedHeight: 200,
floating: false,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
title: Text('Titre'),
background: [Link]('assets/[Link]', fit: [Link]),
),
),
// Liste
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => ListTile(title: Text('Item $index')),
childCount: 20,
),
),
// Grille
SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
delegate: SliverChildBuilderDelegate(
(context, index) => Card(child: Center(child: Text('$index'))),
childCount: 10,
),
),
],
)
23/31
Fiche de révision
Interaction et boutons
Types de boutons
Détection de gestes
StatefulWidget
24/31
Cycle de vie : initState() → build() → didUpdateWidget() → dispose()
Widgets de formulaire
Positionnement avancé
Stack : Empiler des widgets
Autres widgets
25/31
Listes et défilement
Widgets supplémentaires
ListTile(
leading: Icon(),
title: Text(),
subtitle: Text(),
trailing: Icon(),
onTap: () {},
)
26/31
LayoutBuilder pour responsive design
ScrollController doit être dispose() pour éviter les fuites mémoire
Wrap vs Row : Wrap gère le retour à la ligne automatiquement
27/31
QCM
a) textOverflow
b) overflow
c) clip
d) maxLines
e) ellipsis
a) [Link]
b) [Link]
c) [Link]
d) [Link]
e) [Link]
Q3 : Quel widget permet d'afficher une image depuis les assets du projet ?
a) [Link]()
b) [Link]()
c) [Link]()
d) [Link]()
e) AssetImage()
Q4 : Quel type de bouton Material a une élévation et un fond coloré par défaut ?
a) TextButton
b) OutlinedButton
c) IconButton
d) ElevatedButton
e) FlatButton
Q5 : Quelle méthode est essentielle pour mettre à jour l'interface d'un StatefulWidget ?
a) update()
b) rebuild()
c) setState()
d) refresh()
e) notifyListeners()
Q6 : Dans le cycle de vie d'un StatefulWidget, quelle méthode est appelée en premier lors de
la création ?
a) build()
b) initState()
c) didChangeDependencies()
d) createState()
e) dispose()
28/31
Q7 : Quel widget permet de détecter un appui long, un double tap et un glissement ?
a) InkWell
b) GestureDetector
c) TouchDetector
d) TapDetector
e) ClickDetector
Q8 : Quel widget permet de superposer des éléments les uns sur les autres ?
a) Layer
b) Overlay
c) Stack
d) Pile
e) ZIndex
Q10 : Quel widget permet une disposition qui passe automatiquement à la ligne suivante ?
a) Row
b) Column
c) Wrap
d) Flow
e) Flex
a) Aucune différence
b) Expanded force le remplissage de l'espace, Flexible peut être plus petit
c) Flexible est plus rapide qu'Expanded
d) Expanded ne fonctionne que dans Row
e) Flexible ne fonctionne que dans Column
a) MediaQuery
b) ResponsiveBuilder
c) LayoutBuilder
d) ScreenBuilder
e) AdaptiveLayout
Q13 : Quelle méthode de ListView est optimisée pour de longues listes avec construction à la
demande ?
a) ListView()
29/31
b) [Link]()
c) [Link]()
d) [Link]()
e) [Link]()
Q14 : Quel widget permet de rendre un widget unique défilant sans utiliser ListView ?
a) ScrollView
b) Scrollable
c) SingleChildScrollView
d) ScrollWidget
e) ScrollContainer
a) ListController
b) ScrollManager
c) ScrollController
d) ListManager
e) ScrollHandler
a) PullToRefresh
b) RefreshIndicator
c) RefreshWidget
d) ReloadIndicator
e) UpdateIndicator
Q19 : Quel widget affiche une image ou du texte dans un cercle, parfait pour les avatars ?
a) CircularImage
b) RoundedImage
c) CircleAvatar
30/31
d) AvatarWidget
e) ProfilePicture
Q20 : Quelle propriété de Stack définit l'alignement par défaut de ses enfants ?
a) defaultAlignment
b) childAlignment
c) alignment
d) stackAlignment
e) position
Réponses du QCM
Q1 : b
Q2 : c
Q3 : c
Q4 : d
Q5 : c
Q6 : b
Q7 : b
Q8 : c
Q9 : b
Q10 : c
Q11 : b
Q12 : c
Q13 : b
Q14 : c
Q15 : c
Q16 : c
Q17 : b
Q18 : b
Q19 : c
Q20 : c
31/31