3.2Interagir avec l'interface et modifier le contenu dans Flutter
3.2.1 – Ajouter un bouton dans l'interface
Les boutons sont des éléments essentiels dans une application. Ils permettent à l'utilisateur d'interagir avec l'interface en déclenchant des actions. Dans Flutter, le widget ElevatedButton est le type de bouton le plus couramment utilisé.
🎯 Pourquoi utiliser un bouton
Les boutons permettent à l'utilisateur de :
- Déclencher des actions : Valider un formulaire, sauvegarder des données, naviguer vers une autre page
- Confirmer des choix : Accepter ou refuser une proposition
- Interagir avec l'interface : Changer de page, ouvrir un menu, afficher des informations
Un bouton dans une application est comme un interrupteur dans une pièce : quand vous appuyez dessus, quelque chose se passe. Dans une application, appuyer sur un bouton déclenche une action que vous avez programmée.
🔘 Introduction à ElevatedButton
ElevatedButton est un widget qui affiche un bouton avec un effet d'élévation (ombre). C'est le type de bouton principal recommandé dans Material Design pour les actions importantes.
Material Design est le système de design de Google.
ElevatedButton suit les règles de Material Design et offre un rendu professionnel et cohérent avec les autres applications Material.
Voici la structure de base d'un ElevatedButton :
ElevatedButton(
onPressed: () {},
child: Text('Mon bouton'),
)
Analysons ce code :
ElevatedButton(: Le widget qui crée le boutononPressed: () {}: La fonction qui sera appelée quand le bouton est pressé (pour l'instant, elle est vide)child: Text('Mon bouton'): Le contenu du bouton (le texte affiché)
onPressed est un paramètre qui attend une fonction. La syntaxe () {} crée une fonction vide (qui ne fait rien pour l'instant). Nous verrons comment remplir cette fonction dans la prochaine section.
📝 Paramètres essentiels (child, onPressed)
ElevatedButton a deux paramètres essentiels :
child: Le contenu du bouton (généralement unText, mais peut être n'importe quel widget)onPressed: La fonction qui sera exécutée quand l'utilisateur appuie sur le bouton
Le paramètre
child définit ce qui sera affiché à l'intérieur du bouton. Le plus souvent, c'est un Text, mais vous pouvez aussi mettre une Row avec une icône et du texte, ou n'importe quel autre widget.
Le paramètre
onPressed est obligatoire. Si vous ne le fournissez pas, le bouton sera désactivé (grisé). Pour l'instant, nous utilisons une fonction vide () {}, mais dans la prochaine section, nous verrons comment y mettre du code qui s'exécute quand le bouton est pressé.
🧪 Exemple simple : bouton affiché à l'écran
Voici un exemple complet avec un bouton affiché à l'écran :
ElevatedButton affiché au centre de l'écran.
import 'package:flutter/material.dart';
void main() {
runApp(const MaPage());
}
class MaPage extends StatelessWidget {
const MaPage({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: const Text('Mon Application'),
),
body: SafeArea(
child: Center(
child: ElevatedButton(
onPressed: () {},
child: const Text('Cliquez-moi'),
),
),
),
),
);
}
}
Dans cet exemple, un bouton "Cliquez-moi" est affiché au centre de l'écran. Pour l'instant, appuyer sur le bouton ne fait rien car la fonction onPressed est vide.
Même si le bouton ne fait rien pour l'instant, il est important de fournir
onPressed: () {}. Sans ce paramètre, le bouton serait désactivé (grisé) et l'utilisateur ne pourrait pas interagir avec.
Essayez de modifier le texte du bouton en changeant
'Cliquez-moi' par un autre texte. Vous verrez que le bouton s'adapte automatiquement à la taille du texte.
💡 Points clés à retenir
- Les boutons permettent à l'utilisateur d'interagir avec l'application
ElevatedButtonest le type de bouton principal recommandéchilddéfinit le contenu du bouton (généralement unText)onPressedest la fonction exécutée quand le bouton est presséonPressedest obligatoire : sans lui, le bouton est désactivé- Pour l'instant, nous utilisons
onPressed: () {}(fonction vide)
Voici la structure de base d'un
ElevatedButton :
ElevatedButton(
onPressed: () {},
child: Text('Texte du bouton'),
)
Dans la prochaine section, nous verrons comment remplir la fonction onPressed pour que le bouton fasse quelque chose quand on appuie dessus.
3.2.2 – Gérer l'état avec StatefulWidget et setState()
Dans la section précédente, nous avons créé un bouton avec onPressed: () {}, mais ce bouton ne faisait rien. Pour créer une application vraiment interactive, nous devons pouvoir modifier l'interface en fonction des actions de l'utilisateur. C'est là qu'intervient StatefulWidget.
⚠️ Utilisation de onPressed & ses limites
Jusqu'à présent, nous avons utilisé onPressed: () {} avec une fonction vide. Même si nous ajoutons du code dans cette fonction, il y a une limitation importante :
ElevatedButton(
onPressed: () {
print('Bouton pressé !');
},
child: Text('Cliquez-moi'),
)
Dans cet exemple, quand vous appuyez sur le bouton, le message "Bouton pressé !" s'affichera dans la console, mais l'interface ne changera pas. C'est parce que StatelessWidget ne peut pas modifier son apparence après sa création.
La console est l'endroit où Flutter affiche les messages de débogage. Dans VS Code ou Android Studio, vous pouvez voir la console en bas de l'écran. Quand vous utilisez
print(), le message apparaît dans cette console.
StatelessWidget est un widget qui ne peut pas changer après sa création. Une fois construit, il reste identique. C'est pourquoi, même si vous ajoutez du code dans onPressed, l'interface ne change pas visuellement.
🔄 Besoin d'un état pour modifier l'interface
Pour modifier l'interface (par exemple, changer le texte affiché ou incrémenter un compteur), nous avons besoin de stocker des informations qui peuvent changer. Ces informations s'appellent l'état (state) du widget.
L'état d'un widget, c'est toutes les données qui peuvent changer et qui affectent l'apparence du widget. Par exemple :
- Le nombre affiché dans un compteur (0, 1, 2, 3...)
- Le texte d'un bouton qui change ("Cliquez-moi" → "Déjà cliqué")
- Si une case est cochée ou non
- La couleur d'un élément qui change
Pour gérer un état qui change, Flutter fournit StatefulWidget, qui est différent de StatelessWidget.
📊 Différence entre StatelessWidget et StatefulWidget
Voici les principales différences entre ces deux types de widgets :
| Caractéristique | StatelessWidget | StatefulWidget |
|---|---|---|
| Peut changer ? | Non, reste identique après création | Oui, peut changer dynamiquement |
| Utilisation | Affichage de contenu statique | Contenu qui change avec les interactions |
| Exemples | Texte fixe, icônes, images statiques | Compteur, formulaire, liste déroulante |
Utilisez
StatelessWidget quand le contenu ne change jamais. Par exemple, un titre de page, une icône fixe, ou un texte qui reste toujours le même.
Utilisez
StatefulWidget quand le contenu doit changer en fonction des actions de l'utilisateur ou d'autres événements. Par exemple, un compteur qui s'incrémente, un bouton qui change de texte, ou une case à cocher.
🏗️ Structure d'un StatefulWidget
Un StatefulWidget a une structure différente de StatelessWidget. Il est composé de deux classes :
- La classe du widget (qui étend
StatefulWidget) - La classe de l'état (qui étend
State)
Voici la structure de base :
class MonWidget extends StatefulWidget {
const MonWidget({super.key});
@override
State<MonWidget> createState() => _MonWidgetState();
}
class _MonWidgetState extends State<MonWidget> {
@override
Widget build(BuildContext context) {
return Text('Mon contenu');
}
}
Analysons ce code :
class MonWidget extends StatefulWidget: La classe du widget (commeStatelessWidget, mais pour un widget qui peut changer)createState(): Cette méthode crée l'objet qui gère l'étatclass _MonWidgetState extends State<MonWidget>: La classe qui gère l'état et contient la méthodebuild()_MonWidgetState: Le préfixe_signifie que cette classe est privée (utilisée uniquement dans ce fichier)
Flutter sépare le widget (qui décrit ce qu'il faut afficher) de l'état (qui stocke les données qui peuvent changer). Cette séparation permet à Flutter d'optimiser les performances en ne reconstruisant que ce qui est nécessaire quand l'état change.
🧪 Exemple : changer le texte du bouton lui-même
Voici un exemple concret : un bouton qui change son propre texte quand on clique dessus :
ElevatedButton qui change son texte quand on clique dessus.
import 'package:flutter/material.dart';
void main() {
runApp(const MaPage());
}
class MaPage extends StatefulWidget {
const MaPage({super.key});
@override
State<MaPage> createState() => _MaPageState();
}
class _MaPageState extends State<MaPage> {
String texteBouton = 'Cliquez-moi';
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: const Text('Mon Application'),
),
body: SafeArea(
child: Center(
child: ElevatedButton(
onPressed: () {
setState(() {
texteBouton = 'Déjà cliqué !';
});
},
child: Text(texteBouton),
),
),
),
),
);
}
}
Analysons ce code étape par étape :
class MaPage extends StatefulWidget: Nous utilisonsStatefulWidgetau lieu deStatelessWidgetString texteBouton = 'Cliquez-moi': Une variable qui stocke le texte du bouton (c'est notre état)child: Text(texteBouton): Le bouton affiche le texte stocké dans la variableonPressed: () { setState(() { texteBouton = 'Déjà cliqué !'; }); }: Quand on clique, on change le texte avecsetState()
String texteBouton = 'Cliquez-moi' est une variable qui stocke l'état actuel du texte du bouton. Cette variable est déclarée dans la classe _MaPageState, ce qui permet de la modifier et de mettre à jour l'interface.
La variable
texteBouton doit être déclarée dans la classe _MaPageState (pas dans la méthode build()), car elle doit persister entre les reconstructions du widget.
⚡ Rôle de setState()
setState() est une méthode essentielle dans StatefulWidget. Elle fait deux choses importantes :
- Modifie la variable d'état : Change la valeur de la variable (par exemple,
texteBouton = 'Déjà cliqué !') - Demande à Flutter de reconstruire l'interface : Indique à Flutter que quelque chose a changé et qu'il doit mettre à jour l'affichage
Si vous modifiez simplement la variable sans
setState(), la valeur changera en mémoire, mais Flutter ne saura pas qu'il doit mettre à jour l'interface. setState() dit à Flutter : "Hey, quelque chose a changé, reconstruis l'interface !"
Voici la syntaxe de setState() :
setState(() {
// Ici, vous modifiez les variables d'état
maVariable = nouvelleValeur;
});
Vous devez toujours utiliser
setState() pour modifier les variables d'état. Si vous modifiez une variable sans setState(), l'interface ne se mettra pas à jour.
🔢 Exemple : bouton modifiant un texte (compteur)
L'exemple classique d'un StatefulWidget est un compteur. Voici un exemple complet :
ElevatedButton.
import 'package:flutter/material.dart';
void main() {
runApp(const MaPage());
}
class MaPage extends StatefulWidget {
const MaPage({super.key});
@override
State<MaPage> createState() => _MaPageState();
}
class _MaPageState extends State<MaPage> {
int compteur = 0;
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: const Text('Mon Compteur'),
),
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Compteur : $compteur',
style: const TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
setState(() {
compteur = compteur + 1;
});
},
child: const Text('Incrémenter'),
),
],
),
),
),
),
);
}
}
Analysons ce code :
int compteur = 0: Une variable qui stocke le nombre (commence à 0)'Compteur : $compteur': Le texte affiche la valeur du compteur. Le$permet d'insérer la valeur de la variable dans le textecompteur = compteur + 1: Incrémente le compteur de 1 à chaque clicsetState(() { ... }): Indique à Flutter de mettre à jour l'interface après avoir modifié le compteur
En Dart, vous pouvez insérer la valeur d'une variable dans une chaîne de caractères en utilisant
$ :
'Compteur : $compteur': Affiche "Compteur : 0", puis "Compteur : 1", etc.'Le nombre est $compteur': Affiche "Le nombre est 0", puis "Le nombre est 1", etc.
'Compteur : ' + compteur.toString(), mais en plus simple.
Cette ligne prend la valeur actuelle du compteur, ajoute 1, et stocke le résultat dans la variable
compteur. Par exemple :
- Si
compteur = 0, alorscompteur = 0 + 1donnecompteur = 1 - Si
compteur = 1, alorscompteur = 1 + 1donnecompteur = 2 - Et ainsi de suite...
compteur++ qui fait exactement la même chose.
Essayez de modifier le code :
- Changez
compteur = compteur + 1encompteur = compteur + 2pour incrémenter de 2 - Ajoutez un deuxième bouton qui décrémente :
compteur = compteur - 1 - Changez le texte initial :
int compteur = 10pour commencer à 10
🚫 Un bouton désactivé
Pour désactiver un bouton (le rendre grisé et non cliquable), vous devez passer null au paramètre onPressed. Quand onPressed est null, le bouton devient automatiquement grisé.
ElevatedButton(
onPressed: null, // Bouton désactivé
child: Text('Bouton désactivé'),
)
Vous pouvez aussi désactiver un bouton conditionnellement en utilisant l'opérateur ternaire. Voici un exemple avec notre compteur : le bouton "Incrémenter" est désactivé si le compteur dépasse 10.
ElevatedButton est grisé quand il est désactivé (onPressed: null).
import 'package:flutter/material.dart';
void main() {
runApp(const MaPage());
}
class MaPage extends StatefulWidget {
const MaPage({super.key});
@override
State<MaPage> createState() => _MaPageState();
}
class _MaPageState extends State<MaPage> {
int compteur = 0;
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: const Text('Mon Compteur'),
),
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Compteur : $compteur',
style: const TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: compteur < 10 ? () {
setState(() {
compteur = compteur + 1;
});
} : null,
child: const Text('Incrémenter'),
),
],
),
),
),
),
);
}
}
Dans cet exemple, onPressed: compteur < 10 ? () { ... } : null signifie :
- Si
compteur < 10: Le bouton est actif et incrémente le compteur - Si
compteur >= 10: Le bouton est désactivé (grisé) caronPressedestnull
La syntaxe
condition ? valeurSiVrai : valeurSiFaux permet de choisir une valeur selon une condition. Ici, si compteur < 10 est vrai, on retourne la fonction, sinon on retourne null pour désactiver le bouton.
💡 Points clés à retenir
StatelessWidgetne peut pas changer après sa créationStatefulWidgetpeut changer dynamiquement grâce à un état- L'état est stocké dans des variables déclarées dans la classe
State setState()modifie l'état et demande à Flutter de mettre à jour l'interface- Vous devez toujours utiliser
setState()pour modifier les variables d'état - Un
StatefulWidgetest composé de deux classes : le widget et l'état - La méthode
build()se trouve dans la classeState - Pour désactiver un bouton, passez
nullau paramètreonPressed - Un bouton désactivé devient automatiquement grisé
- Vous pouvez utiliser l'opérateur ternaire pour activer/désactiver un bouton conditionnellement
Voici la structure complète d'un
StatefulWidget :
class MonWidget extends StatefulWidget {
const MonWidget({super.key});
@override
State<MonWidget> createState() => _MonWidgetState();
}
class _MonWidgetState extends State<MonWidget> {
// Variables d'état ici
int maVariable = 0;
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {
setState(() {
maVariable = maVariable + 1;
});
},
child: Text('$maVariable'),
);
}
}
3.2.3 – Les types de boutons courants en Flutter
Jusqu'à présent, nous avons utilisé ElevatedButton, qui est un type de bouton parmi d'autres. Flutter propose plusieurs types de boutons, chacun ayant un style et une utilisation spécifique. Dans cette section, nous découvrirons les principaux types de boutons et quand les utiliser.
🎯 Pourquoi plusieurs types de boutons
Flutter propose différents types de boutons pour répondre à différents besoins visuels et fonctionnels :
- Hiérarchie visuelle : Certains boutons sont plus importants que d'autres et doivent se démarquer
- Contexte d'utilisation : Un bouton dans une barre d'outils n'a pas besoin du même style qu'un bouton principal
- Design Material : Suivre les guidelines Material Design pour une interface cohérente
- Flexibilité : Choisir le bon bouton pour chaque situation
Material Design est le système de design de Google. Il définit des règles pour créer des interfaces cohérentes et intuitives. Les différents types de boutons suivent ces règles pour indiquer visuellement leur importance et leur fonction.
Voir la documentation Flutter Material Widgets
🔘 ElevatedButton
ElevatedButton est le bouton principal que nous avons déjà vu. Il a un effet d'élévation (ombre) et est utilisé pour les actions importantes.
ElevatedButton(
onPressed: () {},
child: Text('Action principale'),
)
Utilisez
ElevatedButton pour les actions principales de votre application : valider un formulaire, sauvegarder, confirmer une action importante, etc.
🧪 Exemple : ElevatedButton
Voici un exemple simple avec un ElevatedButton :
ElevatedButton dans une application Flutter.
import 'package:flutter/material.dart';
void main() {
runApp(const MaPage());
}
class MaPage extends StatelessWidget {
const MaPage({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: const Text('ElevatedButton'),
),
body: SafeArea(
child: Center(
child: ElevatedButton(
onPressed: () {
print('Bouton pressé !');
},
child: const Text('Valider'),
),
),
),
),
);
}
}
📝 TextButton
TextButton est un bouton minimaliste qui ressemble à du texte. Il n'a pas d'ombre ni de bordure, juste du texte avec un effet au survol.
TextButton(
onPressed: () {},
child: Text('Action secondaire'),
)
Utilisez
TextButton pour les actions secondaires ou moins importantes : annuler, ignorer, voir plus, etc. Il est moins visible que ElevatedButton, ce qui indique qu'il s'agit d'une action moins prioritaire.
🧪 Exemple : TextButton
Voici un exemple simple avec un TextButton :
TextButton dans une application Flutter.
import 'package:flutter/material.dart';
void main() {
runApp(const MaPage());
}
class MaPage extends StatelessWidget {
const MaPage({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: const Text('TextButton'),
),
body: SafeArea(
child: Center(
child: TextButton(
onPressed: () {
print('Annuler');
},
child: const Text('Annuler'),
),
),
),
),
);
}
}
🔲 OutlinedButton
OutlinedButton est un bouton avec une bordure mais sans fond coloré. Il se situe entre ElevatedButton et TextButton en termes d'importance visuelle.
OutlinedButton(
onPressed: () {},
child: Text('Action avec bordure'),
)
Utilisez
OutlinedButton pour des actions importantes mais secondaires par rapport à l'action principale. Par exemple, si vous avez un bouton "Enregistrer" (ElevatedButton), vous pourriez avoir un bouton "Annuler" (OutlinedButton).
🧪 Exemple : OutlinedButton
Voici un exemple simple avec un OutlinedButton :
OutlinedButton affiché dans une application Flutter.
import 'package:flutter/material.dart';
void main() {
runApp(const MaPage());
}
class MaPage extends StatelessWidget {
const MaPage({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: const Text('OutlinedButton'),
),
body: SafeArea(
child: Center(
child: OutlinedButton(
onPressed: () {
print('Annuler');
},
child: const Text('Annuler'),
),
),
),
),
);
}
}
🧪 Exemple : comparer les trois types de boutons
Voici un exemple qui montre les trois types de boutons côte à côte :
ElevatedButton, OutlinedButton et TextButton.
import 'package:flutter/material.dart';
void main() {
runApp(const MaPage());
}
class MaPage extends StatelessWidget {
const MaPage({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: const Text('Types de boutons'),
),
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {},
child: const Text('ElevatedButton'),
),
const SizedBox(height: 20),
OutlinedButton(
onPressed: () {},
child: const Text('OutlinedButton'),
),
const SizedBox(height: 20),
TextButton(
onPressed: () {},
child: const Text('TextButton'),
),
],
),
),
),
),
);
}
}
Dans une interface, utilisez cette hiérarchie :
ElevatedButton: Action la plus importante (1 seul par écran généralement)OutlinedButton: Actions importantes mais secondairesTextButton: Actions moins importantes ou secondaires
🎨 IconButton
IconButton est un bouton qui affiche uniquement une icône, sans texte. Il est plus compact et idéal pour les barres d'outils ou les actions rapides.
IconButton(
onPressed: () {},
icon: Icon(Icons.favorite),
)
Analysons ce code :
IconButton(: Le widget qui crée un bouton avec une icôneicon: Icon(Icons.favorite): L'icône à afficher (un cœur)onPressed: () {}: L'action à exécuter quand on clique
Utilisez
IconButton pour des actions rapides et fréquentes : favoris, partage, recherche, paramètres, etc. Il prend moins de place qu'un bouton avec texte.
🧪 Exemple : IconButton
Voici un exemple avec plusieurs IconButton :
IconButton côte à côte dans une interface Flutter.
import 'package:flutter/material.dart';
void main() {
runApp(const MaPage());
}
class MaPage extends StatelessWidget {
const MaPage({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: const Text('IconButton'),
),
body: SafeArea(
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
onPressed: () {},
icon: const Icon(Icons.favorite),
color: Colors.red,
),
IconButton(
onPressed: () {},
icon: const Icon(Icons.share),
color: Colors.blue,
),
IconButton(
onPressed: () {},
icon: const Icon(Icons.settings),
color: Colors.grey,
),
],
),
),
),
),
);
}
}
Vous pouvez personnaliser la couleur de l'icône avec le paramètre
color. Par défaut, l'icône utilise la couleur du thème de l'application.
⚡ FloatingActionButton
FloatingActionButton (souvent abrégé FAB) est un bouton circulaire flottant qui apparaît au-dessus du contenu. Il est généralement utilisé pour l'action principale d'un écran.
FloatingActionButton(
onPressed: () {},
child: Icon(Icons.add),
)
Pour utiliser un FloatingActionButton, vous devez l'ajouter dans le Scaffold :
Scaffold(
appBar: AppBar(
title: Text('Mon Application'),
),
body: Center(
child: Text('Contenu'),
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: Icon(Icons.add),
),
)
Utilisez
FloatingActionButton pour l'action principale et la plus fréquente d'un écran. Par exemple : ajouter un nouvel élément, créer un nouveau message, prendre une photo, etc. Il y a généralement un seul FAB par écran.
Par défaut, le
FloatingActionButton apparaît en bas à droite de l'écran. Vous pouvez changer sa position avec le paramètre floatingActionButtonLocation du Scaffold.
🧪 Exemple : FloatingActionButton
Voici un exemple complet avec un FloatingActionButton :
FloatingActionButton dans une application Flutter.
import 'package:flutter/material.dart';
void main() {
runApp(const MaPage());
}
class MaPage extends StatelessWidget {
const MaPage({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: const Text('FloatingActionButton'),
),
body: const Center(
child: Text(
'Appuyez sur le bouton + en bas à droite',
style: TextStyle(fontSize: 18),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
print('FAB pressé !');
},
child: const Icon(Icons.add),
),
),
);
}
}
FloatingActionButton.extended avec texte et icône dans une application Flutter.
Vous pouvez aussi utiliser
FloatingActionButton.extended pour un FAB avec texte :
FloatingActionButton.extended(
onPressed: () {},
icon: Icon(Icons.add),
label: Text('Ajouter'),
)
📱 AppBar actions
L'AppBar peut contenir des boutons dans sa zone d'actions (à droite). Ces boutons sont généralement des IconButton pour des actions rapides liées à la page.
AppBar(
title: Text('Mon Application'),
actions: [
IconButton(
onPressed: () {},
icon: Icon(Icons.search),
),
IconButton(
onPressed: () {},
icon: Icon(Icons.more_vert),
),
],
)
Analysons ce code :
actions: [ ... ]: Une liste de widgets (généralement desIconButton) à afficher à droite de l'AppBarIconButton: Chaque bouton dans la liste d'actions
Utilisez les actions de l'
AppBar pour des actions contextuelles à la page : recherche, filtres, paramètres, partage, etc. Ces actions sont accessibles depuis n'importe où sur la page.
🧪 Exemple : AppBar avec actions
Voici un exemple complet avec une AppBar contenant plusieurs actions :
AppBar avec plusieurs actions (icônes à droite).
import 'package:flutter/material.dart';
void main() {
runApp(const MaPage());
}
class MaPage extends StatelessWidget {
const MaPage({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: const Text('AppBar avec actions'),
actions: [
IconButton(
onPressed: () {
print('Recherche');
},
icon: const Icon(Icons.search),
),
IconButton(
onPressed: () {
print('Favoris');
},
icon: const Icon(Icons.favorite),
),
IconButton(
onPressed: () {
print('Plus d\'options');
},
icon: const Icon(Icons.more_vert),
),
],
),
body: const Center(
child: Text(
'Regardez les icônes dans la barre supérieure',
style: TextStyle(fontSize: 18),
),
),
),
);
}
}
Voici des icônes souvent utilisées dans les actions de l'AppBar :
Icons.search: RechercheIcons.favorite: FavorisIcons.share: PartagerIcons.settings: ParamètresIcons.more_vert: Menu avec plus d'options (trois points verticaux)Icons.notifications: Notifications
🖼️ Image cliquable avec TextButton
Vous pouvez rendre une image cliquable en l'enveloppant dans un TextButton. C'est utile pour créer des zones cliquables sur des images, comme des bannières publicitaires ou des cartes de produit.
TextButton(
onPressed: () {
print('Image cliquée !');
},
child: Image.network(
'https://example.com/image.jpg',
width: 200,
height: 200,
),
)
Analysons ce code :
TextButton(: Le bouton qui rend l'image cliquableonPressed: () { ... }: L'action à exécuter quand on clique sur l'imagechild: Image.network(...): L'image affichée dans le bouton
TextButton est idéal pour rendre une image cliquable car il n'ajoute pas de style visible (pas de bordure, pas de fond). L'image reste telle quelle, mais devient cliquable. Vous pouvez aussi utiliser ElevatedButton ou OutlinedButton si vous voulez un effet visuel au clic.
🧪 Exemple : Image cliquable
Voici un exemple complet avec une image cliquable :
TextButton.
import 'package:flutter/material.dart';
void main() {
runApp(const MaPage());
}
class MaPage extends StatelessWidget {
const MaPage({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: const Text('Image cliquable'),
),
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextButton(
onPressed: () {
print('Image cliquée !');
},
child: Image.network(
'http://mazoul.online/images/courses/flutter/chapitre_3/owl.jpg',
width: 200,
height: 200,
fit: BoxFit.cover,
),
),
const SizedBox(height: 20),
const Text(
'Cliquez sur l\'image',
style: TextStyle(fontSize: 18),
),
],
),
),
),
),
);
}
}
Dans cet exemple, l'image est cliquable grâce au TextButton. Quand vous cliquez sur l'image, le message "Image cliquée !" s'affiche dans la console.
Vous pouvez utiliser une image locale avec
Image.asset() ou une image réseau avec Image.network(). Les deux fonctionnent avec TextButton.
Pour créer une zone cliquable plus grande autour de l'image, vous pouvez envelopper le
TextButton dans un Container avec du padding, ou utiliser un InkWell (nous verrons cela plus tard).
💡 Points clés à retenir
- Flutter propose plusieurs types de boutons pour différentes situations
ElevatedButton: Action principale (avec ombre)OutlinedButton: Action importante mais secondaire (avec bordure)TextButton: Action secondaire (minimaliste, comme du texte)IconButton: Bouton avec uniquement une icône (compact)FloatingActionButton: Bouton circulaire flottant pour l'action principaleAppBar actions: Boutons dans la barre supérieure (généralement des IconButton)- Choisissez le type de bouton selon l'importance de l'action
Voici un récapitulatif rapide :
| Type | Style | Utilisation |
|---|---|---|
ElevatedButton |
Fond coloré + ombre | Action principale |
OutlinedButton |
Bordure uniquement | Action importante secondaire |
TextButton |
Texte simple | Action secondaire |
IconButton |
Icône uniquement | Action rapide, barre d'outils |
FloatingActionButton |
Cercle flottant | Action principale de l'écran |
3.2.4 – Widgets d'interaction avancés
Parfois, vous voulez rendre n'importe quel widget cliquable, pas seulement les boutons. Flutter propose deux widgets pour cela : InkWell et GestureDetector.
💧 InkWell
InkWell ajoute un effet de "splash" (ondulation) Material Design quand on clique dessus. C'est idéal pour rendre des widgets Material cliquables.
📝 Syntaxe
Voici comment utiliser InkWell :
// InkWell simple
InkWell(
onTap: () {
print('Cliqué !');
},
child: Container(
padding: const EdgeInsets.all(16),
child: const Text('Cliquez-moi'),
),
);
// InkWell avec effet personnalisé
InkWell(
onTap: () {
print('Cliqué !');
},
splashColor: Colors.blue,
highlightColor: Colors.blue[100],
borderRadius: BorderRadius.circular(10),
child: Container(
padding: const EdgeInsets.all(16),
child: const Text('Cliquez-moi'),
),
);
Analysons cette syntaxe :
onTap: Action à exécuter quand on cliquechild: Le widget à rendre cliquablesplashColor: Couleur de l'effet de splash (optionnel)highlightColor: Couleur de surbrillance au survol (optionnel)borderRadius: Coins arrondis pour l'effet (optionnel)
InkWell crée un effet visuel Material Design (ondulation) quand on clique. C'est l'effet que vous voyez sur les boutons Material. Pour que l'effet fonctionne correctement, le widget enfant doit être dans un Material ou un Ink widget.
🧪 Exemple : InkWell
Voici un exemple complet qui utilise InkWell :
InkWell pour rendre un widget cliquable.
import 'package:flutter/material.dart';
void main() {
runApp(const MaPage());
}
class MaPage extends StatelessWidget {
const MaPage({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: const HomePage(),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('InkWell'),
),
body: SafeArea(
child: Center(
child: Material(
color: Colors.blue[100],
borderRadius: BorderRadius.circular(10),
child: InkWell(
onTap: () {
print('Carte cliquée !');
},
borderRadius: BorderRadius.circular(10),
child: Container(
padding: const EdgeInsets.all(20),
child: const Text(
'Cliquez sur cette carte',
style: TextStyle(fontSize: 18),
),
),
),
),
),
),
);
}
}
Dans cet exemple :
- Un
Materialentoure l'InkWellpour que l'effet de splash fonctionne - L'
InkWellrend la carte cliquable - Quand on clique, un effet d'ondulation apparaît
👆 GestureDetector
GestureDetector détecte différents types de gestes (tap, double tap, long press, drag, etc.) sans effet visuel Material. C'est plus flexible que InkWell.
📝 Syntaxe
Voici comment utiliser GestureDetector :
// GestureDetector simple (tap)
GestureDetector(
onTap: () {
print('Tap !');
},
child: Container(
padding: const EdgeInsets.all(16),
child: const Text('Cliquez-moi'),
),
);
// GestureDetector avec plusieurs gestes
GestureDetector(
onTap: () {
print('Tap simple');
},
onDoubleTap: () {
print('Double tap');
},
onLongPress: () {
print('Appui long');
},
child: Container(
padding: const EdgeInsets.all(16),
child: const Text('Essayez différents gestes'),
),
);
Analysons cette syntaxe :
onTap: Action pour un tap simpleonDoubleTap: Action pour un double taponLongPress: Action pour un appui longchild: Le widget à rendre interactif
Un geste est une interaction de l'utilisateur avec l'écran : tap, double tap, appui long, glisser, pincer, etc.
GestureDetector peut détecter de nombreux types de gestes différents.
🧪 Exemple : GestureDetector
Voici un exemple complet qui utilise GestureDetector :
GestureDetector pour détecter des gestes.
import 'package:flutter/material.dart';
void main() {
runApp(const MaPage());
}
class MaPage extends StatelessWidget {
const MaPage({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
String message = 'Faites un geste';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('GestureDetector'),
),
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GestureDetector(
onTap: () {
setState(() {
message = 'Tap simple';
});
},
onDoubleTap: () {
setState(() {
message = 'Double tap';
});
},
onLongPress: () {
setState(() {
message = 'Appui long';
});
},
child: Container(
padding: const EdgeInsets.all(40),
decoration: BoxDecoration(
color: Colors.blue[100],
borderRadius: BorderRadius.circular(10),
),
child: const Text(
'Zone interactive',
style: TextStyle(fontSize: 18),
),
),
),
const SizedBox(height: 20),
Text(
message,
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
],
),
),
),
);
}
}
Dans cet exemple :
- Un
GestureDetectordétecte trois types de gestes : tap, double tap, et appui long - Chaque geste met à jour le message affiché
- Le widget enfant est un
Containeravec un style
🔄 Différences et cas d'utilisation
| Caractéristique | InkWell | GestureDetector |
|---|---|---|
| Effet visuel | Oui (splash Material) | Non |
| Types de gestes | Tap principalement | Tap, double tap, long press, drag, etc. |
| Requis | Doit être dans un Material | Aucun requis |
| Style | Material Design | Neutre |
Utilisez
InkWell quand vous voulez :
- Rendre un widget Material cliquable avec un effet visuel
- Créer des cartes, des listes, ou des éléments de menu cliquables
- Avoir un style cohérent avec Material Design
Utilisez
GestureDetector quand vous voulez :
- Détecter plusieurs types de gestes (double tap, appui long, drag...)
- Rendre n'importe quel widget cliquable sans effet Material
- Créer des interactions personnalisées
- Travailler avec des widgets non-Material
💡 Points clés à retenir
InkWellajoute un effet de splash Material DesignGestureDetectordétecte différents types de gestes sans effet visuel- Utilisez
InkWellpour des éléments Material avec effet visuel - Utilisez
GestureDetectorpour des gestes avancés ou des widgets non-Material InkWelldoit être dans un widgetMaterialpour fonctionner correctementGestureDetectorest plus flexible et peut détecter plus de types de gestes
Maintenant que vous connaissez les différents types de boutons, les widgets d'interaction avancés et comment gérer l'état avec StatefulWidget, mettez vos connaissances en pratique avec l'exercice ci-dessous !
🎯 Exercice pratique
Objectif : Reproduire l'interface de portfolio ci-dessous en utilisant tous les concepts appris dans ce chapitre : StatefulWidget, setState(), les différents types de boutons (ElevatedButton, OutlinedButton, TextButton, IconButton, FloatingActionButton), et les widgets AppBar avec actions.