Tu as envie de développer des applications mobiles avec Flutter ? Alors tu dois d'abord maîtriser Dart, le langage qui le propulse. Dart n'est pas juste un langage de plus - c'est un langage moderne, performant, et super agréable à utiliser.
Aujourd'hui, on va explorer Dart de A à Z. Que tu viennes de JavaScript, Python, Java ou que tu débutes complètement, ce guide va te donner toutes les bases nécessaires pour coder comme un pro en Dart.
Prêt à maîtriser Dart ? C'est parti ! 🚀
Pourquoi Dart ?
Avant de plonger dans le code, laisse-moi te convaincre que Dart vaut le coup :
Les points forts de Dart
-
Syntaxe claire : Si tu connais JavaScript ou Java, tu te sentiras chez toi
-
Null safety : Plus de
null reference exceptionssurprises -
AOT et JIT : Compile en natif pour la performance, hot reload pour le développement
-
Asynchrone natif : async/await intégré élégamment
-
Orienté objet : Classes, interfaces, mixins
-
Typage fort et optionnel : Le meilleur des deux mondes
-
Écosystème riche : Des milliers de packages sur pub.dev
Configuration : ton premier programme Dart
Installation
# macOS
brew tap dart-lang/dart
brew install dart
# Linux
sudo apt-get update
sudo apt-get install dart
# Vérifier l'installation
dart --version
Hello World
Créons notre premier programme :
// hello.dart
void main() {
print('Hello, Dart!');
}
Pour l'exécuter :
dart run hello.dart
# Output: Hello, Dart!
C'est aussi simple que ça ! 🎉
Variables et types de données
Déclaration de variables
// var - type inféré
var nom = 'Mpia'; // String
var age = 25; // int
var taille = 1.75; // double
// Type explicite
String prenom = 'M.';
int nombreVies = 3;
double pi = 3.14159;
bool estDev = true;
// dynamic - type peut changer
dynamic valeur = 42;
valeur = 'Maintenant une chaîne'; // OK
// Object - type de base de tout
Object quelqueChose = 'Texte';
quelqueChose = 123; // OK
Constantes
// const - compile-time constant
const PI = 3.14159;
const MAX_SIZE = 100;
// final - runtime constant (une seule assignation)
final maintenant = DateTime.now();
final uuid = generateUuid();
// Différence importante
const liste1 = [1, 2, 3]; // Liste immutable
final liste2 = [1, 2, 3]; // Référence immutable, contenu mutable
liste2.add(4); // OK
// liste1.add(4); // ERREUR
Types de base
void main() {
// Numbers
int entier = 42;
double decimal = 3.14;
num nombre = 10; // peut être int ou double
// String
String simple = 'Bonjour';
String double = "Hello";
String multiligne = '''
Ceci est
sur plusieurs
lignes
''';
// Interpolation de chaînes
String nom = 'Mpia';
print('Bonjour $nom'); // Bonjour Mpia
print('2 + 2 = ${2 + 2}'); // 2 + 2 = 4
print('Majuscules: ${nom.toUpperCase()}'); // Majuscules: MPIA
// Boolean
bool estVrai = true;
bool estFaux = false;
// Runes (caractères Unicode)
var coeur = '\u2665';
var emoji = '😀';
// Symbols
var symbole = #monSymbole;
}
Null Safety : la révolution Dart
Dart a introduit le null safety - une des meilleures fonctionnalités du langage !
Types nullable et non-nullable
// Non-nullable (ne peut pas être null)
String nom = 'Mpia';
// nom = null; // ERREUR de compilation
// Nullable (peut être null)
String? nomOptional;
nomOptional = 'Mpia';
nomOptional = null; // OK
int? age;
print(age); // null
// Late - sera initialisé plus tard
late String description;
// print(description); // ERREUR si accédé avant initialisation
description = 'Une description';
print(description); // OK
Opérateurs null-aware
String? nom;
// ?? - valeur par défaut si null
String affichage = nom ?? 'Anonyme';
print(affichage); // Anonyme
// ??= - assigner seulement si null
nom ??= 'Par défaut';
print(nom); // Par défaut
// ?. - appel conditionnel
String? message;
int? longueur = message?.length;
print(longueur); // null
message = 'Hello';
longueur = message?.length;
print(longueur); // 5
// ! - assertion non-null (utilise avec précaution)
String? texte = 'Bonjour';
String definitif = texte!; // Je garantis que c'est pas null
// Si c'était null → ERREUR runtime
Gestion élégante du null
String? obtenirNom() {
// Peut retourner null
return null;
}
void exemple() {
String? nom = obtenirNom();
// Mauvais
if (nom != null) {
print(nom.toUpperCase());
}
// Bon - plus concis
print(nom?.toUpperCase() ?? 'ANONYME');
// Pattern matching avec if-case
if (nom case String valeur) {
print('Nom trouvé: $valeur');
} else {
print('Pas de nom');
}
}
Collections : Lists, Sets, Maps
Lists (tableaux)
// Déclaration
List<int> nombres = [1, 2, 3, 4, 5];
var fruits = ['Pomme', 'Banane', 'Orange'];
// Accès
print(fruits[0]); // Pomme
print(fruits.length); // 3
print(fruits.first); // Pomme
print(fruits.last); // Orange
// Modification
fruits.add('Mangue');
fruits.addAll(['Kiwi', 'Ananas']);
fruits.insert(1, 'Fraise');
fruits.remove('Banane');
fruits.removeAt(0);
// Méthodes utiles
bool contient = fruits.contains('Pomme');
int index = fruits.indexOf('Orange');
fruits.sort();
fruits.shuffle();
List<String> reversed = fruits.reversed.toList();
// Spread operator
var liste1 = [1, 2, 3];
var liste2 = [0, ...liste1, 4]; // [0, 1, 2, 3, 4]
// Collection if
var includeZero = true;
var liste3 = [
if (includeZero) 0,
1,
2,
3,
];
// Collection for
var multiples = [
for (var i = 1; i <= 5; i++) i * 2
]; // [2, 4, 6, 8, 10]
Sets (ensembles)
// Pas de doublons
Set<String> pays = {'France', 'Allemagne', 'Italie'};
pays.add('France'); // Ignoré (déjà présent)
print(pays.length); // 3
// Opérations d'ensembles
var setA = {1, 2, 3, 4};
var setB = {3, 4, 5, 6};
var union = setA.union(setB); // {1, 2, 3, 4, 5, 6}
var intersection = setA.intersection(setB); // {3, 4}
var difference = setA.difference(setB); // {1, 2}
// Vérifications
bool contient = pays.contains('France');
bool estVide = pays.isEmpty;
Maps (dictionnaires)
// Déclaration
Map<String, int> ages = {
'Alice': 25,
'Bob': 30,
'Charlie': 35,
};
// Accès
print(ages['Alice']); // 25
print(ages['David']); // null
// Modification
ages['David'] = 28;
ages.remove('Bob');
ages.update('Alice', (age) => age + 1);
// Méthodes utiles
bool contientCle = ages.containsKey('Alice');
bool contientValeur = ages.containsValue(25);
List<String> cles = ages.keys.toList();
List<int> valeurs = ages.values.toList();
// Itération
ages.forEach((nom, age) {
print('$nom a $age ans');
});
// Map avec type complexe
Map<String, Map<String, dynamic>> utilisateurs = {
'user1': {
'nom': 'Alice',
'age': 25,
'actif': true,
},
'user2': {
'nom': 'Bob',
'age': 30,
'actif': false,
},
};
Opérateurs
Opérateurs arithmétiques
int a = 10, b = 3;
print(a + b); // 13
print(a - b); // 7
print(a * b); // 30
print(a / b); // 3.3333...
print(a ~/ b); // 3 (division entière)
print(a % b); // 1 (modulo)
// Incrémentation
int x = 5;
x++; // 6
++x; // 7
x--; // 6
--x; // 5
x += 3; // 8
x *= 2; // 16
Opérateurs de comparaison
int a = 5, b = 10;
print(a == b); // false
print(a != b); // true
print(a < b); // true
print(a > b); // false
print(a <= b); // true
print(a >= b); // false
Opérateurs logiques
bool x = true, y = false;
print(x && y); // false (ET logique)
print(x || y); // true (OU logique)
print(!x); // false (NON logique)
Opérateur ternaire
int age = 18;
String statut = age >= 18 ? 'Majeur' : 'Mineur';
print(statut); // Majeur
// Cascade operator (..)
var liste = []
..add(1)
..add(2)
..add(3)
..addAll([4, 5]);
// liste = [1, 2, 3, 4, 5]
Structures de contrôle
If / Else
int age = 20;
if (age < 13) {
print('Enfant');
} else if (age < 18) {
print('Adolescent');
} else if (age < 60) {
print('Adulte');
} else {
print('Senior');
}
// If expression (Dart 3.0+)
String categorie = if (age < 18) 'Mineur' else 'Majeur';
Switch / Case
String jour = 'Lundi';
switch (jour) {
case 'Lundi':
case 'Mardi':
case 'Mercredi':
case 'Jeudi':
case 'Vendredi':
print('Jour de travail');
break;
case 'Samedi':
case 'Dimanche':
print('Week-end');
break;
default:
print('Jour invalide');
}
// Switch expression (Dart 3.0+)
String message = switch (jour) {
'Lundi' || 'Mardi' || 'Mercredi' || 'Jeudi' || 'Vendredi' => 'Travail',
'Samedi' || 'Dimanche' => 'Repos',
_ => 'Invalide',
};
Boucles
// For classique
for (int i = 0; i < 5; i++) {
print(i);
}
// For-in
List<String> fruits = ['Pomme', 'Banane', 'Orange'];
for (var fruit in fruits) {
print(fruit);
}
// While
int compteur = 0;
while (compteur < 5) {
print(compteur);
compteur++;
}
// Do-while
int n = 0;
do {
print(n);
n++;
} while (n < 5);
// Continue et break
for (int i = 0; i < 10; i++) {
if (i == 5) continue; // Sauter 5
if (i == 8) break; // Arrêter à 8
print(i);
}
Fonctions
Déclaration de base
// Fonction simple
void direBonjour(String nom) {
print('Bonjour $nom!');
}
// Fonction avec retour
int additionner(int a, int b) {
return a + b;
}
// Fonction fléchée (arrow function)
int multiplier(int a, int b) => a * b;
// Appels
direBonjour('Mpia');
int somme = additionner(5, 3);
int produit = multiplier(4, 7);
Paramètres
// Paramètres positionnels obligatoires
int calculer(int a, int b) => a + b;
// Paramètres positionnels optionnels
String saluer([String nom = 'Anonyme', String? titre]) {
return 'Bonjour ${titre ?? ''} $nom';
}
saluer(); // Bonjour Anonyme
saluer('Mpia'); // Bonjour Mpia
saluer('Mpia', 'M.'); // Bonjour M. Mpia
// Paramètres nommés
void creerUtilisateur({
required String nom,
required String email,
int age = 18,
bool actif = true,
}) {
print('Utilisateur: $nom, $email, $age ans, actif: $actif');
}
creerUtilisateur(
nom: 'Alice',
email: 'alice@example.com',
age: 25,
);
// Mixte
void exemple(String arg1, int arg2, {String? option}) {
print('$arg1, $arg2, $option');
}
Fonctions d'ordre supérieur
// Fonction en paramètre
void executerOperation(int a, int b, int Function(int, int) operation) {
int resultat = operation(a, b);
print('Résultat: $resultat');
}
executerOperation(5, 3, (a, b) => a + b); // 8
executerOperation(5, 3, (a, b) => a * b); // 15
// Fonction qui retourne une fonction
Function creerMultiplicateur(int facteur) {
return (int valeur) => valeur * facteur;
}
var doubler = creerMultiplicateur(2);
var tripler = creerMultiplicateur(3);
print(doubler(5)); // 10
print(tripler(5)); // 15
Fonctions anonymes et closures
// Fonction anonyme
var liste = [1, 2, 3, 4, 5];
liste.forEach((element) {
print(element * 2);
});
// Closure
Function creerCompteur() {
int compteur = 0;
return () {
compteur++;
return compteur;
};
}
var compteur1 = creerCompteur();
print(compteur1()); // 1
print(compteur1()); // 2
print(compteur1()); // 3
Classes et POO
Classe de base
class Personne {
// Propriétés
String nom;
int age;
// Constructeur
Personne(this.nom, this.age);
// Méthode
void sePresenter() {
print('Je suis $nom, j\'ai $age ans');
}
// Getter
bool get estMajeur => age >= 18;
// Setter
set age(int nouvelAge) {
if (nouvelAge >= 0) {
age = nouvelAge;
}
}
}
// Utilisation
var personne = Personne('Mpia', 25);
personne.sePresenter();
print(personne.estMajeur);
Constructeurs avancés
class Utilisateur {
String nom;
String email;
int age;
bool actif;
// Constructeur principal
Utilisateur({
required this.nom,
required this.email,
this.age = 18,
this.actif = true,
});
// Constructeur nommé
Utilisateur.invité()
: nom = 'Invité',
email = 'invite@example.com',
age = 0,
actif = false;
// Factory constructor
factory Utilisateur.fromJson(Map<String, dynamic> json) {
return Utilisateur(
nom: json['nom'],
email: json['email'],
age: json['age'] ?? 18,
actif: json['actif'] ?? true,
);
}
// Méthode pour convertir en JSON
Map<String, dynamic> toJson() {
return {
'nom': nom,
'email': email,
'age': age,
'actif': actif,
};
}
}
// Utilisation
var user1 = Utilisateur(nom: 'Alice', email: 'alice@test.com');
var user2 = Utilisateur.invité();
var user3 = Utilisateur.fromJson({
'nom': 'Bob',
'email': 'bob@test.com',
'age': 30,
});
Héritage
class Animal {
String nom;
Animal(this.nom);
void faireDuBruit() {
print('$nom fait du bruit');
}
}
class Chien extends Animal {
String race;
Chien(String nom, this.race) : super(nom);
@override
void faireDuBruit() {
print('$nom aboie: Woof!');
}
void rapporter() {
print('$nom rapporte la balle');
}
}
class Chat extends Animal {
Chat(String nom) : super(nom);
@override
void faireDuBruit() {
print('$nom miaule: Miaou!');
}
}
// Utilisation
var chien = Chien('Rex', 'Labrador');
chien.faireDuBruit(); // Rex aboie: Woof!
chien.rapporter();
var chat = Chat('Minou');
chat.faireDuBruit(); // Minou miaule: Miaou!
Classes abstraites et interfaces
// Classe abstraite
abstract class Forme {
// Propriété abstraite
double get aire;
// Méthode abstraite
void dessiner();
// Méthode concrète
void afficherAire() {
print('Aire: $aire');
}
}
class Rectangle extends Forme {
double largeur;
double hauteur;
Rectangle(this.largeur, this.hauteur);
@override
double get aire => largeur * hauteur;
@override
void dessiner() {
print('Dessine un rectangle ${largeur}x${hauteur}');
}
}
class Cercle extends Forme {
double rayon;
Cercle(this.rayon);
@override
double get aire => 3.14159 * rayon * rayon;
@override
void dessiner() {
print('Dessine un cercle de rayon $rayon');
}
}
// Interface (implements)
class Volant {
void voler() {
print('Je vole!');
}
}
class Oiseau implements Volant {
@override
void voler() {
print('L\'oiseau vole avec ses ailes');
}
}
Mixins
mixin Nageur {
void nager() {
print('Je nage');
}
}
mixin Voleur {
void voler() {
print('Je vole');
}
}
class Canard with Nageur, Voleur {
String nom;
Canard(this.nom);
void sePresenter() {
print('Je suis $nom le canard');
}
}
// Utilisation
var canard = Canard('Donald');
canard.sePresenter();
canard.nager();
canard.voler();
Programmation asynchrone
Future
// Fonction asynchrone
Future<String> recupererDonnees() async {
// Simuler un délai réseau
await Future.delayed(Duration(seconds: 2));
return 'Données récupérées';
}
// Utilisation avec then
void exemple1() {
print('Début');
recupererDonnees().then((donnees) {
print(donnees);
}).catchError((erreur) {
print('Erreur: $erreur');
});
print('Fin (mais les données arrivent après)');
}
// Utilisation avec async/await
Future<void> exemple2() async {
print('Début');
try {
String donnees = await recupererDonnees();
print(donnees);
} catch (erreur) {
print('Erreur: $erreur');
}
print('Fin');
}
Opérations parallèles
Future<void> chargerPlusieurs() async {
// Séquentiel (lent)
var user = await recupererUtilisateur();
var posts = await recupererPosts();
var comments = await recupererComments();
// Parallèle (rapide)
var resultats = await Future.wait([
recupererUtilisateur(),
recupererPosts(),
recupererComments(),
]);
var user2 = resultats[0];
var posts2 = resultats[1];
var comments2 = resultats[2];
}
Stream
// Créer un stream
Stream<int> compteur() async* {
for (int i = 1; i <= 5; i++) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}
// Consommer un stream
void exempleStream() async {
print('Début du stream');
await for (var valeur in compteur()) {
print('Valeur: $valeur');
}
print('Fin du stream');
}
// Stream avec listen
void exempleStreamListen() {
compteur().listen(
(valeur) {
print('Reçu: $valeur');
},
onError: (erreur) {
print('Erreur: $erreur');
},
onDone: () {
print('Stream terminé');
},
);
}
Gestion des exceptions
// Lancer une exception
void diviser(int a, int b) {
if (b == 0) {
throw Exception('Division par zéro impossible');
}
print('Résultat: ${a / b}');
}
// Capturer une exception
void exemple() {
try {
diviser(10, 0);
} on Exception catch (e) {
print('Exception capturée: $e');
} catch (e, stackTrace) {
print('Erreur: $e');
print('Stack trace: $stackTrace');
} finally {
print('Toujours exécuté');
}
}
// Exception personnalisée
class MonException implements Exception {
final String message;
MonException(this.message);
@override
String toString() => 'MonException: $message';
}
void lancerErreur() {
throw MonException('Quelque chose s\'est mal passé');
}
Bonnes pratiques
1. Nommage
// Classes - PascalCase
class MaClasse {}
class UtilisateurService {}
// Variables et fonctions - camelCase
var monNom = 'Mpia';
void faireQuelqueChose() {}
// Constantes - camelCase ou lowerCamelCase
const maxTentatives = 3;
const apiUrl = 'https://api.example.com';
// Privé - préfixe _
class MaClasse {
String _variablePrivee;
void _methodePrivee() {}
}
2. Utiliser const quand possible
// Bon pour la performance
const valeur = 42;
const liste = [1, 2, 3];
const map = {'cle': 'valeur'};
3. Préférer final à var
// Si la variable ne change pas
final nom = 'Mpia';
final date = DateTime.now();
4. Utiliser les types explicites
// Plus clair
List<String> noms = [];
Map<String, int> ages = {};
// Plutôt que
var noms = []; // Type List<dynamic>
var ages = {}; // Type Map<dynamic, dynamic>
Conclusion
Dart est un langage moderne et puissant qui rend le développement agréable. Avec sa syntaxe claire, son null safety, et son support natif de l'asynchrone, c'est un excellent choix pour le développement mobile avec Flutter.
Points clés à retenir :
-
Null safety : protège contre les erreurs de null
-
Collections riches : Lists, Sets, Maps avec des méthodes puissantes
-
POO moderne : Classes, mixins, interfaces
-
Async/await : gestion élégante de l'asynchrone
-
Type safety : détection d'erreurs à la compilation
Maintenant que tu maîtrises les fondamentaux de Dart, tu es prêt à créer des applications Flutter incroyables !
Ressources pour aller plus loin :
-
DartPad - Éditeur en ligne
Tu as des questions sur Dart ? Partage-les dans les commentaires !

Laisser un commentaire