MDC-103 Flutter : Utilisation des thèmes de Material Design (couleur, formes, élévation et type)

1. Introduction

logo_components_color_2x_web_96dp.png

Material Components (MDC) aide les développeurs à implémenter Material Design. Conçu par une équipe d'ingénieurs et de spécialistes de l'expérience utilisateur travaillant pour Google, MDC propose des dizaines de composants d'interface utilisateur élégants et fonctionnels. Il est disponible pour Android, iOS, le Web et Flutter.material.io/develop.

Vous pouvez désormais utiliser MDC pour personnaliser le style de vos applications comme jamais auparavant. L'expansion récente de Material Design offre aux graphistes et aux développeurs une plus grande flexibilité pour véhiculer l'image de marque de leur produit.

Dans les ateliers de programmation MDC-101 et MDC-102, vous avez utilisé Material Components (MDC) pour concevoir les bases de Shrine, une application d'e-commerce pour la vente de vêtements et d'articles pour la maison. Le parcours utilisateur de cette application commence par un écran de connexion, puis se poursuit avec un écran d'accueil où sont affichés les produits.

Objectifs de l'atelier

Dans cet atelier de programmation, vous allez personnaliser les éléments suivants de l'application Shrine :

  • Couleur
  • Typographie
  • Élévation
  • Forme
  • Mise en page

Android

iOS

Page de connexion de Shrine, dans un thème marron et rose

Page de connexion de Shrine, dans un thème marron et rose

Page des produits de Shrine avec une barre d'application supérieure et une grille de produits asymétrique qui défile horizontalement, dans un thème rose et marron

Page des produits de Shrine avec une barre d'application supérieure et une grille de produits asymétrique qui défile horizontalement, dans un thème rose et marron

Composants et sous-systèmes de MDC-Flutter dans cet atelier de programmation

  • Thèmes
  • Typographie
  • Élévation
  • Liste d'images

Comment évalueriez-vous votre niveau d'expérience en développement avec Flutter ?

Débutant Intermédiaire Expert

2. Configurer l'environnement de développement Flutter

Pour cet atelier, vous avez besoin de deux logiciels : le SDK Flutter et un éditeur.

Vous pouvez exécuter l'atelier de programmation sur l'un des appareils suivants :

  • Un appareil Android ou iOS physique connecté à votre ordinateur et réglé en mode développeur.
  • Le simulateur iOS (outils Xcode à installer).
  • L'émulateur Android (qui doit être configuré dans Android Studio).
  • Un navigateur (Chrome est requis pour le débogage).
  • En tant qu'application de bureau Windows, Linux ou macOS. Vous devez développer votre application sur la plate-forme où vous comptez la déployer. Par exemple, si vous voulez développer une application de bureau Windows, vous devez le faire sous Windows pour accéder à la chaîne de compilation appropriée. Prenez également connaissance des exigences spécifiques aux systèmes d'exploitation, lesquelles sont détaillées sur docs.flutter.dev/desktop.

3. Télécharger l'application de démarrage de l'atelier de programmation

Vous avez déjà suivi l'atelier MDC-102 ?

Si vous avez fini l'atelier de programmation MDC-102, votre code devrait être prêt pour commencer cet atelier. Passez à l'étape Modifier les couleurs.

Vous partez de zéro ?

Télécharger l'application de départ de l'atelier de programmation

L'application de départ se trouve dans le répertoire material-components-flutter-codelabs-103-starter_and_102-complete/mdc_100_series.

… ou cloner l'atelier depuis GitHub

Pour cloner cet atelier de programmation à partir de GitHub, exécutez les commandes suivantes :

git clone https://github.com/material-components/material-components-flutter-codelabs.git
cd material-components-flutter-codelabs/mdc_100_series
git checkout 103-starter_and_102-complete

Ouvrir le projet et exécuter l'application

  1. Ouvrez le projet dans l'éditeur de votre choix.
  2. Suivez les instructions concernant l'éditeur que vous avez choisi. Elles sont accessibles au paragraphe "Run the app" (Exécuter l'application) sur la page Get Started > Test drive (Premiers pas > Faire un essai).

Opération réussie. La page de connexion de Shrine créée dans les précédents ateliers de programmation devrait s'afficher sur votre appareil.

Android

iOS

Page de connexion de Shrine sans thème

Page de connexion de Shrine sans thème

Cliquez sur "Next" (Suivant) pour afficher la page d'accueil de l'atelier de programmation précédent.

Android

iOS

Page de la grille des produits de Shrine sans thème

Page de la grille des produits de Shrine sans thème

4. Modifier les couleurs

Un jeu de couleurs a été créé pour représenter la marque Shrine, et le graphiste souhaite que vous l'implémentiez partout dans l'application Shrine.

Pour commencer, importons ces couleurs dans notre projet.

Créer colors.dart

Créez un fichier DART dans lib appelé colors.dart. Importez Material Components et ajoutez des constantes de couleurs :

import 'package:flutter/material.dart';

const kShrinePink50 = Color(0xFFFEEAE6);
const kShrinePink100 = Color(0xFFFEDBD0);
const kShrinePink300 = Color(0xFFFBB8AC);
const kShrinePink400 = Color(0xFFEAA4A4);

const kShrineBrown900 = Color(0xFF442B2D);

const kShrineErrorRed = Color(0xFFC5032B);

const kShrineSurfaceWhite = Color(0xFFFFFBFA);
const kShrineBackgroundWhite = Colors.white;

Personnaliser la palette de couleurs

Ce thème de couleur a été créé par un graphiste à partir de couleurs personnalisées (illustrées sur l'image ci-dessous). Il contient des couleurs sélectionnées à partir de la marque Shrine et appliquées à Material Theme Editor, une extension qui permet de créer une palette plus complète. Ces couleurs ne sont pas issues des palettes de couleurs de Material 2014).

Material Theme Editor les a organisées en nuances numérotées (libellés 50, 100, 200, etc. jusqu'à 900 pour chaque couleur). Shrine n'utilise que les nuances 50, 100 et 300 de l'échantillon rose, et la nuance 900 de l'échantillon marron.

b9170eb94fd3b106.jpeg f8b4b97f898f154e.png

Chaque paramètre de couleur d'un widget est associé à une couleur faisant partie de ces nuances. Par exemple, la couleur des décorations d'un champ de texte dans lequel l'utilisateur saisit du texte doit être la couleur principale du thème. S'il n'est pas possible de se servir de cette couleur, car elle est difficile à distinguer de l'arrière-plan, utilisez plutôt la couleur PrimaryVariant.

Elles ont été créées pour les consignes Material 2014 et sont toujours disponibles dans les consignes actuelles (article Système de couleur) et dans MDC-Futter. Pour y accéder dans le code, il vous suffit d'appeler la couleur de base, puis la nuance (généralement un multiple de cent). Par exemple, la commande suivante permet de récupérer la couleur Rose 400 : Colors.pink[400].

Vous pouvez parfaitement utiliser ces palettes pour vos créations et votre code. Si vous disposez déjà de couleurs propres à votre branding, vous pouvez générer vos propres palettes harmonieuses à l'aide de l'outil de génération de palettes ou de Material Theme Editor.

Maintenant que nous avons récupéré les couleurs à utiliser, nous pouvons les appliquer à l'interface utilisateur. Pour ce faire, nous allons définir les valeurs d'un widget ThemeData, que nous appliquerons à l'instance MaterialApp au sommet de la hiérarchie des widgets.

Personnaliser ThemeData.light()

Flutter intègre quelques thèmes, dont le thème clair. Plutôt que de créer intégralement un widget ThemeData, nous allons copier le thème clair et modifier les valeurs afin de le personnaliser pour notre application.

Importez la chaîne colors.dart dans app.dart.

import 'colors.dart';

Ensuite, ajoutez le code suivant à app.dart en dehors du champ d'application de la classe ShrineApp :

// TODO: Build a Shrine Theme (103)
final ThemeData _kShrineTheme = _buildShrineTheme();

ThemeData _buildShrineTheme() {
  final ThemeData base = ThemeData.light();
  return base.copyWith(
    colorScheme: base.colorScheme.copyWith(
      primary: kShrinePink100,
      onPrimary: kShrineBrown900,
      secondary: kShrineBrown900,
      error: kShrineErrorRed,
    ),
    // TODO: Add the text themes (103)
    // TODO: Add the icon themes (103)
    // TODO: Decorate the inputs (103)
  );
}

À présent, définissez theme: à la fin de la fonction build() de ShrineApp (dans le widget MaterialApp) comme nouveau thème :

  // TODO: Add a theme (103)
  theme: _kShrineTheme, // New code

Enregistrez le projet. L'écran de connexion devrait maintenant ressembler à ceci :

Android

iOS

Page de connexion de Shrine, dans un thème rose et marron

Page de connexion de Shrine, dans un thème rose et marron

Votre écran d'accueil devrait ressembler à ceci :

Android

iOS

Page de la grille des produits de Shrine, dans un thème rose et marron

Page de la grille des produits de Shrine, dans un thème rose et marron

5. Modifier la typographie et le style des libellés

En plus des modifications de couleur demandées, le graphiste nous a fourni une typographie spécifique à utiliser. Le widget ThemeData de Flutter inclut trois thèmes de texte. Chacun d'entre eux est une collection de styles de texte, comme "en-tête" et "titre". Nous allons utiliser quelques styles pour notre application et modifier certaines valeurs.

Personnaliser le thème du texte

Pour importer des polices dans le projet, vous devez les ajouter au fichier pubspec.yaml.

Dans pubspec.yaml, ajoutez le code suivant immédiatement après la balise flutter: :

  # TODO: Insert Fonts (103)
  fonts:
    - family: Rubik
      fonts:
        - asset: fonts/Rubik-Regular.ttf
        - asset: fonts/Rubik-Medium.ttf
          weight: 500

Vous pouvez à présent accéder à la police Rubik et l'utiliser.

Résoudre les problèmes liés au fichier pubspec

L'exécution de pub get peut renvoyer des erreurs si vous coupez et collez la déclaration ci-dessus. Si c'est le cas, commencez par supprimer l'espace blanc au début et remplacez-le par des espaces avec un retrait de 2 espaces (deux espaces avant

fonts:

, quatre espaces avant

family: Rubik

, et ainsi de suite).

Si le message Mapping values are not allowed here (Les valeurs de mappage ne sont pas autorisées ici) s'affiche, vérifiez le retrait de la ligne qui pose problème et celui des lignes au-dessus.

Dans login.dart, modifiez les éléments suivants dans Column() :

Column(
  children: <Widget>[
    Image.asset('assets/diamond.png'),
    const SizedBox(height: 16.0),
    Text(
      'SHRINE',
      style: Theme.of(context).textTheme.headline5,
    ),
  ],
)

Dans app.dart, ajoutez le code suivant après _buildShrineTheme() :

// TODO: Build a Shrine Text Theme (103)
TextTheme _buildShrineTextTheme(TextTheme base) {
  return base.copyWith(
    headline5: base.headline5!.copyWith(
      fontWeight: FontWeight.w500,
    ),
    headline6: base.headline6!.copyWith(
      fontSize: 18.0,
    ),
    caption: base.caption!.copyWith(
      fontWeight: FontWeight.w400,
      fontSize: 14.0,
    ),
    bodyText1: base.bodyText1!.copyWith(
      fontWeight: FontWeight.w500,
      fontSize: 16.0,
    ),
  ).apply(
    fontFamily: 'Rubik',
    displayColor: kShrineBrown900,
    bodyColor: kShrineBrown900,
  );
}

Cette opération prend un TextTheme et modifie l'apparence des en-têtes, des titres et des légendes.

En appliquant la fontFamily ainsi, vous n'appliquez les modifications qu'aux valeurs d'échelle de typographie spécifiées dans copyWith() (en-tête, titre, légende).

Pour certaines polices, nous allons définir une épaisseur de police fontWeight personnalisée, par incréments de 100 : w500 (épaisseur de 500) correspond à l'épaisseur moyenne et w400 correspond à l'épaisseur standard.

Utiliser les nouveaux thèmes de texte

Ajoutez les thèmes suivants à _buildShrineTheme après l'erreur :

// TODO: Add the text themes (103)
textTheme: _buildShrineTextTheme(base.textTheme),
textSelectionTheme: const TextSelectionThemeData(
  selectionColor: kShrinePink100,
),

Enregistrez le projet. Cette fois-ci, vous devez redémarrer également l'application (redémarrage à chaud), étant donné que nous avons modifié les polices.

Android

iOS

Page de la grille des produits de Shrine avec les thèmes de texte appliqués

Page de la grille des produits de Shrine avec les thèmes de texte appliqués

Le texte des écrans de connexion et d'accueil change d'aspect : certains textes utilisent la police Rubik et d'autres apparaissent en marron, et non en noir ou blanc. Les icônes sont également affichées en marron.

Réduire le texte

Les libellés sont un peu trop grands.

Dans home.dart, modifiez children: de la colonne la plus au centre :

// TODO: Change innermost Column (103)
children: <Widget>[
// TODO: Handle overflowing labels (103)
  Text(
    product.name,
    style: theme.textTheme.button,
    softWrap: false,
    overflow: TextOverflow.ellipsis,
    maxLines: 1,
  ),
  const SizedBox(height: 4.0),
  Text(
    formatter.format(product.price),
    style: theme.textTheme.caption,
  ),
  // End new code
],

Centrer et déposer le texte

Nous voulons centrer les libellés et aligner le texte avec le bas de chaque carte plutôt qu'avec le bas de chaque image.

Déplacez les libellés jusqu'à la fin (bas) de l'axe principal et centrez-les :

  // TODO: Align labels to the bottom and center (103)
  mainAxisAlignment: MainAxisAlignment.end,
  crossAxisAlignment: CrossAxisAlignment.center,

Enregistrez le projet.

Android

iOS

Page de la grille des produits de Shrine avec un alignement de texte différent

Page de la grille des produits de Shrine avec un alignement de texte différent

Voilà qui est beaucoup mieux.

Appliquer un thème aux champs de texte

Vous pouvez également appliquer un thème à la décoration des champs de texte à l'aide d'une classe InputDecorationTheme.

Dans app.dart, dans la méthode _buildShrineTheme(), spécifiez une valeur inputDecorationTheme: :

// TODO: Decorate the inputs (103)
inputDecorationTheme: const InputDecorationTheme(
  border: OutlineInputBorder(),
),

Actuellement, les champs de texte comportent une décoration filled. Supprimons-la. Si vous supprimez filled et spécifiez inputDecorationTheme, le style de contour est appliqué aux champs de texte.

Dans login.dart, supprimez les valeurs filled: true :

// Remove filled: true values (103)
TextField(
  controller: _usernameController,
  decoration: const InputDecoration(
    // Removed filled: true
    labelText: 'Username',
  ),
),
const SizedBox(height: 12.0),
TextField(
  controller: _passwordController,
  decoration: const InputDecoration(
    // Removed filled: true
    labelText: 'Password',
  ),
  obscureText: true,
),

Effectuez un redémarrage à chaud. Votre écran de connexion devrait ressembler à ceci lorsque le champ "Username" (Nom d'utilisateur) est actif (c'est-à-dire lorsque vous saisissez votre nom d'utilisateur) :

Android

iOS

Page de connexion de Shrine avec le champ "Username" (Nom d'utilisateur) actif

Page de connexion de Shrine avec le champ "Username" (Nom d'utilisateur) actif

Saisissez du texte dans un champ de texte. Les bordures et les libellés flottants s'affichent dans la couleur principale. Mais il est difficile de distinguer le texte. Il n'est pas visible par les personnes qui éprouvent des difficultés à différencier des pixels dont les couleurs ne présentent pas un contraste suffisant. Pour en savoir plus, consultez la section "Accessible colors" (Couleurs accessibles) de l'article Color (Couleur) des consignes de Material Design.

Dans app.dart, spécifiez focusedBorder: sous inputDecorationTheme: :

// TODO: Decorate the inputs (103)
inputDecorationTheme: const InputDecorationTheme(
  border: OutlineInputBorder(),
  focusedBorder: OutlineInputBorder(
    borderSide: BorderSide(
      width: 2.0,
      color: kShrineBrown900,
    ),
  ),
),

Ensuite, spécifiez floatingLabelStyle: sous inputDecorationTheme: :

// TODO: Decorate the inputs (103)
inputDecorationTheme: const InputDecorationTheme(
  border: OutlineInputBorder(),
  focusedBorder: OutlineInputBorder(
    borderSide: BorderSide(
      width: 2.0,
      color: kShrineBrown900,
    ),
  ),
  floatingLabelStyle: TextStyle(
    color: kShrineBrown900,
  ),
),

Enfin, faisons en sorte que le bouton d'annulation utilise la couleur secondaire plutôt que la couleur principale pour augmenter le contraste.

TextButton(
  child: const Text('CANCEL'),
  onPressed: () {
    _usernameController.clear();
    _passwordController.clear();
  },
  style: TextButton.styleFrom(
    primary: Theme.of(context).colorScheme.secondary,
  ),
),

Enregistrez le projet.

Android

iOS

Page de connexion de Shrine avec un bouton d'annulation accessible

Page de connexion de Shrine avec un bouton d'annulation accessible

6. Ajuster l'élévation

Maintenant que vous avez personnalisé la page avec une couleur et une typographie spécifiques correspondant à Shrine, ajustons l'élévation.

Modifier l'élévation du bouton NEXT (SUIVANT)

L'élévation par défaut de ElevatedButton est 2. Augmentons-la.

Dans login.dart, ajoutez une valeur style: au ElevatedButton NEXT (SUIVANT) :

ElevatedButton(
  child: const Text('NEXT'),
  onPressed: () {
    Navigator.pop(context);
  },
  style: ElevatedButton.styleFrom(
    elevation: 8.0,
  ),
),

Enregistrez le projet.

Ajuster l'élévation des cartes

Pour le moment, les cartes sont placées sur une surface blanche, à côté du menu de navigation du site.

Dans home.dart, ajoutez une valeur elevation: aux cartes :

// TODO: Adjust card heights (103)
elevation: 0.0,

Enregistrez le projet.

Android

iOS

Page de la grille des produits de Shrine sans élévation pour chaque carte

Page de la grille des produits de Shrine sans élévation pour chaque carte

Vous avez supprimé l'ombre sous les cartes.

7. Ajouter une forme

Shrine présente un style géométrique tendance, dont les éléments sont de forme octogonale ou rectangulaire. Implémentons ce style de forme dans les cartes de l'écran d'accueil, ainsi que dans les champs de texte et les boutons de l'écran de connexion.

Modifier les formes des champs de texte dans l'écran de connexion

Dans app.dart, importez le fichier suivant :

import 'supplemental/cut_corners_border.dart';

Toujours dans app.dart, modifiez le thème de décoration des champs de texte afin d'utiliser une bordure présentant des angles coupés :

// TODO: Decorate the inputs (103)
inputDecorationTheme: const InputDecorationTheme(
  border: CutCornersBorder(),
  focusedBorder: CutCornersBorder(
    borderSide: BorderSide(
      width: 2.0,
      color: kShrineBrown900,
    ),
  ),
  floatingLabelStyle: TextStyle(
    color: kShrineBrown900,
  ),
),

Modifier la forme des boutons dans l'écran de connexion

Dans login.dart, ajoutez une bordure rectangulaire biseautée au bouton CANCEL (ANNULER) :

TextButton(
  child: const Text('CANCEL'),
  onPressed: () {
    _usernameController.clear();
    _passwordController.clear();
  },
  style: TextButton.styleFrom(
    primary: Theme.of(context).colorScheme.secondary,
    shape: const BeveledRectangleBorder(
      borderRadius: BorderRadius.all(Radius.circular(7.0)),
    ),
  ),
),

Le TextButton n'a pas de forme visible. Pourquoi faut-il donc ajouter une forme de bordure ? Pour que l'animation d'ondulation soit liée à la même forme lorsqu'un utilisateur appuie dessus.

Ajoutez à présent la même forme au bouton NEXT (SUIVANT) :

ElevatedButton(
  child: const Text('NEXT'),
  onPressed: () {
    Navigator.pop(context);
  },
  style: ElevatedButton.styleFrom(
    elevation: 8.0,
    shape: const BeveledRectangleBorder(
      borderRadius: BorderRadius.all(Radius.circular(7.0)),
    ),
  ),

Pour modifier la forme de tous les boutons, nous pouvons également utiliser elevatedButtonTheme ou textButtonTheme dans app.dart. Nous vous laissons relever ce défi tout seul !

Effectuez un redémarrage à chaud.

Android

iOS

Page de connexion de Shrine avec la thématisation des formes appliquée

Page de connexion de Shrine avec la thématisation des formes appliquée

8. Modifier la mise en page

À présent, modifions la mise en page pour afficher les cartes dans différents formats et tailles, de sorte que chaque carte soit unique.

Remplacer GridView par AsymmetricView

Nous avons déjà écrit les fichiers pour une mise en page asymétrique.

Dans home.dart, ajoutez l'importation suivante :

import 'supplemental/asymmetric_view.dart';

Supprimez _buildGridCards et remplacez body :

body: AsymmetricView(
  products: ProductsRepository.loadProducts(Category.all),
),

Enregistrez le projet.

Android

iOS

Page des produits de Shrine avec une mise en page asymétrique défilant à l'horizontale

Page des produits de Shrine avec une mise en page asymétrique défilant à l'horizontale

Les produits défilent maintenant horizontalement, selon un modèle rappelant un tissage.

9. Essayer un autre thème (facultatif)

L'utilisation des couleurs est une manière puissante d'exprimer votre marque. Une légère modification de la couleur peut avoir un impact important sur l'expérience utilisateur. Pour tester cela, voyons à quoi ressemblerait Shrine si le jeu de couleurs de la marque était légèrement différent.

Modifier les couleurs

Dans colors.dart, ajoutez la couleur suivante :

const kShrinePurple = Color(0xFF5D1049);

Dans app.dart, modifiez la fonction _buildShrineTheme() comme suit :

ThemeData _buildShrineTheme() {
  final ThemeData base = ThemeData.light();
  return base.copyWith(
    colorScheme: base.colorScheme.copyWith(
      primary: kShrinePurple,
      secondary: kShrinePurple,
      error: kShrineErrorRed,
    ),
    scaffoldBackgroundColor: kShrineSurfaceWhite,
    textSelectionTheme: const TextSelectionThemeData(
      selectionColor: kShrinePurple,
    ),
    inputDecorationTheme: const InputDecorationTheme(
      border: CutCornersBorder(),
      focusedBorder: CutCornersBorder(
        borderSide: BorderSide(
          width: 2.0,
          color: kShrinePurple,
        ),
      ),
      floatingLabelStyle: TextStyle(
        color: kShrinePurple,
      ),
    ),
  );
}

Effectuez un redémarrage à chaud. Le nouveau thème devrait maintenant s'afficher.

Android

iOS

Page de connexion de Shrine, dans un thème violet et blanc

Page de connexion de Shrine, dans un thème violet et blanc

Android

iOS

Page des produits de Shrine, dans un thème violet et blanc

Page des produits de Shrine, dans un thème violet et blanc

Le résultat est très différent. Repassons app.dart's _buildShrineTheme à la version de l'étape précédente. Vous pouvez également télécharger le code de démarrage pour l'atelier 104.

10. Félicitations !

Vous venez de créer une application qui répond aux spécifications de design de votre graphiste.

Étapes suivantes

Vous avez utilisé les composants MDC suivants : les thèmes, la typographie, l'élévation et les formes. Vous pouvez découvrir d'autres composants et sous-systèmes dans la bibliothèque MDC-Flutter.

Parcourez les fichiers du répertoire supplemental pour voir comment nous avons réalisé la mise en page asymétrique en grille avec défilement horizontal.

Que se passe-t-il si le design prévu pour votre application contient des éléments dont les composants ne figurent pas dans la bibliothèque MDC ? Dans l'atelier de programmation MDC-104 : Composants avancés de Material Design, nous verrons comment créer des composants personnalisés à l'aide de la bibliothèque MDC pour obtenir un rendu spécifique.

La réalisation de cet atelier de programmation m'a demandé un temps et des efforts raisonnables

Tout à fait d'accord D'accord Ni d'accord, ni pas d'accord Pas d'accord Pas du tout d'accord

Je souhaite continuer à utiliser Material Components

Tout à fait d'accord D'accord Ni d'accord, ni pas d'accord Pas d'accord Pas du tout d'accord