Dart Enums y Símbolos

Los enums (enumeraciones) se utilizan para definir un conjunto de constantes con nombre, lo que hace que el código sea más legible.

Este capítulo presenta la forma de definir enums en Dart, los enums mejorados (con métodos y propiedades), y los tipos Symbol y Rune.


Definición de enum (enumeración)

Un enum es un tipo especial de clase que se utiliza para representar un número fijo de valores constantes.

El escenario más típico es representar opciones limitadas como estados, direcciones, colores, etc.

Ejemplo

// Definir un enum
enum Status {
  pending,    // Pendiente
  approved,   // Aprobado
  rejected,   // Rechazado
  cancelled,  // Cancelado
}

void main() {
  // Usar valores del enum
  Status currentStatus = Status.pending;
  print('Estado actual: $currentStatus');

  // Obtener el índice mediante .index (empieza desde 0)
  print('Valor del índice: ${currentStatus.index}');  // 0

  // Obtener todos los valores del enum mediante .values
  print('Todos los estados: ${Status.values}');

  // Iterar sobre todos los valores del enum
  for (var status in Status.values) {
    print('Estado EJEMPLO: $status, índice: ${status.index}');
  }

  // Obtener el valor del enum a partir de una cadena
  Status parsed = Status.values.byName('approved');
  print('Estado analizado: $parsed');

  // Usar el enum en un switch
  switch (currentStatus) {
    case Status.pending:
      print('Pedido pendiente');
      break;
    case Status.approved:
      print('Pedido aprobado');
      break;
    case Status.rejected:
      print('Pedido rechazado');
      break;
    case Status.cancelled:
      print('Pedido cancelado');
      break;
  }
}
当前状态: Status.pending
索引值: 0
所有状态: [Status.pending, Status.approved, Status.rejected, Status.cancelled]
EJEMPLO 状态: Status.pending, 索引: 0
EJEMPLO 状态: Status.approved, 索引: 1
EJEMPLO 状态: Status.rejected, 索引: 2
EJEMPLO 状态: Status.cancelled, 索引: 3
解析出的状态: Status.approved
订单待处理

Los enums y switch son una combinación perfecta. Cuando manejas todos los valores del enum en un switch, el compilador de Dart verifica si has cubierto todos los casos. Esto evita bugs causados por ramas omitidas.


Enums mejorados (con métodos y propiedades)

Dart 3.0 introdujo los enums mejorados (Enhanced Enums), que permiten que los enums tengan campos, métodos y constructores.

Ejemplo

// Enum mejorado: puede tener variables miembro, constructores y métodos
enum HttpStatus {
  // Los valores del enum pasan parámetros a través del constructor
  ok(200, 'Solicitud exitosa'),
  created(201, 'Recurso creado'),
  badRequest(400, 'Error de solicitud'),
  unauthorized(401, 'No autorizado'),
  notFound(404, 'Recurso no encontrado'),
  serverError(500, 'Error interno del servidor');

  // Variables miembro: cada valor del enum tiene code y message
  final int code;
  final String message;

  // Constructor: debe ser const
  const HttpStatus(this.code, this.message);

  // Métodos del enum
  bool get isSuccess => code >= 200 && code < 300;
  bool get isClientError => code >= 400 && code < 500;
  bool get isServerError => code >= 500;

  // Método estático: busca el enum según el código de estado
  static HttpStatus? fromCode(int code) {
    try {
      return HttpStatus.values.firstWhere((s) => s.code == code);
    } catch (_) {
      return null;
    }
  }
}

void main() {
  var status = HttpStatus.notFound;

  print('Estado HTTP EJEMPLO: $status');
  print('Código de estado: ${status.code}');
  print('Mensaje: ${status.message}');
  print('¿Es exitoso?: ${status.isSuccess}');
  print('¿Es error de cliente?: ${status.isClientError}');

  // Usar el método estático para buscar
  var found = HttpStatus.fromCode(201);
  if (found != null) {
    print('Estado encontrado: ${found.message}');
  }

  var unknown = HttpStatus.fromCode(999);
  print('Código de estado desconocido: $unknown');
}
EJEMPLO HTTP 状态: HttpStatus.notFound
状态码: 404
Mensaje: 资源未找到
是否成功: false
是否客户端错误: true
找到状态: 资源已创建
未知状态码: null

Los enums mejorados transforman el enum de un "conjunto de constantes con nombre" a un "tipo con comportamiento", permitiendo encapsular la lógica relacionada con el enum.

El constructor de un enum mejorado debe ser const y las variables miembro deben ser final. Esto se debe a que los valores del enum son en sí mismos constantes en tiempo de compilación.


El tipo Symbol

Symbol representa identificadores en un programa Dart (nombres de variables, nombres de funciones, etc.).

No se usa comúnmente en el desarrollo diario; se utiliza principalmente en escenarios de reflexión (Reflection) y generación de código.

Ejemplo

void main() {
  // Crear un Symbol usando el prefijo #
  Symbol sym1 = #ejemplo;
  Symbol sym2 = #helloWorld;

  print('Symbol 1: $sym1');  // Symbol("ejemplo")
  print('Symbol 2: $sym2');  // Symbol("helloWorld")

  // Comparación de Symbols
  Symbol sym3 = #ejemplo;
  print('sym1 == sym3: ${sym1 == sym3}');  // true

  // Symbol se puede usar como clave de un Map
  var metadata = {
    #author: 'EJEMPLO',
    #version: '1.0.0',
    #description: 'Tutorial de Dart',
  };
  print('Autor: ${metadata[#author]}');
}
Symbol 1: Symbol("ejemplo")
Symbol 2: Symbol("helloWorld")
sym1 == sym3: true
作者: EJEMPLO

Los Symbols generalmente se minifican después de la compilación de Dart, por lo que no debes depender del resultado de Symbol("ejemplo").toString() para tomar decisiones lógicas. El uso correcto de Symbol es realizar operaciones de reflexión mediante la librería dart:mirrors.


Rune y Unicode

Rune es el tipo que representa los puntos de código Unicode en Dart.

En Dart, las cadenas son secuencias codificadas en UTF-16.

Para caracteres fuera del plano básico multilingüe (BMP), como los emojis, se necesita usar Rune para procesarlos.

Ejemplo

void main() {
  // Obtener los puntos de código Unicode de una cadena
  String text = 'EJEMPLO';
  print('Carácter: $text');
  print('Lista de puntos de código: ${text.runes.toList()}');

  // Iterar sobre cada carácter y su punto de código
  for (var char in text.runes) {
    print('${String.fromCharCode(char)} -> U+${char.toRadixString(16).toUpperCase().padLeft(4, '0')}');
  }

  // Crear un carácter a partir de un punto de código
  var heart = String.fromCharCode(0x2764);
  print('Corazón: $heart');

  // Manejar caracteres de múltiples puntos de código (como algunos emojis)
  var smile = '\u{1F600}';  // Usar la sintaxis \u{} para representar caracteres fuera del BMP
  print('Cara sonriente: $smile');

  // Obtener los puntos de código de un carácter de múltiples puntos de código
  print('Puntos de código de la cara sonriente: ${smile.runes.map((r) => 'U+${r.toRadixString(16).toUpperCase()}').toList()}');
}
字符: EJEMPLO
码点列表: [82, 85, 78, 79, 79, 66]
R -> U+0052
U -> U+0055
N -> U+004E
O -> U+004F
O -> U+004F
B -> U+0042
心形: &#x2764;
笑脸: &#x1f600;
笑脸码点: [U+1F600]

En la mayor parte del desarrollo diario no necesitas manipular Rune directamente; las cadenas por sí solas ya manejan bien Unicode.

Rune se usa principalmente cuando se necesita manipular con precisión los puntos de código de los caracteres, como en escenarios de bajo nivel como editores de texto, renderizado de fuentes, etc.

Otras extensiones