Dart
Sommaire
- Introduction à Dart
- Qu’est-ce que Dart ?
- Historique et principes (AOT, JIT)
- Environnement d’exécution (Dart VM, Flutter)
- Compilation et exécution
- Écosystème Dart et cas d’utilisation
- Bases du Langage
- Syntaxe de base
- Variables et types de données (built-in types,
var
,dynamic
) - Opérateurs
- Structures de contrôle (conditions, boucles)
- Fonctions
- Collections (List, Set, Map)
- Programmation Orientée Objet (POO) en Dart
- Classes et Objets
- Encapsulation, Héritage, Polymorphisme
- Interfaces (implicites) et Classes Abstraites
- Mixins
- Constructeurs et
this
- Gestion des Erreurs et Exceptions
- Types d’exceptions
- Blocs
try-on-catch-finally
- Déclaration et levée d’exceptions
- Asynchronisme en Dart
- Futures
async
etawait
- Streams
- Null Safety
- Introduction au Null Safety
- Types nullables et non-nullables
- Opérateurs de null safety (
?
,!
,??
,?.
)
- Développement d’Applications avec Flutter
- Introduction à Flutter
- Widgets (StatelessWidget, StatefulWidget)
- Layouts et UI
- Gestion d’état (State Management)
- Bonnes Pratiques et Patterns
- Conventions de codage (Dart Style Guide)
- Patterns de conception courants
- Principes SOLID
- Ressources et Communauté
- Documentation officielle
- Communautés en ligne
- Livres et cours
1. Introduction à Dart
Qu’est-ce Que Dart ?
Dart est un langage de programmation client optimisé pour le développement rapide d’applications sur n’importe quelle plateforme, avec un accent particulier sur le développement mobile, web et desktop via le framework Flutter. Il est développé par Google.
Historique Et Principes (AOT, JIT)
- Développé par Google, première version en 2011.
- Conçu pour le développement d’applications client.
- Supporte la compilation AOT (Ahead-Of-Time) pour des performances natives sur mobile et desktop.
- Supporte la compilation JIT (Just-In-Time) pour un développement rapide avec “Hot Reload” sur Flutter.
Environnement D’exécution (Dart VM, Flutter)
- Dart VM : Machine virtuelle Dart, utilisée pour le développement rapide (JIT) et l’exécution côté serveur (bien que moins courant).
- Flutter : Framework UI multiplateforme qui utilise Dart comme langage de programmation. Il compile le code Dart en code machine natif pour iOS, Android, web, et desktop.
Compilation Et Exécution
- Écrire le code source Dart (
.dart
). - Exécuter directement avec la Dart VM (
dart mon_programme.dart
) pour le développement. - Compiler en JavaScript pour le web (
dart compile js mon_programme.dart
). - Compiler en code machine natif pour les applications mobiles/desktop avec Flutter.
# Exécution directe
dart main.dart
# Compilation pour le web
dart compile js main.dart
# Exécution d'une application Flutter
flutter run
Écosystème Dart Et Cas D’utilisation
- Développement Mobile (Flutter) : Cas d’utilisation principal, permettant de créer des applications natives pour iOS et Android à partir d’une seule base de code.
- Développement Web : Peut être utilisé pour le frontend web (compilé en JS) ou le backend (avec des frameworks comme Aqueduct ou Shelf).
- Développement Desktop (Flutter) : Création d’applications natives pour Windows, macOS, Linux.
- Scripts et Outils en ligne de commande : Dart est excellent pour écrire des scripts système ou des outils de développement.
2. Bases Du Langage
Syntaxe De Base
- Orienté objet : Tout est objet en Dart.
- Syntaxe inspirée de C, Java et JavaScript.
- Utilisation de classes, fonctions et variables.
- Point d’entrée : la fonction
main()
.
void main() {
print('Hello, Dart!');
}
Variables Et Types De Données (built-in types, var
, dynamic
)
- Types built-in :
int
,double
,String
,bool
,List
,Map
,Set
,Runes
,Symbol
. var
: Déclaration de variable dont le type est inféré par le compilateur.dynamic
: Variable dont le type peut changer à l’exécution (moins sûr, à utiliser avec prudence).final
: Variable qui ne peut être assignée qu’une seule fois.const
: Variable qui est une constante de compilation.
int age = 30;
String name = 'Alice';
double price = 99.99;
bool isActive = true;
var quantity = 100; // int
var message = 'Hello'; // String
dynamic anything = 'Can be anything';
anything = 123;
final DateTime now = DateTime.now();
const double PI = 3.14159;
Opérateurs
- Arithmétiques :
+
,-
,*
,/
,~/
(division entière),%
- Relationnels :
==
,!=
,<
,>
,<=
,>=
- Logiques :
&&
,||
,!
- Affectation :
=
,+=
,-=
,*=
,/=
,%=
- Incrémentation/Décrémentation :
++
,--
- Opérateurs de type :
is
,is!
,as
- Opérateurs de null safety :
??
,?.
,!
(expliqués plus loin)
int x = 10;
int y = 5;
print(x + y); // 15
print(x == y); // false
String? nullableString;
print(nullableString ?? 'Default Value'); // 'Default Value'
Structures De Contrôle (conditions, boucles)
if
,else if
,else
: Conditionnellesfor
,while
,do-while
: Bouclesswitch
: Sélection multiple
int score = 85;
if (score > 90) {
print('Excellent');
} else if (score > 80) {
print('Très bien');
} else {
print('Bien');
}
for (int i = 0; i < 3; i++) {
print(i);
}
int count = 0;
while (count < 2) {
print('Count: $count');
count++;
}
Fonctions
- Déclaration :
type_retour nomFonction(paramètres) { // corps }
- Fonctions fléchées (
=>
) pour les corps de fonction d’une seule expression. - Paramètres nommés et optionnels.
// Fonction simple
int add(int a, int b) {
return a + b;
}
// Fonction fléchée
int multiply(int a, int b) => a * b;
// Paramètres nommés
void greet({String? name, String? message}) {
print('$message, $name!');
}
void main() {
print(add(2, 3)); // 5
print(multiply(4, 5)); // 20
greet(name: 'Alice', message: 'Bonjour'); // Bonjour, Alice!
}
Collections (List, Set, Map)
- List : Collection ordonnée d’éléments (permet les doublons).
- Set : Collection non ordonnée d’éléments uniques (ne permet pas les doublons).
- Map : Collection de paires clé-valeur (chaque clé est unique).
// List
List<int> numbers = [1, 2, 3];
numbers.add(4);
print(numbers[0]); // 1
// Set
Set<String> uniqueNames = {'Alice', 'Bob'};
uniqueNames.add('Alice'); // N'ajoute pas de doublon
print(uniqueNames); // {Alice, Bob}
// Map
Map<String, String> capitals = {
'France': 'Paris',
'Germany': 'Berlin',
};
print(capitals['France']); // Paris
capitals['Italy'] = 'Rome';
3. Programmation Orientée Objet (POO) en Dart
Classes Et Objets
- Une classe est un modèle pour créer des objets.
- Un objet est une instance d’une classe.
class Car {
String brand;
String model;
Car(this.brand, this.model); // Constructeur concis
void displayInfo() {
print('Brand: $brand, Model: $model');
}
}
void main() {
Car myCar = Car('Toyota', 'Corolla');
myCar.displayInfo(); // Brand: Toyota, Model: Corolla
}
Encapsulation, Héritage, Polymorphisme
- Encapsulation : Les membres privés sont préfixés par un underscore (
_
). - Héritage : Utilisation du mot-clé
extends
. - Polymorphisme : Les objets peuvent prendre plusieurs formes (surcharge d’opérateurs, méthodes d’instance).
// Encapsulation
class Account {
double _balance; // Privé
Account(this._balance);
double get balance => _balance; // Getter
set balance(double newBalance) { // Setter
if (newBalance >= 0) {
_balance = newBalance;
}
}
}
// Héritage
class Animal {
void makeSound() {
print('Animal makes a sound');
}
}
class Dog extends Animal {
@override
void makeSound() {
print('Dog barks');
}
}
// Polymorphisme
void main() {
Animal myDog = Dog();
myDog.makeSound(); // Dog barks
}
Interfaces (implicites) et Classes Abstraites
- Interfaces implicites : En Dart, toutes les classes définissent implicitement une interface. Une classe peut implémenter l’interface d’une autre classe en utilisant le mot-clé
implements
. - Classes Abstraites : Classes qui ne peuvent pas être instanciées directement et qui peuvent contenir des méthodes abstraites (sans implémentation).
// Interface implicite
class Flyable {
void fly() {
print('Flying...');
}
}
class Bird implements Flyable {
@override
void fly() {
print('Bird is flying');
}
}
// Classe Abstraite
abstract class Shape {
double getArea(); // Méthode abstraite
void display() {
print('This is a shape');
}
}
class Circle extends Shape {
double radius;
Circle(this.radius);
@override
double getArea() {
return 3.14 * radius * radius;
}
}
Mixins
- Les mixins permettent de réutiliser le code d’une classe dans plusieurs hiérarchies de classes. Ils sont utilisés avec le mot-clé
with
.
mixin Logger {
void log(String message) {
print('[LOG]: $message');
}
}
class UserService with Logger {
void createUser(String name) {
log('User $name created');
}
}
void main() {
UserService service = UserService();
service.createUser('John Doe'); // [LOG]: User John Doe created
}
Constructeurs Et this
- Constructeur : Méthode spéciale pour initialiser les objets.
- Constructeurs nommés : Permettent de définir plusieurs constructeurs pour une classe.
this
: Fait référence à l’instance courante de la classe.
class Point {
double x, y;
// Constructeur par défaut
Point(this.x, this.y);
// Constructeur nommé
Point.origin() : this(0, 0);
// Constructeur nommé avec initialisation
Point.fromJson(Map<String, double> json)
: x = json['x']!,
y = json['y']!;
void display() {
print('Point: ($x, $y)');
}
}
void main() {
Point p1 = Point(10, 20);
p1.display(); // Point: (10.0, 20.0)
Point p2 = Point.origin();
p2.display(); // Point: (0.0, 0.0)
Point p3 = Point.fromJson({'x': 5.0, 'y': 15.0});
p3.display(); // Point: (5.0, 15.0)
}
4. Gestion des Erreurs et Exceptions
Types D’exceptions
- Dart a des exceptions
Error
(erreurs de programmation, non récupérables) etException
(conditions anormales, récupérables). - Exemples :
FormatException
,StateError
,ArgumentError
.
Blocs try-on-catch-finally
try
: Contient le code qui peut potentiellement lever une exception.on
: Gère une exception d’un type spécifique.catch
: Capture l’objet exception et/ou la pile d’appels.finally
: Contient le code qui est exécuté que l’exception soit levée ou non.
void main() {
try {
int result = 10 ~/ 0; // Division par zéro
print(result);
} on IntegerDivisionByZeroException {
print('Cannot divide by zero!');
} catch (e, s) {
print('An unknown error occurred: $e');
print('Stack trace: $s');
} finally {
print('Execution finished.');
}
}
Déclaration Et Levée D’exceptions
- Utilisation du mot-clé
throw
pour lever une exception.
void checkAge(int age) {
if (age < 0) {
throw ArgumentError('Age cannot be negative');
}
print('Age is valid: $age');
}
void main() {
try {
checkAge(-5);
} catch (e) {
print('Error: $e'); // Error: Invalid argument(s): Age cannot be negative
}
}
5. Asynchronisme en Dart
Dart est un langage à thread unique, mais il gère l’asynchronisme via un “event loop” et des “isolates” (pour le parallélisme).
Futures
- Un
Future
représente le résultat d’une opération asynchrone qui n’est pas encore disponible. Il peut être complété avec une valeur ou une erreur.
Future<String> fetchUserData() {
return Future.delayed(Duration(seconds: 2), () => 'User data fetched!');
}
void main() {
print('Fetching data...');
fetchUserData().then((data) {
print(data); // S'exécute après 2 secondes
}).catchError((error) {
print('Error: $error');
});
print('Program continues...');
}
async
et await
- Les mots-clés
async
etawait
simplifient l’écriture de code asynchrone, le rendant plus lisible et similaire au code synchrone. - Une fonction marquée
async
retourne implicitement unFuture
. await
peut être utilisé pour attendre la complétion d’unFuture
dans une fonctionasync
.
Future<String> fetchOrder() async {
print('Fetching order...');
await Future.delayed(Duration(seconds: 3));
return 'Order #123 fetched!';
}
void main() async {
String order = await fetchOrder(); // Attend la complétion du Future
print(order);
print('Main function finished.');
}
Streams
- Un
Stream
est une séquence de données asynchrones. Il peut émettre zéro ou plusieurs événements (données ou erreurs) au fil du temps. Utile pour les événements UI, les données de base de données en temps réel, etc.
Stream<int> countStream(int max) async* {
for (int i = 1; i <= max; i++) {
await Future.delayed(Duration(milliseconds: 500));
yield i; // Émet une valeur
}
}
void main() {
print('Starting stream...');
countStream(3).listen((number) {
print('Received: $number');
}, onDone: () {
print('Stream finished.');
}, onError: (error) {
print('Stream error: $error');
});
print('Main continues after stream setup.');
}
6. Null Safety
Introduction Au Null Safety
- Introduit en Dart 2.12, le null safety est une fonctionnalité qui aide à prévenir les erreurs de référence nulle à l’exécution. Le compilateur Dart analyse le code pour s’assurer que les variables ne peuvent pas être nulles à moins que cela ne soit explicitement autorisé.
Types Nullables Et Non-nullables
- Par défaut, les types sont non-nullables (ex:
String
,int
). - Pour rendre un type nullable, ajoutez un
?
après le type (ex:String?
,int?
).
String name = 'Alice'; // Non-nullable
// name = null; // Erreur de compilation
String? nullableName = 'Bob'; // Nullable
nullableName = null; // OK
Opérateurs De Null Safety (?
, !
, ??
, ?.
)
?
(Nullable type) : Déclare un type comme pouvant être nul.!
(Null assertion operator) : Indique au compilateur que vous êtes sûr qu’une expression nullable n’est pas nulle à ce point. À utiliser avec prudence, car si l’expression est nulle, une erreur d’exécution sera levée.??
(Null-aware operator) : Fournit une valeur par défaut si l’expression de gauche est nulle.?.
(Null-aware access operator) : Accède à un membre d’un objet seulement si l’objet n’est pas nul.
String? firstName;
String lastName = 'Doe';
// Utilisation de !
String fullName = firstName! + ' ' + lastName; // Peut lever une erreur si firstName est null
// Utilisation de ??
String displayFirstName = firstName ?? 'Guest';
print(displayFirstName); // Affiche 'Guest'
// Utilisation de ?.
int? length = firstName?.length; // length sera null si firstName est null
print(length); // Affiche null
// Combinaison
String userStatus = firstName?.isEmpty ?? true ? 'New User' : 'Existing User';
print(userStatus); // Affiche 'New User'
7. Développement d’Applications avec Flutter
Introduction à Flutter
- Flutter est le framework UI de Google pour la création d’applications compilées de manière native pour mobile, web et desktop à partir d’une seule base de code.
- Il utilise Dart et est connu pour ses performances élevées et son “Hot Reload” pour un développement rapide.
Widgets (StatelessWidget, StatefulWidget)
- Tout en Flutter est un widget. Les widgets sont les blocs de construction de l’interface utilisateur.
StatelessWidget
: Widgets qui n’ont pas d’état mutable. Leur configuration est définie au moment de la création.StatefulWidget
: Widgets qui peuvent changer d’état au cours de leur vie. Ils ont un objetState
associé qui gère l’état mutable.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget { // StatelessWidget
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(title: Text('Hello Flutter')),
body: Center(
child: Text('Welcome to Flutter!'),
),
),
);
}
}
class CounterApp extends StatefulWidget { // StatefulWidget
@override
_CounterAppState createState() => _CounterAppState();
}
class _CounterAppState extends State<CounterApp> {
int _counter = 0;
void _incrementCounter() {
setState(() { // Rebuild le widget avec le nouvel état
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Counter App')),
body: Center(
child: Text('Counter: $_counter', style: TextStyle(fontSize: 24)),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
child: Icon(Icons.add),
),
);
}
}
Layouts et UI
- Flutter utilise un système de composition de widgets pour construire des layouts.
- Widgets courants pour le layout :
Row
,Column
,Stack
,Container
,Padding
,Center
.
import 'package:flutter/material.dart';
class LayoutExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Layout Example')),
body: Column( // Widgets organisés verticalement
children: [
Container(
color: Colors.blue,
padding: EdgeInsets.all(16),
child: Text('Header', style: TextStyle(color: Colors.white)),
),
Row( // Widgets organisés horizontalement
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Icon(Icons.star, size: 50),
Icon(Icons.star, size: 50),
Icon(Icons.star, size: 50),
],
),
Expanded( // Prend l'espace restant
child: Center(
child: Text('Main Content'),
),
),
],
),
);
}
}
Gestion D’état (State Management)
- La gestion d’état est cruciale pour les applications complexes. Flutter offre plusieurs approches :
setState
: Pour les états locaux simples (dans unStatefulWidget
).- Provider : Un package populaire pour la gestion d’état simple à moyenne.
- Bloc/Cubit : Pour les applications plus complexes, basé sur des événements et des états.
- Riverpod : Alternative à Provider, offrant une sécurité de type améliorée.
- GetX : Solution complète pour la gestion d’état, les dépendances et les routes.
8. Bonnes Pratiques et Patterns
Conventions De Codage (Dart Style Guide)
- Suivre le Dart Style Guide pour un code cohérent et lisible.
- Utiliser des noms clairs et descriptifs (
camelCase
pour les variables/fonctions,PascalCase
pour les classes). - Formater le code avec
dart format
.
Patterns De Conception Courants
- Singleton : Assure qu’une classe n’a qu’une seule instance.
- Factory : Fournit une méthode pour créer des objets sans exposer la logique de création.
- Observer : Définit une dépendance un-à-plusieurs (souvent utilisé avec les Streams).
- Builder : Sépare la construction d’un objet complexe de sa représentation.
- Provider/Bloc/Cubit : Patterns spécifiques à Flutter pour la gestion d’état.
Principes SOLID
Les principes SOLID sont applicables en Dart et Flutter pour concevoir des applications maintenables et évolutives.
- S (Single Responsibility Principle) : Une classe ou un widget ne devrait avoir qu’une seule raison de changer.
- O (Open/Closed Principle) : Les entités logicielles doivent être ouvertes à l’extension, mais fermées à la modification.
- L (Liskov Substitution Principle) : Les objets d’un programme doivent pouvoir être remplacés par des instances de leurs sous-types sans altérer la justesse du programme.
- I (Interface Segregation Principle) : Les clients ne devraient pas être forcés de dépendre d’interfaces qu’ils n’utilisent pas.
- D (Dependency Inversion Principle) : Les modules de haut niveau ne devraient pas dépendre des modules de bas niveau. Les deux devraient dépendre d’abstractions.
9. Ressources et Communauté
Documentation Officielle
Communautés En Ligne
Livres Et Cours
- “Dart Apprentice” par Ray Wenderlich : Excellent pour apprendre Dart.
- “Flutter Apprentice” par Ray Wenderlich : Pour maîtriser Flutter.
- Cours en ligne : Udemy, Coursera, edX, et la chaîne YouTube officielle de Flutter proposent de nombreux tutoriels et cours.