Dart Enums and Symbols

An enum is used to define a set of named constant values, making code more readable.

This chapter introduces how to define Dart enums, enhanced enums (with methods and properties), as well as the Symbol and Rune types.


enum Enum Definition

An enum is a special kind of class used to represent a fixed number of constant values.

The most typical scenarios are representing finite options such as states, directions, colors, etc.

Example

// Define an enum
enum Status {
  pending,    // Pending
  approved,   // Approved
  rejected,   // Rejected
  cancelled,  // Cancelled
}

void main() {
  // Using enum values
  Status currentStatus = Status.pending;
  print('Current status: $currentStatus');

  // Get the index via .index (starting from 0)
  print('Index value: ${currentStatus.index}');  // 0

  // Get all enum values via .values
  print(All statuses: ${Status.values});

  // Iterate through all enum values
  for (var status in Status.values) {
    print(EXAMPLE status: $status, index: ${status.index});
  }

  // Get enum value by string
  Status parsed = Status.values.byName('approved');
  print(Parsed status: $parsed);

  // Using enums in switch
  switch (currentStatus) {
    case Status.pending:
      print(Order pending);
      break;
    case Status.approved:
      print(Order approved);
      break;
    case Status.rejected:
      print(Order rejected);
      break;
    case Status.cancelled:
      print(Order cancelled);
      break;
  }
}
当前状态: Status.pending
索引值: 0
所有状态: [Status.pending, Status.approved, Status.rejected, Status.cancelled]
EXAMPLE 状态: Status.pending, 索引: 0
EXAMPLE 状态: Status.approved, 索引: 1
EXAMPLE 状态: Status.rejected, 索引: 2
EXAMPLE 状态: Status.cancelled, 索引: 3
解析出的状态: Status.approved
订单待处理

Enums and switch are a perfect match. When you handle all enum values in a switch, the Dart compiler checks whether you have covered all cases. This avoids bugs caused by missing branches.


Enhanced Enums (with Methods and Properties)

Dart 3.0 introduced Enhanced Enums, allowing enums to have fields, methods, and constructors.

Example

// Enhanced enum: can have member variables, constructors, and methods
enum HttpStatus {
  // Enum values pass parameters via the constructor
  ok(200, Request successful),
  created(201, Resource created),
  badRequest(400, Bad request),
  unauthorized(401, Unauthorized),
  notFound(404, Resource not found),
  serverError(500, Internal server error);

  // Member variables: each enum value has code and message
  final int code;
  final String message;

  // Constructor: must be const
  const HttpStatus(this.code, this.message);

  // Enum method
  bool get isSuccess => code >= 200 && code < 300;
  bool get isClientError => code >= 400 && code < 500;
  bool get isServerError => code >= 500;

  // Static method: find enum by status code
  static HttpStatus? fromCode(int code) {
    try {
      return HttpStatus.values.firstWhere((s) => s.code == code);
    } catch (_) {
      return null;
    }
  }
}

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

  print(EXAMPLE HTTP status: $status);
  print(Status code: ${status.code});
  print(Message: ${status.message});
  print(Is successful: ${status.isSuccess});
  print('Is client error: ${status.isClientError}');

  // Look up using a static method
  var found = HttpStatus.fromCode(201);
  if (found != null) {
    print('Found status: ${found.message}');
  }

  var unknown = HttpStatus.fromCode(999);
  print('Unknown status code: $unknown');
}
EXAMPLE HTTP 状态: HttpStatus.notFound
状态码: 404
Message: 资源未找到
是否成功: false
是否客户端错误: true
找到状态: 资源已创建
未知状态码: null

Enhanced enums elevate enums from "named constant collections" to "types with behavior," allowing them to encapsulate logic related to the enum.

The constructors of enhanced enums must be const, and member variables must be final. This is because enum values themselves are compile-time constants.


Symbol type

Symbol represents identifiers in Dart programs (variable names, function names, etc.).

It is not commonly used in daily development; it is mainly used in reflection and code generation scenarios.

Example

void main() {
  // Use # prefix to create Symbol
  Symbol sym1 = #example;
  Symbol sym2 = #helloWorld;

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

  // Symbol comparison
  Symbol sym3 = #example;
  print('sym1 == sym3: ${sym1 == sym3}');  // true

  // Symbol can be used as a Map key
  var metadata = {
    #author: 'EXAMPLE',
    #version: '1.0.0',
    #description: Dart Tutorial,
  };
  print('Author: ${metadata[#author]}');
}
Symbol 1: Symbol("example")
Symbol 2: Symbol("helloWorld")
sym1 == sym3: true
作者: EXAMPLE

Symbols are typically minified after Dart compilation, so do not rely on the result of Symbol("example").toString() for logical decisions. The correct use of Symbol is for reflection operations through the dart:mirrors library.


Runes and Unicode

Rune is the type in Dart that represents Unicode code points.

In Dart, strings are sequences encoded in UTF-16.

For characters outside the Basic Multilingual Plane (BMP), such as emoji, Rune is needed to handle them.

Example

void main() {
  // Get the Unicode code points of the string
  String text = 'EXAMPLE';
  print('Character: $text');
  print('Code point list: ${text.runes.toList()}');

  // Iterate over each character and its code point
  for (var char in text.runes) {
    print('${String.fromCharCode(char)} -> U+${char.toRadixString(16).toUpperCase().padLeft(4, '0')}');
  }

  // Create a character from a code point
  var heart = String.fromCharCode(0x2764);
  print('Heart: $heart');

  // Handle multi-code-point characters (such as some emoji)
  var smile = '\u{1F600}';  // Use \u{} syntax to represent characters beyond the BMP
  print('Smiley: $smile');

  // Get the code points of a multi-code-point character
  print('Smiley code points: ${smile.runes.map((r) => 'U+${r.toRadixString(16).toUpperCase()}').toList()}');
}
字符: EXAMPLE
码点列表: [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]

In most daily development, you don't need to work with Rune directly; strings themselves handle Unicode well.

Rune is mainly used when precise manipulation of character code points is required, such as in low-level scenarios like text editors and font rendering.

other extensions