Loupe

Flutter begins

Dans les articles précédents, nous avons parlé de Flutter puis de l'installation pour Windows.

Aujourd'hui, pour continuer la saga Flutter, nous allons (enfin) attaquer le développement en réalisant une première application contenant simplement un formulaire de connexion.

Un grand nombre d'applications contiennent une page de login. C'est le cas de quasiment toutes les applications que j'ai eu l'occasion de réaliser. Alors, forcément, lorsque j'ai commencé à investiguer Flutter, la première page que j'ai voulu créer fut donc..................(wait for it)..................un formulaire de connexion !

Nouveau projet

Si vous avez bien suivi l'article précédent (et j'espère que c'est le cas !), nous nous sommes quittés sur la création d'un nouveau projet. Pour les cancres du fond de la classe qui n'ont pas suivi, je vais faire un petit rappel :)

Avec VS Code, la création d'un nouveau projet se fait à l'aide de l'option Flutter: New Project accessible dans la palette de commandes (Ctrl+Shift+P). Ouvrons donc la palette de commande et tapons "flutter", la commande Flutter: New Project apparait, cliquons dessus pour appeler la commande. Il nous est ensuite demandé de choisir un nom de projet et le dossier dans lequel le placer. L'application se crée puis le fichier main.dart après l'apparition de cette popup :

01_create_project.png

L'application

L'application de base générée par la commande Flutter : New project n'est pas l'habituel hello world mais une application contenant un texte "You have pushed the button this many times: N" et un bouton. Le clic du bouton incrémente le nombre affiché dans le texte.

02_not_hello_world - Copie.jpg

Le fichier main.dart créé automatiquement lors de l'étape précédante est composé de 3 classes :

  • MyApp
  • MyHomePage
  • _MyHomePageState

MyApp

class MyApp extends StatelessWidget {
    
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

La class MyApp contient la définition de l'application. Ce widget stateless est le widget à la racine de l'application et, comme tout widget stateless, il contient la méthode build pour dessiner l'interface utilisateur représentant le widget. Ici, la méthode retourne simplement le widget MaterialApp afin d'avoir une application utilisant le material design. Nous verrons dans un article prochain comment personnaliser le thème de l'application. Ce qui nous intéresse principalement ici est la propriété home qui permet de définir quel Widget sera la page d'acueil de l'application. Ceci nous amène donc à la classe MyHomePage.

MyHomePage

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  
  // This class is the configuration for the state. It holds the values (in this
  // case the title) provided by the parent (in this case the App widget) and
  // used by the build method of the State. Fields in a Widget subclass are
  // always marked "final".

  final String title;

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

La classe MyHomePage est le widget contenant la page d'accueil de l'application. Comme vous pouvez le voir, il s'agit d'un widget stateful. Si vous vous souvenez bien, un widget stateful est dynamique, il écoute les modifications des propriétés et met à jour l'interface en fonction de ces changements.

À la différence du widget stateless, ce n'est pas le widget qui implémente la méthode de build mais son état (state). Il possède donc la méthode createState() qui permet d'indiquer au widget quel classe sera l'état du widget. Ici, l'état du widget est géré par la classe _MyHomePageState.

_MyHomePageState

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

La classe _MyHomePageState est l'état du widget MyHomePage. C'est donc ici que la méthode build est implémentée. Comme nous l'avons vu dans la classe MyApp, la méthode build dessine l'interface utilisateur. Sauf qu'ici, comme nous sommes dans un widget stateful, à la différence de la méthode MyApp.build qui n'est appelée qu'une seule fois, celle-ci sera appelé à chaque fois d'une propriété de l'état changera.

De base, l'état _MyHomePageState contient une propriété privée counter. Ce nombre est incrémenté à l'aide de la méthode _incrementCounter(). Comme vous pouvez le voir, cette méthode incrémente la variable counter à l'intérieur de l'appel à la méthode setState. C'est cette méthode qui indique à Flutter qu'une propriété de l'état a changé et qu'il faut donc redessiner l'interface. Sans l'appel à setState(), Flutter ne saurait pas qu'un changement a eu lieu et l'interface ne changerait pas.

Détaillons maitenant le contenu de la méthode build qui est plus complèxe que dans la classe MyApp. Le principe de construction d'interface utilisateur avec Flutter est d'imbriquer des widgets dans des widgets. Ici le widget retourné par la méthode build est un Scaffold (littéralement échafaudage, (ou échafaud, mais c'est plus glauque)). Le scaffold est le widget qui implémente la structure de base d'un layout utilisant le material design. Il contient, ici, 3 widgets : appBar, body et floatingActionButton.

03_scaffold.png

Le widget qui va nous intéresser est le body. Si vous souhaitez plus d'information sur l'appBar et le floatingActionButton, rendez-vous sur la documentation officielle de l'AppBar et du FAB ou du Scaffold.

Le widget du body est ici composé d'un widget Center, lui même composé d'un widget Column, lui même composé deux deux widgets Text.

04_body.jpg

Le widget Center contient un seul widget enfant (child) et le positionne au milieu du widget parent. En Flutter, tout est un widget, donc ici pour center un contenu, le widget Center est utilisé.

Ici, son widget enfant est un widget Column. Ce widget contient une liste de widgets enfants (children) et les places les uns à la suite des autres dans une même colonne (d'où le nom). Tous les widgets de la liste seront donc alignés verticalement.

Formulaire de connexion

Maintenant que nous avons vu en détail la construction de la page, modifions-la pour obtenir un formulaire de connexion comme ceci :

04_login.png

Si vous avez bien compris, nos modifications vont donc se porter sur la classe _MyHomePageStateet plus précisément dans la méthode build.

Ménage

Sur cette page de connexion, le floating action button n'a plus intéret, nous pouvons donc le supprimer ainsi que la propriété _counter et la méthode _incrementCounter auxquelles il est lié. Nous pouvons également supprimer tout le code qui compose le body puisque nous allons le réécrire intégralement.

De quel widgets avons-nous besoin pour écrire ce formulaire ?

Fouillons dans la liste des widgets et voyons lesquels nous seront utiles.

  • Form pour définir le formulaire
  • TextFormField pour les entrées texte
  • RaisedButton pour valider le formulaire

Le widget Form a besoin d'une clé pour être identifié de manière unique. Ajoutons donc la propriété ci-dessous à notre classe _MyHomePageState.

// Create a global key that will uniquely identify the Form widget
final formKey = GlobalKey<FormState>();

Nous aurons également besoin de widget de layout tel que Column, et Padding pour rendre notre formulaire légèrement plus joli.

Body

Voici donc à quoi doit ressembler notre body après l'ajout de ces widgets.

body: new Form(
    key: formKey,
    child: Padding(
        padding: new EdgeInsets.all(20),
        child: new Column(
            children: <Widget>[
                new TextFormField(
                controller: emailController,
                keyboardType:
                    TextInputType.emailAddress, // Use email input type for emails.
                decoration: new InputDecoration(
                    hintText: 'me@example.com', labelText: 'Adresse email'),
                ),
                new TextFormField(
                obscureText: true, // Use secure text for passwords.
                decoration:
                    new InputDecoration(hintText: '***', labelText: 'Mot de passe'),
                ),
                new Container(
                    child: new RaisedButton(
                        child: new Text('Connexion'),
                        onPressed: logIn,
                    ),
                    margin: new EdgeInsets.only(top: 20.0),
                ),
            ],
        ),
    ),
),

Comme vous pouvez le constater, le widget TextFormField peut se composer de plusieurs propriétés :

  • controller : pour récupérer le texte entré
  • keyboardType : pour définir le type de clavier (utile pour les adresses email)
  • decoration : pour personnaliser les inputs avec des hint et des label
  • obscureText : pour masquer le texte (utile pour les mots de passe)

05_textformfield.jpg

Ajout de validateurs

Notre formulaire pourrait être terminé à ce point, mais souvent dans les formulaires de connexion il est intéressant d'avoir des validateurs afin de vérifier que les champs sont remplis, que l'adresse mail est valide, ... Ici, j'ai bien envie de rajouter ces validateurs, vous aussi ? Let's go then !

La validation de l'adresse mail se fait à l'aide d'une regex, nous allons donc ajouter une propriété contenant la chaine de caractères de la regex.

final String emailPattern =
      r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';

Ensuite, renseignons le champs validator des widgets TextFormField pour ajouter nos validateurs :

validator: (value) {
    if (value.isEmpty) {
    return 'Veuillez saisir votre adresse email';
    }

    RegExp regExp = new RegExp(emailPattern);
    if (!regExp.hasMatch(value)) {
    return "Veuillez saisir une adresse email valide.";
    }
},

TADA !

Et voila, nous avons réussi à créer un formulaire de connexion avec validateurs tout simplement. Vous pourrez retrouvez l'intégralité du code source de la micro application ici

Ces billets pourraient aussi vous intéresser

Vous nous direz ?!

Commentaires

comments powered by Disqus