dart-modern-features

Directives pour l'utilisation des fonctionnalités modernes de Dart (v3.0 - v3.10) telles que les Records, le Pattern Matching, les Switch Expressions, les Extension Types, les Class Modifiers, les Wildcards, les Null-Aware Elements et les Dot Shorthands.

npx skills add https://github.com/flutter/skills --skill dart-modern-features

Fonctionnalités Dart modernes

1. Quand utiliser cette compétence

Utilisez cette compétence quand :

  • Vous écrivez ou relisez du code Dart ciblant Dart 3.0 ou ultérieur.
  • Vous refactorisez du code Dart hérité pour utiliser des fonctionnalités modernes, concises et sûres.
  • Vous cherchez des moyens idiomatiques de gérer plusieurs valeurs de retour, l'extraction de données profonde ou la vérification exhaustive.

Découverte

Pour trouver des candidats à la modernisation :

Switch Expressions

Recherchez les instructions switch où chaque cas assigne à la même variable ou retourne :

  • Regex : switch\s*\([^)]+\)\s*\{\s*case

Candidats Pattern Matching

Recherchez l'extraction manuelle de propriétés map ou JSON et la vérification de type :

  • Regex : containsKey\(['"][^'"]+['"]\)
  • Regex : json\[['"][^'"]+['"]\]\s+is\s+

Éléments Null-Aware

Recherchez les instructions collection if qui vérifient la nullité :

  • Regex : if\s*\(\w+\s*!=\s*null\)\s*\w+

Séparateurs de chiffres

Recherchez les longs nombres sans séparateurs :

  • Regex : \b\d{6,}\b (Correspond à des nombres avec 6 chiffres ou plus).

2. Fonctionnalités

Records

Utilisez les records comme structures anonymes, immuables et agrégées pour regrouper plusieurs objets sans définir une classe personnalisée. Préférez-les pour retourner plusieurs valeurs d'une fonction ou regrouper temporairement des données connexes.

À éviter : Créer une classe dédiée pour de simples retours multi-valeurs.

class UserResult {
  final String name;
  final int age;
  UserResult(this.name, this.age);
}

UserResult fetchUser() {
  return UserResult('Alice', 42);
}

À préférer : Utiliser les records pour regrouper les types de manière transparente à la volée.

(String, int) fetchUser() {
  return ('Alice', 42);
}

void main() {
  var user = fetchUser();
  print(user.$1); // Alice
}

Patterns et Pattern Matching

Utilisez les patterns pour déstructurer des données complexes en variables locales et correspondre à des formes ou des valeurs spécifiques. Utilisez-les dans switch, if-case ou les déclarations de variables pour dépacker les données directement.

À éviter : Vérifier manuellement les types, les nulls et les clés pour l'extraction de données.

void processJson(Map<String, dynamic> json) {
  if (json.containsKey('name') && json['name'] is String &&
      json.containsKey('age') && json['age'] is int) {
    String name = json['name'];
    int age = json['age'];
    print('$name is $age years old.');
  }
}

À préférer : Combiner la vérification de type, la validation et l'assignation en une seule instruction.

void processJson(Map<String, dynamic> json) {
  if (json case {'name': String name, 'age': int age}) {
    print('$name is $age years old.');
  }
}

Switch Expressions

Utilisez les switch expressions pour retourner une valeur directement, éliminant les lourdes instructions case et break.

À éviter : Utiliser les switch statements où chaque branche retourne ou assigne simplement une valeur.

String describeStatus(int code) {
  switch (code) {
    case 200:
      return 'Success';
    case 404:
      return 'Not Found';
    default:
      return 'Unknown';
  }
}

À préférer : Retourner l'expression évaluée directement en utilisant la syntaxe =>.

String describeStatus(int code) => switch (code) {
  200 => 'Success',
  404 => 'Not Found',
  _ => 'Unknown',
};

Class Modifiers

Utilisez les class modifiers (sealed, final, base, interface) pour restreindre la façon dont les classes peuvent être utilisées en dehors de leur bibliothèque de définition. Préférez sealed pour définir des familles fermées de sous-types afin d'activer la vérification exhaustive.

À éviter : Utiliser des classes abstract ouvertes quand l'ensemble des sous-classes est connu et fixe.

abstract class Result {}

class Success extends Result {}
class Failure extends Result {}

String handle(Result r) {
  if (r is Success) return 'OK';
  if (r is Failure) return 'Error';
  return 'Unknown';
}

À préférer : Utiliser sealed pour garantir au compilateur que tous les cas sont couverts.

sealed class Result {}

class Success extends Result {}
class Failure extends Result {}

String handle(Result r) => switch(r) {
  Success() => 'OK',
  Failure() => 'Error',
};

Extension Types

Utilisez les extension types comme wrapper à zéro coût autour d'un type existant. Utilisez-les pour restreindre les opérations ou ajouter un comportement personnalisé sans surcharge d'exécution.

À éviter : Allouer de nouveaux objets wrapper juste pour la logique spécifique au domaine ou la sécurité des types.

class Id {
  final int value;
  Id(this.value);
  bool get isValid => value > 0;
}

À préférer : Utiliser les extension types qui se compilent au type sous-jacent à l'exécution.

extension type Id(int value) {
  bool get isValid => value > 0;
}

Séparateurs de chiffres

Utilisez les underscores (_) dans les littéraux de nombres strictement pour améliorer la lisibilité visuelle des grandes valeurs numériques.

À éviter : Les longs littéraux de nombres qui sont difficiles à lire d'un coup d'œil.

const int oneMillion = 1000000;

À préférer : Utiliser les underscores pour séparer les milliers ou d'autres regroupements.

const int oneMillion = 1_000_000;

Wildcard Variables

Utilisez les wildcards (_) comme variables ou paramètres non-liants pour signaler explicitement qu'une valeur est intentionnellement inutilisée.

À éviter : Inventer des noms de variables maladroits et distincts pour éviter les avertissements « unused variable ».

void handleEvent(String ignoredName, int status) {
  print('Status: $status');
}

À préférer : Abandonner explicitement le lien avec un underscore.

void handleEvent(String _, int status) {
  print('Status: $status');
}

Null-Aware Elements

Utilisez les null-aware elements (?) à l'intérieur des collection literals pour inclure condionnellement des éléments seulement s'ils évaluent à une valeur non-nulle.

À éviter : Utiliser les instructions collection if pour de simples vérifications de nullité.

var names = [
  'Alice',
  if (optionalName != null) optionalName,
  'Charlie'
];

À préférer : Utiliser le préfixe ? en ligne.

var names = ['Alice', ?optionalName, 'Charlie'];

Dot Shorthands

Utilisez les dot shorthands pour omettre le nom de type explicite quand il peut être confidentement déduit du contexte, comme avec les enums ou les champs statiques.

À éviter : Qualifier complètement les noms de type quand le type est évident du contexte.

LogLevel currentLevel = LogLevel.info;

À préférer : Réduire le bruit visuel avec le shorthand déduit.

LogLevel currentLevel = .info;

Compétences connexes

  • dart-best-practices : Style de code général et idiomes Dart fondamentaux qui précèdent ou complètent les fonctionnalités de syntaxe modernes.

Skills similaires