4h pour découvrir…Flutter


Qu’est-ce que c’est ?

Flutter transforms the app development process. Build, test, and deploy beautiful mobile, web, desktop, and embedded apps from a single codebase.

Site Flutter

Framework créé par Google en 2018, Flutter permet donc de créer des applications pour Android, iOS, le web, Linux, Windows, Mac et matériel embarqué !

Contrairement à Ionic ou Cordova, Flutter compile en natif pour les différentes plateformes.

Flutter est basé sur le langage Dart, lui aussi créé par Google.

Le but originel de l’équipe de Flutter était de fournir une meilleure manière de développer des applications pour mobile.


De quoi a-t’on besoin pour commencer ?

SDK

Il suffit d’installer le SDK Flutter en suivant le guide disponible à l’adresse https://flutter.dev/docs/get-started/install en fonction de votre environnement.

IDE

IDEs recommandés

  • VSCode
  • Android Studio / IntelliJ
  • Plugin Flutter correspondant

Appareil cible

En fonction des plateformes ciblées, il est possible d’utiliser des appareils physiques (browser, Android phone, iPhone…) ou des émulateurs.


Est-ce que mon environnement est prêt ?

La commande flutter doctor vérifie si tous les éléments nécessaires au développement Flutter sont disponibles et donc si votre environnement de dev est prêt.

Exemple de retour de la commande

➜ flutter doctor 
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.10.5, on Ubuntu 22.04.2 LTS 5.17.0-1033-oem, locale fr_FR.UTF-8)
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
[✓] Chrome - develop for the web
[✓] Linux toolchain - develop for Linux desktop
[✓] Android Studio (version 2022.2)
[✓] Connected device (3 available)
[✓] Network resources

• No issues found!

Go !

Création du projet

La commande flutter create my_app initalise un nouveau projet dans le dossier my_app du répertoire courant.

Structure d’un projet Flutter

Pour commencer, voici les fichiers/dossiers importants:

  • android et ios : dossiers spécifiques aux plateformes
  • lib : c’est ici que presque tout se passe. Le code source Dart est écrit dans ce répertoire.
  • test : les tests unitaires, s’il y en a
  • pubsec.yaml : la config du projet (nom, description, dépendances…). C’est un peu l’équivalent de composer.json ou package.json
  • .metadata et .packages : fichiers de config utiles au fonctionnement de Flutter, c’est tout ce qu’on a besoin de savoir pour le moment sur ces 2 fichiers.

Un petit dernier, analysis-options.yaml, sert à informer l’IDE de la manière dont il doit analyser le code.


Widgets

In Flutter, everything in Widgets

Flutter permet de créer des applications avec un seul langage : Dart.
Ce langage se charge aussi bien de créer la logique métier, que de générer les interfaces nécessaires.
Tout cela est fait dans des Widgets. (les widges fournis sont disponibles ici)

Un widget est en fait une classe, qui peut avoir des propriétés et des méthodes.
D’ailleurs, chaque widget a une méthode build(BuildContext context).

Exemple de widget Flutter:

class RootWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Text('Hello world');
  }
}

Ici, on a donc un Widget RootWidget qui étend le StatelessWidget (voir plus loin).
Sa méthode build renvoie un Widget (ici le widget Text) et reçoit un BuildContext nommé context en argument.

Pour lancer une application Flutter, on a besoin d’une fonction main.
Cette fonction appellera la fonction runApp qui reçoit un widget.

Voici comment lancer l’application avec le widget RootWidget précédent:

// import the Dart package needed for all Flutter apps
import 'package:flutter/material.dart';

// Here is main calling runApp
void main() => runApp(RootWidget());

// And here is your root widget
class RootWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Text('Hello world');
  }
}

Types de widgets

Valeur

Ici, ce sont des widgets qui gèrent l’affichage ou l’obtention de valeur.

En voici quelques exemples

Text

Vu plus haut, il est chargé, comme son nom l’indique, d’afficher le texte qui lui est passé en paramètre.

Text('Hello world'),

Icon / Image

Permettent d’afficher des icônes (liste ici)

Icon(
  Icons.cake,
  color: Colors.red,
  size: 200,
)

ou des images.

TextField

Input de type texte

TextField(
  onChanged: (String val) => _searchTerm = val,
),

La méthode onChanged de ce widget est appelée à chaque modification de la valeur.


Layout

Ces widgets sont utiles pour assurer la mise en page des widgets.

MaterialApp

C’est le widget principal (mais invisible pour l’utilisateur) de mise en page, c’est ici que sont définis:

  • le thème (font, colors…)
  • les routes
  • la page d’accueil

Exemple

Widget build(BuildContext context) {
  return MaterialApp(
    home: MainWidget(),
    title: "Ch 6 Layouts",
    theme: ThemeData(primarySwatch: Colors.deepPurple),
    routes: <String, WidgetBuilder>{
      '/scene1: (BuildContext ctx) => MyWidget1(),
      '/scene2: (BuildContext ctx) => MyWidget2(),
      '/scene3: (BuildContext ctx) => MyWidget3(),
    },
  );
}

Scaffold

Celui-ci vient juste après MaterialApp et fournit la structure de l’app, avec par exemple, la possibilité d’ajouter à l’app:

  • l'AppBar (barre de titre de l’app)
  • une navigation en bas de l’écran
  • un menu hamburger
  • un FAB (floating action button)
@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: MyAppBar(),
    drawer: MyNavigationDrawer(),
    body: TheRealContentOfThisPartOfTheApp(),
    floatingActionButton: FloatingActionButton(
    child: Icon(Icons.add),
      onPressed: () { /* Do things here */},
    ),
    bottomNavigationBar: MyBottomNavBar,
  );
}

SafeArea

Les écrans de smartphones ayant des angles arrondis, une barre d’état…, il se peut que certaines parties de l’app soient tronquées ou cachées.
SafeArea est là pour empêcher ça.

Il suffit d’encadrer le contenu du body par un SafeArea widget pour qu’il se charge de faire le rendu de l’app en fonction de l’écran.


State

State is widget data whose change requires a re-render.

—Rap Payne

Fonctionnement

Les widgets peuvent être Stateless ou Stateful.

Les widgets Stateless peuvent contenir des données, mais celles-ci ne changent pas de valeur OU ne changent pas la manière dont doit être affichée l’écran.

Par opposition, les Stateful ont des données qui changent et qui impactent l’affichage pour que celui-ci soit à jour.

Voici à quoi ressemble la base d’un widget Stateful

class Foo extends StatefulWidget {
  @override
  _FooState createState() => _FooState();
}

class _FooState extends State<Foo> {
  //Private variables here are considered the 'state'
  @override
  Widget build(BuildContext context) {
    return someWidget;
  }
}

Deux classes sont nécessaires:

  • la classe du widget
    • publique
    • étend StatefulWidget
  • celle du state
    • private (underscore devant le nom)
    • responsable de:
      • maintenir le state
      • définir la méthode build

Le state doit être modifié:

  • depuis sa classe
  • via la méthode setState
setState(() {
  // Make all changes to state variables here...
  _value = 42; // <-- ... Like this
});

Le changement de state amène à la re-création du widget. S’il contient lui-même des widgets, tous les éléments « enfants » seront recrées !

Passer le state vers le bas

Pour « faire descendre » le state dans l’arborescence, il est possible de déclarer une variable de classe (la classe du widget, pas celle du state).

Cette variable est ensuite accessible dans la classe du state via l’objet widget mis à disposition par Flutter.

Exemple

class Foo extends StatefulWidget {
  final String passedIn;
  // Value passed in from its host
  ColorValueChanger({Key key, this.passedIn}) : super(key: key);
  _FooState createState() => new _FooState();
}

class _FooState extends State<Foo> {
  @override
  Widget build(BuildContext context) {
    return Text(widget.passedIn,);
  }
}

Passer le state vers le haut

Dans Flutter, il n’est pas possible de faire « remonter » des informations.

Il existe tout de même un trick pour faire en sorte qu’un widget « parent » soit informé d’une modification sur un widget « enfant ».

Il est possible de passer une fonction par référence du « parent » vers l’ »enfant ».
Ce qui fait que, lorsque l’ »enfant » appellera cette fonction, c’est en réalité la fonction du « parent » qu’il appellera.


Publié

dans

par

Étiquettes :