Creative Bracket

Understanding Reflection and Annotations in Dart

In this video, we will explore the topic of Reflection and how that can be used to write cleaner software. Reflection allows us to examine and modify the structure and behaviour of a program at runtime. We will be working with the dart:mirrors library which contains helper classes and functions for achieving this. We will end by looking at a common use case with Annotations.


Basic example with Reflection

Create a main.dart file and start by importing our dart:mirrors dependency. Before we implement our reflection logic, let’s also define a class with a method we wish to invoke:

import 'dart:mirrors';

main() {
  // TODO: Implement Reflection logic
}

// Our example class to reflect on
class Endpoint {
  handle() => print('Request received');
}

To reflect on types and objects we have three methods to work with:

  1. reflect(): Reflects on an instance of a class
  2. reflectClass(): Reflects on a class declaration
  3. reflectType() Reflects on the type passed in as the argument

In our use case we will use reflect(). Let’s instantiate Endpoint and invoke the handle method via reflection:

main() {
  var reflectedClass = reflect(Endpoint());

  // Invoke the handle() method
  reflectedClass.invoke(Symbol('handle'), []);
}

And then let’s run this file:

dart /path/to/main.dart

Reflecting an instance provides us with an InstanceMirror that exposes the invoke() method. The members of our instance are depicted as Symbol types. The example above invokes the handle method by passing its Symbolic name along with an array of positional arguments if any are specified.

To pass arguments across, let’s modify the signature of the handle() method:

handle(String a) => print('Request received $a');

And pass this to the invocation:

reflectedClass.invoke(Symbol('handle'), ['argument 1']);
// => Request received argument 1

You can also use the literal form of the handle() symbol:

reflectedClass.invoke(#handle, ['argument 1']);

You can use the Symbol('methodName') approach if the method name is received as a value from an operation or provided as user input. Ideally when you don’t know beforehand the method to invoke.

Check out the full tutorial in the video above.

Further reading


Sharing is caring smile emoji

If you enjoyed reading this post, please share this through the various social buttons hovering on the left/top side of the screen ↖️arrow up emoji. Also, check out and subscribe to my YouTube channel (hit the bell icon too) for videos on Dart.

Subscribe to the newsletter for my free 35-page Get started with Dart eBook and to be notified when new content is released.

Like, share and follow me heart eyes emoji for more content on Dart.

Jermaine Oppong

Hello 👋, I show programmers how to build full-stack web applications with the Dart SDK. I am passionate about teaching others, having received tremendous support on sites like dev.to and medium.com for my articles covering various aspects of the Dart language and ecosystem.

Useful learning materials