Les Fondamentaux des Widgets Flutter et la Création d'Interfaces Utilisateur Simples
Bienvenue dans ce module de notre cours "Maîtrisez Flutter : Créez des Applications Mobiles Multiplateformes Performantes". Aujourd'hui, nous plongeons au cœur de Flutter : les Widgets. Si vous avez déjà interagi avec une application Flutter, vous avez en fait interagi avec une composition complexe de widgets. Comprendre les widgets est la clé de voûte pour construire des interfaces utilisateur élégantes, performantes et réactives.
1. Introduction : Qu'est-ce qu'un Widget en Flutter ?
Dans Flutter, tout est un widget. C'est la philosophie centrale du framework. Un widget est la brique fondamentale de l'interface utilisateur. Il peut s'agir de quelque chose de simple comme un bouton ou un texte, ou de quelque chose de plus complexe comme une mise en page entière d'écran, une animation ou même un événement tactile.
Pensez aux widgets comme à des descriptions immuables d'une partie de votre interface utilisateur à un moment donné. Lorsque l'état de votre application change, Flutter reconstruit l'arbre de widgets, et le framework détermine les modifications minimales nécessaires pour mettre à jour l'interface.
Objectifs de la Leçon :
À la fin de cette leçon, vous serez capable de :
- Comprendre la philosophie "tout est un widget" de Flutter.
- Distinguer et utiliser les
StatelessWidgetetStatefulWidget. - Manipuler les widgets les plus courants pour construire des interfaces simples.
- Appréhender le concept d'arbre de widgets.
- Créer votre première interface utilisateur Flutter fonctionnelle.
2. Tout est un Widget : La Philosophie Fondamentale
Contrairement à d'autres frameworks où vous pourriez avoir des vues, des contrôleurs, des layouts et des composants séparés, Flutter unifie tout sous le concept de widget.
- Texte est un widget (
Text). - Image est un widget (
Image). - Bouton est un widget (
ElevatedButton,TextButton). - Une barre d'applications est un widget (
AppBar). - Même la disposition des éléments (comme un alignement vertical ou horizontal) est gérée par des widgets (
Column,Row). - Et la page entière de votre application est également un widget (
Scaffold).
Cette approche basée sur la composition plutôt que sur l'héritage rend la construction d'interfaces très flexible et réutilisable. Vous construisez des interfaces complexes en imbriquant et en combinant des widgets plus petits.
3. Les Deux Types Fondamentaux de Widgets
Flutter propose deux catégories principales de widgets, basées sur la gestion de leur état interne :
3.1. StatelessWidget (Widget Sans État)
Un StatelessWidget est un widget qui n'a pas d'état modifiable. Cela signifie qu'une fois le widget créé, ses propriétés restent les mêmes. Il est immuable.
- Quand l'utiliser ? Pour les parties de l'interface qui ne changent pas après leur création initiale. Par exemple, un widget
Textaffichant un titre fixe, uneIcon, ou une image statique. - Méthode clé :
build(BuildContext context). Cette méthode est appelée une seule fois par Flutter pour décrire la partie de l'interface utilisateur représentée par ce widget. Elle doit retourner un arbre de widgets.
import 'package:flutter/material.dart';
// Un StatelessWidget simple pour afficher un titre de bienvenue.
class BienvenueWidget extends StatelessWidget {
// Le constructeur peut prendre des paramètres pour personnaliser le widget.
final String titre;
const BienvenueWidget({Key? key, required this.titre}) : super(key: key);
@override
Widget build(BuildContext context) {
// La méthode build décrit l'interface utilisateur de ce widget.
return Center(
child: Text(
titre,
style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
);
}
}
Dans l'exemple ci-dessus, BienvenueWidget est un StatelessWidget car son titre ne changera pas après sa construction.
3.2. StatefulWidget (Widget Avec État)
Un StatefulWidget est un widget qui peut changer d'état au cours de sa durée de vie. Cela signifie que l'interface utilisateur qu'il représente peut se modifier dynamiquement en réponse à des interactions utilisateur, des données reçues, ou des événements système.
Un StatefulWidget est en réalité composé de deux classes :
- La classe
StatefulWidgetelle-même : Elle est immuable et ne contient pas l'état modifiable. Son rôle principal est de créer un objetState. - La classe
State<T>(oùTest la classe duStatefulWidgetassocié) : C'est ici que l'état mutable est géré et que la méthodebuildest implémentée.
- Quand l'utiliser ? Pour les parties de l'interface qui nécessitent de se mettre à jour visuellement. Par exemple, un compteur qui s'incrémente, une case à cocher, un champ de saisie de texte.
- Méthode clé :
createState(). Cette méthode est appelée par Flutter pour créer l'objetStateassocié auStatefulWidget. - Méthode pour mettre à jour l'état :
setState(() {}). C'est la méthode que vous appelez dans votre objetStatepour notifier à Flutter qu'une donnée a changé et que le widget doit être reconstruit pour refléter ce changement.
import 'package:flutter/material.dart';
// 1. La classe StatefulWidget (immuable)
class CompteurWidget extends StatefulWidget {
const CompteurWidget({Key? key}) : super(key: key);
@override
// Crée l'objet State associé
State<CompteurWidget> createState() => _CompteurWidgetState();
}
// 2. La classe State (mutable)
class _CompteurWidgetState extends State<CompteurWidget> {
int _compteur = 0; // L'état interne modifiable
void _incrementerCompteur() {
// Appelle setState pour notifier à Flutter que l'état a changé
setState(() {
_compteur++; // Modifie l'état
});
}
@override
Widget build(BuildContext context) {
// La méthode build est appelée chaque fois que l'état change
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Valeur du compteur : $_compteur',
style: const TextStyle(fontSize: 22),
),
ElevatedButton(
onPressed: _incrementerCompteur, // Associe le bouton à la fonction d'incrémentation
child: const Text('Incrémenter'),
),
],
);
}
}
Dans cet exemple, le _compteur est l'état mutable. Lorsque le bouton est pressé, _incrementerCompteur est appelée, qui à son tour appelle setState. setState signale à Flutter de reconstruire le widget, affichant la nouvelle valeur du compteur.
4. Widgets Communs pour la Création d'Interfaces Simples
Flutter offre une pléthore de widgets prêts à l'emploi. Voici quelques-uns des plus fondamentaux que vous utiliserez constamment :
4.1. Widgets de Disposition (Layout Widgets)
Ces widgets sont essentiels pour organiser et positionner d'autres widgets sur l'écran.
Container: Un widget polyvalent qui peut contenir un seul enfant. Il permet d'ajouter du remplissage (padding), des marges (margin), des couleurs de fond (color), des décorations (decoration), etc.Row: Dispose ses enfants en ligne (horizontalement).mainAxisAlignment: Aligne les enfants le long de l'axe principal (horizontal pourRow).crossAxisAlignment: Aligne les enfants le long de l'axe transversal (vertical pourRow).
Column: Dispose ses enfants en colonne (verticalement).mainAxisAlignment: Aligne les enfants le long de l'axe principal (vertical pourColumn).crossAxisAlignment: Aligne les enfants le long de l'axe transversal (horizontal pourColumn).
Scaffold: Le widget le plus couramment utilisé comme structure de base pour une page d'application. Il fournit une structure visuelle de Material Design, incluant :appBar: Une barre en haut de l'écran.body: Le contenu principal de l'écran.floatingActionButton: Un bouton d'action flottant.drawer,bottomNavigationBar, etc.
4.2. Widgets d'Affichage de Texte et Images
Text: Affiche une chaîne de caractères. Peut être stylisé avec la propriétéstyle(couleur, taille, police, gras, etc.).Image: Affiche une image. Peut charger des images depuis les ressources de l'application (AssetImage), un réseau (NetworkImage), un fichier, ou la mémoire.
4.3. Widgets d'Interaction (Boutons)
Ces widgets permettent à l'utilisateur d'interagir avec l'application.
ElevatedButton: Un bouton avec une ombre et une élévation.TextButton: Un bouton de texte simple (sans élévation).IconButton: Un bouton sous forme d'icône.- Tous les boutons ont une propriété
onPressedqui prend une fonction à exécuter lorsque le bouton est pressé. SionPressedestnull, le bouton est désactivé.
5. Création d'une Interface Utilisateur Simple : Exemple Pratique
Nous allons créer une application simple qui affiche un écran de bienvenue avec un titre, une image et un bouton. Nous utiliserons principalement des StatelessWidget pour cette première version.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp()); // Démarre l'application en lui passant le widget racine
}
// Widget racine de l'application, un StatelessWidget car l'application elle-même n'a pas d'état global ici.
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
// MaterialApp est un widget qui fournit la structure de base Material Design
title: 'Mon Application Flutter Simple',
theme: ThemeData(
primarySwatch: Colors.blue, // Définit la couleur primaire de l'application
),
home: const PageAccueil(), // Définit la page d'accueil de l'application
);
}
}
// Widget représentant la page d'accueil, un StatelessWidget car son contenu est statique pour cet exemple.
class PageAccueil extends StatelessWidget {
const PageAccueil({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
// Scaffold fournit la structure visuelle de base d'une page Material Design
appBar: AppBar(
// Barre d'application en haut de l'écran
title: const Text('Bienvenue dans Flutter !'),
),
body: Center(
// Center centre son enfant au milieu de l'écran
child: Column(
// Column dispose ses enfants verticalement
mainAxisAlignment: MainAxisAlignment.center, // Centre les enfants verticalement
crossAxisAlignment: CrossAxisAlignment.center, // Centre les enfants horizontalement
children: <Widget>[
// Une liste de widgets enfants
const Text(
'Découvrez les Widgets Flutter !',
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: Colors.deepPurple,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 30), // Ajoute un espacement vertical
// Utilisation d'un Asset Image (assurez-vous d'avoir une image 'flutter_logo.png' dans assets/images/
// et de la déclarer dans pubspec.yaml)
// Pour cet exemple, utilisons une icône pour simplifier :
const Icon(
Icons.flutter_dash,
size: 150,
color: Colors.blue,
),
const SizedBox(height: 30), // Autre espacement
ElevatedButton.icon(
onPressed: () {
// Fonction exécutée lorsque le bouton est pressé
// Pour l'instant, juste un message dans la console
print('Bouton "Commencer" pressé !');
// Plus tard, vous pourriez naviguer vers une autre page ici
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Vous avez cliqué sur Commencer !')),
);
},
icon: const Icon(Icons.arrow_forward), // Icône du bouton
label: const Text(
'Commencer l\'aventure', // Texte du bouton
style: TextStyle(fontSize: 18),
),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 15),
),
),
],
),
),
);
}
}
Explication du Code :
main()etrunApp(): Le point d'entrée de toute application Flutter est la fonctionmain(). Elle appellerunApp()qui prend un widget (souvent unStatelessWidgetouStatefulWidgetprincipal) comme racine de l'arbre de widgets.MyApp: C'est notre widget racine. Nous utilisonsMaterialAppqui fournit des fonctionnalités essentielles pour une application Material Design, comme la gestion des routes, des thèmes, etc.PageAccueil: C'est la structure de notre page principale.Scaffold: Il s'agit du widget qui fournit la structure de base visuelle de l'application :AppBar(la barre du haut),body(le contenu principal de l'écran).AppBar: Contient le titre de notre application.body: Contient l'ensemble de notre interface utilisateur. Nous l'avons enveloppé dans unCenterpour que son contenu soit centré sur l'écran.Column: C'est un widget de disposition qui empile ses enfants verticalement. Nous avons utilisémainAxisAlignmentetcrossAxisAlignmentpour centrer le contenu à l'intérieur de laColumn.Text: Affiche le titre de notre page. Nous avons appliqué un style personnalisé (TextStyle).SizedBox: Un widget simple pour créer de l'espace vide entre d'autres widgets, très utile pour le positionnement.Icon: Affiche une icône Material Design.ElevatedButton.icon: Un bouton avec un texte et une icône. La propriétéonPressedprend une fonction anonyme qui est exécutée lorsque le bouton est cliqué. Ici, nous affichons un message dans la console (print) et unSnackBartemporaire.
6. Le Concept d'Arbre de Widgets (Widget Tree)
Lorsque vous construisez une interface Flutter, vous créez en réalité un arbre de widgets. Chaque widget peut contenir d'autres widgets, qui à leur tour peuvent en contenir d'autres, et ainsi de suite.
Considérez l'exemple précédent :
MyApp
└── MaterialApp
└── PageAccueil
└── Scaffold
├── AppBar
│ └── Text
└── body
└── Center
└── Column
├── Text
├── SizedBox
├── Icon
├── SizedBox
└── ElevatedButton.icon
├── Icon
└── Text
Cet arbre de widgets est la représentation de la structure de votre interface utilisateur. Flutter utilise cet arbre pour déterminer ce qui doit être affiché à l'écran, comment les événements (comme les clics) sont propagés, et comment optimiser le rendu. Chaque nœud de cet arbre est une instance de widget.
7. Conclusion et Prochaines Étapes
Félicitations ! Vous avez fait vos premiers pas dans le monde des widgets Flutter. Vous avez appris que :
- Tout est un widget en Flutter, favorisant une approche de composition.
- Les
StatelessWidgetsont pour les éléments qui ne changent pas. - Les
StatefulWidgetsont pour les éléments dont l'état peut changer, et nécessitentsetStatepour déclencher une reconstruction de l'interface. - Des widgets de base comme
Scaffold,AppBar,Text,Image,Row,Column, et les différents boutons sont vos outils pour bâtir des interfaces. - La composition de ces widgets forme un "arbre de widgets", essentiel pour la compréhension du rendu Flutter.
Cette compréhension des fondamentaux est cruciale. Dans les prochaines leçons, nous explorerons des widgets plus avancés, la gestion d'état plus complexe, la navigation entre les pages, et bien d'autres concepts essentiels pour créer des applications Flutter performantes et complètes.
Continuez à expérimenter avec ces widgets de base, changez leurs propriétés, et observez l'impact sur l'interface. La meilleure façon d'apprendre est de pratiquer !