Communitygithub.com

Revisión de Código Flutter/Dart

Lista de verificación de revisión de código Flutter/Dart independiente de librerías que cubre mejores prácticas de widgets, patrones de gestión de estado (BLoC, Riverpod, Provider, GetX, MobX, Signals), modismos de Dart, rendimiento, accesibilidad, seguridad y arquitectura limpia. Úsala al revisar código Flutter o Dart, independientemente de la librería de gestión de estado que utilice el proyecto.

¿Qué es Dart?

Esta habilidad proporciona una lista de verificación integral y agnóstica de librerías para revisiones de código de aplicaciones Flutter/Dart. Es útil para desarrolladores y equipos que buscan garantizar la calidad del código, el rendimiento y la mantenibilidad, ya que cubre desde la salud general del proyecto hasta preocupaciones de seguridad específicas de la plataforma. Soporta todos los frameworks de gestión de estado populares como BLoC, Riverpod, Provider, GetX, MobX y Signals, ofreciendo principios aplicables a cualquier enfoque. La lista de verificación se basa en el principio de separación de preocupaciones, inmutabilidad del estado y patrones de diseño reactivos, con secciones dedicadas a la descomposición de widgets, rendimiento, pruebas, accesibilidad y seguridad. Cada sección incluye elementos accionables que los revisores pueden marcar, junto con ejemplos de código (como tipos sellados para estados mutuamente excluyentes) para ilustrar buenas y malas prácticas. Esta habilidad está diseñada para cualquier agente de IA o desarrollador que realice revisiones de código, asegurando que no se pasen por alto problemas comunes de Dart y Flutter.

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

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Mejores Prácticas para Revisión de Código Flutter/Dart

Lista de verificación integral e independiente de librerías para revisar aplicaciones Flutter/Dart. Estos principios se aplican independientemente de la solución de gestión de estado, librería de enrutamiento o marco de inyección de dependencias utilizado.


1. Salud General del Proyecto

  • El proyecto sigue una estructura de carpetas consistente (primero por funcionalidad o por capas)
  • Separación adecuada de preocupaciones: capas de UI, lógica de negocio y datos
  • No hay lógica de negocio en los widgets; los widgets son puramente de presentación
  • pubspec.yaml está limpio — sin dependencias no utilizadas, versiones fijadas adecuadamente
  • analysis_options.yaml incluye un conjunto de reglas estrictas con la configuración estricta del analizador habilitada
  • No hay sentencias print() en el código de producción — usa dart:developer log() o un paquete de registro
  • Los archivos generados (.g.dart, .freezed.dart, .gr.dart) están actualizados o en .gitignore
  • El código específico de la plataforma está aislado detrás de abstracciones

2. Errores Comunes del Lenguaje Dart

  • dynamic implícito: Faltan anotaciones de tipo que llevan a dynamic — habilita strict-casts, strict-inference, strict-raw-types
  • Mal uso de la seguridad de nulos: Uso excesivo de ! (operador bang) en lugar de comprobaciones de nulo adecuadas o coincidencia de patrones de Dart 3 (if (value case var v?))
  • Fallos en la promoción de tipos: Usar this.field donde funcionaría la promoción de variables locales
  • Capturar de forma demasiado amplia: catch (e) sin cláusula on; siempre especifica los tipos de excepción
  • Capturar Error: Los subtipos de Error indican errores y no deben capturarse
  • async no utilizado: Funciones marcadas como async que nunca usan await — sobrecarga innecesaria
  • Uso excesivo de late: late usado donde una variable nullable o la inicialización en el constructor serían más seguras; difiere errores al tiempo de ejecución
  • Concatenación de cadenas en bucles: Usa StringBuffer en lugar de + para la construcción iterativa de cadenas
  • Estado mutable en contextos const: Los campos en clases de constructor const no deben ser mutables
  • Ignorar valores de retorno de Future: Usa await o llama explícitamente a unawaited() para indicar la intención
  • var cuando final funciona: Prefiere final para variables locales y const para constantes en tiempo de compilación
  • Importaciones relativas: Usa importaciones package: para consistencia
  • Colecciones mutables expuestas: Las API públicas deben devolver vistas no modificables, no List/Map crudos
  • Falta de coincidencia de patrones de Dart 3: Prefiere expresiones switch y if-case sobre comprobaciones is verbosas y casting manual
  • Clases desechables para múltiples retornos: Usa registros de Dart 3 (String, int) en lugar de DTOs de un solo uso
  • print() en código de producción: Usa dart:developer log() o el paquete de registro del proyecto; print() no tiene niveles de registro y no se puede filtrar

3. Mejores Prácticas de Widgets

Descomposición de widgets:

  • Ningún widget con un método build() que supere ~80-100 líneas
  • Widgets divididos por encapsulación Y por cómo cambian (límites de reconstrucción)
  • Los métodos auxiliares privados _build*() que devuelven widgets se extraen a clases de widget separadas (permite la reutilización de elementos, la propagación de const y las optimizaciones del framework)
  • Se prefieren widgets sin estado (StatelessWidget) sobre los que tienen estado (StatefulWidget) cuando no se necesita estado mutable local
  • Los widgets extraídos están en archivos separados cuando son reutilizables

Uso de const:

  • Se usan constructores const siempre que sea posible — evita reconstrucciones innecesarias
  • Literales const para colecciones que no cambian (const [], const {})
  • El constructor se declara const cuando todos los campos son final

Uso de claves (Key):

  • Se usa ValueKey en listas/grids para preservar el estado a través de reordenaciones
  • GlobalKey se usa con moderación — solo cuando realmente se necesita acceder al estado a través del árbol
  • Se evita UniqueKey en build() — fuerza la reconstrucción en cada fotograma
  • Se usa ObjectKey cuando la identidad se basa en un objeto de datos en lugar de un valor único

Tematización y sistema de diseño:

  • Los colores provienen de Theme.of(context).colorScheme — sin colores Colors.red o valores hexadecimales hardcodeados
  • Los estilos de texto provienen de Theme.of(context).textTheme — sin TextStyle en línea con tamaños de fuente crudos
  • Compatibilidad con modo oscuro verificada — sin asumir fondo claro
  • El espaciado y el tamaño usan tokens de diseño consistentes o constantes, no números mágicos

Complejidad del método build():

  • Sin llamadas de red, E/S de archivos ni cálculos pesados en build()
  • Sin Future.then() ni trabajo async en build()
  • Sin creación de suscripciones (.listen()) en build()
  • setState() localizado en el subárbol más pequeño posible

4. Gestión de Estado (Independiente de Librería)

Estos principios se aplican a todas las soluciones de gestión de estado de Flutter (BLoC, Riverpod, Provider, GetX, MobX, Signals, ValueNotifier, etc.).

Arquitectura:

  • La lógica de negocio vive fuera de la capa de widgets — en un componente de gestión de estado (BLoC, Notifier, Controller, Store, ViewModel, etc.)
  • Los gestores de estado reciben dependencias a través de inyección, no construyéndolas internamente
  • Una capa de servicio o repositorio abstrae las fuentes de datos — los widgets y gestores de estado no deben llamar a APIs o bases de datos directamente
  • Los gestores de estado tienen una única responsabilidad — sin gestores "dios" que manejen preocupaciones no relacionadas
  • Las dependencias entre componentes siguen las convenciones de la solución:
    • En Riverpod: se espera que los proveedores dependan de otros proveedores mediante ref.watch — solo señalar cadenas circulares o demasiado enredadas
    • En BLoC: los blocs no deben depender directamente de otros blocs — prefiere repositorios compartidos o coordinación en la capa de presentación
    • En otras soluciones: sigue las convenciones documentadas para la comunicación entre componentes

Inmutabilidad e igualdad de valores (para soluciones de estado inmutable: BLoC, Riverpod, Redux):

  • Los objetos de estado son inmutables — se crean nuevas instancias mediante copyWith() o constructores, nunca se mutan en el lugar
  • Las clases de estado implementan == y hashCode correctamente (todos los campos incluidos en la comparación)
  • El mecanismo es consistente en todo el proyecto — anulación manual, Equatable, freezed, registros de Dart u otros
  • Las colecciones dentro de los objetos de estado no se exponen como List/Map mutables crudos

Disciplina de reactividad (para soluciones de mutación reactiva: MobX, GetX, Signals):

  • El estado solo se muta a través de la API reactiva de la solución (@action en MobX, .value en signals, .obs en GetX) — la mutación directa de campos evita el seguimiento de cambios
  • Los valores derivados usan el mecanismo de computación de la solución en lugar de almacenarse de forma redundante
  • Las reacciones y los dispositivos de limpieza se limpian adecuadamente (ReactionDisposer en MobX, limpieza de efectos en Signals)

Diseño de la forma del estado:

  • Los estados mutuamente excluyentes usan tipos sellados, variantes de unión o el tipo de estado asíncrono incorporado de la solución (por ejemplo, AsyncValue de Riverpod) — no banderas booleanas (isLoading, isError, hasData)
  • Cada operación asíncrona modela los estados de carga, éxito y error como estados distintos
  • Todas las variantes de estado se manejan exhaustivamente en la UI — sin casos ignorados silenciosamente
  • Los estados de error llevan información de error para mostrar; los estados de carga no llevan datos obsoletos
  • Los datos anulables no se utilizan como indicador de carga — los estados son explícitos
// MALO — la mezcla de banderas booleanas permite estados imposibles
class UserState {
  bool isLoading = false;
  bool hasError = false; // ¡isLoading && hasError es representable!
  User? user;
}

// BUENO (enfoque inmutable) — los tipos sellados hacen que los estados imposibles no sean representables
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);
}

// BUENO (enfoque reactivo) — observable enum + datos, mutaciones a través de la API de reactividad
// enum UserStatus { initial, loading, loaded, error }
// Usa el observable/signal de tu solución para envolver el estado y los datos por separado

Optimización de reconstrucción:

  • Los widgets consumidores de estado (Builder, Consumer, Observer, Obx, Watch, etc.) se limitan al ámbito más estrecho posible
  • Se usan selectores para reconstruir solo cuando cambian campos específicos — no en cada emisión de estado
  • Se usan widgets const para detener la propagación de la reconstrucción a través del árbol
  • El estado computado/derivado se calcula de forma reactiva, no se almacena de forma redundante

Suscripciones y disposición:

  • Todas las suscripciones manuales (.listen()) se cancelan en dispose() / close()
  • Los controladores de flujo (StreamController) se cierran cuando ya no son necesarios
  • Los temporizadores se cancelan en el ciclo de vida de disposición
  • Se prefiere el ciclo de vida gestionado por el framework sobre la suscripción manual (constructores declarativos sobre .listen())
  • Verificación de mounted antes de setState en callbacks asíncronos
  • BuildContext no se usa después de await sin verificar context.mounted (Flutter 3.7+) — un contexto obsoleto causa fallos
  • Sin navegación, diálogos o mensajes de scaffold después de brechas asíncronas sin verificar que el widget sigue montado
  • BuildContext nunca se almacena en singletons, gestores de estado o campos estáticos

Estado local vs global:

  • El estado efímero de la UI (checkbox, slider, animación) usa estado local (setState, ValueNotifier)
  • El estado compartido se eleva solo hasta donde sea necesario — no se globaliza en exceso
  • El estado específico de una funcionalidad se elimina adecuadamente cuando la funcionalidad ya no está activa

5. Rendimiento

Reconstrucciones innecesarias:

  • setState() no se llama al nivel del widget raíz — localiza los cambios de estado
  • Se usan widgets const para detener la propagación de la reconstrucción
  • Se usa RepaintBoundary alrededor de subárboles complejos que se repintan de forma independiente
  • Se usa el parámetro child de AnimatedBuilder para subárboles independientes de la animación

Operaciones costosas en build():

  • Sin ordenar, filtrar ni mapear grandes colecciones en build() — calcular en la capa de gestión de estado
  • Sin compilación de expresiones regulares en build()
  • El uso de MediaQuery.of(context) es específico (por ejemplo, MediaQuery.sizeOf(context))

Optimización de imágenes:

  • Las imágenes de red usan caché (cualquier solución de caché adecuada para el proyecto)
  • Resolución de imagen apropiada para el dispositivo de destino (no cargar imágenes 4K para miniaturas)
  • Image.asset con cacheWidth/cacheHeight para decodificar al tamaño de visualización
  • Widgets de marcador de posición y error proporcionados para imágenes de red

Carga diferida:

  • Se usa ListView.builder / GridView.builder en lugar de ListView(children: [...]) para listas grandes o dinámicas (los constructores concretos están bien para listas pequeñas y estáticas)
  • Paginación implementada para grandes conjuntos de datos
  • Carga diferida (deferred as) utilizada para librerías pesadas en compilaciones web

Otros:

  • Se evita el widget Opacity en animaciones — usa AnimatedOpacity o FadeTransition
  • Se evita el recorte en animaciones — recortar imágenes previamente
  • operator == no se anula en widgets — usa constructores const en su lugar
  • Los widgets de dimensiones intrínsecas (IntrinsicHeight, IntrinsicWidth) se usan con moderación (paso de diseño adicional)

6. Pruebas

Tipos y expectativas de pruebas:

  • Pruebas unitarias: Cubren toda la lógica de negocio (gestores de estado, repositorios, funciones de utilidad)
  • Pruebas de widgets: Cubren el comportamiento individual del widget, las interacciones y la salida visual
  • Pruebas de integración: Cubren flujos de usuario críticos de principio a fin
  • Pruebas golden: Comparaciones perfectas de píxeles para componentes de UI críticos de diseño

Objetivos de cobertura:

  • Apuntar a un 80%+ de cobertura de línea en la lógica de negocio
  • Todas las transiciones de estado tienen pruebas correspondientes (carga → éxito, carga → error, reintento, etc.)
  • Casos límite probados: estados vacíos, estados de error, estados de carga, valores límite

Aislamiento de pruebas:

  • Las dependencias externas (clientes API, bases de datos, servicios) se simulan o falsean
  • Cada archivo de prueba prueba exactamente una clase/unidad
  • Las pruebas verifican el comportamiento, no los detalles de implementación
  • Los stubs definen solo el comportamiento necesario para cada prueba (stubbing mínimo)
  • No hay estado mutable compartido entre casos de prueba

Calidad de las pruebas de widgets:

  • pumpWidget y pump se usan correctamente para operaciones asíncronas
  • find.byType, find.text, find.byKey se usan adecuadamente
  • No hay pruebas inestables que dependan del tiempo — usa pumpAndSettle o pump(Duration) explícito
  • Las pruebas se ejecutan en CI y los fallos bloquean las fusiones

7. Accesibilidad

Widgets semánticos:

  • Se usa el widget Semantics para proporcionar etiquetas de lector de pantalla donde las etiquetas automáticas son insuficientes
  • Se usa ExcludeSemantics para elementos puramente decorativos
  • Se usa MergeSemantics para combinar widgets relacionados en un único elemento accesible
  • Las imágenes tienen la propiedad semanticLabel establecida

Soporte de lector de pantalla:

  • Todos los elementos interactivos son enfocables y tienen descripciones significativas
  • El orden de enfoque es lógico (sigue el orden de lectura visual)

Accesibilidad visual:

  • Relación de contraste >= 4.5:1 para texto sobre fondo
  • Los objetivos táctiles tienen al menos 48x48 píxeles
  • El color no es el único indicador de estado (usar iconos/texto junto con él)
  • El texto escala con la configuración del tamaño de fuente del sistema

Accesibilidad de interacción:

  • Sin callbacks onPressed que no hagan nada — cada botón hace algo o está deshabilitado
  • Los campos con error sugieren correcciones
  • El contexto no cambia inesperadamente mientras el usuario ingresa datos

8. Preocupaciones Específicas de la Plataforma

Diferencias iOS/Android:

  • Se usan widgets adaptativos a la plataforma cuando corresponde
  • La navegación hacia atrás se maneja correctamente (botón de retroceso de Android, deslizar hacia atrás en iOS)
  • La barra de estado y el área segura se manejan mediante el widget SafeArea
  • Los permisos específicos de la plataforma se declaran en AndroidManifest.xml e Info.plist

Diseño responsivo:

  • Se usa LayoutBuilder o MediaQuery para diseños responsivos
  • Los puntos de interrupción se definen de forma consistente (teléfono, tableta, escritorio)
  • El texto no se desborda en pantallas pequeñas — usa Flexible, Expanded, FittedBox
  • La orientación horizontal se prueba o se bloquea explícitamente
  • Específico para web: se admiten interacciones de mouse/teclado, estados hover presentes

9. Seguridad

Almacenamiento seguro:

  • Datos sensibles (tokens, contraseñas) almacenados en almacenamiento seguro de la plataforma (flutter_secure_storage o equivalente)
  • No almacenar datos sensibles en texto plano en preferencias compartidas o almacenamiento local
  • Usar HTTPS para todas las comunicaciones de red — sin tráfico HTTP
  • Validar y sanitizar todas las entradas del usuario para prevenir inyección de código
  • Implementar manejo adecuado de sesiones y tokens JWT
  • Verificar que las URLs de red sean seguras y no apunten a destinos maliciosos

Prevención de ataques comunes:

  • Implementar limitación de velocidad en formularios de inicio de sesión
  • Usar CSRF tokens para operaciones sensibles
  • No exponer información interna del sistema en mensajes de error
  • Implementar cifrado de datos en reposo cuando sea necesario

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 relacionados