Écrire votre première application Flutter, partie 1

Flutter est un kit d'interface utilisateur Google qui permet de développer des applications esthétiques compilées de manière native pour les mobiles, le Web et les ordinateurs de bureau, à partir d'un seul codebase. Gratuit et Open Source, Flutter fonctionne avec votre code existant. Il est utilisé par les développeurs et les entreprises du monde entier.

Dans cet atelier de programmation, vous allez créer une simple application mobile Flutter. Pour le suivre, il vous suffit de maîtriser les concepts de programmation de base (tels que les variables, les boucles et les conditions) et de programmation orientée objet. Il n'est pas nécessaire d'avoir de l'expérience en programmation Dart, mobile ou Web.

Points abordés dans la partie 1

  • Écrire une application Flutter qui s'intègre naturellement dans iOS, Android et le Web
  • Découvrir la structure de base d'une application Flutter
  • Rechercher et utiliser des packages pour étendre les fonctionnalités
  • Utiliser l'actualisation à chaud pour accélérer le cycle de développement
  • Implémenter un widget avec état
  • Créer une liste infinie à chargement différé

Dans la partie 2 de cet atelier de programmation, vous allez ajouter de l'interactivité, modifier le thème de l'application et ajouter la possibilité d'accéder à une nouvelle page (appelée "route" dans Flutter).

Objectifs de l'atelier (partie 1)

Vous implémenterez une application simple qui génère des noms d'entreprise. L'utilisateur pourra sélectionner et désélectionner les propositions, afin d'enregistrer celles qu'ils préfèrent. Le code génère 10 noms à la fois. À mesure que l'utilisateur fait défiler la page, d'autres noms sont générés. Le défilement est infini.

Le fichier GIF animé suivant montre le fonctionnement de l'application à la fin de cette partie de l'atelier :

6556f8b61acd6a89.gif

Qu'attendez-vous de cet atelier de programmation ?

Je suis novice en la matière et je voudrais avoir un bon aperçu. Je connais un peu le sujet, mais j'aimerais revoir certains points. Je recherche un exemple de code à utiliser dans mon projet. Je cherche des explications sur un point spécifique.

Pour cet atelier, vous avez besoin de deux logiciels : le SDK Flutter et un éditeur. Cet atelier de programmation suppose que vous utilisez Android Studio, mais vous pouvez vous servir de l'éditeur de votre choix.

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 (les outils Xcode doivent être installés)
  • L'émulateur Android (doit être configuré dans Android Studio)
  • Un navigateur (Chrome est requis pour le débogage)

b2f84ff91b0e1396.png Créez une application Flutter simple basée sur un modèle. Créez un projet Flutter appelé startup_namer, puis effectuez une migration vers Null Safety :

$ flutter create startup_namer
$ cd startup_namer
$ dart migrate --apply-changes

Vous allez principalement modifier lib/main.dart, où réside le code Dart.

b2f84ff91b0e1396.png Remplacez le contenu de lib/main.dart. Supprimez l'intégralité du code de lib/main.dart et remplacez-le par le code suivant, qui affiche "Hello World" au centre de l'écran.

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Welcome to Flutter',
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Welcome to Flutter'),
        ),
        body: const Center(
          child: const Text('Hello World'),
        ),
      ),
    );
  }
}

b2f84ff91b0e1396.png Exécutez l'application. Selon l'appareil utilisé, vous devriez voir le résultat pour Android, iOS ou le Web.

Android

iOS

cf1e10b838bf60ee.png Observations

  • Cet exemple crée une application Material Design. Le Material Design est un langage visuel standard sur mobile et sur le Web. Flutter offre une large gamme de widgets Material Design.
  • La méthode principale utilise la notation fléchée (=>) qui vous permet de raccourcir les fonctions ou méthodes d'une ligne.
  • L'application étend le widget StatelessWidget, ce qui en fait également un widget. Dans Flutter, pratiquement tout est un widget, y compris l'alignement, la marge intérieure et la mise en page.
  • Le widget Scaffold, issu de la bibliothèque Material, fournit une barre d'application par défaut, un titre et un corps qui contient l'arborescence de widgets de l'écran d'accueil. La sous-arborescence du widget peut être assez complexe.
  • La tâche principale d'un widget consiste à fournir une méthode build qui décrit comment afficher le widget en fonction d'autres widgets de niveau inférieur.
  • Le corps de cet exemple consiste en un widget Center contenant un widget enfant Text. Le widget Center aligne sa sous-arborescence au centre de l'écran.

Dans cette étape, vous allez utiliser un package Open Source nommé english_words, qui contient quelques milliers de mots anglais parmi les plus fréquents, ainsi que des fonctions utilitaires.

Vous trouverez le package english_words et de nombreux autres packages Open Source à l'adresse pub.dev.

b2f84ff91b0e1396.png Le fichier pubspec gère les éléments d'une application Flutter. Dans pubspec.yaml, ajoutez english_words: ^4.0.0 (english_words 4.0.0 ou version ultérieure) à la liste de dépendances :

dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^1.0.2
  english_words: ^4.0.0   # add this line

b2f84ff91b0e1396.png Lorsque vous affichez le fichier pubspec dans la vue de l'éditeur d'Android Studio, cliquez sur Packages get. Le package est alors intégré dans votre projet. Vous devriez obtenir le résultat suivant dans la console :

flutter packages get
Running "flutter packages get" in startup_namer...
Process finished with exit code 0

L'exécution de dart pub get génère également automatiquement le fichier pubspec.lock contenant la liste de tous les packages intégrés au projet ainsi que leurs numéros de version.

b2f84ff91b0e1396.png Dans lib/main.dart, importez le nouveau package :

import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';  // Add this line.

Lors de la saisie, Android Studio offre des suggestions de bibliothèques à importer. Il affiche ensuite la chaîne d'importation en gris, pour indiquer que la bibliothèque importée n'est pas encore utilisée.

Vous allez maintenant utiliser le package english_words pour générer du texte au lieu de "Hello World".

b2f84ff91b0e1396.png Apportez les modifications suivantes :

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

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final wordPair = WordPair.random(); // Add this line.
    return MaterialApp(
      title: 'Welcome to Flutter',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Welcome to Flutter'),
        ),
        body: Center(                       // Drop the const, and
          //child: Text('Hello World'),     // Replace this text...
          child: Text(wordPair.asPascalCase),  // With this text.
        ),
      ),
    );
  }
}

b2f84ff91b0e1396.png Si l'application est en cours d'exécution, effectuez une actualisation à chaud e11f6ccd1560a28b.png pour la mettre à jour. Si vous utilisez la ligne de commande, saisissez r. Chaque fois que vous cliquez sur l'icône d'actualisation à chaud ou que vous enregistrez le projet, une paire de mots différente, sélectionnée de manière aléatoire, s'affiche dans l'application en cours d'exécution. En effet, l'association de mots est générée par la méthode build, qui s'exécute chaque fois que MaterialApp nécessite un rendu, ou lorsque vous activez ou désactivez la plate-forme dans l'outil d'inspection Flutter.

Android

iOS

Des problèmes ?

Si votre application ne s'exécute pas correctement, vérifiez que vous n'avez pas fait de faute de frappe. Si nécessaire, utilisez le code contenu dans les liens suivants pour résoudre la situation.

Les widgets sans état sont immuables. En d'autres termes, leurs propriétés ne peuvent pas être modifiées, car toutes les valeurs sont définitives.

Les widgets avec état conservent leur état pendant leur durée de vie. L'implémentation d'un widget avec état nécessite au moins deux classes : la classe StatefulWidget qui crée à son tour une instance d'une classe State. L'objet StatefulWidget est lui-même immuable et peut être supprimé et généré à nouveau, mais l'objet State persiste pendant la durée de vie du widget.

Dans cette étape, vous allez ajouter un widget avec état, RandomWords, qui crée sa classe State (_RandomWordsState). Vous utiliserez ensuite RandomWords en tant qu'enfant du widget existant sans état MyApp.

b2f84ff91b0e1396.png Créez un code récurrent pour un widget avec état.

Bien que la solution l'insère au bas du fichier, vous pouvez l'ajouter n'importe où en dehors de MyApp. Dans lib/main.dart, placez votre curseur à la fin du code, puis appuyez sur la touche Entrée quelques fois pour passer à la ligne. Dans votre IDE, saisissez stful. L'éditeur vous demande si vous souhaitez créer un widget Stateful. Appuyez sur Entrée pour accepter. Le code récurrent pour deux classes s'affiche et le curseur est positionné pour vous permettre de saisir le nom de votre widget sans état.

b2f84ff91b0e1396.png Saisissez RandomWords comme nom de votre widget.

Comme vous pouvez le voir dans le code ci-dessous, le widget RandomWords se borne pratiquement à créer sa classe State.

Une fois que vous avez indiqué RandomWords comme nom du widget avec état, l'IDE met automatiquement à jour la classe State associée, en la nommant _RandomWordsState. Par défaut, le nom de la classe State est précédé d'un trait de soulignement. Dans le langage Dart, l'utilisation d'un trait de soulignement comme préfixe d'un identifiant permet d'appliquer la confidentialité. Cette méthode est recommandée pour les objets State.

L'IDE met également à jour automatiquement la classe d'état pour étendre State<RandomWords>, indiquant que vous utilisez une classe State générique dédiée à RandomWords. La logique principale de l'application est de conserver l'état du widget RandomWords. Cette classe enregistre la liste des paires de mots générées, qui s'étend à l'infini à mesure que l'utilisateur fait défiler l'écran. Dans la deuxième partie de cet atelier, elle permet d'enregistrer les paires de mots favorites à mesure que l'utilisateur les ajoute ou les supprime de la liste en activant ou en désactivant l'icône en forme de cœur.

Les deux classes se présentent maintenant comme suit :

class RandomWords extends StatefulWidget {
  @override
  _RandomWordsState createState() => _RandomWordsState();
}

class _RandomWordsState extends State<RandomWords> {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

b2f84ff91b0e1396.png Mettez à jour la méthode build() dans _RandomWordsState.

Remplacez return Container(); par les deux lignes suivantes :

class _RandomWordsState extends State<RandomWords> {
  @override
  Widget build(BuildContext context) {
    final wordPair = WordPair.random();      // NEW
    return Text(wordPair.asPascalCase);      // NEW
  }
}

b2f84ff91b0e1396.png Supprimez le code de génération de mots de MyApp en apportant les modifications suivantes :

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final wordPair = WordPair.random();  // DELETE

    return MaterialApp(
      title: 'Welcome to Flutter',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Welcome to Flutter'),
        ),
        body: Center(
          //child: Text(wordPair.asPascalCase), // REPLACE with...
          child: RandomWords(),                 // ...this line
        ),
      ),
    );
  }
}

b2f84ff91b0e1396.png Effectuez une actualisation à chaud de l'application. Celle-ci doit se comporter comme auparavant et afficher des paires de mots chaque fois que vous actualisez la page ou que vous l'enregistrez.

Des problèmes ?

Si votre application ne fonctionne pas correctement, vous pouvez utiliser le code contenu dans le lien suivant pour résoudre la situation.

Dans cette étape, vous allez développer _RandomWordsState afin qu'il génère et affiche la liste des paires de mots. À mesure que l'utilisateur fait défiler la page, la liste (affichée dans un widget ListView) s'étend à l'infini. Le constructeur de fabrique builder dans ListView vous permet de créer une liste à la demande.

b2f84ff91b0e1396.png Ajoutez des variables d'état à la classe _RandomWordsState.

Ajoutez une liste _suggestions pour enregistrer les paires de mots suggérées. Ajoutez également une variable _biggerFont pour augmenter la taille de la police.

class _RandomWordsState extends State<RandomWords> {
  final _suggestions = <WordPair>[];                 // NEW
  final _biggerFont = const TextStyle(fontSize: 18); // NEW
  ...
}

Ensuite, ajoutez une fonction _buildSuggestions() à la classe _RandomWordsState. Cette méthode crée le widget ListView qui affiche la paire de mots proposée.

La classe ListView fournit une propriété Builder (itemBuilder), qui est un générateur de fabrique et une fonction de rappel spécifiée en tant que fonction anonyme. Deux paramètres sont transmis à la fonction : BuildContext et l'itérateur de ligne, i. L'itérateur commence avec la valeur 0 et s'incrémente de 1 chaque fois que la fonction est appelée (une seule fois par paire de mots proposée). Avec ce modèle, la liste de suggestions continue à se développer à mesure que l'utilisateur fait défiler la page.

b2f84ff91b0e1396.png Ajoutez toute la fonction _buildSuggestions.

Dans la classe _RandomWordsState, ajoutez la fonction suivante en supprimant les commentaires, si vous le souhaitez :

  Widget _buildSuggestions() {
    return ListView.builder(
      padding: const EdgeInsets.all(16),
      // The itemBuilder callback is called once per suggested
      // word pairing, and places each suggestion into a ListTile
      // row. For even rows, the function adds a ListTile row for
      // the word pairing. For odd rows, the function adds a
      // Divider widget to visually separate the entries. Note that
      // the divider may be difficult to see on smaller devices.
      itemBuilder: (BuildContext _context, int i) {
        // Add a one-pixel-high divider widget before each row
        // in the ListView.
        if (i.isOdd) {
          return Divider();
        }

        // The syntax "i ~/ 2" divides i by 2 and returns an
        // integer result.
        // For example: 1, 2, 3, 4, 5 becomes 0, 1, 1, 2, 2.
        // This calculates the actual number of word pairings
        // in the ListView,minus the divider widgets.
        final int index = i ~/ 2;
        // If you've reached the end of the available word
        // pairings...
        if (index >= _suggestions.length) {
          // ...then generate 10 more and add them to the
          // suggestions list.
          _suggestions.addAll(generateWordPairs().take(10));
        }
        return _buildRow(_suggestions[index]);
      }
    );
  }

La fonction _buildSuggestions appelle _buildRow une seule fois par paire de mots. Elle affiche chaque nouvelle paire dans un élément ListTile, ce qui vous permettra d'améliorer l'apparence des lignes dans la partie 2 de cet atelier de programmation.

b2f84ff91b0e1396.png Ajoutez une fonction _buildRow à _RandomWordsState :

  Widget _buildRow(WordPair pair) {
    return ListTile(
      title: Text(
        pair.asPascalCase,
        style: _biggerFont,
      ),
    );
  }

b2f84ff91b0e1396.png Mettez à jour la méthode build pour _RandomWordsState.

Modifiez-la pour qu'elle utilise _buildSuggestions() au lieu d'appeler directement la bibliothèque de génération de mots. En effet, le widget Scaffold implémente la mise en page visuelle de base du Material Design.

  @override
  Widget build(BuildContext context) {
    //final wordPair = WordPair.random(); // Delete these...
    //return Text(wordPair.asPascalCase); // ... two lines.

    return Scaffold (                     // Add from here...
      appBar: AppBar(
        title: Text('Startup Name Generator'),
      ),
      body: _buildSuggestions(),
    );                                      // ... to here.
  }

b2f84ff91b0e1396.png Mettez à jour la méthode build pour MyApp, modifiez le titre, supprimez AppBar et remplacez la propriété Home par un widget RandomWords.

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Startup Name Generator',
      home: RandomWords(),
    );
  }

b2f84ff91b0e1396.png Redémarrez l'application. La liste des paires de mots devrait continuer à se développer, quelle que soit la durée du défilement.

Android

iOS

Des problèmes ?

Si votre application ne fonctionne pas correctement, vous pouvez utiliser le code contenu dans le lien suivant pour résoudre la situation.

Félicitations !

Vous avez terminé la première partie de cet atelier de programmation. Pour améliorer cette application, passez à la deuxième partie, où vous pourrez la modifier comme suit :

  • Ajouter de l'interactivité
  • Ajouter la possibilité d'accéder à une nouvelle route
  • Modifier la couleur du thème

Une fois la partie 2 terminée, l'application se présentera comme suit :

7fcab088cd22cff7.gif

Pour aller plus loin

Pour en savoir plus sur le SDK Flutter, consultez les ressources suivantes :

Autres ressources :

Vous pouvez également échanger avec la communauté Flutter.