Bienvenue dans ce nouveau tuto Flutter animation sur la création d’une interface de connexion d’application de Yoga.

Nous allons recréer une interface d’accueil d’application mobile, que vous pourrez réutiliser directement pour votre projet.

Nous aborderons tout d’abord les animations Flutter qui vous permettrons de dynamiser les différentes pages de votre application.

Puis nous créerons étape par étape le design de nos différentes pages pour créer une interface de bienvenue complète.

J’ai pris l’exemple d’une application de yoga pour créer cette interface d’accueil, à vous bien-sûr de l’adapter à votre projet.

Pour ce tuto Flutter animation, nous allons suivre les étapes suivantes:

  1. Configuration de l’application
  2. Création du widget d’animation (delayed_animation)
  3. Design de la page de bienvenue (welcome_page)
  4. Design de la page de connexion sociale (social_page)
  5. Design de la page de connexion par mail (login_page)

N’hésitez pas à télécharger le code source de l’application pour la tester rapidement sur un émulateur.

Tuto Flutter Animation | 1. Création et configuration de l’application

Dans cette partie nous allons créer un projet Flutter et configurer les éléments principaux de notre application, comme les ressources et les packages que nous utiliserons pour la suite.

1.1. Création du projet

Ici nous allons tout simplement utiliser la commande Flutter permettant de créer un nouveau projet et ensuite préparer notre application.

Nous nous positionnons dans notre dossier de développement et nous ouvrons ensuite le terminal pour lancer la commande:

flutter create yoga

Une fois que le projet est créé, nous pouvons l’ouvrir avec notre éditeur VS Code afin de le modifier.

On commence par se rendre dans le fichier principal main.dart et à supprimer tout son contenu, pour construire notre design en partant de zéro.

1.2 Importation des images

Notre application aura besoin de différentes photos et icônes à afficher, nous devons donc ajouter ces images dans le projet.

Pour cela, nous allons créer un dossier images dans lequel nous allons mettre les photos dont nous aurons besoin.

Ce dossier contiendra donc quatre images que nous utiliserons dans notre application:

Tuto Flutter Animation Images

Ensuite nous nous rendons dans le fichier pubspec.yaml afin de déclarer l'adresse de notre dossier images comme ci-dessous:

# To add assets to your application, add an assets section, like this:
  assets:
    - images/

Nous entrons ensuite la commande suivante pour prendre en compte l’ajout des images dans notre application:

flutter pub get

1.3 Installation des packages

Dans ce tuto Flutter animation, nous aurons besoin de plusieurs packages de design pour recréer notre application.

Nous allons utiliser en premier temps le package google_fonts qui donne accès à des centaines de polices: https://pub.dev/packages/google_fonts

Et en deuxième temps au package font_awesome_flutter pour afficher l'icône de Facebook dans notre page de connexion sociale: https://pub.dev/packages/font_awesome_flutter

Pour les importer, nous devons ajouter leur dépendance dans le fichier pubspec.yaml comme ceci:

dependencies:
  flutter:
    sdk: flutter

  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^1.0.2
  google_fonts: ^2.1.0
  font_awesome_flutter: ^9.1.0

Une fois les packages déclarés, enregistrez le fichier et le téléchargement devrait se faire automatiquement, sinon entrez la commande suivante:

flutter pub get

Nous pourrons ensuite importer nos deux packages en utilisant le code dart suivant:

import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:google_fonts/google_fonts.dart';

Nos deux packages de base étant importés, nous allons commencer à construire notre application Flutter.

1.4 Déclaration de la fonction main() et de la classe MyApp

Tout d’abord, nous allons commencer par déclarer la fonction main() dans notre fichier main.dart.

C’est elle qui lancera le code de notre application, grâce à la fonction runApp() qui chargera la classe MyApp:

void main() {
  runApp(MyApp());
}

La classe MyApp va contenir la méthode build() qui permettra de dessiner l’interface utilisateur avec le widget MaterialApp().

Nous donnons un titre à notre application avec le champ title, et nous mettons également l’attribut debugShowCheckedModeBanner à false pour supprimer la bannière de débogage.

Enfin nous avons le champ home qui va nous permettre de définir le Widget qui sera la page d’accueil de l’application.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Yoga App',
      home: WelcomePage(),
    );
  }
}

Ici nous appelons le widget WelcomePage() qui représentera notre page d’accueil que nous allons créer dans la prochaine partie.

Pour finir, nous définissons au début de notre fichier une variable de couleur appelée d_red qui va contenir un code couleur hexadécimal:

const d_red = const Color(0xFFE9717D);

Nous utiliserons cette couleur dans toute notre application, en important le fichier main.dart dans les autres fichiers.

Tuto Flutter Animation

Tuto Flutter Animation | 2. Création du widget d'animation

Dans cette partie, nous allons créer un widget personnalisé qui nous permettra de réaliser des animations de déplacement et d'apparition.

Notre application de yoga possèdera donc plusieurs widgets qui seront animé avec le même type d'animation.

Pour cela nous allons créer un seul code qui sera appelé à chaque fois qu'on voudra animer un widget (Text, Image, Button, TextField, etc).

2.1 Création du fichier de la page de gestion de l'animation

Nous allons créer le widget pour l'animation dans un fichier différent, afin d'éviter de réécrire le même code plusieurs fois.

Pour créer votre fichier, rendez-vous dans le dossier "lib" et faites un clic droit, puis créer le nouveau fichier “delayed_animation.dart”.

Ce fichier devra être importé depuis vos différentes pages afin d'accéder à notre widget d'animation.

Comme le nom du fichier l'indique, le widget va nous permettre d'animer nos éléments avec un délai à préciser à chaque fois.

2.2. Structure de la classe DelayedAnimation

Juste avant de déclarer notre classe, nous devons importer les deux packages qui seront utilisés:

import 'dart:async';
import 'package:flutter/material.dart';

Le package async nous permettra de définir le délai de notre animation avec la classe Timer().

Le package Material nous donne accès aux classes indispensables de Flutter, notamment les widgets de bases.

Notre widget d'animation sera un StatefulWidget, puisque par définition il sera dynamique et donc son état va changer à plusieurs reprises.

La structure de base de notre widget ressemble à ceci :

class DelayedAnimation extends StatefulWidget {
  final Widget child;
  final int delay;
  const DelayedAnimation({required this.delay, required this.child});

  @override
  _DelayedAnimationState createState() => _DelayedAnimationState();
}

Notre widget possède deux attributs et paramètres à prendre en compte:

  • child: le widget à animer
  • delay: le délai d'apparition du widget en millisecondes

Ce délai permettra de dire à quel moment ou après combien de temps l'amination commencera pour que le widget apparaisse.

Par la suite nous créons la classe d'état de notre StatefulWidget en lui associant un SingleTickerProviderStateMixin permettant aux animations de se synchroniser:

class _DelayedAnimationState extends State<DelayedAnimation>
    with SingleTickerProviderStateMixin {
  [...]

  @override
  Widget build(BuildContext context) {
    return [...];
  }
}

Pour plus de détails sur son utilisation je vous invite à consulter la documentation:

Pour faire fonctionner notre widget d'animation, nous avons de deux variables à manipuler, un contrôleur d'animation et un Offset pour gérer le décalage de notre widget:

late AnimationController _controller;
late Animation<Offset> _animOffset;

C'est deux variables nous permettrons de contrôler notre animation et d'en préciser le comportement en donnant une valeur à notre Offset.

2.3 Initialisation de notre widget

En ce qui concerne la suite de notre widget, le plus gros du code va se trouver dans la méthode initState(), qui se lancera au chargement du widget:

void initState() {
  super.initState();
  [...]
}

Pour le _controller, nous utilisons le paramètre vsync (via par le SingleTickerProviderStateMixin) et nous fixons la durée de l'animation à 800 millisecondes:

_controller = AnimationController(
  vsync: this,
  duration: Duration(milliseconds: 800),
);

On définit ensuite une variable pour définir le comportement de notre animation, en utilisant le widget CurvedAnimation():

final curve = CurvedAnimation(
  curve: Curves.decelerate,
  parent: _controller,
);

Cela permettra à notre animation globale d'avoir un rendu plus naturel, notamment en précisant ici un comportement de décélération.

Pour la variable _animOffset, on utilise la classe Tween pour faire varier la position de notre widget, l'idée est de créer une animation du bas vers le haut:

_animOffset = Tween<Offset>(
  begin: Offset(0.0, -0.35),
  end: Offset.zero,
).animate(curve);

Le widget donnera l'impression de tomber de haut en bas, avec le décalage que nous lui avons précisé dans le Offset.

La fin de notre code nous permettra de déclencher cette animation (et donc l'apparition du widget) avec un certain délai.

Pour démarrer notre animation, on utilise le _controller qui contrôle toutes les animations qui lui sont liées.

On utilise enfin la classe Timer qui va nous permettre de dire à quel moment l'animation commencera:

Timer(Duration(milliseconds: widget.delay), () {
  _controller.forward();
});

Voilà pour le code d'initialisation de notre widget d'animation Flutter, pas évident à coder au début, mais vous pourrez reprendre ce schéma pour d'autres types d'animation.

2.4 Contenu de notre widget

Pour le moment, nous n'avons défini qu'un Controller qui n'est rattaché à aucun widget concret et donc visuel.

Ce qui est intéressant, c'est que Flutter propose déjà par défaut des widgets d'animations que nous allons connecter à nos variables pour préciser leurs comportements.

Nous utilisons notamment le widget FadeTransition() pour faire apparaître le widget de manière fondu et le widget SlideTransition() pour le faire glisser en arrivant:

@override
  Widget build(BuildContext context) {
    return FadeTransition(
      opacity: _controller,
      child: SlideTransition(
        position: _animOffset,
        child: widget.child,
      ),
    );
  }

Nous renvoyons ces deux widgets qui prennent en compte touts les paramètres d'animation que nous avons définie plus haut.

Je vous invite à tester aussi d'autres widgets d'animations ainsi que de modifier les valeurs des paramètres de bases, comme la durée de l'animation et le décalage (Offset).

Je vous remet aussi le code complet de fichier de notre widget d'animation:

import 'package:flutter/material.dart';
import 'dart:async';

class DelayedAnimation extends StatefulWidget {
  final Widget child;
  final int delay;
  const DelayedAnimation({required this.delay, required this.child});

  @override
  _DelayedAnimationState createState() => _DelayedAnimationState();
}

class _DelayedAnimationState extends State<DelayedAnimation>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<Offset> _animOffset;
  @override
  void initState() {
    super.initState();

    _controller = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: 800),
    );

    final curve = CurvedAnimation(
      parent: _controller,
      curve: Curves.decelerate,
    );

    _animOffset = Tween<Offset>(
      begin: Offset(0.0, -0.35),
      end: Offset.zero,
    ).animate(curve);

    Timer(Duration(milliseconds: widget.delay), () {
      _controller.forward();
    });
  }

  Widget build(BuildContext context) {
    return FadeTransition(
      opacity: _controller,
      child: SlideTransition(
        position: _animOffset,
        child: widget.child,
      ),
    );
  }
}
Tuto Flutter Animation

Tuto Flutter Animation | 3. Création de la Welcome page

On commence donc cette troisième partie de ce tuto Flutter animation, dans laquelle nous allons créer le design de notre page de bienvenue.

Pour plus de simplicité, j'ai créé un fichier séparé pour chacune de nos pages, donc ici il s'agit du fichier "welcome_page.dart".

Notre page de bienvenue sera constitué d'une seule classe, le widget WelcomePage() que nous appellerons dans le fichier main.dart:

import 'package:flutter/material.dart';
import 'package:yoga/welcome_page.dart';

const d_red = const Color(0xFFE9717D);

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Yoga App',
      home: WelcomePage(),
    );
  }
}

Reprenons donc notre fichier welcome_page.dart, et commeçons par importer tous les packages et fichier nécessaires:

import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:yoga/social_page.dart';
import 'package:yoga/delayed_animation.dart';
import 'package:yoga/main.dart';

Il faut importer:

  • le package Material pour les widgets de base Flutter
  • le package Google Fonts pour les polices personnalisées
  • le fichier social_page.dart pour accéder à la deuxième page
  • le fichier delayed_animation.dart pour accéder au widget d'animation
  • le fichier main.dart pour accéder à notre variable de couleur

Pour la structure de notre page, nous déclarons notre classe WelcomePage() comme un StatelessWidget qui va contenir la page d’accueil de notre application:

class WelcomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(),
    );
  }
}

La méthode build retourne un Scaffold() qui est le widget qui implémente l'architecture de base d’une page en utilisant le Material Design.

Notre page n'aura pas de barre de navigation, on utilisera seulement le champ body constitué d'une seule section principale.

Le corps de notre page de bienvenue sera contenu dans un widget SingleChildScrollView pour permettre de scroller à la verticale dans le cas d'un dépassement.

Les différents éléments de notre page sont ensuite placé dans une Column, pour placer nos éléments les uns à la suite des autres.

Finalement, notre page aura quatre widgets principaux qui seront affichés, deux images, un texte et un bouton:

class WelcomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Color(0xFFEDECF2),
      body: SingleChildScrollView(
        child: Container(
          margin: EdgeInsets.symmetric(
            vertical: 60,
            horizontal: 30,
          ),
          child: Column(
            children: [
              DelayedAnimation(
                delay: 1500,
                child: Container(
                  height: 170,
                  child: Image.asset('images/yoga_1.png'),
                ),
              ),
              DelayedAnimation(
                delay: 2500,
                child: Container(
                  height: 400,
                  child: Image.asset('images/yoga_2.jpeg'),
                ),
              ),
              DelayedAnimation(
                delay: 3500,
                child: Container(
                  margin: EdgeInsets.only(
                    top: 30,
                    bottom: 20,
                  ),
                  child: Text(
                    "Get fitter, stronger, and embrasse a helthier lifestyle",
                    textAlign: TextAlign.center,
                    style: GoogleFonts.poppins(
                      color: Colors.grey,
                      fontSize: 16,
                    ),
                  ),
                ),
              ),
              DelayedAnimation(
                delay: 4500,
                child: Container(
                  width: double.infinity,
                  child: ElevatedButton(
                    style: ElevatedButton.styleFrom(
                        primary: d_red,
                        shape: StadiumBorder(),
                        padding: EdgeInsets.all(13)),
                    child: Text('GET STARTED'),
                    onPressed: () {
                      Navigator.push(
                        context,
                        MaterialPageRoute(
                          builder: (context) => SocialPage(),
                        ),
                      );
                    },
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Tous les widgets à afficher sont contenus dans le widget d'animation DelayedAnimation que nous avons créé dans la partie précédente:

DelayedAnimation(
  delay: 1500,
  child: Container(
    height: 170,
    child: Image.asset('images/yoga_1.png'),
  ),
),

Ici par exemple, notre image sera animée et apparaitra après 1.5 seconde en fondu et du bas vers le haut.

Pour permettre la navigation de la page de bienvenue vers la SocialPage qui sera créée dans la suite de ce tutoriel, nous ajoutons la propriété onPressed à notre ElevatedButton:

onPressed: () {
   Navigator.push(
     context,
     MaterialPageRoute(
       builder: (context) => SocialPage(),
     ),
   );
},

Nous devons bien sûr déclarer et importer notre fichier social_page.dart pour faire fonctionner ce code et ainsi activer la navigation entre les deux pages.

Tuto Flutter Animation

Tuto Flutter Animation | 4. Création de la page de connexion sociale

On continue avec la deuxième page de connexion sociale, qui propose à l'utilisateur les boutons Facebook et Google.

Comme nous l’avons déjà fait pour la page de bienvenue, nous mettons le contenu de la SocialPage dans un fichier différent, ceci afin de bien organiser notre code et le rendre plus lisible.

Pour créer votre SocialPage, rendez-vous dans le dossier lib et faite un clic droit, puis créez le nouveau fichier “social_page.dart”.

Une fois notre page créé, nous importons les packages et fichiers que nous utiliserons:

import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:yoga/delayed_animation.dart';
import 'package:yoga/login_page.dart';
import 'package:yoga/main.dart';

Une fois les packages importés, nous déclarons notre classe principale dans laquelle se trouvera tout notre code, le widget SocialPage().

class SocialPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Social Page'),
      ),
      body: Container(),
    );
  }
}

Notre appBar est assez simple, elle est constituée d'une seule icône, on va donc placer le code directement dans notre Scaffold.

Comme vous pouvez le voir, notre appBar n'a pas d'ombre, pour faire cela nous utilisons la propriété elevation pour contrôler la taille de l'ombre sous la barre d'application.

Le code de notre appBar est le suivant:

appBar: AppBar(
  elevation: 0,
  backgroundColor: Colors.white.withOpacity(0),
  leading: IconButton(
    icon: Icon(
      Icons.arrow_back,
      color: Colors.black,
      size: 30,
    ),
    onPressed: () {
      Navigator.pop(context);
    },
  ),
),

Dans la création de notre appBar, nous ajoutons notre icône de retour dans le champ leading grâce au widget IconButton.

Sans oublier de donner une taille et une couleur à notre icône pour la rendre plus conforme au design de base.

Maintenant, nous passons au corps de notre SocialPage, qui utilisera aussi le widget SingleChildScrollView().

Ici, nous plaçons ensuite les éléments du corps dans un widget Column dans lequel nous mettrons tous les widgets qui vont constituer notre page.

Nous avons le code complet de notre body ci-dessous :

body: SingleChildScrollView(
  child: Column(
    children: [
      DelayedAnimation(
        delay: 1500,
        child: Container(
          height: 280,
          child: Image.asset('images/yoga_3.png'),
        ),
      ),
      DelayedAnimation(
        delay: 2500,
        child: Container(
          margin: const EdgeInsets.symmetric(
            vertical: 40,
            horizontal: 30,
          ),
          child: Column(
            children: [
              Text(
                "Change starts here",
                style: GoogleFonts.poppins(
                  color: d_red,
                  fontSize: 16,
                  fontWeight: FontWeight.w600,
                ),
              ),
              SizedBox(height: 10),
              Text(
                "Save your progress to access your personal training program!",
                textAlign: TextAlign.center,
                style: GoogleFonts.poppins(
                  color: Colors.grey,
                  fontSize: 15,
                ),
              ),
            ],
          ),
        ),
      ),
      DelayedAnimation(
        delay: 3500,
        child: Container(
          margin: EdgeInsets.symmetric(
            vertical: 14,
            horizontal: 40,
          ),
          child: Column(
            children: [
              ElevatedButton(
                onPressed: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (context) => LoginPage(),
                    ),
                  );
                },
                style: ElevatedButton.styleFrom(
                  shape: StadiumBorder(),
                  primary: d_red,
                  padding: EdgeInsets.all(13),
                ),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Icon(Icons.mail_outline_outlined),
                    SizedBox(width: 10),
                    Text(
                      'EMAIL',
                      style: GoogleFonts.poppins(
                        color: Colors.white,
                        fontSize: 16,
                        fontWeight: FontWeight.w500,
                      ),
                    )
                  ],
                ),
              ),
              SizedBox(height: 20),
              ElevatedButton(
                onPressed: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (context) => LoginPage(),
                    ),
                  );
                },
                style: ElevatedButton.styleFrom(
                  shape: StadiumBorder(),
                  primary: Color(0xFF576dff),
                  padding: EdgeInsets.all(13),
                ),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    FaIcon(FontAwesomeIcons.facebook),
                    SizedBox(width: 10),
                    Text(
                      'FACEBOOK',
                      style: GoogleFonts.poppins(
                        color: Colors.white,
                        fontSize: 16,
                        fontWeight: FontWeight.w500,
                      ),
                    ),
                  ],
                ),
              ),
              SizedBox(height: 20),
              ElevatedButton(
                onPressed: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (context) => LoginPage(),
                    ),
                  );
                },
                style: ElevatedButton.styleFrom(
                  shape: StadiumBorder(),
                  primary: Colors.white,
                  padding: EdgeInsets.all(13),
                ),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Image.asset(
                      'images/google.png',
                      height: 20,
                    ),
                    SizedBox(width: 10),
                    Text(
                      'GOOGLE',
                      style: GoogleFonts.poppins(
                        color: Colors.black,
                        fontSize: 16,
                        fontWeight: FontWeight.w500,
                      ),
                    ),
                  ],
                ),
              ),
              SizedBox(height: 20),
            ],
          ),
        ),
      ),
    ],
  ),
),

Chaque élément de la page sera animé comme dans la page précédente avec le widget DelayedAnimation().

Le premier élément est par exemple une image qu'on insère en utilisant le widget Image qui est contenu dans notre widget d'animation:

DelayedAnimation(
  delay: 1500,
  child: Container(
    height: 280,
    child: Image.asset('images/yoga_3.png'),
  ),
),

À la suite de notre image, nous avons deux textes que nous plaçons en utilisant le widget Text accompagné des différentes propriétés utilisées comme vous pouvez le voir dans le code plus haut.

Enfin, nous avons les différentes boutons contenant les icônes et texte, qui vont nous permettre de naviguer vers la LoginPage.

Pour permettre la navigation vers la page Login de qui sera créée dans la dernière partie, nous ajoutons la propriété onPress à chaque ElevatedButton:

Vous devrez bien sûr déclarer et importer votre page LoginPage() pour faire fonctionner votre code et ainsi activer la navigation:

onPressed: () {
  Navigator.push(
    context,
    MaterialPageRoute(
      builder: (context) => LoginPage(),
    ),
  );
},

Tuto Flutter Animation | 5. Création de la page de connexion par mail

Dans cette dernière partie de ce tuto, nous allons créer notre page de connexion avec un formulaire dynamique.

Comme d'habitude, nous mettons le contenu de cette LoginPage dans un fichier différent, que nous appelons “login_page.dart”.

Une fois notre page créée, nous importons les packages que nous utiliserons comme le montre le code suivant et nous déclarons notre classe LoginPage():

import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:yoga/delayed_animation.dart';
import 'package:yoga/main.dart';

class LoginPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Container(),
    );
  }
}

La AppBar de cette page reste similaire à la précédente, il y a simplement l'icône de fermeture qui remplace la flèche retour.

Le code de notre appBar est le suivant:

appBar: AppBar(
  elevation: 0,
  backgroundColor: Colors.white.withOpacity(0),
  leading: IconButton(
    icon: Icon(
      Icons.close,
      color: Colors.black,
      size: 30,
    ),
    onPressed: () {
      Navigator.pop(context);
    },
  ),
),

En ce qui concerne le corps de notre page, il sera divisé en deux grandes sections, la première sera directement mise dans le body et la deuxième sera créé dans une classe séparée.

Nous avons le code complet de notre body ci-dessous :

body: SingleChildScrollView(
  child: Column(
    children: [
      Container(
        margin: EdgeInsets.symmetric(
          vertical: 40,
          horizontal: 30,
        ),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            DelayedAnimation(
              delay: 1500,
              child: Text(
                "Connect email address",
                style: GoogleFonts.poppins(
                  color: d_red,
                  fontSize: 25,
                  fontWeight: FontWeight.w600,
                ),
              ),
            ),
            SizedBox(height: 22),
            DelayedAnimation(
              delay: 2500,
              child: Text(
                "It's recommended to connect your email address for us to better protect your information.",
                style: GoogleFonts.poppins(
                  color: Colors.grey[600],
                  fontSize: 16,
                  fontWeight: FontWeight.w500,
                ),
              ),
            ),
          ],
        ),
      ),
      SizedBox(height: 35),
      LoginForm(),
      SizedBox(height: 125),
      DelayedAnimation(
        delay: 5500,
        child: ElevatedButton(
          style: ElevatedButton.styleFrom(
            shape: StadiumBorder(),
            primary: d_red,
            padding: EdgeInsets.symmetric(
              horizontal: 125,
              vertical: 13,
            ),
          ),
          child: Text(
            'CONFIRM',
            style: GoogleFonts.poppins(
              color: Colors.white,
              fontSize: 15,
              fontWeight: FontWeight.w500,
            ),
          ),
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => MyApp(),
              ),
            );
          },
        ),
      ),
      SizedBox(height: 90),
      Align(
        alignment: Alignment.centerRight,
        child: Padding(
          padding: EdgeInsets.only(right: 35),
          child: TextButton(
            onPressed: () {
              Navigator.pop(context);
            },
            child: DelayedAnimation(
              delay: 6500,
              child: Text(
                "SKIP",
                style: GoogleFonts.poppins(
                  color: Colors.black,
                  fontSize: 18,
                  fontWeight: FontWeight.w600,
                ),
              ),
            ),
          ),
        ),
      ),
    ],
  ),
),

Notre body est structuré de la même façon que les autres pages de notre application, avec un widget SingleChildScrollView() en racine pour une structure de Column().

La première section comprend deux widgets Text que nous personnalisons avec Google Fonts et notre couleur globale, sans oublié les marges pour se rapprocher du design de base.

Après notre formulaire, nous avons un bouton SKIP qui a été personnalisé afin de permettre d'annuler par exemple la connexion.

Nous avons donc utilisé le widget Align pour l'aligner à droite et différentes propriétés du widget TextButton pour le personnaliser.

La deuxième section est le widget LoginForm que nous allons créer dans une classe séparée afin de le rendre dynamique:

class LoginForm extends StatefulWidget {
  @override
  _LoginFormState createState() => _LoginFormState();
}

class _LoginFormState extends State<LoginForm> {
  var _obscureText = true;
  @override
  Widget build(BuildContext context) {
    return Container(
      margin: EdgeInsets.symmetric(
        horizontal: 30,
      ),
      child: Column(
        children: [
          DelayedAnimation(
            delay: 3500,
            child: TextField(
              decoration: InputDecoration(
                labelText: 'Your Email',
                labelStyle: TextStyle(
                  color: Colors.grey[400],
                ),
              ),
            ),
          ),
          SizedBox(height: 30),
          DelayedAnimation(
            delay: 4500,
            child: TextField(
              obscureText: _obscureText,
              decoration: InputDecoration(
                labelStyle: TextStyle(
                  color: Colors.grey[400],
                ),
                labelText: 'Password',
                suffixIcon: IconButton(
                  icon: Icon(
                    Icons.visibility,
                    color: Colors.black,
                  ),
                  onPressed: () {
                    setState(() {
                      _obscureText = !_obscureText;
                    });
                  },
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

Cette section est constituée de nos deux principaux champs de texte que nous créons en utilisant le widget TextField et du bouton de confirmation du formulaire.

On utilise aussi le type StatefulWidget pour pouvoir masquer et afficher le contenu du champ mot de passe en modifiant la variable _obscureText.

Conclusion

Nous sommes donc rendus au terme de ce tuto Flutter animation sur la création d’une interface de connexion d’application de Yoga.

C’est le troisième d’une longue série qui viendra compléter la formation Flutter Révolution avec du contenu plus orienté sur le design pratique avec Flutter.

C’est aussi l’occasion pour un plus grand nombre de personnes de découvrir Flutter et de tester en direct son fonctionnement.

Si ce n’est pas déjà fait, commencez le cours gratuit Flutter pour installer Flutter et le configurer sur votre ordinateur.

Rejoignez le cours complet Flutter Révolution si vous souhaitez créer des applications complètes avec Firebase, Google Maps et Stripe.