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 :
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.
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
.
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
.
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 :
Si vous avez bien compris, nos modifications vont donc se porter sur la classe _MyHomePageState
et 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 formulaireTextFormField
pour les entrées texteRaisedButton
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 deshint
et deslabel
obscureText
: pour masquer le texte (utile pour les mots de passe)
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
Commentaires