Communitygithub.com

flutter-dart-code-review

Checklist de revue de code Flutter/Dart indépendant de toute bibliothèque, couvrant les meilleures pratiques pour les 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. À utiliser lors de la revue de code Flutter ou Dart, quelle que soit la bibliothèque de gestion d'état utilisée.

Qu'est-ce que flutter-dart-code-review ?

Cette compétence fournit une checklist exhaustive et indépendante de toute bibliothèque pour la revue de code Flutter/Dart. Elle couvre des aspects essentiels comme l'architecture du projet, les pièges du langage Dart (null safety, typage implicite, etc.), les meilleures pratiques pour les widgets (décomposition, utilisation de `const`, gestion des clés), la gestion d'état (BLoC, Riverpod, Provider, GetX, MobX, Signals), les performances, les tests, l'accessibilité, les plateformes spécifiques et la sécurité. Elle est conçue pour être utilisée avec n'importe quelle solution de gestion d'état ou bibliothèque de routage, en mettant l'accent sur l'immutabilité, la séparation des préoccupations et l'optimisation des reconstructions. Utile pour les développeurs Flutter souhaitant garantir la qualité et la maintenabilité de leur code, elle s'intègre facilement dans des workflows de revue de code automatisés ou manuels.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/ECC/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

Meilleures pratiques de revue de code Flutter/Dart

Checklist complète et indépendante de toute bibliothèque 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
  • Absence de logique métier dans les widgets ; les widgets sont purement présentationnels
  • 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 règles strictes avec les paramètres de l'analyseur strict 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 à la plateforme est isolé derrière des abstractions

2. Pièges du langage Dart

  • Dynamique implicite : Annotations de type manquantes menant à dynamic — activer strict-casts, strict-inference, strict-raw-types
  • Mauvais usage de la null safety : Utilisation excessive de ! (opérateur bang) au lieu de vérifications null appropriées ou du pattern matching Dart 3 (if (value case var v?))
  • Échecs de promotion de type : Utilisation de this.field alors que 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 devraient pas être attrapés
  • async inutilisé : Fonctions marquées async qui n'utilisent jamais await — surcharge inutile
  • Surabondance de late : late utilisé là où une valeur nullable ou une initialisation dans le 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 des classes avec constructeur const ne doivent pas être mutables
  • Ignorer les valeurs de retour Future : Utiliser await ou appeler explicitement unawaited() pour signaler l'intention
  • var alors que final fonctionne : Préférer final pour les variables locales et const pour les constantes à la compilation
  • Importations relatives : Utiliser des importations package: pour la cohérence
  • Collections mutables exposées : Les API publiques doivent retourner des vues non modifiables, pas des List/Map bruts
  • Pattern matching 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 DTO à 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 journal et ne peut pas être filtré

3. Meilleures pratiques pour les 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 façon de changer (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 des éléments, la propagation de const et les optimisations du framework)
  • Les widgets sans état sont préférés aux widgets avec état lorsqu'aucun état mutable local n'est nécessaire
  • Les widgets extraits sont dans des fichiers séparés lorsqu'ils sont réutilisables

Utilisation de const :

  • Des constructeurs const sont utilisés partout où c'est possible — empêche les reconstructions inutiles
  • Des littéraux const pour les collections qui ne changent pas (const [], const {})
  • Le constructeur est déclaré const lorsque tous les champs sont final

Utilisation des clés :

  • ValueKey utilisé dans les listes/grilles pour préserver l'état lors des réorganisations
  • GlobalKey utilisé avec parcimonie — uniquement lorsque l'accès à l'état à travers l'arbre est vraiment nécessaire
  • UniqueKey évité dans build() — il force une reconstruction à chaque image
  • 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 ou de valeurs hexadécimales en dur
  • 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 jetons de design cohérents ou des constantes, 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 (indépendante de toute bibliothèque)

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 leurs dépendances via injection, pas en les construisant en interne
  • Une couche de service ou de repository 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 seule responsabilité — pas de gestionnaires « dieu » 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 repositories partagés ou la coordination au niveau de la couche de présentation
    • Dans les autres solutions : suivre les conventions documentées pour la communication entre composants

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

  • Les objets d'état sont immuables — de nouvelles instances sont 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 sous forme de List/Map bruts mutables

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 de calcul de la solution plutôt que d'être stockées de manière redondante
  • Les réactions et les disposers sont correctement nettoyés (ReactionDisposer dans MobX, nettoyage d'effet 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 transportent des informations d'erreur pour l'affichage ; les états de chargement ne transportent 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) — enum observable + données, mutations via API réactive
// enum UserStatus { initial, loading, loaded, error }
// Utiliser l'observable/signal de votre solution pour envelopper l'état et les données séparément

Optimisation des reconstructions :

  • Les widgets consommateurs d'état (Builder, Consumer, Observer, Obx, Watch, etc.) sont limités au périmètre le plus étroit 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 libération :

  • 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 minuteries sont annulées dans le cycle de vie de libération
  • Le cycle de vie géré par le framework est préféré aux abonnements manuels (builders déclaratifs plutôt que .listen())
  • Vérification de 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 plantages
  • Pas de navigation, dialogues ou messages de scaffold après des pauses asynchrones sans vérifier que le widget est toujours monté
  • BuildContext jamais stocké dans des singletons, gestionnaires d'état ou champs statiques

État local vs global :

  • L'état UI éphémère (case à cocher, curseur, animation) utilise l'état local (setState, ValueNotifier)
  • L'état partagé n'est remonté qu'aussi haut que nécessaire — pas de globalisation excessive
  • L'état propre à une fonctionnalité est correctement libéré 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 utilisé pour les sous-arbres indépendants de l'animation

Opérations coûteuses dans build() :

  • Pas de tri, filtrage ou 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 placeholder et d'erreur fournis pour les images réseau

Chargement paresseux :

  • ListView.builder / GridView.builder utilisés au lieu de ListView(children: [...]) pour les listes grandes ou dynamiques (les constructeurs concrets sont acceptables pour les petites listes 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 == n'est pas surchargé sur les widgets — utiliser les 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, repositories, 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, réessai, 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 définissent uniquement le comportement nécessaire pour chaque test (stubbing minimal)
  • Aucun é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 en CI et les échecs bloquent les fusions

7. Accessibilité

Widgets sémantiques :

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

Support 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 visuelle)

Accessibilité visuelle :

  • Ratio 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 sans opération — 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 :

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

Conception réactive :

  • LayoutBuilder ou MediaQuery utilisés pour les mises en page réactives
  • Les points de rupture sont 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
  • Web spécifique : interactions souris/clavier supportées, états de survol présents

9. Sécurité

Stockage sécurisé :

  • Les données sensibles (jetons, mots de passe, clés API) sont stockées dans un stockage sécurisé (flutter_secure_storage, keychain/keystore)
  • Pas de données sensibles dans le code source, les fichiers de configuration ou les préférences partagées

Validation des entrées :

  • Toutes les entrées utilisateur sont validées et assainies (empêcher l'injection XSS/NoSQL/commande)
  • Les entrées API ne sont pas directement affichées sans échappement approprié (risque XSS dans les applications web)

Communication réseau :

  • Toutes les communications API utilisent HTTPS — pas de requêtes HTTP
  • Les certificats SSL sont correctement gérés — pas d'épinglage de certificat en dur (utiliser un mécanisme sécurisé comme pinning via secure store ou service worker)
  • Les jetons d'authentification sont stockés de manière sécurisée et inclus dans les en-têtes de requête, pas dans les URL

=== SECTION TRONQUÉE POUR LA TRADUCTION ===

(La suite du document original, si elle existe, continuerait ici avec d'autres sections sur les préoccupations de sécurité supplémentaires, le débogage, la journalisation, les considérations de construction et de déploiement, etc. La structure resterait cohérente avec le format de checklist ci-dessus.)


Ce document reflète les meilleures pratiques de la communauté et les recommandations de l'équipe Flutter. Il doit être adapté aux conventions et exigences spécifiques du projet.

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/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

affaan-m/ECC

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

affaan-m/fal-ai-media

Unified media generation via fal.ai MCP — image, video, and audio. Covers text-to-image (Nano Banana), text/image-to-video (Seedance, Kling, Veo 3), text-to-speech (CSM-1B), and video-to-audio (ThinkSound). Use when the user wants to generate images, videos, or audio with AI.

affaan-m/manim-video

日本語翻訳:このファイルは manim-video 用の日本語翻訳が必要です

affaan-m/remotion-video-creation

Remotion のベストプラクティス - React で動画を作成する。3D、アニメーション、音声、字幕、チャート、トランジションなどをカバーするドメイン固有の29のルール。

affaan-m/video-editing

AI-assisted video editing workflows for cutting, structuring, and augmenting real footage. Covers the full pipeline from raw capture through FFmpeg, Remotion, ElevenLabs, fal.ai, and final polish in Descript or CapCut. Use when the user wants to edit video, cut footage, create vlogs, or build video content.

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.

agent-self-evaluation

Use after completing any non-trivial task. The agent self-rates its output on 5 axes — accuracy, completeness, clarity, actionability, conciseness — with concrete evidence per criterion. Produces a structured 1-5 scorecard with specific improvement suggestions.

agent-sort

Build an evidence-backed ECC install plan for a specific repo by sorting skills, commands, rules, hooks, and extras into DAILY vs LIBRARY buckets using parallel repo-aware review passes. Use when ECC should be trimmed to what a project actually needs instead of loading the full bundle.

ai-first-engineering

Engineering operating model for teams where AI agents generate a large share of implementation output. Use when setting team process, review gates, or ownership rules for a codebase largely written by agents.

ai-regression-testing

Regression testing strategies for AI-assisted development. Sandbox-mode API testing without database dependencies, automated bug-check workflows, and patterns to catch AI blind spots where the same model writes and reviews code. Use when adding regression coverage to AI-assisted code, or when the same model both wrote and reviewed a change.

android-clean-architecture

Clean Architecture patterns for Android and Kotlin Multiplatform projects — module structure, dependency rules, UseCases, Repositories, and data layer patterns. Use when structuring modules, layers, or data flow in an Android or KMP project.

angular-developer

Generates Angular code and provides architectural guidance. Trigger when creating projects, components, or services, or for best practices on reactivity (signals, linkedSignal, resource), forms, dependency injection, routing, SSR, accessibility (ARIA), animations, styling (component styles, Tailwind CSS), testing, or CLI tooling.

Skills associés