Le widget Theme dans Flutter
Le widget Theme dans Flutter
-
Objectifs
- Connaitre la notion thme dans Flutter
- Être capable d’utilisez un thème général pour votre application.
-
Présentation
- Les thèmes sont des packages prédéfinis qui contiennent les apparences graphiques de notre site Web ou de l’écran de notre application mobile. Ils rendent l’interface utilisateur plus attrayante. Nous utilisons principalement des thèmes pour partager les couleurs et les styles de police dans toute l’application.
- Dans le développement mobile, il devient obligatoire d’ajouter le thème clair et sombre pour notre application. Aujourd’hui, la plupart des gens préfèrent la version sombre du thème à la version claire, car elle les rend plus confortables pour leurs yeux et augmente la durée de vie de la batterie.
- Dans Flutter , nous pouvons soit utiliser des widgets de thème qui contiennent les couleurs et les styles de police pour une zone spécifique de l’application, soit définir des thèmes pour l’ensemble de l’application . Les thèmes pour l’ensemble de l’application sont également des widgets de thème, qui sont créés à la racine de notre application sous le widget MaterialApp .
- La plupart des widgets visuels dans Flutter ont une propriété style dont le type varie en fonction du widget. Les exemples incluent TextStyle pour le widget Text ou ButtonStyle pour le widget Button. La spécification d’un style n’affectera que ce widget spécifique.
- Le widget Theme dans Flutter vous permet de définir un thème pour votre application qui contrôle les éléments visuels de l’interface utilisateur de votre application, tels que les couleurs, les styles de texte et les formes. Vous pouvez utiliser le widget Theme pour appliquer un thème à une sous-arborescence spécifique de l’arborescence de widgets de votre application, ou vous pouvez l’utiliser à la racine de votre application pour l’appliquer à l’ensemble de l’application.
-
Propriétés du Widget «
Theme» en flutter - Le widget «
Theme» dans Flutter est un moyen puissant de définir l’apparence d’une application en encapsulant les styles et les paramètres de thème dans une arborescence de widgets. Voici un aperçu détaillé des propriétés du widget Theme, ainsi qu’un exemple d’utilisation.: - data: Cette propriété est utilisée pour définir les données de thème pour l’arborescence des widgets. Il prend une instance de ThemeData comme valeur.
- child: Cette propriété spécifie le widget auquel le thème doit être appliqué. Tous les enfants de ce widget hériteront des styles définis dans data.
- shadowThemeOnly:Cette propriété détermine si le thème ne doit s’appliquer qu’aux éléments du thème de l’ombre, comme DropShadow, au lieu de s’appliquer à l’ensemble de l’arborescence des widgets. Cela permet de personnaliser uniquement les ombres sans affecter d’autres styles.
- key: Cette propriété est utilisée pour définir une clé unique pour le widget, ce qui est utile dans les scénarios où vous devez trouver le widget dans l’arborescence des widgets.
-
Exemple: Application de Thème Personnalisé
- Cet exemple a pour objectif de vous apprendre à définir et appliquer un thème cohérent sur l’ensemble d’une application Flutter en utilisant ThemeData.
- Créez un nouveau projet Flutter :
flutter create flutter_theme_tppuiscd flutter_theme_tp - Nettoyez le fichier main.dart : Supprimez tout le contenu du fichier lib/main.dart par défaut.
- Créez les fichiers nécessaires : Dans le dossier lib/, créez deux nouveaux fichiers :
- lib/theme.dart : Pour définir le thème.
- lib/home_page.dart : Pour construire l’interface utilisateur.
- Votre structure lib/ doit ressembler à ceci :
- Dans ce fichier, nous définissons l’objet ThemeData qui contient tous nos styles par défaut (couleurs, polices, apparence des boutons).
- Collez le code suivant dans lib/theme.dart :
- C’est ici que nous importons le thème et l’appliquons à notre application entière.
- Collez le code suivant dans lib/main.dart :
- Cette page utilise les widgets sans leur donner de styles explicites pour voir l’effet du thème global.
- Collez le code suivant dans lib/home_page.dart :
- L’AppBar aura un fond Teal et le titre en Blanc (22pt, Gras).
- Le texte principal aura une taille de 18pt et sera Noir.
- Les boutons (ElevatedButton et TextButton) seront remplis en Teal, avec du texte en Blanc, des coins légèrement arrondis et un espacement uniforme, grâce aux TextButtonThemeData et ElevatedButtonThemeData.
- Le FloatingActionButton sera BlueAccent (hérité de colorScheme.secondary).
-
Activité : 01
- Énoncé:
- Votre tâche est de modifier le fichier lib/theme.dart pour transformer radicalement l’apparence de l’application sans toucher aux fichiers main.dart ni home_page.dart.
- L’objectif est de passer d’un thème clair et professionnel (Teal) à un thème sombre et énergique (Rouge/Orange).
- Modifications Détaillées à Effectuer
- Vous devez réaliser les 5 changements suivants :
- 1. Changement de Palette (Thème Sombre)
- Action : Basculer le thème de ColorScheme.light (clair) à ColorScheme.dark (sombre).
- Impact : Le fond de la page (Scaffold) deviendra Noir ou Gris très foncé par défaut.
- Code concerné : La propriété colorScheme.
- 2. Couleurs Principales
- Action : Changer la primaryColor pour Colors.red.
- Impact : L’AppBar et le fond des boutons deviendront Rouges.
- 3. Couleur Secondaire
- 4. Style de Texte Principal
- Dans un thème sombre, le texte de corps doit être clair pour une bonne lisibilité.
- Action : Modifier le textTheme.bodyLarge pour que la couleur du texte soit Colors.white70 (Blanc légèrement transparent) et augmenter la taille de la police à 20.
- Impact : Le texte principal sur la HomePage sera plus grand et clair sur le fond sombre.
- 5. Modification du Style du TextButton
- Le style actuel du TextButton est un bouton rempli (Teal sur Teal), ce qui n’est pas le comportement typique d’un TextButton. Modifiez-le pour qu’il ressemble à un lien thémé.
- Action : Supprimer la couleur de fond (backgroundColor) dans le textButtonTheme.style.
- Action : Changer la couleur du texte (foregroundColor) pour utiliser la couleur secondaire du thème (Colors.orangeAccent).
- Impact : Le TextButton deviendra un lien de couleur Orange Vif sans remplissage.
- Attendu (Résultat Final)
- Après la modification du seul fichier lib/theme.dart, l’application doit présenter les caractéristiques suivantes :
-
Activité : 02
- Réaliser un écran Flutter en utilisant un thème global afin d’unifier les couleurs, les textes et les boutons de l’application.
- Question 1 – Création et importation du thème
- Créez un fichier nommé mes_themes.dart.
- Dans ce fichier, définissez un objet ThemeData contenant :une primaryColor, un ColorScheme.light, un TextTheme avec bodyLarge et titleLarge
- un thème global pour :TextButton, ElevatedButton
- Importez ce fichier dans main.dart.
- Question 2 – Application du thème à l’application
- Créez un widget MyApp.
- Appliquez le thème créé à l’application via la propriété theme de MaterialApp.
- Désactivez le bandeau debug.
- Expliquez le rôle de la propriété theme.
- Question 3 – Structure générale de l’écran
- Utilisez un Scaffold comme widget principal.
- Ajoutez : un AppBar, un SafeArea, un SingleChildScrollView
- Justifiez l’utilisation de SingleChildScrollView.
- Question 4 – AppBar et titre
- Ajoutez un AppBar avec le titre : »Exemple de widget de thème », Centre le titre horizontalement.
- Expliquez comment le thème influence l’apparence de l’AppBar.
- Question 5 – Texte utilisant le TextTheme
- Ajoutez un widget Text affichant : « Ce texte utilisera le style bodyLarge du thème. »
- Appliquez le style via : Theme.of(context).textTheme.bodyLarge
- Expliquez l’intérêt d’utiliser TextTheme au lieu d’un style local.
- Question 6 – Container avec couleur primaire
- Créez un Container avec : un padding, des coins arrondis, une ombre (BoxShadow)
- Appliquez la couleur primaire du thème comme couleur de fond.
- Utilisez titleLarge pour le texte.
- Question 7 – Bouton TextButton thématisé
- Ajoutez un TextButton avec le texte « Cliquez-moi ».
- N’ajoutez aucun style directement dans le widget.
- Expliquez comment le style du bouton est appliqué automatiquement via le thème.
- Question 8 – Bouton ElevatedButton thématisé
- Ajoutez un ElevatedButton avec le texte « Bouton Elevé ».
- Vérifiez que le style (couleurs, forme, padding) provient du thème global.
- Comparez TextButton et ElevatedButton.
- Question 9 – Card avec image et texte
- Créez une Card avec :des coins arrondis,une élévation
- Insérez une image à l’aide de Image.network.
- Superposez un texte sur l’image en utilisant Stack et Positioned.
- Appliquez un style provenant du thème.
- Question 10 – ListTile thématisé
- Ajoutez deux ListTile contenant :une icône, un titre,un sous-titre
- Appliquez les styles de texte depuis le thème.
- Ajoutez une couleur de fond cohérente avec le thème.
- Question 11 – Question de réflexion
- Répondez brièvement :
- Pourquoi utiliser Theme.of(context) ?
- Quels sont les avantages d’un thème global ?
- Que se passe-t-il si on change la couleur primaire dans le thème ?

-
Commençons par choisir les couleurs de notre application.
-
Personnaliser votre thème
- Nous sommes dans une application de type Material, cela implique donc de définir au moins deux couleurs :
PrimaryColoretSecondaryColor. - La couleur primaire sera celle de votre barre de navigation.
- La couleur secondaire sera la couleur d’accent, par exemple, la couleur de votre bouton flottant (FloatingActionButton).
- Une fois ces deux couleurs choisies, il nous faut une déclinaison
lightetdarkde ces couleurs. Vous pouvez les définir vous même, ou bien utiliser cet outil qui les génèrera pour vous. -
appliquer votre thème
- Maintenant que votre thème est prêt, il est temps de le porter fièrement. Enveloppez votre application dans le widget MaterialApp et transmettez votre objet ThemeData au themeparamètre, comme suit :
-
Application
- Objectif de l’Exercice
- Créer une application Flutter qui permet à l’utilisateur de choisir entre différents thèmes (par exemple, un thème « Fête », un thème « Nature », et un thème « Minimaliste ») à l’aide d’une interface utilisateur simple.
- Étapes à Suivre
- Créer une Application Flutter de Base
- Définir Plusieurs Thèmes
- Implémenter un Menu pour Changer de Thème
- Appliquer le Thème Choisi à l’Application
- Étape 1 : Créer une Application Flutter de Base
- Commencez par créer une nouvelle application Flutter :
- Étape 2 : Définir Plusieurs Thèmes
- Modifiez votre fichier lib/main.dart pour y inclure plusieurs thèmes :
- Étape 3 : Implémenter un Menu pour Changer de Thème
- Créez un écran d’accueil qui permet à l’utilisateur de sélectionner un thème :
- Étape 4 : Appliquer le Thème Choisi à l’Application
- Dans cet exemple, chaque bouton appelle _changeTheme avec un thème spécifique lorsque l’utilisateur le sélectionne.
- Résultat Attendu
- Lorsque vous exécutez cette application, vous devriez voir un écran avec un titre et trois boutons. En cliquant sur chaque bouton, le thème de l’application doit changer immédiatement pour refléter le thème sélectionné.
- Améliorations Possibles
- Ajouter des Images de Fond : Personnalisez chaque thème avec une image de fond différente.
- Animations de Transition : Ajoutez des animations lors du changement de thème pour une expérience utilisateur plus fluide.
- Stockage des Préférences de Thème : Utilisez SharedPreferences pour mémoriser le choix de thème de l’utilisateur entre les sessions.
lib/
├── main.dart
├── theme.dart
└── home_page.dart
Fchier :theme.dart
// theme.dart
// Ce fichier définit le thème global de l'application Flutter.
// Il permet d'unifier les couleurs, les styles de texte et l'apparence des boutons.
import 'package:flutter/material.dart';
// Déclaration du thème principal de l'application
final ThemeData themeData = ThemeData(
// Couleur principale utilisée dans l'application
// (AppBar, boutons, indicateurs, etc.)
primaryColor: Colors.teal,
// Schéma de couleurs clair (Light Theme)
// La couleur secondaire est utilisée pour les accents
colorScheme: const ColorScheme.light(
secondary: Colors.blueAccent,
),
// Définition des styles de texte globaux
textTheme: const TextTheme(
// Style utilisé pour le texte principal (paragraphes)
bodyLarge: TextStyle(
color: Colors.black,
fontSize: 18,
),
// Style utilisé pour les titres importants
titleLarge: TextStyle(
color: Colors.white,
fontSize: 22,
fontWeight: FontWeight.bold,
),
),
// Thème global des boutons TextButton
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
// Couleur du texte du bouton
foregroundColor: Colors.white,
// Couleur de fond du bouton
backgroundColor: Colors.teal,
// Espacement interne du bouton
padding: const EdgeInsets.symmetric(
vertical: 12,
horizontal: 24,
),
// Forme du bouton avec coins arrondis
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
),
// Thème global des boutons ElevatedButton
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
// Couleur du texte du bouton
foregroundColor: Colors.white,
// Couleur de fond du bouton
backgroundColor: Colors.teal,
// Espacement interne du bouton
padding: const EdgeInsets.symmetric(
vertical: 12,
horizontal: 24,
),
// Forme du bouton avec coins arrondis
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
),
);
Fchier :main.dart
// main.dart
import 'package:flutter/material.dart';
import 'theme.dart'; // Importez votre fichier de thème
import 'home_page.dart'; // Importez la page d'accueil
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Application à Thème Flutter',
// APPLIQUER LE THÈME ICI
theme: themeData,
home: const HomePage(),
);
}
}
Fchier :home_page.dart
// home_page.dart
import 'package:flutter/material.dart';
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
// Récupérer le thème actuel pour montrer l'utilisation des styles
final textTheme = Theme.of(context).textTheme;
return Scaffold(
// L'AppBar utilisera primaryColor et titleLarge de votre thème
appBar: AppBar(
backgroundColor: Theme.of(context).primaryColor, // Utilisation de primaryColor (Teal)
title: Text(
'Démonstration du Thème',
style: textTheme.titleLarge, // Utilisation du style titleLarge (Blanc, Gras, 22)
),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Utilisation de bodyLarge pour le texte
Text(
'Bienvenue dans votre application stylisée !',
textAlign: TextAlign.center,
style: textTheme.bodyLarge, // Utilisation du style bodyLarge (Noir, 18)
),
const SizedBox(height: 30),
// ElevatedButton stylisé par elevatedButtonTheme
ElevatedButton(
onPressed: () {
// Le bouton utilisera le style défini dans elevatedButtonTheme (Teal, coins arrondis, padding)
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Bouton Élevé Cliqué', style: TextStyle(color: textTheme.titleLarge?.color)),
backgroundColor: Theme.of(context).colorScheme.secondary, // Utilisation de secondary (BlueAccent)
),
);
},
child: const Text('Bouton Élevé'),
),
const SizedBox(height: 20),
// TextButton stylisé par textButtonTheme
TextButton(
onPressed: () {
// Le bouton utilisera le style défini dans textButtonTheme (Teal, coins arrondis, padding)
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('TextButton', style: textTheme.titleLarge?.copyWith(color: Colors.black)),
content: Text('Le style du TextButton est appliqué.', style: textTheme.bodyLarge),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Fermer'), // Ce TextButton utilisera aussi le thème
),
],
);
},
);
},
child: const Text('Bouton Texte'),
),
],
),
),
),
// Le FloatingActionButton utilisera par défaut colorScheme.secondary si non spécifié,
// ce qui dans votre cas est Colors.blueAccent
floatingActionButton: FloatingActionButton(
onPressed: () {},
backgroundColor: Theme.of(context).colorScheme.secondary, // BlueAccent
child: const Icon(Icons.palette, color: Colors.white),
),
);
}
}
Résultat attendu :
Remarque : Le widget «
Theme» ne peut être utilisé qu’une seule fois dans une arborescence de widgets. Si vous souhaitez appliquer le même thème à plusieurs widgets, vous devez envelopper chaque arborescence de widgets avec le widget Theme et transmettre la même instance ThemeData à chacun.
-
Action : Changer la couleur secondaire (secondary) de Colors.blueAccent à Colors.orangeAccent.
Impact : Le FloatingActionButton (FAB) deviendra Orange Vif.
| Élément | Propriété du Thème Utilisée | Nouvelle Valeur Attendue |
|---|---|---|
| Fond d’écran | ColorScheme.dark |
Noir / Gris foncé |
| Fond d’AppBar & Boutons | primaryColor |
Rouge |
| FAB | colorScheme.secondary |
Orange Vif |
| Texte principal | textTheme.bodyLarge |
Blanc cassé (white70), 20pt |
| TextButton | textButtonTheme |
Texte Orange Vif, Pas de fond |
Solution
import 'package:flutter/material.dart';
import 'mes_themes.dart'; // Assurez-vous d'importer votre thème
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: themeData, // Applique le thème défini dans theme.dart
home: Scaffold(
backgroundColor: Colors.teal[50], // Fond doux pour l'écran
appBar: AppBar(
title: Text('Exemple de widget de thème'),
centerTitle: true, // Centrer le titre
),
body: SafeArea(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.center, // Centrer horizontalement
children: [
// Premier texte avec style du thème
Text(
"Ce texte utilisera le style bodyLarge du thème.",
style: Theme.of(context).textTheme.bodyLarge,
textAlign: TextAlign.center,
),
SizedBox(height: 30), // Espacement
// Container avec la couleur primaire
Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
borderRadius: BorderRadius.circular(12),
boxShadow: [
// Ombre pour effet 3D
BoxShadow(
color: Colors.black26,
blurRadius: 6,
offset: Offset(0, 4),
),
],
),
child: Text(
"Ce conteneur utilise la couleur primaire.",
style: Theme.of(context).textTheme.titleLarge,
textAlign: TextAlign.center,
),
),
SizedBox(height: 30),
// TextButton avec le style du thème
// le style du TextButton est automatiquement appliqué grâce à la configuration dans votre fichier mes_themes.dart, où vous avez défini un style global pour tous les TextButton via le widget TextButtonThemeData.
TextButton(
onPressed: () {},
// Afficher le SnackBar
child: const Text("Cliquez-moi"),
),
const SizedBox(height: 30),
// ElevatedButton avec le style du thème
ElevatedButton(
onPressed: () {},
child: const Text("Bouton Elevé"),
),
SizedBox(height: 30),
// Card avec image et texte
Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
elevation: 5,
child: ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Stack(
children: [
Image.network(
'https://via.placeholder.com/150',
width: 150,
height: 150,
fit: BoxFit.cover,
),
Positioned(
bottom: 16,
left: 16,
child: Text(
"Image de la Card",
style: Theme.of(context).textTheme.titleLarge,
),
),
],
),
),
),
SizedBox(height: 30),
// ListTile avec thème
ListTile(
leading: Icon(Icons.home, color: Colors.teal),
title: Text("Item 1",
style: Theme.of(context).textTheme.bodyLarge),
subtitle: Text("Description de l'item 1",
style: Theme.of(context).textTheme.bodyLarge),
tileColor: Colors.teal[100],
),
SizedBox(height: 10),
ListTile(
leading: Icon(Icons.business, color: Colors.teal),
title: Text("Item 2",
style: Theme.of(context).textTheme.bodyLarge),
subtitle: Text("Description de l'item 2",
style: Theme.of(context).textTheme.bodyLarge),
tileColor: Colors.teal[100],
),
],
),
),
),
),
),
);
}
}
final ThemeData myAwesomeTheme = ThemeData(
primaryColor: Colors.lime,
accentColor: Colors.orange,
fontFamily: 'Roboto',
);
MaterialApp(
theme: myAwesomeTheme,
home: MyHomePage(),
);
