Last active
August 17, 2025 21:17
-
-
Save psygo/44a661296547e4cb21c9f4b3707013cf to your computer and use it in GitHub Desktop.
Revisions
-
psygo revised this gist
Sep 12, 2020 . 1 changed file with 5 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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 -
psygo created this gist
Sep 12, 2020 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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 — 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.'); } ```