Skip to main content
A Symbol in Dart is an opaque, immutable object representing an operator or identifier declared in a Dart program. Symbols serve as canonicalized keys used primarily in APIs that refer to identifiers by name, such as noSuchMethod or the dart:mirrors reflection library.

Syntax and Instantiation

Symbols are created via the symbol literal syntax or the class constructor.

Symbol Literal

The # operator creates a compile-time constant symbol. This syntax allows the compiler to track the identifier, ensuring the symbol remains valid even if the identifier is renamed during minification.
const Symbol lib = #library_name;
const Symbol cls = #MyClass;
const Symbol method = #myMethod;
const Symbol operator = #+;
Note: The symbol literal syntax cannot be used with private identifiers (identifiers starting with _). Attempting to write #_privateIdentifier results in a syntax error.

Symbol Constructor

The Symbol() constructor creates a symbol at runtime from a string. This is used when the identifier name is dynamic.
// Creates a symbol for the string 'myMethod'
final Symbol dynamicSym = Symbol('myMethod');

Equality and Canonicalization

Symbols are value objects; two symbols are equal if they represent the same identifier. However, the behavior of equality between a symbol literal and a constructed symbol depends on the compilation mode.
  1. Dart VM (JIT): The compiler preserves identifier names. #myMethod is equivalent to Symbol('myMethod').
  2. Dart Web / AOT (Minified): The compiler minifies identifiers to reduce code size (e.g., myMethod becomes a).
    • The literal #myMethod is compiled into the symbol for the minified name (e.g., #a).
    • The constructor Symbol('myMethod') creates a symbol for the literal string “myMethod”.
    • Consequently, #myMethod != Symbol('myMethod').
void main() {
  const literalSym = #myMethod;
  final constructedSym = Symbol('myMethod');

  // True in JIT (VM).
  // False in AOT/Web if minification is enabled.
  print(literalSym == constructedSym); 
}

Compilation and Minification Behavior

The distinction between String and Symbol is critical during the compilation process.
  • Symbol Literals (#id): These are compiler-aware. If the Dart compiler minifies the function myMethod to x, the symbol literal #myMethod compiles to #x. This ensures that references to the identifier remain consistent within the compiled binary.
  • Symbol Constructors (Symbol('id')): These are opaque to the compiler. Symbol('myMethod') will always produce a symbol representing the string “myMethod”, regardless of how the actual code is minified.
Therefore, relying on Symbol(string_name) to access members dynamically will fail in minified environments, as the runtime member name (e.g., x) no longer matches the constructed symbol name (e.g., myMethod).

Private Identifiers

Dart distinguishes between public and private identifiers. A symbol representing a private identifier encodes both the name and the library in which it is defined. This prevents name collisions between private members of the same name defined in different libraries. While private symbols exist at runtime (for example, passed to noSuchMethod when a private member is missing), they cannot be created using the # syntax.
Master Dart with Deep Grasping Methodology!Learn More