Skip to main content

Code that compiles on the first try! CodeWithMpia wishes you very happy holidays.✨

Aller au contenu principal

Dart: maîtrise les fondamentaux du langage de Flutter

Dans cet article, je t’apprends pas à pas à maîtriser les bases de Dart à travers des explications claires et des exemples concrets, afin que tu puisses écrire un code moderne, robuste, et prêt pour développer des applications Flutter.

M
Mpia
12/15/2025 38 min
80 vues
0 comme.
#Dart#Programming#Flutter
Dart: maîtrise les fondamentaux du langage de Flutter

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 exceptions surprises

  • 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 :

Tu as des questions sur Dart ? Partage-les dans les commentaires !

Commentaires (0)

Laisser un commentaire

Aucun commentaire pour le moment. Soyez le premier à commenter !