Communitygithub.com

Flutter/Dart Code Review

Checklist de revue de code Flutter/Dart indépendant de toute bibliothèque, couvrant les bonnes pratiques des widgets, les modèles de gestion d'état (BLoC, Riverpod, Provider, GetX, MobX, Signals), les idiomes Dart, les performances, l'accessibilité, la sécurité et l'architecture propre.

Qu'est-ce que Dart Code Review ?

Un outil de revue de code complet et agnostique conçu pour les développeurs Flutter et Dart. Il fournit une checklist systématique qui s'applique à tous les projets, quelle que soit la solution de gestion d'état (BLoC, Riverpod, Provider, GetX, MobX, Signals) ou autres choix d'architecture. La checklist couvre la santé générale du projet, les pièges du langage Dart (comme l'utilisation abusive de 'dynamic', la sécurité null mal gérée, les pièges async/await), les bonnes pratiques des widgets (décomposition, utilisation de 'const', optimisation des rebuilds via 'Key'), et les performances. Il traite également des schémas de gestion d'état immuables et réactifs, insistant sur des états explicites (types scellés) et une optimisation des rebuilds. Le résumé inclut des sections sur les tests (unitaires, de widgets, d'intégration, dorés), l'accessibilité, les préoccupations spécifiques à la plateforme (iOS/Android, responsive design) et la sécurité. Ce skill est particulièrement utile pour les révisions de code en équipe, garantissant une qualité et une cohérence élevées dans les projets Flutter et Dart, quelles que soient les bibliothèques utilisées.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/flutter-dart-code-review

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Bonnes Pratiques de Revue de Code Flutter/Dart

Checklist complète et agnostique pour la revue d'applications Flutter/Dart. Ces principes s'appliquent indépendamment de la solution de gestion d'état, de la bibliothèque de routage ou du framework d'injection de dépendances utilisé.


1. Santé Générale du Projet

  • Le projet suit une structure de dossiers cohérente (feature-first ou layer-first)
  • Séparation correcte des préoccupations : UI, logique métier, couches de données
  • Aucune logique métier dans les widgets ; les widgets sont purement présentatifs
  • pubspec.yaml est propre — pas de dépendances inutilisées, versions épinglées de manière appropriée
  • analysis_options.yaml inclut un ensemble de lints strict avec les paramètres d'analyse stricts activés
  • Pas d'instructions print() dans le code de production — utiliser dart:developer log() ou un package de journalisation
  • Les fichiers générés (.g.dart, .freezed.dart, .gr.dart) sont à jour ou dans .gitignore
  • Le code spécifique à une plateforme est isolé derrière des abstractions

2. Pièges du Langage Dart

  • dynamic implicite : Annotations de type manquantes conduisant à dynamic — activer strict-casts, strict-inference, strict-raw-types
  • Mauvaise utilisation de la null safety : Utilisation excessive de ! (opérateur bang) au lieu de vérifications null appropriées ou du filtrage par motif Dart 3 (if (value case var v?))
  • Échecs de promotion de type : Utilisation de this.field là où la promotion de variable locale fonctionnerait
  • Attraper trop largement : catch (e) sans clause on ; toujours spécifier les types d'exceptions
  • Attraper Error : Les sous-types de Error indiquent des bugs et ne doivent pas être attrapés
  • async inutilisé : Fonctions marquées async qui n'utilisent jamais await — surcharge inutile
  • Surcharge de late : late utilisé là où nullable ou l'initialisation par constructeur serait plus sûre ; reporte les erreurs à l'exécution
  • Concaténation de chaînes dans les boucles : Utiliser StringBuffer au lieu de + pour la construction itérative de chaînes
  • État mutable dans les contextes const : Les champs dans les classes de constructeur const ne doivent pas être mutables
  • Ignorer les valeurs de retour Future : Utiliser await ou appeler explicitement unawaited() pour signaler l'intention
  • var là où final fonctionne : Préférer final pour les variables locales et const pour les constantes de compilation
  • Imports relatifs : Utiliser des imports package: pour la cohérence
  • Collections mutables exposées : Les API publiques doivent retourner des vues non modifiables, pas des List/Map bruts
  • Filtrage par motif Dart 3 manquant : Préférer les expressions switch et if-case aux vérifications is verbeuses et au casting manuel
  • Classes jetables pour les retours multiples : Utiliser les enregistrements Dart 3 (String, int) au lieu de DTOs à usage unique
  • print() dans le code de production : Utiliser dart:developer log() ou le package de journalisation du projet ; print() n'a pas de niveaux de log et ne peut pas être filtré

3. Bonnes Pratiques des Widgets

Décomposition des widgets :

  • Aucun widget avec une méthode build() dépassant ~80-100 lignes
  • Widgets divisés par encapsulation ET par leur mode de changement (limites de reconstruction)
  • Les méthodes d'aide privées _build*() qui retournent des widgets sont extraites dans des classes de widgets séparées (permet la réutilisation d'éléments, la propagation de const et les optimisations du framework)
  • Les widgets Stateless sont préférés aux Stateful là où aucun état local mutable n'est nécessaire
  • Les widgets extraits sont dans des fichiers séparés lorsqu'ils sont réutilisables

Utilisation de const :

  • Les constructeurs const sont utilisés autant que possible — évite les reconstructions inutiles
  • Littéraux const pour les collections qui ne changent pas (const [], const {})
  • Le constructeur est déclaré const lorsque tous les champs sont finaux

Utilisation des clés :

  • ValueKey utilisé dans les listes/grilles pour préserver l'état lors des réorganisations
  • GlobalKey utilisé avec parcimonie — seulement lorsque l'accès à l'état à travers l'arbre est vraiment nécessaire
  • UniqueKey évité dans build() — il force une reconstruction à chaque frame
  • ObjectKey utilisé lorsque l'identité est basée sur un objet de données plutôt que sur une valeur unique

Thème et système de design :

  • Les couleurs proviennent de Theme.of(context).colorScheme — pas de Colors.red codé en dur ni de valeurs hexadécimales
  • Les styles de texte proviennent de Theme.of(context).textTheme — pas de TextStyle en ligne avec des tailles de police brutes
  • La compatibilité avec le mode sombre est vérifiée — pas d'hypothèses sur un fond clair
  • L'espacement et le dimensionnement utilisent des tokens de design ou des constantes cohérents, pas de nombres magiques

Complexité de la méthode build :

  • Pas d'appels réseau, d'E/S fichier ou de calculs lourds dans build()
  • Pas de Future.then() ou de travail async dans build()
  • Pas de création d'abonnement (.listen()) dans build()
  • setState() localisé au plus petit sous-arbre possible

4. Gestion d'État (Agnostique aux Bibliothèques)

Ces principes s'appliquent à toutes les solutions de gestion d'état Flutter (BLoC, Riverpod, Provider, GetX, MobX, Signals, ValueNotifier, etc.).

Architecture :

  • La logique métier vit en dehors de la couche widget — dans un composant de gestion d'état (BLoC, Notifier, Controller, Store, ViewModel, etc.)
  • Les gestionnaires d'état reçoivent les dépendances via injection, pas en les construisant en interne
  • Une couche de service ou de référentiel abstrait les sources de données — les widgets et les gestionnaires d'état ne doivent pas appeler les API ou les bases de données directement
  • Les gestionnaires d'état ont une responsabilité unique — pas de gestionnaires «dieux» gérant des préoccupations non liées
  • Les dépendances entre composants suivent les conventions de la solution :
    • Dans Riverpod : les providers dépendant d'autres providers via ref.watch est attendu — signaler uniquement les chaînes circulaires ou trop emmêlées
    • Dans BLoC : les blocs ne doivent pas dépendre directement d'autres blocs — préférer les référentiels partagés ou la coordination au niveau de la couche de présentation
    • Dans d'autres solutions : suivre les conventions documentées pour la communication entre composants

Immuabilité et égalité des valeurs (pour les solutions à état immuable : BLoC, Riverpod, Redux) :

  • Les objets d'état sont immuables — de nouvelles instances créées via copyWith() ou des constructeurs, jamais mutées sur place
  • Les classes d'état implémentent correctement == et hashCode (tous les champs inclus dans la comparaison)
  • Le mécanisme est cohérent dans tout le projet — remplacement manuel, Equatable, freezed, enregistrements Dart, ou autre
  • Les collections à l'intérieur des objets d'état ne sont pas exposées comme des List/Map mutables bruts

Discipline de réactivité (pour les solutions à mutation réactive : MobX, GetX, Signals) :

  • L'état n'est muté qu'à travers l'API réactive de la solution (@action dans MobX, .value sur les signaux, .obs dans GetX) — la mutation directe de champ contourne le suivi des changements
  • Les valeurs dérivées utilisent le mécanisme calculé de la solution plutôt que d'être stockées de manière redondante
  • Les réactions et les dispositifs de nettoyage sont correctement nettoyés (ReactionDisposer dans MobX, nettoyage d'effets dans Signals)

Conception de la forme de l'état :

  • Les états mutuellement exclusifs utilisent des types scellés, des variantes d'union, ou le type d'état asynchrone intégré de la solution (par exemple AsyncValue de Riverpod) — pas de drapeaux booléens (isLoading, isError, hasData)
  • Chaque opération asynchrone modélise le chargement, le succès et l'erreur comme des états distincts
  • Toutes les variantes d'état sont traitées de manière exhaustive dans l'UI — pas de cas ignorés silencieusement
  • Les états d'erreur portent des informations d'erreur pour l'affichage ; les états de chargement ne portent pas de données obsolètes
  • Les données nullables ne sont pas utilisées comme indicateur de chargement — les états sont explicites
// MAUVAIS — la soupe de drapeaux booléens permet des états impossibles
class UserState {
  bool isLoading = false;
  bool hasError = false; // isLoading && hasError est représentable !
  User? user;
}

// BON (approche immuable) — les types scellés rendent les états impossibles non représentables
sealed class UserState {}
class UserInitial extends UserState {}
class UserLoading extends UserState {}
class UserLoaded extends UserState {
  final User user;
  const UserLoaded(this.user);
}
class UserError extends UserState {
  final String message;
  const UserError(this.message);
}

// BON (approche réactive) — observable enum + données, mutations via l'API de réactivité
// enum UserStatus { initial, loading, loaded, error }
// Utilisez l'observable/signal de votre solution pour envelopper le statut et les données séparément

Optimisation des reconstructions :

  • Les widgets consommateurs d'état (Builder, Consumer, Observer, Obx, Watch, etc.) sont délimités aussi étroitement que possible
  • Les sélecteurs sont utilisés pour ne reconstruire que lorsque des champs spécifiques changent — pas à chaque émission d'état
  • Les widgets const sont utilisés pour arrêter la propagation des reconstructions dans l'arbre
  • L'état calculé/dérivé est calculé de manière réactive, pas stocké de manière redondante

Abonnements et élimination :

  • Tous les abonnements manuels (.listen()) sont annulés dans dispose() / close()
  • Les contrôleurs de flux sont fermés lorsqu'ils ne sont plus nécessaires
  • Les minuteurs sont annulés dans le cycle de vie de l'élimination
  • Le cycle de vie géré par le framework est préféré à l'abonnement manuel (builders déclaratifs plutôt que .listen())
  • Vérification mounted avant setState dans les callbacks asynchrones
  • BuildContext non utilisé après await sans vérifier context.mounted (Flutter 3.7+) — un contexte obsolète provoque des crashes
  • Pas de navigation, de dialogues ou de messages de scaffold après des pauses asynchrones sans vérifier que le widget est toujours monté
  • BuildContext jamais stocké dans des singletons, des gestionnaires d'état ou des champs statiques

État local vs global :

  • L'état UI éphémère (checkbox, slider, animation) utilise l'état local (setState, ValueNotifier)
  • L'état partagé est remonté uniquement aussi haut que nécessaire — pas de sur-globalisation
  • L'état limité à une fonctionnalité est correctement éliminé lorsque la fonctionnalité n'est plus active

5. Performances

Reconstructions inutiles :

  • setState() n'est pas appelé au niveau du widget racine — localiser les changements d'état
  • Les widgets const sont utilisés pour arrêter la propagation des reconstructions
  • RepaintBoundary utilisé autour de sous-arbres complexes qui se repeignent indépendamment
  • Le paramètre child de AnimatedBuilder est utilisé pour les sous-arbres indépendants de l'animation

Opérations coûteuses dans build() :

  • Pas de tri, de filtrage ou de mappage de grandes collections dans build() — calculer dans la couche de gestion d'état
  • Pas de compilation de regex dans build()
  • L'utilisation de MediaQuery.of(context) est spécifique (par exemple, MediaQuery.sizeOf(context))

Optimisation des images :

  • Les images réseau utilisent la mise en cache (toute solution de mise en cache appropriée pour le projet)
  • Résolution d'image appropriée pour l'appareil cible (pas de chargement d'images 4K pour les vignettes)
  • Image.asset avec cacheWidth/cacheHeight pour décoder à la taille d'affichage
  • Widgets de remplacement et d'erreur fournis pour les images réseau

Chargement différé :

  • ListView.builder / GridView.builder utilisé au lieu de ListView(children: [...]) pour les listes grandes ou dynamiques (les constructeurs concrets sont acceptables pour les listes petites et statiques)
  • Pagination implémentée pour les grands ensembles de données
  • Chargement différé (deferred as) utilisé pour les bibliothèques lourdes dans les builds web

Autres :

  • Widget Opacity évité dans les animations — utiliser AnimatedOpacity ou FadeTransition
  • Clipping évité dans les animations — pré-clipper les images
  • operator == non surchargé sur les widgets — utiliser des constructeurs const à la place
  • Widgets de dimensions intrinsèques (IntrinsicHeight, IntrinsicWidth) utilisés avec parcimonie (passage de layout supplémentaire)

6. Tests

Types de tests et attentes :

  • Tests unitaires : Couvrent toute la logique métier (gestionnaires d'état, référentiels, fonctions utilitaires)
  • Tests de widgets : Couvrent le comportement individuel des widgets, les interactions et la sortie visuelle
  • Tests d'intégration : Couvrent les flux utilisateur critiques de bout en bout
  • Tests golden : Comparaisons pixel-parfaites pour les composants UI critiques pour le design

Objectifs de couverture :

  • Viser 80%+ de couverture de lignes sur la logique métier
  • Toutes les transitions d'état ont des tests correspondants (chargement → succès, chargement → erreur, nouvelle tentative, etc.)
  • Cas limites testés : états vides, états d'erreur, états de chargement, valeurs limites

Isolation des tests :

  • Les dépendances externes (clients API, bases de données, services) sont mockées ou simulées
  • Chaque fichier de test teste exactement une classe/unité
  • Les tests vérifient le comportement, pas les détails d'implémentation
  • Les stubs ne définissent que le comportement nécessaire pour chaque test (stub minimal)
  • Pas d'état mutable partagé entre les cas de test

Qualité des tests de widgets :

  • pumpWidget et pump utilisés correctement pour les opérations asynchrones
  • find.byType, find.text, find.byKey utilisés de manière appropriée
  • Pas de tests instables dépendant du timing — utiliser pumpAndSettle ou pump(Duration) explicite
  • Les tests s'exécutent dans le CI et les échecs bloquent les fusions

7. Accessibilité

Widgets sémantiques :

  • Widget Semantics utilisé pour fournir des étiquettes de lecteur d'écran là où les étiquettes automatiques sont insuffisantes
  • ExcludeSemantics utilisé pour les éléments purement décoratifs
  • MergeSemantics utilisé pour combiner des widgets liés en un seul élément accessible
  • Les images ont la propriété semanticLabel définie

Prise en charge du lecteur d'écran :

  • Tous les éléments interactifs sont focusables et ont des descriptions significatives
  • L'ordre de focus est logique (suit l'ordre de lecture visuel)

Accessibilité visuelle :

  • Rapport de contraste >= 4.5:1 pour le texte par rapport à l'arrière-plan
  • Les cibles tactiles font au moins 48x48 pixels
  • La couleur n'est pas le seul indicateur d'état (utiliser des icônes/texte en complément)
  • Le texte s'adapte aux paramètres de taille de police du système

Accessibilité des interactions :

  • Pas de callbacks onPressed inopérants — chaque bouton fait quelque chose ou est désactivé
  • Les champs d'erreur suggèrent des corrections
  • Le contexte ne change pas de manière inattendue pendant que l'utilisateur saisit des données

8. Préoccupations Spécifiques à la Plateforme

Différences iOS/Android :

  • Widgets adaptatifs à la plateforme utilisés là où approprié
  • La navigation arrière est gérée correctement (bouton retour Android, balayage pour revenir en arrière iOS)
  • La barre d'état et la zone de sécurité sont gérées via le widget SafeArea
  • Les permissions spécifiques à la plateforme sont déclarées dans AndroidManifest.xml et Info.plist

Design responsive :

  • LayoutBuilder ou MediaQuery utilisé pour les mises en page responsives
  • Points de rupture définis de manière cohérente (téléphone, tablette, bureau)
  • Le texte ne déborde pas sur les petits écrans — utiliser Flexible, Expanded, FittedBox
  • L'orientation paysage est testée ou explicitement verrouillée
  • Spécifique au web : interactions souris/clavier prises en charge, états de survol présents

9. Sécurité

Stockage sécurisé :

  • Les données sensibles (tokens, mots de passe) ne sont pas stockées dans des préférences partagées non cryptées
  • Utiliser flutter_secure_storage (ou équivalent) pour les tokens et secrets
  • Les données sensibles ne sont pas codées en dur dans le code ou le pubspec.yaml

Risques réseau/serveur :

  • Les requêtes réseau utilisent HTTPS — pas de requêtes HTTP non sécurisées
  • Les en-têtes de sécurité (CORS, Content-Security-Policy, HSTS) sont vérifiés pour les builds web
  • La validation d'entrée se produit à la fois côté client et côté serveur — la validation côté client uniquement est insuffisante
  • Les clés API et autres secrets ne sont pas exposés dans les dépôts git (.env ou variables d'environnement, pas de fichiers commités)
  • Les jetons et cookies qui expirent sont gérés élégamment — déconnexion ou renouvellement

Prévention des injections :

  • Les entrées utilisateur sont échappées avant d'être rendues dans des vues Web ou des interfaces natives — pas d'injection d'HTML, SQL ou commande
  • La construction de requêtes SQL utilise des requêtes paramétrées ou ORM, pas de concaténation de chaînes

Validation des entrées :

  • Les limites de longueur sont appliquées sur les champs de texte pour éviter les débordements de mémoire
  • Les types de fichiers sont validés par le backend, pas seulement par l'extension de fichier côté client

10. Recommandations Supplémentaires

Documentation et commentaires :

  • Les API publiques ont des commentaires de documentation (///)
  • Les correctifs ou solutions de contournement non évidents ont des commentaires expliquant pourquoi l'approche a été choisie
  • Les commentaires TODO ou FIXME ont des tickets de suivi associés ou des propriétaires
  • Les fonctions complexes ou non triviales ont des commentaires décrivant le comportement attendu

Gestion des erreurs :

  • Les exceptions sont attrapées à la limite de la couche de service, pas enfouies dans les gestionnaires UI
  • Les messages d'erreur présentés aux utilisateurs sont conviviaux et exploitables, pas des traces de pile
  • Les erreurs de l'état d'échec sont enregistrées (en utilisant le système de journalisation du projet) pour le débogage
  • Les échecs de récupération d'état ont des mécanismes de nouvelle tentative (retry bouton, pull-to-refresh)
  • Les opérations critiques sont validées avec des mécanismes de mise hors service (circuit breaker) pour éviter des échecs en cascade

Organisation des imports :

  • Les imports sont regroupés (dart:, package:, relative) avec des lignes vides entre les groupes
  • Aucun import inutilisé — exécuter dart fix --apply ou utiliser les actions rapides de l'IDE
  • Les imports sont triés par ordre alphabétique dans les groupes pour des raisons de cohérence

Cette checklist s'inspire des principes de Clean Architecture, des directives officielles de Flutter et de l'expérience pratique de la communauté.

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

Skills associés