Saltar al contenido principal

Dart eficaz: Estilo

Reglas de formato y nombramiento para un código consistente y legible.

Una parte sorprendentemente importante del buen código es el buen estilo. Un estilo consistente en nombramiento, ordenamiento y formato ayuda a que el código que es el mismo se vea igual. Aprovecha el potente hardware de reconocimiento de patrones que la mayoría de nosotros tiene en sus sistemas oculares. Si usamos un estilo consistente en todo el ecosistema de Dart, nos resultará más fácil a todos aprender del código de los demás y contribuir a él.

Identificadores

#

Los identificadores vienen en tres variantes en Dart.

  • Los nombres en UpperCamelCase escriben en mayúscula la primera letra de cada palabra, incluyendo la primera.

  • Los nombres en lowerCamelCase escriben en mayúscula la primera letra de cada palabra, excepto la primera, que siempre va en minúscula, incluso si es un acrónimo.

  • Los nombres en lowercase_with_underscores usan solo letras minúsculas, incluso para acrónimos, y separan las palabras con _.

NOMBRA los tipos usando UpperCamelCase

#

Regla de linter: camel_case_types

Las clases, los tipos enum, los typedefs y los parámetros de tipo deben tener en mayúscula la primera letra de cada palabra (incluida la primera palabra), y no usar separadores.

bien dart
class SliderMenu {
   ...
}

class HttpRequest {
   ...
}

typedef Predicate<T> = bool Function(T value);

Esto incluye incluso clases destinadas a ser utilizadas en anotaciones de metadatos.

bien dart
class Foo {
  const Foo([Object? arg]);
}

@Foo(anArg)
class A {
   ...
}

@Foo()
class B {
   ...
}

Si el constructor de la clase de anotación no recibe parámetros, es posible que desees crear una constante lowerCamelCase separada para ella.

bien dart
const foo = Foo();

@foo
class C {
   ...
}

NOMBRA las extensiones usando UpperCamelCase

#

Regla de linter: camel_case_extensions

Al igual que los tipos, las extensiones deben escribir en mayúscula la primera letra de cada palabra (incluida la primera palabra), y no usar separadores.

bien dart
extension MyFancyList<T> on List<T> {
   ...
}

extension SmartIterable<T> on Iterable<T> {
   ...
}

NOMBRA los paquetes, directorios y archivos fuente usando lowercase_with_underscores

#

Reglas de linter: file_names, package_names

Algunos sistemas de archivos no distinguen entre mayúsculas y minúsculas, por lo que muchos proyectos requieren que los nombres de archivo sean todos en minúsculas. El uso de un carácter separador permite que los nombres sigan siendo legibles en esa forma. El uso de guiones bajos como separador garantiza que el nombre siga siendo un identificador válido de Dart, lo que puede ser útil si el lenguaje admite posteriormente importaciones simbólicas.

bien
my_package
└─ lib
   └─ file_system.dart
   └─ slider_menu.dart
mal
mypackage
└─ lib
   └─ file-system.dart
   └─ SliderMenu.dart

NOMBRA los prefijos de importación usando lowercase_with_underscores

#

Regla de linter: library_prefixes

bien dart
import 'dart:math' as math;
import 'package:angular_components/angular_components.dart' as angular_components;
import 'package:js/js.dart' as js;
mal dart
import 'dart:math' as Math;
import 'package:angular_components/angular_components.dart' as angularComponents;
import 'package:js/js.dart' as JS;

NOMBRA otros identificadores usando lowerCamelCase

#

Regla de linter: non_constant_identifier_names

Los miembros de clase, las definiciones de nivel superior, las variables, los parámetros y los parámetros nombrados deben escribir en mayúscula la primera letra de cada palabra excepto la primera palabra, y no usar separadores.

bien dart
var count = 3;

HttpRequest httpRequest;

void align(bool clearItems) {
  // ...
}

PREFIERE usar lowerCamelCase para nombres de constantes

#

Regla de linter: constant_identifier_names

En código nuevo, usa lowerCamelCase para variables constantes, incluidos los valores enum.

bien dart
const pi = 3.14;
const defaultTimeout = 1000;
final urlScheme = RegExp('^([a-z]+):');

class Dice {
  static final numberGenerator = Random();
}
mal dart
const PI = 3.14;
const DefaultTimeout = 1000;
final URL_SCHEME = RegExp('^([a-z]+):');

class Dice {
  static final NUMBER_GENERATOR = Random();
}

Puedes usar SCREAMING_CAPS para mantener la consistencia con el código existente, como en los siguientes casos:

  • Al agregar código a un archivo o librería que ya utiliza SCREAMING_CAPS.
  • Al generar código de Dart que sea paralelo al código Java (por ejemplo, en tipos enumerados generados a partir de protobufs.)

ESCRIBE con mayúscula inicial los acrónimos y abreviaturas de más de dos letras como si fueran palabras

#

Los acrónimos en mayúsculas pueden ser difíciles de leer, y múltiples acrónimos adyacentes pueden dar lugar a nombres ambiguos. Por ejemplo, dado el identificador HTTPSFTP, el lector no puede saber si se refiere a HTTPS FTP o a HTTP SFTP. Para evitar esto, escribe con mayúscula inicial la mayoría de los acrónimos y abreviaturas como si fueran palabras normales. Este identificador sería HttpsFtp si se refiere al primero o HttpSftp para el segundo.

Las abreviaturas y acrónimos de dos letras son la excepción. Si ambas letras se escriben con mayúscula en inglés, entonces ambas deben permanecer en mayúscula cuando se usan en un identificador. De lo contrario, escríbelo con mayúscula inicial como si fuera una palabra.

bien dart
// Longer than two letters, so always like a word:
Http // "hypertext transfer protocol"
Nasa // "national aeronautics and space administration"
Uri // "uniform resource identifier"
Esq // "esquire"
Ave // "avenue"

// Two letters, capitalized in English, so capitalized in an identifier:
ID // "identifier"
TV // "television"
UI // "user interface"

// Two letters, not capitalized in English, so like a word in an identifier:
Mr // "mister"
St // "street"
Rd // "road"
mal dart
HTTP // "hypertext transfer protocol"
NASA // "national aeronautics and space administration"
URI // "uniform resource identifier"
esq // "esquire"
ave // "avenue"

Id // "identifier"
Tv // "television"
Ui // "user interface"

MR // "mister"
ST // "street"
RD // "road"

Cuando cualquier forma de abreviatura aparece al principio de un identificador en lowerCamelCase, la abreviatura debe estar toda en minúscula:

dart
var httpConnection = connect();
var tvSet = Television();
var mrRogers = 'hello, neighbor';

PREFIERE usar comodines (wildcards) para parámetros de callback no utilizados

#

A veces, la firma de tipo de una función callback requiere un parámetro, pero la implementación del callback no usa el parámetro. En este caso, es idóneo nombrar al parámetro no utilizado _, lo que declara una variable comodín (wildcard variable) que no es vinculante (non-binding).

bien dart
futureOfVoid.then((_) {
  print('Operation complete.');
});

Debido a que las variables comodín no son vinculantes, puedes nombrar múltiples parámetros no utilizados como _.

bien dart
.onError((_, _) {
  print('Operation failed.');
});

Esta pauta es solo para funciones que son a la vez anónimas y locales. Estas funciones generalmente se usan de inmediato en un contexto donde está claro qué representa el parámetro no utilizado. Por el contrario, las funciones de nivel superior y las declaraciones de métodos no tienen ese contexto, por lo que sus parámetros deben ser nombrados de manera que quede claro para qué sirve cada parámetro, incluso si no se utiliza.

NO uses un guion bajo inicial para identificadores que no sean privados

#

Dart utiliza un guion bajo inicial en un identificador para marcar los miembros y las declaraciones de nivel superior como privados. Esto entrena a los usuarios a asociar un guion bajo inicial con uno de esos tipos de declaraciones. Ven "_" y piensan "privado".

No existe el concepto de "privado" para variables locales, parámetros, funciones locales o prefijos de librería. Cuando uno de estos tiene un nombre que comienza con un guion bajo, envía una señal confusa al lector. Para evitar eso, no uses guiones bajos iniciales en esos nombres.

NO uses letras de prefijo

#

La notación húngara y otros esquemas surgieron en la época de BCPL, cuando el compilador no hacía mucho para ayudarte a entender tu código. Dado que Dart puede decirte el tipo, el alcance, la mutabilidad y otras propiedades de tus declaraciones, no hay razón para codificar esas propiedades en los nombres de los identificadores.

bien dart
defaultTimeout
mal dart
kDefaultTimeout

NO nombres las librerías explícitamente

#

Adjuntar un nombre a la directiva library es técnicamente posible, pero es una característica obsoleta y desaconsejada.

Dart genera una etiqueta única para cada librería basándose en su ruta y nombre de archivo. Nombrar las librerías anula esta URI generada. Sin la URI, puede ser más difícil para las herramientas encontrar el archivo de librería principal en cuestión.

mal dart
library my_library;
bien dart
/// A really great test library.
@TestOn('browser')
library;

Ordenamiento

#

Para mantener ordenado el preámbulo de tu archivo, tenemos un orden prescrito en el que deben aparecer las directivas. Cada "sección" debe estar separada por una línea en blanco.

Una sola regla de linter maneja todas las pautas de ordenamiento: directives_ordering.

COLOCA las importaciones dart: antes de otras importaciones

#

Regla de linter: directives_ordering

bien dart
import 'dart:async';
import 'dart:collection';

import 'package:bar/bar.dart';
import 'package:foo/foo.dart';

COLOCA las importaciones package: antes de las importaciones relativas

#

Regla de linter: directives_ordering

bien dart
import 'package:bar/bar.dart';
import 'package:foo/foo.dart';

import 'util.dart';

ESPECIFICA las exportaciones en una sección separada después de todas las importaciones

#

Regla de linter: directives_ordering

bien dart
import 'src/error.dart';
import 'src/foo_bar.dart';

export 'src/error.dart';
mal dart
import 'src/error.dart';
export 'src/error.dart';
import 'src/foo_bar.dart';

ORDENA las secciones alfabéticamente

#

Regla de linter: directives_ordering

bien dart
import 'package:bar/bar.dart';
import 'package:foo/foo.dart';

import 'foo.dart';
import 'foo/foo.dart';
mal dart
import 'package:foo/foo.dart';
import 'package:bar/bar.dart';

import 'foo/foo.dart';
import 'foo.dart';

Formato

#

Al igual que muchos lenguajes, Dart ignora los espacios en blanco. Sin embargo, los humanos no lo hacen. Tener un estilo de espacio en blanco consistente ayuda a garantizar que los lectores humanos vean el código de la misma manera en que lo hace el compilador.

FORMATEA tu código usando dart format

#

El formateo es un trabajo tedioso y consume mucho tiempo, especialmente durante la refactorización. Afortunadamente, no tienes que preocuparte por eso. Proporcionamos un sofisticado formateador automático de código llamado dart format que lo hace por ti. Las reglas oficiales para el manejo de espacios en blanco en Dart son cualquier cosa que produzca dart format. Las preguntas frecuentes (FAQ) del formateador pueden proporcionar más información sobre las opciones de estilo que aplica.

Las pautas de formato restantes son para las pocas cosas que dart format no puede corregir por ti.

CONSIDERA cambiar tu código para hacerlo más amigable con el formateador

#

El formateador hace lo mejor que puede con cualquier código que le arrojes, pero no puede hacer milagros. Si tu código tiene identificadores particularmente largos, expresiones profundamente anidadas, una mezcla de diferentes tipos de operadores, etc., el resultado formateado puede seguir siendo difícil de leer.

Cuando eso suceda, reorganiza o simplifica tu código. Considera acortar el nombre de una variable local o extraer una expresión en una nueva variable local. En otras palabras, realiza los mismos tipos de modificaciones que harías si estuvieras formateando el código a mano y tratando de hacerlo más legible. Piensa en dart format como una asociación en la que trabajan juntos, a veces de forma iterativa, para producir un código hermoso.

PREFIERE líneas de 80 caracteres o menos

#

Regla de linter: lines_longer_than_80_chars

Los estudios de legibilidad muestran que las líneas largas de texto son más difíciles de leer porque el ojo tiene que viajar más lejos al moverse al principio de la siguiente línea. Es por esto que los periódicos y las revistas utilizan múltiples columnas de texto.

Si realmente te encuentras deseando líneas de más de 80 caracteres, nuestra experiencia es que es probable que tu código sea demasiado verboso y podría ser un poco más compacto. El principal culpable suele ser VeryLongCamelCaseClassNames. Pregúntate a ti mismo: "¿Cada palabra en ese nombre de tipo me dice algo crítico o evita una colisión de nombres?" Si no es así, considera omitirla.

Ten en cuenta que dart format tiene como valor predeterminado 80 caracteres o menos, aunque puedes configurar ese valor predeterminado. No divide los literales de cadena largos para que quepan en 80 columnas, por lo que tienes que hacer eso manualmente.

Excepción: Cuando aparece una URI o ruta de archivo en un comentario o cadena (generalmente en una importación o exportación), puede permanecer completa incluso si hace que la línea supere los 80 caracteres. Esto facilita la búsqueda de una ruta en los archivos fuente.

Excepción: Las cadenas multilínea pueden contener líneas de más de 80 caracteres porque las nuevas líneas son significativas dentro de la cadena y dividir las líneas en otras más cortas puede alterar el programa.

USA llaves para todas las sentencias de control de flujo

#

Regla de linter: curly_braces_in_flow_control_structures

Hacerlo evita el problema del else colgante (dangling else).

bien dart
if (isWeekDay) {
  print('Bike to work!');
} else {
  print('Go dancing or read a book!');
}

Excepción: Cuando tienes una sentencia if sin cláusula else y toda la sentencia if cabe en una sola línea, puedes omitir las llaves si lo prefieres:

bien dart
if (arg == null) return defaultValue;

Sin embargo, si el cuerpo pasa a la siguiente línea, usa llaves:

bien dart
if (overflowChars != other.overflowChars) {
  return overflowChars < other.overflowChars;
}
mal dart
if (overflowChars != other.overflowChars)
  return overflowChars < other.overflowChars;