Aller au contenu principal

Style et Thématisation

Le système de thème Flutter

Flutter utilise un système de thème centralisé pour définir l'apparence globale de l'application. Cela permet de maintenir une cohérence visuelle et de faciliter les changements de design.

ThemeData : Configuration du thème

Définir un thème global

Le thème global est défini dans le widget MaterialApp :

final colorScheme = ColorScheme.fromSeed(
seedColor: Colors.blue,
brightness: Brightness.light,
);

MaterialApp(
title: 'Mon Application',
theme: ThemeData(
// Couleurs principales
primaryColor: Colors.blue,
colorScheme: colorScheme,
brightness: Brightness.light,

// AppBar - Utilise les couleurs du colorScheme
appBarTheme: AppBarTheme(
backgroundColor: colorScheme.primary,
foregroundColor: colorScheme.onPrimary,
elevation: 4,
centerTitle: true,
),

// Boutons élevés
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
),

// Boutons texte
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
foregroundColor: Colors.blue,
),
),

// Champs de texte
inputDecorationTheme: const InputDecorationTheme(
border: OutlineInputBorder(),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blue, width: 2),
),
labelStyle: TextStyle(color: Colors.grey),
),

// Cartes
cardTheme: CardTheme(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),

// Typographie
textTheme: const TextTheme(
displayLarge: TextStyle(fontSize: 32, fontWeight: FontWeight.bold),
displayMedium: TextStyle(fontSize: 28, fontWeight: FontWeight.bold),
bodyLarge: TextStyle(fontSize: 16),
bodyMedium: TextStyle(fontSize: 14),
),
),
darkTheme: ThemeData(
brightness: Brightness.dark,
primarySwatch: Colors.indigo,
),
themeMode: ThemeMode.system, // Suit le thème du système
home: const MyHomePage(),
)

Voir aussi

Voir l'exemple ci-dessus pour comprendre comment :

  • Utiliser colorScheme.primary et colorScheme.onPrimary pour synchroniser automatiquement les couleurs de l'AppBar avec le thème général
  • Personnaliser les boutons, champs de texte, cartes et typographie dans un seul bloc ThemeData

Utilisation du thème dans les widgets

Accéder au thème

// Dans un widget

Widget build(BuildContext context) {
final theme = Theme.of(context);

return Container(
color: theme.colorScheme.primary,
child: Text(
'Texte stylé',
style: theme.textTheme.displayLarge,
),
);
}

Surcharger le thème localement

Theme(
data: Theme.of(context).copyWith(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.red),
),
child: ElevatedButton(
onPressed: () {},
child: const Text('Bouton rouge'),
),
)

TextStyle : Personnalisation du texte

Styles de texte basiques

Text(
'Texte stylé',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
fontStyle: FontStyle.italic,
color: Colors.blue,
letterSpacing: 2,
wordSpacing: 5,
decoration: TextDecoration.underline,
decorationColor: Colors.red,
decorationStyle: TextDecorationStyle.dotted,
),
)

Combinaison de styles

Text(
'Texte avec style combiné',
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
color: Colors.blue,
fontWeight: FontWeight.bold,
),
)

RichText : Texte avec plusieurs styles

RichText(
text: TextSpan(
style: DefaultTextStyle.of(context).style,
children: [
const TextSpan(text: 'Texte normal '),
TextSpan(
text: 'texte en gras',
style: const TextStyle(fontWeight: FontWeight.bold),
),
const TextSpan(text: ' et '),
TextSpan(
text: 'texte en couleur',
style: const TextStyle(color: Colors.red),
),
],
),
)

Colors : Gestion des couleurs

Couleurs prédéfinies

Container(color: Colors.blue)
Container(color: Colors.red[500])
Container(color: Colors.amber.shade300)

Couleurs personnalisées

// Couleur ARGB
Container(color: const Color(0xFF2196F3))

// Couleur RGB
Container(color: const Color.fromARGB(255, 33, 150, 243))

// Couleur RGBA
Container(color: const Color.fromRGBO(33, 150, 243, 0.5))

MaterialColor : Palette de couleurs

MaterialColor createMaterialColor(Color color) {
List strengths = <double>[.05];
Map<int, Color> swatch = {};
final int r = color.red, g = color.green, b = color.blue;

for (int i = 1; i < 10; i++) {
strengths.add(0.1 * i);
}

for (var strength in strengths) {
final double ds = 0.5 - strength;
swatch[(strength * 1000).round()] = Color.fromRGBO(
r + ((ds < 0 ? r : (255 - r)) * ds).round(),
g + ((ds < 0 ? g : (255 - g)) * ds).round(),
b + ((ds < 0 ? b : (255 - b)) * ds).round(),
1,
);
}

return MaterialColor(color.value, swatch);
}

BoxDecoration : Décoration de Container

Décoration complète

Container(
decoration: BoxDecoration(
// Couleur de fond
color: Colors.blue,

// Dégradé
gradient: const LinearGradient(
colors: [Colors.blue, Colors.purple],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),

// Bordure
border: Border.all(
color: Colors.black,
width: 2,
),

// Coins arrondis
borderRadius: BorderRadius.circular(12),

// Ombre
boxShadow: const [
BoxShadow(
color: Colors.grey,
blurRadius: 10,
offset: Offset(0, 5),
),
],

// Image de fond
image: const DecorationImage(
image: NetworkImage('https://example.com/image.jpg'),
fit: BoxFit.cover,
),
),
child: const Text('Contenu'),
)

Formes personnalisées

// Cercle
Container(
decoration: const BoxDecoration(
color: Colors.blue,
shape: BoxShape.circle,
),
)

// Bordure personnalisée
Container(
decoration: BoxDecoration(
border: Border(
top: BorderSide(color: Colors.red, width: 3),
bottom: BorderSide(color: Colors.blue, width: 3),
),
),
)

Polices personnalisées

Ajout dans pubspec.yaml

flutter:
fonts:
- family: Roboto
fonts:
- asset: fonts/Roboto-Regular.ttf
- asset: fonts/Roboto-Bold.ttf
weight: 700

Utilisation

Text(
'Texte avec police personnalisée',
style: const TextStyle(
fontFamily: 'Roboto',
fontSize: 20,
),
)

Bonnes pratiques

  1. Cohérence : Utiliser le thème global plutôt que des valeurs en dur
  2. Accessibilité : Assurer un bon contraste entre texte et fond
  3. Responsive : Adapter les tailles de police selon la taille d'écran
  4. Mode sombre : Toujours prévoir un thème sombre
  5. ColorScheme : Préférer ColorScheme à des couleurs isolées pour une meilleure cohérence

Build Release et Versioning

Après avoir conçu l'interface visuelle, il est temps de préparer l'application pour la distribution.

Préparation du build release

  • Mode debug vs mode release (différences de performances et taille)
  • flutter build apk --release pour générer un APK optimisé
  • flutter build ios --release pour générer un IPA optimisé
  • Taille attendue : 50-150 MB pour les assets de base

Optimisation de la taille

  • Utiliser flutter build apk --target-platform android-arm64 pour architecture spécifique
  • Compresser les images (format WebP plutôt que PNG)
  • Supprimer les dépendances inutiles
  • Utiliser flutter pub global run app_size_analyzer pour analyser la taille

Versioning sémantique et gestion des builds

  • Mettre à jour version dans pubspec.yaml avant chaque release
  • Format : major.minor.patch+build
  • Maintenir un CHANGELOG pour tracker les changements
  • Exemple : de 1.0.0+1 à 1.1.0+2 pour une release mineure

Préparation de la métadonnée

  • Préparer les icônes de l'app aux bonnes dimensions (192x192, 512x512)
  • Composer une description courte et longue pour les stores
  • Préparer les screenshots et preview videos
  • Écrire les release notes et changelogs