Question #460MediumDart BasicsAdvanced Concepts

What is Symbol in Dart?

#dart#symbol#reflection#mirrors#identifier

Answer

Overview

A

text
Symbol
in Dart is an opaque, immutable representation of a Dart identifier (like a class name, method name, or variable name). Symbols are primarily used with reflection (the
text
dart:mirrors
library) and as constant identifiers that survive code minification.


Creating Symbols

There are two ways to create a Symbol:

dart
void main() {
  // 1. Symbol literal (compile-time constant)
  const symbol1 = #myVariable;
  const symbol2 = #MyClass;
  const symbol3 = #myMethod;
  const symbol4 = #dart.core;  // Library name

  // 2. Symbol constructor (runtime)
  final symbol5 = Symbol('myVariable');

  // Both are equivalent
  print(#myVariable == Symbol('myVariable')); // true
}

Why Symbols Exist

The Minification Problem

When Dart code is compiled for the web, identifiers are minified (shortened) to reduce file size. Strings would not survive this process, but Symbols do.

dart
// In source code:
class UserService {
  void fetchUser() {}
}

// After minification:
class a {
  void b() {}
}

// String 'fetchUser' ❌ — broken after minification
// Symbol #fetchUser  ✅ — minified along with the code

Symbols get minified together with the code they reference, so they always refer to the correct identifier — even after minification.


Symbols with Reflection (dart:mirrors)

The primary use case for Symbols is with Dart's mirror-based reflection system:

dart
import 'dart:mirrors';

class User {
  String name;
  int age;

  User(this.name, this.age);

  String greet() => 'Hello, I am $name';
}

void main() {
  final user = User('Alice', 30);

  // Reflect on the instance
  final mirror = reflect(user);

  // Access field using Symbol
  final nameMirror = mirror.getField(#name);
  print(nameMirror.reflectee); // 'Alice'

  // Set field using Symbol
  mirror.setField(#name, 'Bob');
  print(user.name); // 'Bob'

  // Invoke method using Symbol
  final result = mirror.invoke(#greet, []);
  print(result.reflectee); // 'Hello, I am Bob'

  // Inspect class members
  final classMirror = reflectClass(User);
  classMirror.declarations.forEach((symbol, declaration) {
    print(MirrorSystem.getName(symbol)); // name, age, greet, User
  });
}

Symbol Properties and Methods

dart
void main() {
  const sym = #myMethod;

  // Symbols are constants — can be used in switch/case
  switch (sym) {
    case #myMethod:
      print('Matched myMethod');
    case #otherMethod:
      print('Matched otherMethod');
  }

  // Symbols support equality
  print(#myMethod == #myMethod);         // true
  print(#myMethod == #otherMethod);       // false
  print(#myMethod == Symbol('myMethod')); // true

  // Symbols can be used as Map keys
  final metadata = <Symbol, String>{
    #name: 'Alice',
    #role: 'Developer',
  };
  print(metadata[#name]); // 'Alice'
}

Converting Between Symbol and String

dart
import 'dart:mirrors';

void main() {
  // Symbol → String (requires dart:mirrors)
  const sym = #myVariable;
  final name = MirrorSystem.getName(sym);
  print(name); // 'myVariable'

  // String → Symbol
  final sym2 = Symbol('myVariable');
  print(sym2); // Symbol("myVariable")
}

Practical Use Cases

1. No-Such-Method Forwarding

dart
class DynamicProxy {
  
  dynamic noSuchMethod(Invocation invocation) {
    final methodName = invocation.memberName; // This is a Symbol
    print('Called: $methodName');
    print('Positional args: ${invocation.positionalArguments}');
    print('Named args: ${invocation.namedArguments}');
    return null;
  }
}

void main() {
  dynamic proxy = DynamicProxy();
  proxy.anyMethod('hello', count: 5);
  // Called: Symbol("anyMethod")
  // Positional args: [hello]
  // Named args: {Symbol("count"): 5}
}

2. Constant Map Keys

dart
const permissions = <Symbol, bool>{
  #read: true,
  #write: false,
  #delete: false,
};

bool hasPermission(Symbol action) => permissions[action] ?? false;

void main() {
  print(hasPermission(#read));   // true
  print(hasPermission(#delete)); // false
}

Important Limitations

LimitationDetails
No
text
dart:mirrors
in Flutter
Flutter uses AOT compilation, which disables
text
dart:mirrors
Reflection alternativesUse code generation (
text
json_serializable
,
text
freezed
) instead
Limited toString
text
#name.toString()
returns
text
Symbol("name")
, not
text
'name'
Cannot extract name without mirrors
text
MirrorSystem.getName()
requires
text
dart:mirrors

Symbol in Flutter (Without Mirrors)

Since

text
dart:mirrors
is unavailable in Flutter, Symbols are rarely used directly. However, they appear internally:

dart
// noSuchMethod still uses Symbols in Flutter
class SafeMap {
  final Map<String, dynamic> _data;
  SafeMap(this._data);

  
  dynamic noSuchMethod(Invocation invocation) {
    final key = invocation.memberName.toString()
        .replaceAll('Symbol("', '')
        .replaceAll('")', '');
    return _data[key];
  }
}

Key Insight:

text
Symbol
is a compile-time constant representation of a Dart identifier. It was designed for reflection (
text
dart:mirrors
) and survives minification. In Flutter, direct Symbol usage is rare because
text
dart:mirrors
is disabled — but the concept is important for understanding Dart's type system and
text
noSuchMethod
forwarding.