1.2Fondements du langage Dart
1.2.1 – Types, variables, fonctions
Avant de créer des interfaces avec Flutter, il est essentiel de maîtriser les bases du langage Dart. Dans cette section, nous allons découvrir les types de données, comment déclarer des variables, et comment créer des fonctions. Ces concepts sont les fondations de tout programme Dart.
🔤 Les types de base en Dart
Dart est un langage typé. Cela signifie que chaque variable a un type qui détermine quel genre de données elle peut contenir. Commençons par les types les plus courants.
int - Les nombres entiers
Le type int représente les nombres entiers (sans décimales).
// Déclaration de nombres entiers
int age = 25;
int nombreEtudiants = 150;
int temperature = -5;
// Opérations mathématiques
int somme = 10 + 20; // 30
int produit = 5 * 6; // 30
int division = 15 ~/ 3; // 5 (division entière)
int reste = 17 % 5; // 2 (modulo)
double - Les nombres décimaux
Le type double représente les nombres à virgule flottante (avec décimales).
// Déclaration de nombres décimaux
double prix = 19.99;
double taille = 1.75;
double pi = 3.14159;
// Opérations
double total = prix * 2; // 39.98
double moyenne = 15.5 / 2; // 7.75
~/ pour une division entière et / pour une division décimale. C'est différent de langages comme Java où / fait une division entière entre deux int.
String - Les chaînes de caractères
Le type String représente du texte.
// Déclaration de chaînes
String prenom = 'Alice';
String nom = "Dupont"; // Simple ou double guillemets
String message = 'Bonjour tout le monde!';
// Concaténation
String nomComplet = prenom + ' ' + nom; // 'Alice Dupont'
// Interpolation (recommandé)
String presentation = 'Je m\'appelle $prenom';
String phrase = 'Bonjour $prenom $nom, bienvenue !';
// Interpolation avec expression
int age = 25;
String info = 'J\'ai $age ans, soit ${age * 12} mois';
// Chaînes multi-lignes
String description = '''
Ceci est un texte
sur plusieurs lignes.
C'est pratique pour les longs textes.
''';
$variable ou ${expression} est la méthode préférée en Dart. C'est plus lisible que la concaténation avec des +.
bool - Les booléens
Le type bool ne peut avoir que deux valeurs : true ou false.
// Déclaration de booléens
bool estConnecte = true;
bool aDesEnfants = false;
// Conditions
bool estMajeur = age >= 18;
bool estMineur = !estMajeur; // Négation avec !
// Opérateurs logiques
bool condition1 = true && false; // false (ET logique)
bool condition2 = true || false; // true (OU logique)
num - Type générique pour les nombres
Le type num accepte à la fois les int et les double.
// num peut contenir int ou double
num nombre1 = 42; // OK (int)
num nombre2 = 3.14; // OK (double)
num somme = nombre1 + nombre2; // OK
📦 Déclaration de variables
Dart offre plusieurs façons de déclarer des variables. Voyons les différentes options.
Typage explicite
Vous spécifiez le type de la variable lors de sa déclaration.
// Type explicite
String prenom = 'Marie';
int age = 30;
double taille = 1.68;
bool estEtudiant = true;
Inférence de type avec var
Dart peut déduire automatiquement le type à partir de la valeur assignée.
// Dart infère automatiquement le type
var prenom = 'Marie'; // Dart comprend que c'est un String
var age = 30; // Dart comprend que c'est un int
var taille = 1.68; // Dart comprend que c'est un double
var estEtudiant = true; // Dart comprend que c'est un bool
// Une fois le type inféré, il ne peut plus changer
age = 31; // ✅ OK (toujours un int)
// age = 'trente'; // ❌ ERREUR (ne peut pas changer de type)
Type dynamic
Le type dynamic permet de créer des variables qui peuvent changer de type à l'exécution. C'est utile dans certains cas spécifiques, mais doit être utilisé avec précaution car il désactive les vérifications de type.
// Variable dynamic peut changer de type
dynamic valeur = 42; // Commence comme int
print(valeur); // 42
valeur = 'Bonjour'; // ✅ OK (devient String)
print(valeur); // Bonjour
valeur = 3.14; // ✅ OK (devient double)
print(valeur); // 3.14
valeur = true; // ✅ OK (devient bool)
print(valeur); // true
// Attention : pas de vérification de type à la compilation
dynamic nombre = 'texte';
// int resultat = nombre + 5; // ❌ ERREUR à l'exécution (runtime error)
// Utilisation avec des APIs dynamiques (JSON, etc.)
dynamic donnees = {
'nom': 'Alice',
'age': 25,
'actif': true,
};
String nom = donnees['nom']; // OK
int age = donnees['age']; // OK
bool actif = donnees['actif']; // OK
• Travailler avec des données JSON non typées
• Interagir avec des APIs externes dont la structure est inconnue
• Migration de code : Lorsque vous modernisez un ancien projet ou du code dont les types ne sont pas clairement définis
Quand éviter dynamic ?
• Dans la plupart des cas, préférez des types explicites
•
dynamic désactive les vérifications de type et peut causer des erreurs à l'exécution• Utilisez plutôt des types optionnels (
String?, int?) ou des classes pour structurer vos données
Variables finales avec final
Le mot-clé final crée une variable qui ne peut être assignée qu'une seule fois.
// Variable finale (ne peut pas être réassignée)
final String ville = 'Paris';
final int anneeNaissance = 1995;
// Tentative de modification
// ville = 'Lyon'; // ❌ ERREUR : ne peut pas modifier une variable final
// final avec inférence de type
final pays = 'France'; // Le type String est inféré
Constantes avec const
Le mot-clé const crée une constante de compilation. La valeur doit être connue au moment de la compilation.
// Constantes de compilation
const double pi = 3.14159;
const int joursParSemaine = 7;
const String appName = 'Mon App Flutter';
// Différence entre final et const
final dateActuelle = DateTime.now(); // ✅ OK (valeur connue à l'exécution)
// const dateConst = DateTime.now(); // ❌ ERREUR (valeur pas connue à la compilation)
•
var : Pour les variables normales qui peuvent changer•
final : Pour les valeurs qui ne changeront pas après l'initialisation•
const : Pour les constantes vraiment fixes (valeurs connues à la compilation)En général, préférez
final quand c'est possible. C'est une bonne pratique qui évite les bugs.
Variables nullables
En Dart moderne (depuis la version 2.12), les variables sont non-nullables par défaut. Pour accepter une valeur nulle, ajoutez ? au type.
// Variable non-nullable (par défaut)
String nom = 'Alice';
// nom = null; // ❌ ERREUR : ne peut pas être null
// Variable nullable (avec ?)
String? nomOptional = 'Bob';
nomOptional = null; // ✅ OK
// int nullable
int? age; // Vaut null par défaut
age = 25; // ✅ OK
age = null; // ✅ OK aussi
?. C'est une protection importante contre les erreurs.
⚙️ Les fonctions en Dart
Les fonctions permettent de regrouper du code réutilisable. Dart offre une syntaxe simple et puissante pour créer des fonctions.
Fonction simple
// Fonction qui affiche un message
void direBonjour() {
print('Bonjour !');
}
// Appel de la fonction
direBonjour(); // Affiche : Bonjour !
Fonction avec paramètres
// Fonction avec un paramètre
void saluer(String nom) {
print('Bonjour $nom !');
}
// Fonction avec plusieurs paramètres
void presenter(String prenom, String nom, int age) {
print('Je m\'appelle $prenom $nom et j\'ai $age ans.');
}
// Appels
saluer('Alice'); // Bonjour Alice !
presenter('Bob', 'Martin', 30); // Je m'appelle Bob Martin et j'ai 30 ans.
Fonction avec retour de valeur
// Fonction qui retourne un int
int additionner(int a, int b) {
return a + b;
}
// Fonction qui retourne un String
String obtenirMessage() {
return 'Ceci est un message';
}
// Fonction qui retourne un bool
bool estPair(int nombre) {
return nombre % 2 == 0;
}
// Utilisation
int resultat = additionner(5, 3); // 8
String msg = obtenirMessage(); // 'Ceci est un message'
bool pair = estPair(10); // true
Fonction avec syntaxe courte (arrow function)
Pour les fonctions simples qui retournent une seule expression, Dart propose une syntaxe raccourcie avec =>.
// Syntaxe complète
int multiplier(int a, int b) {
return a * b;
}
// Syntaxe courte (équivalent)
int multiplierCourt(int a, int b) => a * b;
// Autres exemples
String direBonjour(String nom) => 'Bonjour $nom !';
bool estPositif(int n) => n > 0;
double calculerCarre(double x) => x * x;
// Utilisation identique
int resultat = multiplierCourt(4, 5); // 20
String msg = direBonjour('Alice'); // 'Bonjour Alice !'
=> est très utilisée en Flutter pour les fonctions simples, notamment dans les callbacks. Elle rend le code plus concis et lisible.
Paramètres optionnels positionnels
Vous pouvez rendre certains paramètres optionnels en les plaçant entre crochets [].
// Paramètres optionnels entre crochets
void afficherInfos(String nom, [int? age]) {
if (age != null) {
print('$nom a $age ans');
} else {
print('Nom: $nom');
}
}
// Appels possibles
afficherInfos('Alice'); // Nom: Alice
afficherInfos('Bob', 25); // Bob a 25 ans
// Avec valeur par défaut
void saluer(String nom, [String salutation = 'Bonjour']) {
print('$salutation $nom !');
}
saluer('Alice'); // Bonjour Alice !
saluer('Bob', 'Salut'); // Salut Bob !
Paramètres nommés
Les paramètres nommés permettent d'appeler une fonction en spécifiant explicitement le nom des paramètres. C'est très utilisé en Flutter.
// Paramètres nommés entre accolades
void creerUtilisateur({
required String nom, // required = obligatoire
required String email,
int age = 18, // valeur par défaut
String? ville, // optionnel (peut être null)
}) {
print('Utilisateur: $nom');
print('Email: $email');
print('Age: $age');
if (ville != null) print('Ville: $ville');
}
// Appel avec paramètres nommés (ordre libre)
creerUtilisateur(
nom: 'Alice',
email: 'alice@exemple.com',
ville: 'Paris',
);
// Autre ordre, même résultat
creerUtilisateur(
email: 'bob@exemple.com',
nom: 'Bob',
age: 25,
);
Imaginez une fonction avec beaucoup de paramètres :
creerBouton(true, 'OK', 16, Colors.blue, null, false)Difficile de comprendre ce que représente chaque paramètre !
Avec des paramètres nommés :
creerBouton(
actif: true,
texte: 'OK',
taille: 16,
couleur: Colors.blue,
)Beaucoup plus clair ! C'est pourquoi Flutter utilise massivement cette syntaxe.
🎲 Types collections
List - Les listes
Une List est une collection ordonnée d'éléments.
// Liste de String
List<String> fruits = ['Pomme', 'Banane', 'Orange'];
// Avec inférence de type
var nombres = [1, 2, 3, 4, 5];
// Accès aux éléments (indexation à partir de 0)
String premierFruit = fruits[0]; // 'Pomme'
String dernierFruit = fruits[2]; // 'Orange'
// Propriétés utiles
int longueur = fruits.length; // 3
bool estVide = fruits.isEmpty; // false
// Ajouter des éléments
fruits.add('Kiwi'); // Ajoute à la fin
fruits.insert(1, 'Mangue'); // Insère à l'index 1
// Retirer des éléments
fruits.remove('Banane'); // Retire 'Banane'
fruits.removeAt(0); // Retire l'élément à l'index 0
// Parcourir une liste
for (var fruit in fruits) {
print(fruit);
}
Map - Les dictionnaires
Une Map associe des clés à des valeurs (comme un dictionnaire).
// Map de String vers String
Map<String, String> capitales = {
'France': 'Paris',
'Espagne': 'Madrid',
'Italie': 'Rome',
};
// Avec inférence de type
var ages = {
'Alice': 25,
'Bob': 30,
'Charlie': 28,
};
// Accès aux valeurs
String? capitale = capitales['France']; // 'Paris'
int? ageAlice = ages['Alice']; // 25
// Ajouter/Modifier
capitales['Allemagne'] = 'Berlin'; // Ajoute
ages['Alice'] = 26; // Modifie
// Vérifier l'existence d'une clé
bool existe = capitales.containsKey('France'); // true
// Parcourir un Map
capitales.forEach((pays, ville) {
print('La capitale de $pays est $ville');
});
Set - Les ensembles
Un Set est une collection d'éléments uniques (pas de doublons).
// Set de String
Set<String> langages = {'Dart', 'Flutter', 'JavaScript'};
// Ajouter des éléments
langages.add('Python');
langages.add('Dart'); // Ignoré (déjà présent)
print(langages.length); // 4 (pas 5, car Dart n'a pas été ajouté deux fois)
List quand l'ordre est important et les doublons sont autorisés. Utilisez Set quand vous voulez éviter les doublons. Utilisez Map pour associer des clés à des valeurs.
🔄 Opérateurs et expressions
Opérateurs arithmétiques
int a = 10;
int b = 3;
int addition = a + b; // 13
int soustraction = a - b; // 7
int multiplication = a * b; // 30
double division = a / b; // 3.333...
int divisionEntiere = a ~/ b; // 3
int modulo = a % b; // 1 (reste de la division)
// Incrémentation / Décrémentation
a++; // a = 11
b--; // b = 2
Opérateurs de comparaison
int x = 10;
int y = 20;
bool egal = x == y; // false
bool different = x != y; // true
bool inferieur = x < y; // true
bool superieur = x > y; // false
bool infEgal = x <= y; // true
bool supEgal = x >= y; // false
Opérateur null-aware
Dart fournit des opérateurs pratiques pour gérer les valeurs potentiellement nulles.
String? nomOptional = null;
// Opérateur ?? (valeur par défaut si null)
String nom = nomOptional ?? 'Anonyme'; // 'Anonyme' car nomOptional est null
// Opérateur ??= (assigner seulement si null)
String? prenom;
prenom ??= 'Jean'; // prenom devient 'Jean'
prenom ??= 'Paul'; // prenom reste 'Jean' (pas null)
// Opérateur ?. (accès conditionnel)
String? ville = null;
int? longueur = ville?.length; // null (pas d'erreur grâce à ?.)
ville = 'Paris';
longueur = ville?.length; // 5
🎯 Exemple pratique complet
Mettons en pratique ce que nous avons appris avec un exemple concret.
// Fonction pour calculer le prix TTC
double calculerPrixTTC(double prixHT, {double tauxTVA = 0.20}) {
double montantTVA = prixHT * tauxTVA;
double prixTTC = prixHT + montantTVA;
return prixTTC;
}
// Fonction pour afficher un produit
void afficherProduit({
required String nom,
required double prix,
String? description,
bool enStock = true,
}) {
print('=== Produit ===');
print('Nom: $nom');
print('Prix: ${prix.toStringAsFixed(2)} €');
if (description != null) {
print('Description: $description');
}
String statut = enStock ? 'En stock' : 'Rupture de stock';
print('Statut: $statut');
}
// Fonction principale
void main() {
// Variables
var nomProduit = 'Ordinateur portable';
var prixHT = 800.0;
final tauxTVA = 0.20;
// Calculs
double prixTTC = calculerPrixTTC(prixHT, tauxTVA: tauxTVA);
// Liste de produits
List<String> categories = ['Électronique', 'Informatique', 'High-Tech'];
// Affichage
afficherProduit(
nom: nomProduit,
prix: prixTTC,
description: 'PC portable 15 pouces, 16 Go RAM',
enStock: true,
);
print('\nCatégories: ${categories.join(', ')}');
}
=== Produit ===
Nom: Ordinateur portable
Prix: 960.00 €
Description: PC portable 15 pouces, 16 Go RAM
Statut: En stock
Catégories: Électronique, Informatique, High-Tech
💡 Points clés à retenir
- Types de base :
int,double,String,bool - Collections :
List(listes),Map(dictionnaires),Set(ensembles) - Déclaration :
var(inférence),dynamic(type variable),final(non modifiable),const(constante) - Nullabilité : Type? pour accepter null, opérateurs
??et?. - Fonctions : Paramètres positionnels, nommés, optionnels, syntaxe courte
=> - Interpolation :
$variableou${expression}dans les String
1.2.2 – Classes, objets et OOP
Dart est un langage orienté objet. Cela signifie que tout est organisé autour du concept de "classes" et d'"objets". Dans cette section, nous allons découvrir comment créer et utiliser des classes, et comprendre les principes fondamentaux de la programmation orientée objet (OOP).
🏛️ Qu'est-ce qu'une classe ?
Une classe est un modèle (ou un plan) qui définit la structure et le comportement d'un type d'objet. Pensez à une classe comme à un moule à gâteau : elle définit la forme, mais ce n'est pas le gâteau lui-même.
Un objet est une instance concrète d'une classe. C'est le gâteau qui sort du moule.
• La classe "Voiture" définit ce qu'est une voiture (elle a une marque, un modèle, une couleur, elle peut rouler, s'arrêter, etc.)
• Un objet est une voiture spécifique : "une Toyota Corolla rouge de 2023"
📦 Créer une classe simple
// Définition d'une classe
class Personne {
// Attributs (propriétés)
String nom;
String prenom;
int age;
// Constructeur
Personne(this.nom, this.prenom, this.age);
// Méthode (comportement)
void sePresenter() {
print('Bonjour, je m\'appelle $prenom $nom et j\'ai $age ans.');
}
}
// Utilisation de la classe
void main() {
// Créer un objet (instance de la classe)
Personne alice = Personne('Dupont', 'Alice', 25);
Personne bob = Personne('Martin', 'Bob', 30);
// Appeler une méthode
alice.sePresenter(); // Bonjour, je m'appelle Alice Dupont et j'ai 25 ans.
bob.sePresenter(); // Bonjour, je m'appelle Bob Martin et j'ai 30 ans.
// Accéder aux attributs
print('${alice.prenom} a ${alice.age} ans'); // Alice a 25 ans
}
🔧 Les constructeurs
Un constructeur est une méthode spéciale qui permet de créer et d'initialiser un objet. Dart offre plusieurs façons de définir des constructeurs.
Constructeur simple
class Rectangle {
double largeur;
double hauteur;
// Constructeur avec paramètres
Rectangle(this.largeur, this.hauteur);
}
Constructeur avec paramètres nommés
class Utilisateur {
String nom;
String email;
int age;
// Constructeur avec paramètres nommés
Utilisateur({
required this.nom,
required this.email,
this.age = 18, // Valeur par défaut
});
}
// Utilisation
var user = Utilisateur(
nom: 'Alice',
email: 'alice@exemple.com',
age: 25,
);
this.nom dans le constructeur est un raccourci Dart très pratique. Elle signifie "assigner automatiquement le paramètre nom à l'attribut this.nom". C'est équivalent à écrire this.nom = nom; dans le corps du constructeur.
Constructeurs nommés
Dart permet de créer plusieurs constructeurs pour une même classe en leur donnant des noms différents.
class Point {
double x;
double y;
// Constructeur principal
Point(this.x, this.y);
// Constructeur nommé pour créer un point à l'origine
Point.origine()
: x = 0,
y = 0;
// Constructeur nommé pour créer un point depuis des coordonnées polaires
Point.polaire(double rayon, double angle)
: x = rayon * cos(angle),
y = rayon * sin(angle);
}
// Utilisation
var p1 = Point(10, 20); // Constructeur principal
var p2 = Point.origine(); // À l'origine (0, 0)
var p3 = Point.polaire(5, 45); // Depuis coordonnées polaires
🔒 Encapsulation et propriétés privées
L'encapsulation est un principe de l'OOP qui consiste à cacher les détails internes d'une classe et à ne fournir qu'une interface publique. En Dart, on rend une propriété ou méthode privée en préfixant son nom par un underscore _.
class CompteBancaire {
String titulaire;
double _solde; // Attribut privé (commence par _)
CompteBancaire(this.titulaire, this._solde);
// Getter pour accéder au solde (lecture seule)
double get solde => _solde;
// Méthode pour déposer de l'argent
void deposer(double montant) {
if (montant > 0) {
_solde += montant;
print('Dépôt de $montant € effectué. Nouveau solde: $_solde €');
}
}
// Méthode pour retirer de l'argent
void retirer(double montant) {
if (montant > 0 && montant <= _solde) {
_solde -= montant;
print('Retrait de $montant € effectué. Nouveau solde: $_solde €');
} else {
print('Retrait impossible: solde insuffisant');
}
}
}
// Utilisation
void main() {
var compte = CompteBancaire('Alice', 1000);
print('Solde initial: ${compte.solde} €'); // ✅ OK (via getter)
// compte._solde = 999999; // ❌ ERREUR : propriété privée
compte.deposer(500); // Dépôt de 500 € effectué. Nouveau solde: 1500 €
compte.retirer(200); // Retrait de 200 € effectué. Nouveau solde: 1300 €
compte.retirer(2000); // Retrait impossible: solde insuffisant
}
🎯 Getters et Setters
Les getters et setters sont des méthodes spéciales qui permettent de contrôler l'accès aux propriétés d'une classe.
class Rectangle {
double _largeur;
double _hauteur;
Rectangle(this._largeur, this._hauteur);
// Getter pour la largeur
double get largeur => _largeur;
// Setter pour la largeur avec validation
set largeur(double valeur) {
if (valeur > 0) {
_largeur = valeur;
} else {
throw ArgumentError('La largeur doit être positive');
}
}
// Getter calculé pour l'aire
double get aire => _largeur * _hauteur;
// Getter calculé pour le périmètre
double get perimetre => 2 * (_largeur + _hauteur);
}
// Utilisation
void main() {
var rect = Rectangle(10, 5);
print('Largeur: ${rect.largeur}'); // 10
print('Aire: ${rect.aire}'); // 50
print('Périmètre: ${rect.perimetre}'); // 30
rect.largeur = 15; // Utilise le setter
print('Nouvelle aire: ${rect.aire}'); // 75
// rect.largeur = -5; // ❌ Lève une exception
}
aire et perimetre ci-dessus). C'est plus élégant que de créer des méthodes getAire() ou calculerPerimetre().
🧬 Héritage
L'héritage permet de créer une nouvelle classe basée sur une classe existante. La nouvelle classe (classe fille ou sous-classe) hérite des propriétés et méthodes de la classe parente (classe mère ou super-classe).
// Classe parente
class Animal {
String nom;
int age;
Animal(this.nom, this.age);
void manger() {
print('$nom mange...');
}
void dormir() {
print('$nom dort...');
}
}
// Classe fille qui hérite de Animal
class Chien extends Animal {
String race;
// Constructeur qui appelle le constructeur parent
Chien(String nom, int age, this.race) : super(nom, age);
// Nouvelle méthode spécifique au chien
void aboyer() {
print('$nom aboie: Wouf Wouf!');
}
// Surcharge (override) d'une méthode parente
@override
void manger() {
print('$nom (un $race) dévore ses croquettes!');
}
}
// Utilisation
void main() {
var chien = Chien('Rex', 3, 'Labrador');
chien.manger(); // Rex (un Labrador) dévore ses croquettes!
chien.dormir(); // Rex dort...
chien.aboyer(); // Rex aboie: Wouf Wouf!
print('${chien.nom} est un ${chien.race} de ${chien.age} ans');
}
L'annotation
@override indique que vous redéfinissez intentionnellement une méthode de la classe parente. Ce n'est pas obligatoire, mais c'est une bonne pratique car :• Cela rend le code plus lisible
• Dart vous avertira si vous faites une faute de frappe dans le nom de la méthode
🎭 Classes abstraites et interfaces
Une classe abstraite est une classe qui ne peut pas être instanciée directement. Elle sert de modèle pour d'autres classes.
// Classe abstraite
abstract class Forme {
// Méthode abstraite (sans implémentation)
double calculerAire();
// Méthode concrète (avec implémentation)
void afficher() {
print('Aire: ${calculerAire()}');
}
}
// Classe concrète qui implémente la méthode abstraite
class Cercle extends Forme {
double rayon;
Cercle(this.rayon);
@override
double calculerAire() {
return 3.14159 * rayon * rayon;
}
}
class Carre extends Forme {
double cote;
Carre(this.cote);
@override
double calculerAire() {
return cote * cote;
}
}
// Utilisation
void main() {
// var forme = Forme(); // ❌ ERREUR : impossible d'instancier une classe abstraite
Forme cercle = Cercle(5);
Forme carre = Carre(4);
cercle.afficher(); // Aire: 78.53975
carre.afficher(); // Aire: 16.0
}
Implémenter une interface avec implements
En Dart, toute classe peut servir d'interface. Quand vous utilisez implements, vous devez réimplémenter toutes les méthodes de la classe.
// Classe utilisée comme interface
class Volant {
void voler() {
print('Je vole...');
}
}
// Implémentation de l'interface
class Oiseau implements Volant {
String nom;
Oiseau(this.nom);
@override
void voler() {
print('$nom bat des ailes et vole!');
}
}
class Avion implements Volant {
String modele;
Avion(this.modele);
@override
void voler() {
print('L\'avion $modele décolle avec ses réacteurs!');
}
}
// Utilisation
void main() {
List<Volant> objetsVolants = [
Oiseau('Aigle'),
Avion('Boeing 747'),
];
for (var obj in objetsVolants) {
obj.voler();
}
// Aigle bat des ailes et vole!
// L'avion Boeing 747 décolle avec ses réacteurs!
}
🔀 Mixins
Les mixins sont un moyen de réutiliser du code dans plusieurs hiérarchies de classes. C'est une forme de composition qui permet d'ajouter des fonctionnalités à une classe sans utiliser l'héritage.
// Définition d'un mixin
mixin Nageur {
void nager() {
print('Je nage...');
}
}
mixin Marcheur {
void marcher() {
print('Je marche...');
}
}
// Classe de base
class Animal {
String nom;
Animal(this.nom);
}
// Classe qui utilise plusieurs mixins
class Canard extends Animal with Nageur, Marcheur {
Canard(String nom) : super(nom);
void voler() {
print('$nom vole!');
}
}
class Poisson extends Animal with Nageur {
Poisson(String nom) : super(nom);
}
// Utilisation
void main() {
var canard = Canard('Donald');
canard.marcher(); // Je marche...
canard.nager(); // Je nage...
canard.voler(); // Donald vole!
var poisson = Poisson('Nemo');
poisson.nager(); // Je nage...
// poisson.marcher(); // ❌ ERREUR : Poisson n'a pas le mixin Marcheur
}
SingleTickerProviderStateMixin est un mixin couramment utilisé pour les animations.
🎯 Exemple pratique : Système de bibliothèque
Mettons en pratique tous ces concepts avec un exemple complet d'un système de gestion de bibliothèque.
// Classe abstraite de base
abstract class Document {
String titre;
String auteur;
int anneePublication;
Document(this.titre, this.auteur, this.anneePublication);
// Méthode abstraite
void afficherInfo();
// Méthode concrète
String get description => '$titre par $auteur ($anneePublication)';
}
// Classe Livre
class Livre extends Document {
int nombrePages;
String isbn;
Livre({
required String titre,
required String auteur,
required int anneePublication,
required this.nombrePages,
required this.isbn,
}) : super(titre, auteur, anneePublication);
@override
void afficherInfo() {
print('📖 Livre: $description');
print(' Pages: $nombrePages | ISBN: $isbn');
}
}
// Classe Magazine
class Magazine extends Document {
int numeroEdition;
String periodicite;
Magazine({
required String titre,
required String auteur,
required int anneePublication,
required this.numeroEdition,
required this.periodicite,
}) : super(titre, auteur, anneePublication);
@override
void afficherInfo() {
print('📰 Magazine: $description');
print(' Édition: $numeroEdition | Périodicité: $periodicite');
}
}
// Classe Bibliothèque
class Bibliotheque {
String nom;
List<Document> _documents = [];
Bibliotheque(this.nom);
// Ajouter un document
void ajouterDocument(Document doc) {
_documents.add(doc);
print('✅ Document ajouté: ${doc.titre}');
}
// Afficher tous les documents
void afficherCatalogue() {
print('\n📚 Catalogue de la bibliothèque "$nom"');
print('=' * 50);
for (var doc in _documents) {
doc.afficherInfo();
print('');
}
print('Total: ${_documents.length} document(s)');
}
// Rechercher par titre
List<Document> rechercherParTitre(String recherche) {
return _documents
.where((doc) => doc.titre.toLowerCase().contains(recherche.toLowerCase()))
.toList();
}
}
// Programme principal
void main() {
// Créer une bibliothèque
var biblio = Bibliotheque('Bibliothèque Municipale');
// Ajouter des documents
biblio.ajouterDocument(Livre(
titre: 'Clean Code',
auteur: 'Robert C. Martin',
anneePublication: 2008,
nombrePages: 464,
isbn: '978-0132350884',
));
biblio.ajouterDocument(Livre(
titre: 'Flutter Apprentice',
auteur: 'raywenderlich.com Team',
anneePublication: 2021,
nombrePages: 723,
isbn: '978-1950325382',
));
biblio.ajouterDocument(Magazine(
titre: 'Flutter Magazine',
auteur: 'Équipe Flutter',
anneePublication: 2024,
numeroEdition: 12,
periodicite: 'Mensuel',
));
// Afficher le catalogue
biblio.afficherCatalogue();
// Rechercher
print('\n🔍 Recherche "Flutter":');
var resultats = biblio.rechercherParTitre('Flutter');
for (var doc in resultats) {
print(' - ${doc.titre}');
}
}
💡 Points clés à retenir
- Classe : Modèle définissant la structure et le comportement d'objets
- Objet : Instance concrète d'une classe
- Constructeur : Méthode spéciale pour créer et initialiser un objet
- Encapsulation : Cacher les détails internes avec
_pour les membres privés - Getters/Setters : Contrôler l'accès aux propriétés
- Héritage :
extendspour réutiliser et étendre une classe - Classes abstraites : Modèles qui ne peuvent pas être instanciés directement
- Interfaces :
implementspour implémenter un contrat - Mixins :
withpour composer des fonctionnalités
1.2.3 – Bonnes pratiques
Maintenant que vous maîtrisez les bases de Dart, il est temps d'apprendre à écrire du code de qualité. Dans cette section, nous allons découvrir les bonnes pratiques de développement en Dart, les conventions de nommage, et les techniques qui rendront votre code plus lisible, maintenable et professionnel.
📝 Conventions de nommage
Dart suit des conventions de nommage strictes qui rendent le code cohérent et facile à lire. Ces règles sont appliquées par l'outil d'analyse statique de Dart.
Classes, Enums et Types
Utilisez UpperCamelCase (chaque mot commence par une majuscule, pas d'underscore).
// ✅ BON
class Utilisateur { }
class CompteBancaire { }
class GestionnaireDeCache { }
enum StatutCommande { enAttente, validee, livree }
// ❌ MAUVAIS
class utilisateur { }
class compte_bancaire { }
class gestionnaireDeCache { }
Variables, fonctions et paramètres
Utilisez lowerCamelCase (commence par une minuscule, majuscule pour les mots suivants).
// ✅ BON
var nomUtilisateur = 'Alice';
var nombreDeConnexions = 0;
void calculerMontantTotal() { }
void envoyerEmailDeConfirmation() { }
// ❌ MAUVAIS
var NomUtilisateur = 'Alice';
var nombre_de_connexions = 0;
void CalculerMontantTotal() { }
void envoyer_email() { }
Constantes
Utilisez lowerCamelCase pour les constantes (contrairement à d'autres langages qui utilisent UPPER_CASE).
// ✅ BON
const double pi = 3.14159;
const int maxUtilisateurs = 100;
const String urlApi = 'https://api.exemple.com';
// ❌ MAUVAIS (style Java/JavaScript, pas Dart)
const double PI = 3.14159;
const int MAX_UTILISATEURS = 100;
Fichiers et bibliothèques
Utilisez snake_case (tout en minuscules avec underscores) pour les noms de fichiers.
// ✅ BON
compte_bancaire.dart
gestionnaire_utilisateur.dart
utils_date.dart
// ❌ MAUVAIS
CompteBancaire.dart
GestionnaireUtilisateur.dart
UtilsDate.dart
Propriétés privées
Préfixez avec un underscore _ pour indiquer qu'une propriété ou méthode est privée.
class CompteBancaire {
String titulaire; // Public
double _solde; // Privé
CompteBancaire(this.titulaire, this._solde);
double get solde => _solde; // Getter public pour accéder au solde privé
void _validerTransaction() { // Méthode privée
// Logique interne
}
}
•
UpperCamelCase : Classes, Enums, Types•
lowerCamelCase : Variables, fonctions, constantes•
snake_case : Noms de fichiers•
_private : Membres privés
🎯 Préférer const et final
Utilisez final et const autant que possible pour rendre votre code plus sûr et performant.
// ❌ MAUVAIS : Variables mutables alors qu'elles ne changent jamais
var ville = 'Paris';
var pi = 3.14159;
var maxConnexions = 100;
// ✅ BON : Variables immuables
final ville = 'Paris';
const pi = 3.14159;
const maxConnexions = 100;
// Avec des objets
// ❌ MAUVAIS
var coordonnees = Point(10, 20);
// ✅ BON
final coordonnees = Point(10, 20); // La référence ne change pas
const origine = Point(0, 0); // Constante de compilation
const quand la valeur est connue à la compilation, final pour les valeurs qui ne changent pas mais sont calculées à l'exécution, et var uniquement pour les variables qui doivent vraiment changer.
📚 Organisation du code
Ordre des membres d'une classe
Organisez les membres d'une classe dans un ordre logique et cohérent.
class Utilisateur {
// 1. Constantes statiques
static const int ageMinimum = 18;
// 2. Propriétés statiques
static int nombreUtilisateurs = 0;
// 3. Propriétés d'instance (publiques puis privées)
String nom;
String email;
int _age;
String? _telephone; // Propriétés optionnelles à la fin
// 4. Constructeurs
Utilisateur(this.nom, this.email, this._age) {
nombreUtilisateurs++;
}
// Constructeurs nommés après le principal
Utilisateur.anonyme()
: nom = 'Anonyme',
email = 'anonyme@exemple.com',
_age = 18;
// 5. Getters et Setters
int get age => _age;
set age(int valeur) {
if (valeur >= ageMinimum) {
_age = valeur;
}
}
// 6. Méthodes publiques
void afficherProfil() {
print('Utilisateur: $nom ($email)');
}
void envoyerEmail(String message) {
print('Email envoyé à $email: $message');
}
// 7. Méthodes privées
void _validerEmail() {
// Logique de validation
}
// 8. Méthodes statiques
static void afficherStatistiques() {
print('Nombre d\'utilisateurs: $nombreUtilisateurs');
}
// 9. Méthodes override (equals, hashCode, toString)
@override
String toString() => 'Utilisateur($nom, $email)';
}
Imports organisés
Organisez vos imports dans cet ordre : Dart core, packages externes, fichiers locaux.
// 1. Imports Dart core
import 'dart:async';
import 'dart:io';
// 2. Imports de packages externes
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
// 3. Imports locaux (relatifs)
import '../models/utilisateur.dart';
import '../services/api_service.dart';
import 'widgets/custom_button.dart';
🔗 Les différentes manières d'importer des fichiers en Dart
En Dart, l'importation de fichiers permet d'utiliser le code provenant d'autres fichiers ou packages. Il existe plusieurs manières d'importer selon vos besoins :
-
Import standard :
Utilisé pour importer des packages externes ou internes utilisant le systèmeimport 'package:nom_du_package/nom_du_fichier.dart';pub(ex : Flutter, http, etc.). -
Import relatif :
Utile pour importer des fichiers locaux dans votre projet (remonter ou descendre dans l'arborescence).import '../chemin/vers/fichier.dart'; -
Import absolu (Dart core) :
Sert à importer les bibliothèques natives du langage Dart (import 'dart:math';dart:io,dart:convert, etc.). -
Import avec alias :
Permet de donner un préfixe (iciimport 'package:http/http.dart' as http;http) à toutes les classes/fonctions importées pour éviter les conflits de noms ou améliorer la lisibilité. -
Import sélectif (show/hide) :
Pratique pour n'exposer que certaines fonctions/classes ou en masquer d'autres.import 'dart:math' show pi, sqrt; // Importera seulement pi et sqrt import 'dart:math' hide Random; // Importera tout sauf Random
– Tri : Organisez vos imports par ordre : Dart core, packages externes, imports locaux.
– Alias : Utilisez toujours
as lorsqu’il y a des conflits de noms.– Evitez les doublons et supprimez les imports inutilisés (l'IDE signale souvent ces erreurs).
💡 Utiliser l'inférence de type intelligemment
Dart peut inférer automatiquement les types, mais il est parfois préférable d'être explicite pour la clarté.
// ✅ BON : Inférence claire
var nom = 'Alice'; // Clairement un String
var age = 25; // Clairement un int
var liste = [1, 2, 3]; // Clairement une List<int>
// ✅ BON : Type explicite pour la clarté
List<Utilisateur> utilisateurs = []; // Mieux que var pour les collections vides
Map<String, int> scores = {}; // Type explicite utile
// ❌ MAUVAIS : Inférence ambiguë
var resultat = calculComplexe(); // Quel est le type ? Pas clair
// ✅ BON : Type explicite quand c'est ambigu
double resultat = calculComplexe();
// Variables de classe : toujours typées explicitement
class Configuration {
String apiUrl; // ✅ BON
int maxRetries; // ✅ BON
// var timeout; // ❌ MAUVAIS dans une classe
}
🔍 Null Safety - Bonnes pratiques
Dart impose la null safety. Voici comment l'utiliser efficacement.
// ✅ BON : Utiliser ?? pour valeur par défaut
String getNom(String? nomOptional) {
return nomOptional ?? 'Anonyme';
}
// ✅ BON : Utiliser ?. pour accès conditionnel
int? getLongueur(String? texte) {
return texte?.length;
}
// ✅ BON : Vérifier null avec if
void afficher(String? message) {
if (message != null) {
// Dans ce bloc, message est automatiquement non-null
print(message.toUpperCase());
}
}
// ❌ MAUVAIS : Utiliser ! sans vérification
void mauvais(String? texte) {
print(texte!.length); // Risque d'exception si texte est null
}
// ✅ BON : N'utiliser ! que quand vous êtes CERTAIN
void bon(String? texte) {
if (texte != null && texte.isNotEmpty) {
// Ici, on est certain que texte n'est pas null
traiterTexte(texte);
}
}
// ✅ BON : late pour initialisation différée
class Service {
late DatabaseConnection connection;
Future<void> initialiser() async {
connection = await DatabaseConnection.connect();
}
}
! (force unwrap). Il indique "je suis sûr que ce n'est pas null", mais si vous vous trompez, votre application plantera. Préférez les vérifications explicites ou les opérateurs ?? et ?..
📖 Documentation et commentaires
Écrivez des commentaires utiles et documentez votre code avec des doc comments.
Doc comments (///) pour APIs publiques
/// Calcule le montant total TTC à partir d'un prix HT.
///
/// Le [prixHT] doit être un nombre positif.
/// Le [tauxTVA] est un nombre décimal (ex: 0.20 pour 20%).
///
/// Retourne le prix TTC calculé.
///
/// Exemple:
/// ```dart
/// double prixTTC = calculerPrixTTC(100, 0.20); // 120.0
/// ```
double calculerPrixTTC(double prixHT, double tauxTVA) {
return prixHT * (1 + tauxTVA);
}
/// Représente un utilisateur de l'application.
///
/// Chaque utilisateur a un [nom], un [email] et un [age].
/// L'email doit être unique dans le système.
class Utilisateur {
/// Le nom complet de l'utilisateur.
final String nom;
/// L'adresse email (doit être valide et unique).
final String email;
/// L'âge de l'utilisateur en années.
final int age;
/// Crée un nouvel utilisateur.
Utilisateur(this.nom, this.email, this.age);
}
Commentaires simples pour la logique
void traiterCommande(Commande commande) {
// Vérifier si la commande est valide
if (!commande.estValide) {
throw ArgumentError('Commande invalide');
}
// Calculer le montant total avec les remises
final montantBase = commande.calculerMontantBase();
final remise = commande.calculerRemise();
final montantFinal = montantBase - remise;
// TODO: Implémenter le traitement du paiement
// FIXME: Bug potentiel avec les remises cumulées
// Enregistrer la commande
_sauvegarderCommande(commande, montantFinal);
}
•
// TODO: Pour marquer du code à compléter plus tard•
// FIXME: Pour indiquer un bug connu à corriger•
// NOTE: Pour des remarques importantesLa plupart des IDE détectent ces mots-clés et les mettent en évidence.
⚡ Performance et optimisation
Éviter les calculs répétés
// ❌ MAUVAIS : Calcul répété dans une boucle
for (var i = 0; i < liste.length; i++) { // liste.length calculé à chaque itération
print(liste[i]);
}
// ✅ BON : Utiliser for-in
for (var element in liste) {
print(element);
}
// ✅ BON : Stocker la longueur si nécessaire
final longueur = liste.length;
for (var i = 0; i < longueur; i++) {
print(liste[i]);
}
Utiliser const pour les widgets Flutter
// ❌ MAUVAIS : Widget recréé à chaque rebuild
Widget build(BuildContext context) {
return Column(
children: [
Text('Titre fixe'),
SizedBox(height: 20),
],
);
}
// ✅ BON : Widgets const ne sont pas recréés
Widget build(BuildContext context) {
return Column(
children: const [
Text('Titre fixe'),
SizedBox(height: 20),
],
);
}
Préférer les collections literals
// ❌ MAUVAIS : Constructeurs
var liste = List<int>();
var map = Map<String, int>();
var set = Set<String>();
// ✅ BON : Literals (plus court et plus rapide)
var liste = <int>[];
var map = <String, int>{};
var set = <String>{};
// Avec valeurs
var nombres = [1, 2, 3];
var ages = {'Alice': 25, 'Bob': 30};
var langages = {'Dart', 'Flutter', 'JavaScript'};
🧪 Gestion des erreurs
// ✅ BON : Try-catch spécifique
Future<void> chargerDonnees() async {
try {
final data = await api.fetchData();
traiterDonnees(data);
} on NetworkException catch (e) {
// Gérer les erreurs réseau spécifiquement
print('Erreur réseau: ${e.message}');
afficherMessageHorsLigne();
} on FormatException catch (e) {
// Gérer les erreurs de format
print('Données invalides: $e');
} catch (e, stackTrace) {
// Attraper toutes les autres erreurs
print('Erreur inattendue: $e');
print('Stack trace: $stackTrace');
rethrow; // Relancer si on ne peut pas gérer
}
}
// ✅ BON : Validation avec exceptions claires
double diviser(double a, double b) {
if (b == 0) {
throw ArgumentError('Division par zéro impossible');
}
return a / b;
}
// ✅ BON : Finally pour le nettoyage
Future<void> traiterFichier(String path) async {
File? file;
try {
file = File(path);
final content = await file.readAsString();
traiterContenu(content);
} catch (e) {
print('Erreur de lecture: $e');
} finally {
// Exécuté dans tous les cas (erreur ou non)
await file?.close();
}
}
🎨 Lisibilité du code
Fonctions courtes et ciblées
// ❌ MAUVAIS : Fonction trop longue qui fait trop de choses
void traiterUtilisateur(Map<String, dynamic> data) {
// Validation
if (data['nom'] == null) throw ArgumentError('Nom manquant');
if (data['email'] == null) throw ArgumentError('Email manquant');
// Création
final user = Utilisateur(data['nom'], data['email']);
// Sauvegarde
database.save(user);
// Notification
emailService.sendWelcome(user.email);
// Logging
logger.log('Utilisateur créé: ${user.nom}');
}
// ✅ BON : Fonctions courtes et spécifiques
void traiterUtilisateur(Map<String, dynamic> data) {
_validerDonnees(data);
final user = _creerUtilisateur(data);
_sauvegarderUtilisateur(user);
_envoyerEmailBienvenue(user);
_loggerCreation(user);
}
void _validerDonnees(Map<String, dynamic> data) {
if (data['nom'] == null) throw ArgumentError('Nom manquant');
if (data['email'] == null) throw ArgumentError('Email manquant');
}
Utilisateur _creerUtilisateur(Map<String, dynamic> data) {
return Utilisateur(data['nom'], data['email']);
}
// ... autres fonctions privées
Noms de variables descriptifs
// ❌ MAUVAIS : Noms courts et ambigus
var d = 86400;
var t = DateTime.now();
var l = users.length;
// ✅ BON : Noms descriptifs
var secondesParJour = 86400;
var maintenant = DateTime.now();
var nombreUtilisateurs = users.length;
// Exception : variables de boucle
for (var i = 0; i < liste.length; i++) { // i est acceptable
// ...
}
// Mais si imbriqué, soyez plus explicite
for (var ligne = 0; ligne < grille.length; ligne++) {
for (var colonne = 0; colonne < grille[ligne].length; colonne++) {
// ...
}
}
✅ Checklist des bonnes pratiques
✓ Les conventions de nommage sont respectées
✓ J'ai utilisé
final ou const quand possible✓ Pas d'utilisation abusive de
! (force unwrap)✓ Les classes publiques ont des doc comments
✓ Les imports sont organisés
✓ Pas de code mort (commenté ou inutilisé)
✓ Les fonctions font une seule chose
✓ Les noms de variables sont clairs
✓ Les erreurs sont gérées correctement
✓ Le code passe l'analyse statique (
dart analyze)
🛠️ Outils pour maintenir la qualité
Analyse statique
# Analyser votre code
dart analyze
# Formater automatiquement votre code
dart format .
# Vérifier le code Flutter
flutter analyze
Fichier analysis_options.yaml
Configurez les règles d'analyse dans votre projet.
# analysis_options.yaml
include: package:flutter_lints/flutter.yaml
linter:
rules:
# Conventions de nommage
- camel_case_types
- file_names
- non_constant_identifier_names
# Null safety
- avoid_null_checks_in_equality_operators
- prefer_is_not_empty
# Style
- prefer_const_constructors
- prefer_final_fields
- prefer_final_locals
# Performance
- unnecessary_null_in_if_null_operators
# Documentation
- public_member_api_docs
💡 Points clés à retenir
- Conventions : UpperCamelCase (classes), lowerCamelCase (variables/méthodes), snake_case (fichiers)
- Immutabilité : Préférer
constetfinalàvar - Null safety : Utiliser
??et?., éviter! - Documentation : Doc comments (
///) pour les APIs publiques - Organisation : Ordre logique des membres, imports groupés
- Lisibilité : Fonctions courtes, noms descriptifs
- Outils :
dart analyzeetdart format
Consultez le guide officiel des bonnes pratiques Dart :
dart.dev/guides/language/effective-dart
Il couvre en détail le style, la documentation, l'usage et le design du code Dart.
1.2.4 – Installation et premiers programmes
Pour développer en Dart et Flutter, vous avez le choix entre plusieurs éditeurs de code. Dans cette section, nous allons découvrir Visual Studio Code (VSCode), un éditeur de code léger, rapide et très populaire dans la communauté Flutter. VSCode est une excellente option pour débuter en Dart et créer vos premiers programmes. Nous verrons comment le configurer et créer votre premier programme "Hello World". (Note : nous découvrirons également Android Studio dans la section 1.3.1, qui est un autre excellent choix pour le développement Flutter.)
💻 Pourquoi choisir VSCode ?
VSCode offre plusieurs avantages pour le développement Flutter et Dart :
- Léger et rapide : Démarrage beaucoup plus rapide qu'Android Studio
- Gratuit et open source : Développé par Microsoft
- Extensible : Des milliers d'extensions disponibles
- Multi-plateforme : Fonctionne sur Windows, macOS et Linux
- Intégration Git : Gestion de version intégrée
- Terminal intégré : Exécuter des commandes sans quitter l'éditeur
📥 Étape 1 : Installation de Visual Studio Code
Commençons par installer VSCode sur votre ordinateur Windows.
Téléchargement
- Rendez-vous sur code.visualstudio.com
- Cliquez sur le bouton "Download for Windows"
- Le fichier d'installation (environ 100 Mo) se télécharge automatiquement
Installation
- Double-cliquez sur le fichier téléchargé (
VSCodeUserSetup-x64-xxx.exe) - L'assistant d'installation s'ouvre
- Options recommandées à cocher :
- ☑ Ajouter à PATH (permet d'ouvrir VSCode depuis le terminal)
- ☑ Créer une entrée dans le menu contextuel "Ouvrir avec Code"
- ☑ Enregistrer les fichiers .code associés à VSCode
- Cliquez sur "Suivant" puis "Installer"
- L'installation prend quelques secondes
- Cliquez sur "Terminer" pour lancer VSCode
🔌 Étape 2 : Installation de l'extension Flutter
VSCode fonctionne avec des extensions qui ajoutent des fonctionnalités. Pour développer en Flutter et Dart, nous devons installer l'extension officielle Flutter. Vous pouvez également la trouver directement sur le Marketplace VSCode.
Méthode 1 : Via le panneau Extensions
- Ouvrez VSCode
- Cliquez sur l'icône Extensions dans la barre latérale gauche (ou appuyez sur Ctrl+Shift+X)
- Dans la barre de recherche, tapez :
Flutter - Recherchez "Flutter" par Dart Code (publié par flutter.dev)
- Cliquez sur "Installer"
- L'extension Dart sera automatiquement installée en même temps (c'est une dépendance)
Méthode 2 : Via la commande
- Appuyez sur Ctrl+Shift+P pour ouvrir la palette de commandes
- Tapez :
Extensions: Install Extensions - Recherchez "Flutter" et installez
Vérification de l'installation
Après l'installation, vous devriez voir :
- Un message de notification indiquant que l'extension est installée
- VSCode peut demander de redémarrer (cliquez sur "Reload" si demandé)
- L'extension apparaît dans la liste des extensions installées (panneau Extensions)
pubspec.yaml). Pour l'instant, vous pouvez vérifier que l'extension est installée en allant dans le panneau Extensions et en cherchant "Flutter" dans les extensions installées.
- Coloration syntaxique Dart
- Autocomplétion intelligente
- Débogage intégré
- Hot Reload depuis VSCode
- Détection automatique des appareils Flutter
⚙️ Étape 3 : Configuration de VSCode pour Flutter
Une fois l'extension installée, vérifions que VSCode détecte bien Flutter.
Vérifier le chemin Flutter
- Appuyez sur Ctrl+Shift+P pour ouvrir la palette de commandes
- Tapez :
Dart: Change SDK - Si Flutter est dans votre PATH, VSCode le détectera automatiquement
- Sinon, indiquez le chemin :
C:\dev\flutter(ou votre emplacement)
Vérifier la configuration
Dans le terminal intégré de VSCode (Terminal → Nouveau terminal ou Ctrl+`), tapez :
# Vérifier que Flutter est détecté
flutter --version
# Diagnostic complet
flutter doctor
flutter n'est pas reconnu dans le terminal de VSCode, vérifiez que vous avez bien ajouté Flutter au PATH système (voir section 1.3.1). Vous devrez peut-être redémarrer VSCode après avoir modifié le PATH.
📝 Étape 4 : Créer votre premier programme Dart
Maintenant que tout est configuré, créons votre premier programme Dart : un classique "Hello World" !
Créer un nouveau fichier
- Dans VSCode, cliquez sur Fichier → Nouveau fichier (ou Ctrl+N)
- Enregistrez le fichier avec Ctrl+S
- Nommez-le :
hello_world.dart - Choisissez un emplacement (ex:
C:\dev\dart_programs) - Cliquez sur "Enregistrer"
C:\dev\dart_programs. Cela vous aidera à organiser vos fichiers.
Écrire le code "Hello World"
Dans le fichier hello_world.dart, tapez le code suivant :
// Mon premier programme Dart
void main() {
print('Hello, World!');
print('Bienvenue dans le monde de Dart !');
}
Explication du code
Analysons ce code ligne par ligne :
- Ligne 1 :
// Mon premier programme Dart- C'est un commentaire. Tout ce qui suit//sur une ligne est ignoré par Dart. Les commentaires servent à documenter votre code. - Ligne 2 :
void main() {- Définit la fonctionmain(), qui est le point d'entrée de tout programme Dart.voidsignifie que cette fonction ne retourne rien. - Ligne 3 :
print('Hello, World!');- Affiche le texte "Hello, World!" dans la console.print()est une fonction intégrée à Dart. - Ligne 4 :
print('Bienvenue dans le monde de Dart !');- Affiche un second message. - Ligne 5 :
}- Ferme le bloc de la fonctionmain().
void main() {
// Votre code ici
}
Tous les programmes Dart commencent par la fonction
main(). C'est là que l'exécution commence.
▶️ Étape 5 : Exécuter le programme
Maintenant, exécutons notre programme pour voir le résultat !
Méthode 1 : Via le terminal intégré de VSCode
- Ouvrez le terminal intégré : Terminal → Nouveau terminal (ou Ctrl+`)
- Assurez-vous d'être dans le bon dossier. Si nécessaire, naviguez :
cd C:\dev\dart_programs - Exécutez le programme avec la commande :
dart run hello_world.dart
Vous devriez voir dans le terminal :
Hello, World!
Bienvenue dans le monde de Dart !
En haut : Barre de commande avec
dart run hello_world.dartRésultat affiché :
Hello, World!
Bienvenue dans le monde de Dart !
En bas : Prompt du terminal prêt pour la prochaine commande
Méthode 2 : Via le bouton "Run" de VSCode
- Ouvrez votre fichier
hello_world.dart - Un bouton Run ▷ (ou Run and Debug) apparaît en haut à droite
- Cliquez dessus
- Le résultat s’affiche dans Debug Console ou Terminal
Exemple du terminal affichant le résultat via le bouton « Run » de VSCode.
Variante : Programme interactif
Créons une version plus interactive de notre programme :
import 'dart:io';
void main() {
print('=== Bienvenue dans Dart ===');
print('');
// Demander le nom de l'utilisateur
stdout.write('Quel est votre nom ? ');
String? nom = stdin.readLineSync();
// Afficher un message personnalisé
if (nom != null && nom.isNotEmpty) {
print('Bonjour, $nom ! Bienvenue dans le monde de Dart !');
} else {
print('Bonjour, visiteur anonyme !');
}
print('');
print('Programme terminé. À bientôt !');
}
Ce programme :
- Demande votre nom à l'utilisateur
- Lit la réponse depuis le terminal
- Affiche un message personnalisé
Exécutez-le avec dart run et testez-le !
dart:io. C'est nécessaire pour stdin.readLineSync() et stdout.write().
🎨 Fonctionnalités utiles de VSCode pour Dart
VSCode offre de nombreuses fonctionnalités qui facilitent le développement Dart :
Autocomplétion intelligente
Tapez quelques lettres et VSCode vous propose des suggestions. Appuyez sur Tab ou Entrée pour accepter.
Coloration syntaxique
Le code est automatiquement coloré pour améliorer la lisibilité : mots-clés en bleu, chaînes en vert, commentaires en gris, etc.
Détection d'erreurs en temps réel
VSCode souligne les erreurs en rouge avant même d'exécuter le code. Passez la souris pour voir le message d'erreur.
Formatage automatique
Formatez votre code automatiquement :
- Raccourci : Shift+Alt+F (Windows) ou Shift+Option+F (Mac)
- Menu : Clic droit → "Format Document"
Navigation rapide
- Ctrl+P : Ouvrir rapidement un fichier
- Ctrl+Shift+F : Rechercher dans tous les fichiers
- F12 : Aller à la définition d'une fonction/classe
- Alt+F12 : Voir la définition sans quitter le fichier
🌐 Étape 6 : Tester Dart en ligne (sans installation)
Si vous n'avez pas encore installé Flutter ou Dart, ou si vous voulez tester rapidement du code, vous pouvez utiliser des outils en ligne gratuits !
DartPad - L'outil officiel en ligne
DartPad est l'éditeur Dart en ligne officiel créé par l'équipe Dart. C'est parfait pour :
- Tester rapidement du code Dart
- Partager des exemples de code
- Apprendre sans installer quoi que ce soit
- Expérimenter avec les fonctionnalités de Dart
Comment utiliser DartPad
- Rendez-vous sur dartpad.dev
- Vous voyez un éditeur de code dans votre navigateur
- Tapez ou collez votre code Dart
- Cliquez sur le bouton "Run" (ou appuyez sur Ctrl+Enter)
- Le résultat s'affiche dans le panneau de droite
À gauche : Éditeur de code avec le code Dart
• Bouton "Run" en haut
• Bouton "Format" pour formater le code
• Bouton "Reset" pour réinitialiser
À droite : Panneau de sortie
• Affiche le résultat de
print()• Affiche les erreurs s'il y en a
En bas : Options (null safety, SDK version)
Exemple avec DartPad
Essayez ce code dans DartPad :
void main() {
print('Hello depuis DartPad !');
// Calcul simple
int a = 10;
int b = 20;
int somme = a + b;
print('La somme de $a et $b est $somme');
// Boucle
for (int i = 1; i <= 5; i++) {
print('Compteur: $i');
}
}
Cliquez sur "Run" et voyez le résultat instantanément !
À gauche : code Dart tapé dans l’éditeur.
À droite : panneau de sortie affichant le résultat du programme après avoir cliqué sur « Run ».
✅ Aucune installation requise
✅ Fonctionne sur n'importe quel ordinateur avec un navigateur
✅ Partage facile (lien URL)
✅ Toujours à jour avec la dernière version de Dart
✅ Parfait pour les tutoriels et démonstrations
Limitations :
❌ Pas d'accès aux fichiers système
❌ Pas de packages externes (sauf quelques-uns préchargés)
❌ Pas de débogage avancé
Autres outils en ligne
Outre DartPad, vous pouvez aussi utiliser :
- Replit (replit.com) : Environnement de développement complet en ligne, supporte Dart
- CodePen (codepen.io) : Principalement pour le web, mais peut être utilisé pour du code Dart simple
- GitHub Codespaces (github.com/codespaces) : Environnement de développement dans le cloud (nécessite un compte GitHub)
• DartPad : Pour tester rapidement, apprendre, partager des exemples
• VSCode : Pour développer des programmes complets, projets locaux
• Android Studio : Pour développer des applications Flutter complètes
Pour débuter, DartPad est excellent. Passez à VSCode quand vous voulez créer des projets plus complexes.
💡 Points clés à retenir
- VSCode : Éditeur léger et rapide, excellent pour apprendre Dart
- Extension Flutter : Nécessaire pour le support Dart/Flutter dans VSCode
- Structure d'un programme Dart : Commence toujours par
void main() { } - print() : Fonction pour afficher du texte dans la console
- dart run : Commande pour exécuter un fichier Dart
- DartPad : Outil en ligne gratuit pour tester Dart sans installation
- Autocomplétion : VSCode suggère automatiquement du code
- Formatage : Shift+Alt+F pour formater le code automatiquement
Maintenant que vous savez créer et exécuter des programmes Dart simples, vous êtes prêt à :
- Explorer les types de données (section 1.2.1)
- Créer des fonctions plus complexes
- Découvrir les classes et l'OOP (section 1.2.2)
- Passer à Flutter pour créer des interfaces graphiques (chapitre suivant)
Créez un dossier
mes_programmes_dart et gardez tous vos fichiers d'apprentissage organisés. Chaque nouveau concept que vous apprenez, créez un fichier pour le tester. Par exemple :
hello_world.darttest_variables.darttest_fonctions.darttest_classes.dart