tom_reflection
Runtime reflection support for Dart based on code generation, using capabilities to select which operations to support. It exposes class structure and metadata and invokes methods and properties by name.
Overview
From the module readme.md file:
What it enables
Enables Runtime introspection, Dynamic method invocation, Application auto-assembly.
Relationships
Standalone — no declared relationships.
tom_reflection
> Part of the Tom Framework reflection toolkit. Derived from the > reflectable package by the Dart team
> (google/reflectable.dart, > "Copyright (c) 2015, Dart", BSD-3-Clause); Tom-specific refactoring, fixes and > enhancements © 2024–2026 Peter Nicolai Alexis Kyaw, released under the same > BSD-3-Clause terms. See
LICENSE and
NOTICE.
Runtime reflection for Dart based on code generation, using capabilities to declare exactly which reflective operations to support — so the generated code stays small.
tom_reflection is engine 1 of the Tom reflection toolkit: it gives you
mirrors on live objects at runtime. You can invoke a method by name, read or write a field by name, construct an instance, walk a class hierarchy, and read metadata — all without knowing the type at compile time. For the build-time
structural model (a serializable graph of code shape for tooling), see the sibling engine tom_reflector.
Overview
Dart's built-in dart:mirrors is unavailable in AOT/Flutter and bloats output because the compiler cannot know which members you will reflect on. This package solves that with
generated, capability-gated reflection:
1. You declare a reflector — a const subclass of Reflection
whose constructor lists the capabilities you need (invoke methods? read declarations? walk type relations?). 2. You
annotate target classes with that reflector (@myReflector). 3. A generator
(tom_reflection_generator) reads those annotations and emits a
*.reflection.dart file holding the precomputed mirror data — only for the capabilities you asked for. 4. At startup you call the generated
initializeReflection(), then use myReflector.reflect(obj) to get an InstanceMirror
and drive it.
Because capabilities gate what the generator emits, a reflector that only needs invokingCapability
produces far less code than one asking for everything. This is the core idea inherited from reflectable: pay only for the reflection you use.
annotate generate run
@myReflector ─────► *.reflection.dart ─────► myReflector.reflect(obj)
class Foo (capability-gated) .invoke('bar', [...])
Installation
dependencies:
tom_reflection: ^1.0.2
dev_dependencies:
build_runner: ^2.4.0
tom_reflection_generator: ^1.1.1
Or:
dart pub add tom_reflection
dart pub add --dev build_runner tom_reflection_generator
SDK: Dart ^3.9.0. The generator is a dev-only dependency — your shipped package depends on
tom_reflection alone.
Configure which files to generate reflection for via build.yaml:
targets:
$default:
builders:
tom_reflection_generator:
generate_for:
- lib/**.dart
- example/**.dart
Features
Reflective operations
| Operation | Mirror API | Capability required |
|---|---|---|
| Invoke a method by name | InstanceMirror.invoke('m', [...]) |
invokingCapability / instanceInvokeCapability |
| Read a field/getter | InstanceMirror.invokeGetter('f') |
invokingCapability |
| Write a field/setter | InstanceMirror.invokeSetter('f', v) |
invokingCapability |
| Construct an instance | ClassMirror.newInstance('', [...]) |
newInstanceCapability |
| Call a static method | ClassMirror.invoke('m', [...]) |
staticInvokeCapability |
| Enumerate declarations | ClassMirror.declarations |
declarationsCapability |
| Class of an instance | InstanceMirror.type |
typeCapability |
| Superclass / interfaces | ClassMirror.superclass, .superinterfaces |
typeRelationsCapability |
| Read metadata | DeclarationMirror.metadata |
metadataCapability |
reflectedType on mirrors |
TypeMirror.reflectedType |
reflectedTypeCapability |
| Library mirrors | reflector.findLibrary(...) |
libraryCapability |
Capabilities
Capabilities are passed to your reflector's super(...) constructor (up to ten positional, or
Reflection.fromList([...]) for more).
| Capability | What it enables |
|---|---|
invokingCapability |
Instance + static method/constructor invocation (the common all-rounder) |
instanceInvokeCapability |
Instance method / getter / setter invocation only |
staticInvokeCapability | Static method invocation |
topLevelInvokeCapability | Top-level function invocation |
newInstanceCapability | Construct instances via newInstance |
declarationsCapability | Enumerate fields, methods, constructors |
typeCapability | Obtain class/type mirrors (instance.type) |
typeRelationsCapability | Superclass, interfaces, type arguments |
metadataCapability | Read annotation metadata |
reflectedTypeCapability | reflectedType on type mirrors |
libraryCapability | Library mirrors |
uriCapability | Library URIs |
libraryDependenciesCapability | Import/export dependency mirrors |
typingCapability | Full type-information support |
Pattern capabilities restrict to members matching a regular expression, so the generator emits even less:
class GettersOnly extends Reflection {
const GettersOnly() : super(const InstanceInvokeCapability('^get'));
}
Metadata-quantified capabilities (InstanceInvokeMetaCapability(Annotation)) restrict to members carrying a given annotation.
Quantifier capabilities (superclassQuantifyCapability, subtypeQuantifyCapability,
typeAnnotationQuantifyCapability, admitSubtypeCapability) widen the reflected
set of classes beyond the directly-annotated ones.
Quick start
import 'package:tom_reflection/tom_reflection.dart';
import 'quick_start.reflection.dart'; // generated
class MyReflector extends Reflection {
const MyReflector() : super(invokingCapability, declarationsCapability);
}
const myReflector = MyReflector();
@myReflector
class Person {
String name;
Person(this.name);
String greet() => 'Hello, I am $name!';
}
void main() {
initializeReflection(); // generated; call once before reflecting
final mirror = myReflector.reflect(Person('Alice'));
print(mirror.invoke('greet', [])); // Hello, I am Alice!
print(mirror.invokeGetter('name')); // Alice
mirror.invokeSetter('name', 'Bob');
print(mirror.invoke('greet', [])); // Hello, I am Bob!
}
Generate the quick_start.reflection.dart first:
dart run build_runner build # build_runner
# or:
reflectiongenerator lib/quick_start.dart # standalone CLI
# or:
buildkit :reflectiongenerator # nested under buildkit
Example projects
| Sample | Demonstrates |
|---|---|
example/tom_reflection_example.dart
|
Thirteen scenarios: invoke, getters/setters, declarations, subclass reflection, type reflection,
isSubtype<S>()
,
isInstanceOf
, typed-collection creation, superclass-chain walking.
|
reflection_introduction_sample
|
Guided beginner tutorial — annotate → generate → reflect → invoke. |
reflection_advanced_sample
|
Declarations, newInstance, static members, type relations, generics & mixins. |
reflection_capability_sample
|
How each capability gates generated code; minimal vs all capabilities. |
Usage
Define a reflector and annotate
class MyReflector extends Reflection {
const MyReflector()
: super(
invokingCapability,
declarationsCapability,
typeRelationsCapability,
metadataCapability,
);
}
const myReflector = MyReflector();
@myReflector
class Employee {
String name;
String department;
Employee(this.name, this.department);
String jobInfo() => '$name works in $department';
}
The reflector class must have a zero-argument const constructor (so it is a singleton the generator can encode). Use it as
@MyReflector() or as a top-level const myReflector identifier (@myReflector).
Invoke methods, getters and setters
final m = myReflector.reflect(Employee('Bob', 'Engineering'));
m.invoke('jobInfo', []); // 'Bob works in Engineering'
m.invokeGetter('department'); // 'Engineering'
m.invokeSetter('department', 'Sales');
m.invokeGetter('department'); // 'Sales'
invoke also takes named arguments: m.invoke('f', [pos], {#named: v}).
Enumerate declarations
With declarationsCapability, walk the members of a class:
final cls = m.type; // ClassMirror, requires typeCapability
for (final entry in cls.declarations.entries) {
final kind = switch (entry.value) {
MethodMirror mm when mm.isGetter => 'getter',
MethodMirror mm when mm.isConstructor => 'constructor',
MethodMirror() => 'method',
VariableMirror() => 'field',
_ => 'other',
};
print('${entry.key} ($kind)');
}
// name (field)
// department (field)
// jobInfo (method)
// ...
Construct instances
With newInstanceCapability, build objects reflectively:
final cls = myReflector.reflectType(Employee) as ClassMirror;
final e = cls.newInstance('', ['Carol', 'Product']); // unnamed constructor
print((e as Employee).jobInfo()); // Carol works in Product
Type relations
With typeRelationsCapability, walk the hierarchy:
final cls = m.type;
print(cls.simpleName); // Employee
print(cls.superclass?.simpleName ?? 'none'); // Person (or 'none' at an unmarked base)
ClassMirror also offers convenience checks that need no extra capability:
final personType = myReflector.reflectType(Person) as ClassMirror;
final employeeType = myReflector.reflectType(Employee) as ClassMirror;
personType.isSubtype<Employee>(); // true — Employee is-a Person
employeeType.isSubtype<Person>(); // false
personType.isInstanceOf(Employee(...)); // true
Metadata
With metadataCapability, read annotations off declarations:
for (final entry in cls.declarations.entries) {
final meta = entry.value.metadata; // List<Object>
if (meta.isNotEmpty) print('${entry.key}: $meta');
}
Typed collections
ClassMirror can build correctly-typed collections of its reflectee type:
final list = personType.createList(); // List<Person>
final set = personType.createSet(); // Set<Person>
final map = personType.createValuedMap<String>(); // Map<String, Person>
Reflect a type, and list covered classes
final tm = myReflector.reflectType(Person);
print(tm.simpleName); // Person
print(tm.isOriginalDeclaration); // true
for (final c in myReflector.annotatedClasses) {
print(c.simpleName); // every class covered by this reflector
}
Architecture
package:tom_reflection/tom_reflection.dart (public API)
├── Reflection / ReflectionInterface the reflector base + contract
├── capability.dart ─────────────────────► ReflectCapability hierarchy
└── mirrors.dart ─────────────────────► Mirror hierarchy (interfaces)
generated *.reflection.dart (per target file, by tom_reflection_generator)
├── ReflectorData precomputed member/type tables
└── initializeReflection() installs the data into the runtime
The public library re-exports capability.dart and mirrors.dart. Generated files import the internal
generated.dart barrel (mirror implementation classes, ReflectorData) — never import that directly from app code.
Key types
| Type | Responsibility |
|---|---|
Reflection |
Abstract base for your reflector; its
const
constructor takes the capabilities. Used as the annotation.
|
ReflectionInterface |
The reflector contract:
reflect
,
reflectType
,
findLibrary
,
annotatedClasses
,
canReflect
.
|
InstanceMirror |
Mirror on a live object:
invoke
,
invokeGetter
,
invokeSetter
,
type
.
|
ClassMirror<T> |
Mirror on a class:
newInstance
,
declarations
,
superclass
,
superinterfaces
,
isSubtype<S>
,
isInstanceOf
, collection factories.
|
TypeMirror<T> |
Mirror on a type:
simpleName
,
qualifiedName
,
isOriginalDeclaration
,
reflectedType
.
|
MethodMirror |
A method/getter/setter/constructor declaration (
isGetter
,
isSetter
,
isConstructor
,
parameters
).
|
VariableMirror |
A field declaration (type, isFinal, isStatic). |
ParameterMirror |
A parameter (isNamed, isOptional, hasDefaultValue). |
LibraryMirror | A library declaration and its members. |
ReflectCapability |
Base of the capability hierarchy gating what the generator emits. |
Ecosystem
tom_reflection (this package — runtime mirror library)
▲
│ dev-dependency (generation only)
tom_reflection_generator (emits *.reflection.dart: builder + `reflectiongenerator` CLI)
▲
│ exercised end-to-end by
tom_reflection_test (fixture suite)
tom_reflection is the only runtime dependency your code needs. The generator and test suite are development-time companions. The build-time
structural engine (tom_reflector) is a separate technology — see the repo
README for when to use which.
Further documentation
-
Generator usage (builder + CLI):
tom_reflection_generator/doc/reflection_generator.md -
Generator CLI reference:
tom_reflection_generator/doc/reflectiongenerator_user_reference.md -
Repo map and engine selection:
../README.md - Upstream design rationale (capabilities): the
reflectable documentation, which this package's capability model follows.
Status
- Version: 1.0.2 (published to pub.dev).
- SDK: Dart
^3.9.0. - Tests: exercised end-to-end by 72 fixture suites in
tom_reflection_test (basic invocation, capabilities, declarations,
newInstance, static members, metadata, type relations, reflectType, enums, generic mixins, and more).
License
BSD-3-Clause. This package is derived from the reflectable
package by the Dart team (google/reflectable.dart, "Copyright (c) 2015, Dart") and
retains the upstream copyright alongside Tom's modifications (© 2024–2026 Peter Nicolai Alexis Kyaw), released under the same BSD-3-Clause terms. See
LICENSE and
NOTICE.
License
Copyright (c) 2015, Dart All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of reflectable nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Refactoring, modifications and enhancements (c) 2026, Peter Nicolai Alexis Kyaw