Skip to content

Instantly share code, notes, and snippets.

@psygo
Last active August 17, 2025 21:17
Show Gist options
  • Select an option

  • Save psygo/44a661296547e4cb21c9f4b3707013cf to your computer and use it in GitHub Desktop.

Select an option

Save psygo/44a661296547e4cb21c9f4b3707013cf to your computer and use it in GitHub Desktop.

Revisions

  1. psygo revised this gist Sep 12, 2020. 1 changed file with 5 additions and 0 deletions.
    5 changes: 5 additions & 0 deletions reflection_and_annotations.md
    Original file line number Diff line number Diff line change
    @@ -1,13 +1,18 @@
    # Reflection in Dart

    > **Unfortunately, [since mirrors currently bloat compiled code][no_support_mirrors], it isn't supported for Flutter or web apps.** A [workaround][source_gen_tutorial] would be to use a package like [`source_gen`][source_gen].
    > [`dart:mirrors`][dart_mirrors]
    Most of the info here, comes from [Understanding Reflection and Annotations in Dart tutorial][reflection_tutorial], from the *Fullstack Dart and Flutter Tutorials* YouTube channel. He also created a [gist][gist_tutorial] with it.


    [dart_mirrors]: https://api.dart.dev/stable/2.9.1/dart-mirrors/dart-mirrors-library.html
    [gist_tutorial]: https://gist.github.com/graphicbeacon/9fb6c6a951168d6a31d9b2cc442bcbe2
    [no_support_mirrors]: https://flutter.dev/docs/resources/faq#does-flutter-come-with-a-reflectionmirrors-system
    [reflection_tutorial]: https://youtu.be/_2yjPLVEGs4
    [source_gen]: https://pub.dev/packages/source_gen
    [source_gen_tutorial]: https://youtu.be/hgDbFLnLDNc

    ## 1. Basics

  2. psygo created this gist Sep 12, 2020.
    222 changes: 222 additions & 0 deletions reflection_and_annotations.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,222 @@
    # Reflection in Dart

    > [`dart:mirrors`][dart_mirrors]
    Most of the info here, comes from [Understanding Reflection and Annotations in Dart tutorial][reflection_tutorial], from the *Fullstack Dart and Flutter Tutorials* YouTube channel. He also created a [gist][gist_tutorial] with it.


    [dart_mirrors]: https://api.dart.dev/stable/2.9.1/dart-mirrors/dart-mirrors-library.html
    [gist_tutorial]: https://gist.github.com/graphicbeacon/9fb6c6a951168d6a31d9b2cc442bcbe2
    [reflection_tutorial]: https://youtu.be/_2yjPLVEGs4

    ## 1. Basics

    There are basically 3 functions which let you access metadata on your program:

    - `reflect`: for instances of objects (*reflectee*) — the one you're most likely going to use the most.
    - `reflectClass`: for classes.
    - `reflectType`: for `Type`s — good for when working with generics.

    Symbols can be called through `Symbol()` or simply prefixing the symbol with `#`.

    ## 2. Simple Reflected Call

    ```dart
    import 'dart:mirrors' show InstanceMirror, reflect;
    void main(List<String> arguments) {
    final InstanceMirror ref = reflect(Endpoint());
    ref.invoke(#handle, []);
    print(ref.type.instanceMembers);
    }
    class Endpoint {
    final String field = '';
    void handle() => print('Request received');
    }
    ```

    The `invoke` method can also handle optional and named parameters.

    ## 3. Creating Annotations

    Annotations are simple Dart classes with `const` constructors used in tandem with reflection.

    Use the `metadata` property to extract the annotation's data. Metadata is a list of `InstanceMirror`s, so you could potentially use multiple ones at the same time &mdash; and use list methods to filter the annotation you want by type.

    ```dart
    import 'dart:io' show HttpServer, HttpRequest;
    import 'dart:mirrors' show InstanceMirror, reflect;
    Future<void> main() async {
    final HttpServer server = await HttpServer.bind('localhost', 8080);
    await for (HttpRequest req in server) {
    final InstanceMirror reflectedEndpoint = reflect(Endpoint());
    final String path = req.uri.path;
    final Route routeAnnotation = reflectedEndpoint.type.metadata
    .firstWhere((InstanceMirror metadata) => metadata.reflectee is Route,
    orElse: () => null)
    .reflectee;
    if (routeAnnotation != null && path == routeAnnotation.url)
    reflectedEndpoint.invoke(#handle, []);
    await req.response.close();
    }
    }
    class Route {
    final String url;
    const Route(this.url);
    }
    @Route('/')
    class Endpoint {
    void handle() => print('Request received.');
    }
    ```

    ## 3. Refactoring a `switch` Statement

    Originally, you could have handled different incoming requests like this:

    ```dart
    @Route('/')
    class Endpoint {
    final HttpRequest _req;
    Endpoint(this._req);
    void handle() {
    switch (_req.method) {
    case 'GET':
    _handleGet();
    break;
    case 'POST':
    _handlePost();
    break;
    case 'PUT':
    _handlePut();
    break;
    case 'DELETE':
    _handleDelete();
    break;
    }
    }
    void _handleGet() => _req.response.write('Got GET request.');
    void _handlePost() => _req.response.write('Got POST request.');
    void _handlePut() => _req.response.write('Got PUT request.');
    void _handleDelete() => _req.response.write('Got DELETE request.');
    }
    ```

    With reflection, you will be able to get something like this:

    ```dart
    import 'dart:io' show HttpServer, HttpRequest;
    import 'dart:mirrors' show InstanceMirror, MethodMirror, reflect;
    Future<void> main() async {
    final HttpServer server = await HttpServer.bind('localhost', 8080);
    await for (HttpRequest req in server) {
    final InstanceMirror reflectedEndpoint = reflect(Endpoint(req));
    final String path = req.uri.path;
    final Route routeAnnotation = reflectedEndpoint.type.metadata
    .firstWhere((InstanceMirror metadata) => metadata.reflectee is Route,
    orElse: () => null)
    .reflectee;
    if (routeAnnotation != null && path == routeAnnotation.url) {
    reflectedEndpoint.invoke(#handle, []);
    reflectedEndpoint.type.instanceMembers
    .forEach((_, MethodMirror methodMirror) {
    if (methodMirror.isOperator ||
    !methodMirror.isRegularMethod ||
    methodMirror.owner.simpleName != #Endpoint) return;
    final InstanceMirror routeMethod = methodMirror.metadata.firstWhere(
    (InstanceMirror instanceMirror) =>
    instanceMirror.reflectee is RouteMethod,
    orElse: () => null);
    if (routeMethod != null && (routeMethod.reflectee as RouteMethod).method == req.method) {
    reflectedEndpoint.invoke(methodMirror.simpleName, []);
    }
    });
    }
    await req.response.close();
    }
    }
    class Route {
    final String url;
    const Route(this.url);
    }
    class RouteMethod {
    final String method;
    const RouteMethod(this.method);
    }
    @Route('/')
    class Endpoint {
    final HttpRequest _req;
    Endpoint(this._req);
    @RouteMethod('GET')
    void handleGet() => _req.response.write('Got GET request.');
    @RouteMethod('POST')
    void handlePost() => _req.response.write('Got POST request.');
    @RouteMethod('PUT')
    void handlePut() => _req.response.write('Got PUT request.');
    @RouteMethod('DELETE')
    void handleDelete() => _req.response.write('Got DELETE request.');
    }
    ```

    ### 3.1 More Programmatic Calls with Named Constructors

    Static analysis and exception handling might benefit from using named constructors instead of one constructor with strings.

    ```dart
    class RouteMethod {
    final String method;
    const RouteMethod(this.method);
    const RouteMethod.get() : this('GET');
    const RouteMethod.post() : this('POST');
    const RouteMethod.put() : this('PUT');
    const RouteMethod.delete() : this('DELETE');
    }
    @Route('/')
    class Endpoint {
    final HttpRequest _req;
    Endpoint(this._req);
    @RouteMethod.get()
    void handleGet() => _req.response.write('Got GET request.');
    @RouteMethod.post()
    void handlePost() => _req.response.write('Got POST request.');
    @RouteMethod.put()
    void handlePut() => _req.response.write('Got PUT request.');
    @RouteMethod.delete()
    void handleDelete() => _req.response.write('Got DELETE request.');
    }
    ```