Chapitre 2 Les Bases de Dart Pour Flutter MR NDIAYE
Chapitre 2 Les Bases de Dart Pour Flutter MR NDIAYE
Flutter
Environnement Dart
Dans ce chapitre nous allons apprendre les bases du langage Dart qui vont nous
permettre de coder nos premières applications Flutter.
Exécuter du Dart
Vous avez plusieurs moyens d'exécuter du Dart et nous allons vous les apprendre.
DartPad
An online Dart editor with support for console, web, and Flutter apps.
https://dartpad.dev/
Bases de Dart
Si vous connaissez Java, JavaScript ou Typescript, apprendre les bases de Dart sera
extrêmement simple et ne nécessitera que ce chapitre !
Nous allons commencer par un petit Hello World ! comme l'usage nous y oblige ;)
En Dart chaque application doit avoir une fonction main() qui sert d'entrée dans
l'application.
Voici l'exemple :
Ici, nous avons une variable qui stocke une référence à un objet String.
Nous n'avons pas besoin de spécifier le type car il est inféré par Dart automatiquement.
Vous aurez une erreur car prenom a été typé en String automatiquement par Dart, et
son type ne peut pas être modifié ultérieurement.
Pour cela il faut déclarer un des types Dart : numbers, strings, booleans, lists, sets,
maps, runes et les symbols.
1. Numbers (Nombres)
Les nombres en Dart représentent les valeurs numériques. Il existe deux types de
nombres : les entiers (int) et les décimaux qui sont des nombres flottants(double).
Les nombres peuvent être utilisés pour effectuer des calculs mathématiques et des
opérations arithmétiques.
Exemple :
Le type num
En Dart num est un type générique qui représente tous les types numériques, à la
fois les entiers et les décimaux. Il peut être utilisé pour stocker des valeurs
numériques sans se soucier du type spécifique.
Voici un exemple d'utilisation du type num :
Dans cet exemple, la variable prix est déclarée avec le type num et est initialisée
avec une valeur décimale. La variable quantite est également de type num et
stocke un entier. En multipliant prix par quantite , nous obtenons le total, qui est
également de type num .
Le type num est utile lorsque vous voulez travailler avec des valeurs numériques
sans vous soucier du type spécifique (int ou double). Cependant, il convient de
noter que si vous utilisez le type num , certaines opérations spécifiques aux
entiers ou aux décimaux peuvent ne pas être disponibles, car elles dépendent
du type sous-jacent.
Si vous avez besoin d'opérations spécifiques aux entiers ou aux décimaux, il est
recommandé d'utiliser les types int ou double respectivement, plutôt que le
type générique num .
Exemple :
3. Booleans (Booléens)
Les booléens en Dart représentent les valeurs logiques. Ils peuvent être soit vrai
(true) soit faux (false). Les booléens sont couramment utilisés pour les décisions
conditionnelles et les expressions logiques.
Exemple :
4. Lists (Listes)
5. Sets (Ensembles)
Les ensembles en Dart sont des collections non ordonnées d'éléments uniques. Ils
ne permettent pas d'avoir des doublons. Les ensembles sont utiles lorsque vous
avez besoin de vérifier rapidement si un élément est présent ou non.
Exemple :
6. Maps (Dictionnaires)
Les dictionnaires en Dart, également appelés maps, sont des collections
d'associations clé-valeur. Chaque élément est composé d'une clé unique et d'une
valeur correspondante. Les clés doivent être uniques, mais les valeurs peuvent être
dupliquées.
Exemple :
7. Runes
Les runes en Dart représentent un point de code Unicode. Ils sont utilisés pour
manipuler des caractères Unicode spécifiques, tels que des emojis ou des
caractères internationaux.
Exemple :
print(heart);
8. Symbols (Symboles)
Les symboles en Dart sont utilisés pour représenter des identifiants uniques. Ils sont
souvent utilisés dans les métaprogrammes ou les réflexions pour identifier des
éléments tels que des fonctions, des classes ou des variables.
Exemple :
Ces types de données en Dart sont utilisés pour représenter et manipuler différentes
valeurs dans vos programmes. En comprenant ces types, vous pouvez créer des
variables et des structures de données appropriées pour votre application.
Interpolation de variable
Comme en JavaScript, vous pouvez interpoler des variables avec $var ou ${var} :
Les const sont des constantes qui doivent être définies au moment de la compilation.
Elles sont initialisées et assignées au même moment.
Les final sont des constantes, qui ne peuvent donc être initialisées qu'une seule fois,
mais qui peuvent être assignées pendant l'exécution. Autrement dit, elles peuvent
recevoir une valeur déterminée au moment de l'exécution.
void main() {
// Deux moyens de déclarer des const :
const pi = 3.14;
const double pi2 = 3.13;
// Ne compilera pas :
pi = 24;
1. Les constantes (const) ne peuvent pas être réassignées une fois qu'elles ont été
déclarées. Par conséquent, la ligne pi = 24; entraînera une erreur de compilation.
Ici, Dart va inférer que le type est List<int>, c'est-à-dire qu'il va attribuer
automatiquement le type List contenant des int.
Il ne sera donc pas possible d'ajouter autre chose :
Vous pouvez également définir explicitement une list et la typer comme vous le
souhaitez :
La propriété length
Comme en JavaScript, les lists commencent à l'index 0.
Les lists ont une propriété length pour connaître la longueur de celle-ci.
Le dernier élément d'une list est donc à l'index list.length - 1.
Quelques méthodes
Il existe environ 50 méthodes en Dart pour les lists. Nous les verrons au fur à
mesure dès que nous en aurons besoin.
Mais nous allons voir quelques méthodes basiques dès maintenant pour les lists :
add(), addAll(), clear(), indexOf(), contains(), remove() et removeAt().
void main() {
var list = [1,2,1];
2. Les maps
Un map est un objet qui associe des clés et des valeurs. Les clés et les valeurs
peuvent avoir tout type d'objet.
Les clés doivent être uniques mais pas les valeurs.
En fait, les maps en Dart sont extrêmement semblables aux maps en JavaScript. Il
n'existe par contre pas d'objets littéraux comme en JavaScript.
Définir un map
Vous pouvez définir un map avec var :
var map = {
'clé1': 'valeur1',
print(map1['clé1']); // 'valeur1'
print(map2[1]); // 'valeur1'
print(map3['clé3']); // 45.22
La propriété length
Les maps ont également une propriété length qui fonctionne de la même manière
que pour les lists.
var map1 = {
'clé1': 'valeur1',
1: 'valeur2'
};
Opérateur spread
Il est possible d'utiliser l'opérateur spread avec un map également :
void main() {
var map1 = {
'clé1': 'valeur1',
1: 'valeur2'
};
var map2 = {
...map1,
'clé3': 44.23
};
Quelques méthodes
Nous allons voir quelques méthodes basiques pour les maps : containsKey,
containsValue et remove :
Bien sûr nous sommes loin d'avoir fait le tour des lists et des maps en Dart ! Mais
c'est une approche suffisante pour commencer Flutter.
Les fonctions
Les fonctions en Dart
Comme le reste en Dart, les fonctions sont des objets.
Elles peuvent donc être assignées à des variables et passées comme arguments à
d'autres fonctions.
void direBonjour() {
print('Bonjour');
}
direBonjour();
Par exemple :
Les paramètres
En Dart il vaut mieux également typer les paramètres.
void main() {
var nomComplet = getFullName('Babacar', 'NDIAYE');
print(nomComplet);
}
//Console
Babacar NDIAYE
Ce programme déclare une fonction getFullName qui prend deux paramètres : prenom
Et lors de l'utilisation :
Voici un exemple :
void main() {
var nomComplet = getFullName(nom: 'NDIAYE');
print(nomComplet);
}
Si vous utilisez des paramètres nommés et que vous ne fournissez pas une valeur
par défaut avec = (nous le verrons plus bas dans la leçon) ou que vous ne spécifiez
pas qu'ils sont obligatoires avec required, vous aurez une erreur.
En effet, dans ce cas, leur valeur par défaut est null et donc il faut le préciser en
ajoutant un ? à la fin du type.
Le point d'interrogation à la fin d'un type permet d'ajouter la valeur null à ce type.
En effet, Dart a une fonctionnalité appelée null safety (sécurité contre les null) qui
vous oblige à spécifier si une variable ou un paramètre peut contenir la valeur null
ou non.
Voici un exemple :
void main() {
var nomComplet = getFullName(prenom: 'Babacar', nom: 'NDIAYE');
print(nomComplet);
}
void main() {
var nomComplet = getFullName('Babacar'); // Error: Too few posi
// 2 required, 1 given.
print(nomComplet);
}
void main() {
var nomComplet = getFullName('Babacar');
print(nomComplet);
}
void main() {
var nomComplet = getFullName();
print(nomComplet);
}
void main() {
var nomComplet = getFullName(prenom: 'Babacar');
print(nomComplet);
}
Par exemple :
void main() {
List<int> list = [1,2,3,4];
list.forEach((item) {
print(item);
});
print('\nFonction fléchée anonyme\n');
// Ou encore avec une fonction fléchée anonyme :
list.forEach((item) => print(item));
}
//Console
1
2
3
4
1
2
3
4
Attention ! Contrairement au JavaScript, si vous n'avez qu'un seul paramètre dans une
fonction fléchée anonyme vous ne pouvez pas omettre les parenthèses.
void main() {
var numbers = [1, 2, 3, 4, 5];
Dans cet exemple, nous utilisons une fonction fléchée anonyme avec un seul paramètre
number pour calculer le carré de chaque nombre dans la liste numbers . La syntaxe (number)
=> number * number est utilisée pour définir la fonction fléchée anonyme.
Notez que les parenthèses autour du paramètre number sont nécessaires. Si vous
essayez de les omettre en utilisant simplement number => number * number , cela entraînera
une erreur de syntaxe.
Donc, pour récapituler, lorsque vous utilisez une fonction fléchée anonyme en Dart avec
un seul paramètre, assurez-vous d'inclure les parenthèses autour du paramètre. Cela
garantit que votre code est correctement interprété par le compilateur Dart.
main() {
List<int> list = [1,2,3,4];
list.forEach(print);
}
Voici un autre exemple avec une fonction fléchée que nous définissons :
void main() {
List<String> list = ['je', 'suis', 'en', 'lowercase'];
//Console
(JE, SUIS, EN, LOWERCASE)
void main() {
var result = calculate(5, 3, (a, b) => a + b);
print(result);
}
//Console
8
Dans cet exemple, nous avons une fonction anonyme passée comme argument à la
fonction calculate . La fonction anonyme prend deux paramètres a et b , et retourne la
somme des deux nombres.
La fonction calculate elle-même prend trois paramètres : a , b et operation . operation est
une fonction qui attend deux entiers et retourne un entier. Dans notre cas, nous passons
une fonction anonyme (a, b) => a + b comme operation , qui effectue simplement
l'addition des deux nombres.
Lorsque nous appelons calculate(5, 3, (a, b) => a + b) , la fonction calculate exécute
l'opération fournie (dans notre cas, l'addition) en utilisant les valeurs 5 et 3 comme
arguments, et retourne le résultat 8 . Ce résultat est ensuite stocké dans la variable
result et affiché à l'aide de la fonction print .
Les fonctions anonymes en Dart sont utiles lorsque vous souhaitez définir des
fonctionnalités spécifiques à un certain contexte ou lorsque vous souhaitez passer des
fonctions en tant qu'arguments à d'autres fonctions. Elles offrent une flexibilité et une
expressivité accrues dans la manière dont vous définissez et utilisez les fonctions dans
votre code.
Les structures de contrôle
void main() {
bool test1 = false;
bool test2 = true;
if (test1) {
print('Par là !');
} else if (test2) {
print('Ici !');
} else {
print('Là !');
}
}
A noter qu'il n'y a pas de coercition contrairement au JavaScript. Cela signifie qu'une
chaîne de caractères ne sera pas transformée en true par exemple, dans un contexte de
test.
Vous ne pouvez donc pas écrire :
void main() {
String test1 = 'J\'existe !';
bool test2 = true;
void main() {
String test1 = 'J'existe !';
if (test1 != null) {
print('Par là !'); // Par là !
} else if (test2) {
print('Ici !');
} else {
print('Là !');
}
}
L'opérateur ternaire
Comme en JavaScript vous pouvez utiliser des ternaires.
void main() {
int age = 17;
Les boucles
En Dart, nous pouvons utiliser les boucles for, while et do/while.
void main() {
for (int i = 0; i < 5; i++) {
print('Hello ${i + 1}');
}
}
Vous pouvez utiliser également forEach sur des itérables commes les List, mais
vous ne pouvez pas accéder à l'index de l'itération en cours :
void main() {
List<int> liste = [1, 2, 3];
void main() {
List<int> liste = [1, 2, 3];
int result = 0;
print(result);
}
void main() {
List<int> liste = [1, 2, 3];
print(liste);
}
//Console
[1, 2, 3]
[1, 2]
[1]
[]
void main() {
List<int> liste = [1, 2, 3];
do {
print(liste);
liste.removeLast();
} while (liste.length != 0);
print(liste);
}
//Console
[1, 2, 3]
[1, 2]
[1]
[]
void main() {
List<int> liste = [1, 2, 3];
print("break");
while (liste.length != 0) {
// Si la liste n'a plus qu'un élément nous stoppons la boucle
if (liste.length == 1) break;
print(liste);
liste.removeLast();
}
print("continue");
for (var i = 2; i < 10; i++) {
// Nous sautons l'itération si i est pair :
if (i % 2 == 0) continue;
// Donc seuls les éléments impairs sont ajoutés :
print(liste);
}
//Console
break
[1, 2, 3]
[1, 2]
continue
[1, 3, 5, 7, 9]
void main() {
const int a = 3;
switch (a) {
case 1:
print('Ici');
break;
case 2:
print('là');
break;
default:
print('LA !');
break;
}
}
//Console
LA !
Les classes
Généralités sur les classes en Dart
En Dart tous les objets sont les instances d'une classe, et toutes les classes
descendent de la classe Object.
class Point {
}
class Point {
// Déclaration d'une variable d'instance x de type num et initiali
num? x;
// Déclaration d'une variable d'instance y de type num et initiali
num y = 0;
}
Depuis une version récente de Dart, a été introduit la "null safety", à savoir qu'il faut
explicitement déclarer qu'une variable ou un argument peut être nul en ajoutant un point
d'interrogation. C'est une vérification supplémentaire introduite par Dart.
Les constructeurs
En Dart les constructeurs de base ont le même nom que la classe auxquels ils
appartiennent et ce sont de simples fonctions :
class Point {
// raccourci syntaxique pour déclarer deux variables du même type
num? x, y;
Point(num x, num y) {
this.x = x;
this.y = y;
}
}
Ici, this se réfère à l'instance courante. En Dart, nous n'utilisons this que dans le cas
d'un conflit de nom.
En effet, ici la classe et le constructeur ont le même temps, et c'est pour cette raison
que nous utilisons this pour se référer à l'instance courante.
class Point {
num? x, y;
Point(this.x, this.y);
}
class Point {
num? x, y;
Point.origin() {
x = 0;
y = 0;
}
}
import 'dart:math';
void main() {
class Point {
num x, y;
Point(this.x, this.y);
Console
15
class Homme {
String? prenom, nom;
static const genre = 'masculin';
Homme(this.prenom, this.nom);
static direBonjour() => print('Bonjour !');
}
void main() {
Homme babs = Homme('Babacar', 'NDIAYE');
print(Homme.genre); // masculin
Homme.direBonjour(); // Bonjour !
print(babs.genre); // Error: The getter 'genre' isn't defined for
}
Un autre exemple
class Homme {
String? prenom, nom, matricule;
static const genre = 'masculin';
static int code = 1;
String generateMatricule(){
String nomFirstLetter = nom!.substring(0,1);
String prenomFirstLetter = prenom!.substring(0,1);
String id = code.toString().padLeft(4, '0');
code++;
return nomFirstLetter + prenomFirstLetter + id;
}
@override
String toString() {
return 'nom= ${nom ?? ''}\n'
'prenom= ${prenom ?? ''}\n'
'matricule= ${matricule ?? ''}';
}
void main() {
Homme babs = Homme('Babacar', 'NDIAYE');
Homme moha = Homme('Mohamed', 'NDIAYE');
print('$babs\n');
print(moha);
Ici, si la variable code n'était pas déclarée en tant que statique (static), chaque instance
de la classe Homme aurait son propre compteur initialisé à 1. En la déclarant statique, la
variable code est partagée entre toutes les instances de la classe.
Les interfaces
Une interface définit la syntaxe à laquelle une classe doit adhérer.
Les interfaces définissent un ensemble de méthodes disponibles sur un objet.
Cette interface contient toutes les propriétés et les méthodes implémentées sur la
classe, nous pouvons le voir comme le "type" d'une classe.
Elle comprend également toutes les interfaces implémentées par la classe.
2. L'implémentation d'interface
Lorsqu'une classe implémente une autre classe, cela signifie qu'elle déclare toutes
les propriétés définies sur la première classe.
Par exemple :
ClasseA(this.prop1);
La ClasseB implémente la ClasseA car elle respecte son API. Elle doit contenir
toutes les propriétés et méthodes de la ClasseA mais peut en implémenter d'autres,
comme par exemple prop2.
Il faut également respecter les types des propriétés, les types du retour et des
paramètres de la ClasseA.
Nous reverrons des cas concrets plus tard.
void methodeAbstraite();
}
Nous appelons méthodes abstraites les méthodes définies sur une classe abstraite
car elles ne sont pas implémentés : elles ne font rien.
import 'dart:math';
void main() {
class Point {
num x, y;
Point(this.x, this.y);
Console
10
11
9
class Person {
String prenom, nom;
Person(this.prenom, this.nom);
void main() {
Person babs = Person('Babacar', 'NDIAYE');
print(babs.fullName);
Console
Babacar NDIAYE
Mohamed
DIAKHATE
L'héritage
Contrairement au JavaScript, nous ne sommes pas dans un langage prototypal. Les
classes ne sont pas du sucre syntaxique.
L'héritage est la possibilité de créer des classes à partir d'autres classes.
Nous appelons la classe à partir duquelle une autre classe est créée la classe parente
ou la super class.
La classe créée à partir de la classe parente s'appelle la classe fille ou la sub class.
Il existe deux types d'héritage : l'héritage simple et l'héritage multiple.
L'héritage multiple est le fait qu'une classe peut hériter de plusieurs classes, ce n'est
pas possible dans Dart qui ne supporte que l'héritage simple.
En revanche Dart supporte l'héritage multi-niveaux : une classe peut hériter d'une
classe, héritant elle-même d'une autre classe.
1. Utilisation d'extends
Une classe hérite d'une autre classe en utilisant extends.
La classe fille hérite de toutes les propriétés et les méthodes de la classe parente,
mais pas de son constructeur.
void main() {
Enfant enfant = new Enfant();
2. Utilisation de super
Le mot clé super permet de se référer à la classe parente :
class ClasseParente {
void direBonjour() => print('Bonjour !');
}
direBonjourEtAuRevoir() {
super.direBonjour();
print('Au revoir !');
}
void main() {
ClasseEnfant enfant = ClasseEnfant();
enfant.direBonjourEtAuRevoir();
}
class Person {
String prenom, nom;
Person(this.prenom, this.nom);
}
void main() {
Homme babs= Homme('Babacar', 'NDIAYE');
print(babs.prenom);
print(babs.nom);
print(babs.sexe);
}
3. Utilisation de @override
Si dans une classe fille vous voulez remplacer une méthode d'instance, un getter ou
un setter, vous pouvez utiliser l'annotation @override pour indiquer que vous
souhaitez faire la surcharge (override) d'une méthode.Cela permet de remplacer la
définition de la classe parente par une nouvelle implémentation dans la classe
enfant.
class ClasseParente {
void direBonjour() => print('Bonjour du parent !');
}
void main() {
ClasseEnfant enfant = ClasseEnfant();
enfant.direBonjour(); // Bonjour de l'enfant !
}
qui affiche "Bonjour de l'enfant !" plutôt que "Bonjour du parent !".
L'utilisation de l'annotation @override est une bonne pratique en Dart pour assurer
que vous faites réellement la surcharge d'une méthode existante. Cela aide
également à détecter les erreurs de saisie de noms de méthodes ou de signatures
incorrectes lors de la surcharge.
void main() {
Direction direction = Direction.gauche;
print(direction); // Affiche "Direction.gauche"
direction = Direction.droite;
print(direction); // Affiche "Direction.droite"
direction = Direction.devant;
print(direction); // Affiche "Direction.devant"
direction = Direction.derriere;
print(direction); // Affiche "Direction.derriere"
}
Il est possible d'accéder à toutes les valeurs d'un enum avec la propriété values :
void main() {
var direction = Direction.devant;
switch (direction) {
case Direction.gauche:
print('A gauche !');
break;
case Direction.droite:
print('A droite !');
break;
case Direction.devant:
print('Devant !');
break;
case Direction.derriere:
print('Derriere !');
break;
}
}
Console
Devant !
Les génériques
Les generics permettent de spécifier qu'une collection en Dart ne peut contenir que des
valeurs d'un certain type.
Ils fonctionnent sur toutes les collections Dart. Pour le moment, nous avons vu deux
types de collections : les lists et les maps.
La syntaxe est la suivante :
Collection <type> identifiant = assignation
Nous en avons déjà vus dans le chapitre précédemment, voici un exemple :
Les collections qui utilisent des génériques sont appelés des collections type-safe.
Il est également possible d'utiliser un type indéfini souvent indiqué par T pour type, par
convention.
Nous pouvons également typer des fonctions et des méthodes avec un type générique
en faisant :
function<T> () { … };
Avec cette notation nous passons en paramètre un type générique que nous utiliserons
dans la fonction ou la méthode.
Prenons un exemple :
main() {
List<int> values = [1, 2, 4, 5];
print(soustraire(15, values));
Dans cette exemple nous définissons une fonction soustraire() et nous la typons en
indiquant qu'elle va retourner un type T, nous restreignons également les arguments
qu'elle peut recevoir grâce à extends.
spécifié.
Les génériques en Dart sont utiles lorsque vous souhaitez créer des structures de
données et des fonctions réutilisables, tout en assurant le type de sécurité et en évitant
les erreurs de type.
Utiliser import
Pour importer une librairie il suffit de faire, par exemple pour une librairie native :
import 'dart:math'
Une fois une librairie importée vous pouvez utiliser toutes ses fonctions :
import 'dart:math';
void main() {
print("La racine carrée de 16 est : ${sqrt(16)}");
}
import 'package:test/test.dart';
Pour les fichiers, il suffit de passer le chemin vers le fichier à importer directement.
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
Quand vous utilisez as il faut forcément préfixer vos appels avec le nom donné à la
librairie.
// Importe SEULEMENT pi
import 'package:lib1/lib1.dart' show pi;
Nous verrons comment gérer l'asynchrone en Dart dans la leçon suivante. Ici, le
chargement de la librairie est asynchrone et nous ne pouvons pas l'utiliser
immédiatement.
Nous mettons un code d'exemple pour attendre le chargement de la librairie, mais les
notions seront abordées dans la leçon suivante :
Dans cet exemple, nous utilisons import 'package:test/test.dart' deferred as test; pour
importer la librairie de tests de manière différée et lui donner un alias test .
Ensuite, dans la fonction exectuerUneMethode() , nous utilisons await test.loadLibrary(); pour
charger la librairie de manière asynchrone. Une fois que la librairie est chargée, nous
pouvons appeler la méthode uneMethode() de la librairie test .
Notez que lorsque vous utilisez le chargement différé, vous devez utiliser await avant
d'appeler des éléments de la librairie chargée. Cela garantit que la librairie est chargée
et prête à être utilisée avant d'y accéder.
Le chargement différé est utile lorsque vous avez des parties de votre code qui ne sont
pas nécessaires au chargement initial de l'application, mais qui peuvent être chargées à
la demande. Cela peut contribuer à améliorer les performances et à réduire la taille de
l'application initiale.
// Dans la librairie 1 :
void _test() {
print("test");
}
// Dans la librairie 2
import 'test.dart' as test;
void main() {
test._test(); // Error
}
Vous pouvez ainsi importer directement les trois librairies en important tests.dart :
import 'tests.dart';
L'asynchrone
Dart propose de nombreuses fonctionnalités permettant de gérer l'asynchrone
facilement.
Les fonctions asynchrones retourne un résultat après une période de temps nécessaire
à une opération asynchrone : par exemple une requête HTTP.
await permet d'attendre le retour d'une fonction asynchrone, et permet donc de rendre
synchrone une exécution asynchrone.
Remarquez que le type retourné par une fonction est un Future, nous y reviendrons.
Gestion d'erreur
De même qu'en JavaScript, la gestion d'erreur des fonctions asynchrones se fait avec
des blocs try / catch, et éventuellement avec finally.
try {
valeur = await actionAsynchrone();
} catch (e) {
print(e);
}
Si une erreur survient dans l'exécution d'await, elle sera interceptée par catch et vous
pouvez la gérer comme vous le souhaitez dans le bloc catch.
Les Futures
Dart offre une API pour manipuler les Future autrement qu'avec async / await.
Depuis Dart 2.1, la librairie est incluse dans dart:core et il n'y a plus besoin de l'importer
avec dart:async.
Il faut privilégier l'utilisation d'async / await qui est plus lisible, mais nous vous
détaillons brièvement les deux méthodes utiles car vous pourriez les rencontrer dans
Flutter.
then() permet de prendre en argument une fonction qui reçoit la valeur retournée par le
Future et qui sera exécutée quand le Future aura terminé son exécution.
catchError() prend en paramètre une fonction qui reçoit l'erreur en argument et qui sera
exécutée dans le cas où l'exécution du Future rencontre une erreur,.
Future<T>.delayed(periode) permet de créer un Future qui s'exécutera après une
période passé en argument.
Prenons un exemple :
Future<String> actionAsync() {
return Future<String>.delayed(Duration(milliseconds: 2000),() => "V
}
Console
Valeur synchrone
Valeur asynchrone
Dans cet exemple, la fonction actionAsync() renvoie un Future qui se termine après une
période de 2000 millisecondes et retourne la valeur "Valeur asynchrone". Dans la
fonction main() , nous appelons actionAsync() et utilisons la méthode then() pour
spécifier une fonction qui sera exécutée lorsque le Future sera terminé. Cette fonction
prend la valeur retournée par le Future en argument et imprime cette valeur.
Nous utilisons également la méthode catchError() pour spécifier une fonction qui sera
exécutée si une erreur se produit lors de l'exécution du Future.
Enfin, nous imprimons "Valeur synchrone" pour illustrer que cette instruction est
exécutée de manière synchrone, tandis que l'exécution du Future est asynchrone.
Cependant, il est préférable d'utiliser les mots clés async et await chaque fois que cela
est possible, car ils offrent une syntaxe plus claire et permettent d'éviter des niveaux
d'imbrication excessifs lors de l'utilisation de plusieurs futures.