flutter_aop 0.0.3+4 copy "flutter_aop: ^0.0.3+4" to clipboard
flutter_aop: ^0.0.3+4 copied to clipboard

Annotation driven proxies to add lightweight AOP style hooks to Flutter code.

flutter_aop #

Annotation driven proxies that bring small, framework free AOP style hooks to Flutter and Dart code.
Mark methods with @Aop and let build_runner generate proxy classes that run your logic before, after, or when an error happens.

한국어 문서 보기

Features #

  • Simple @Aop annotation with before, after, onError, and tag options.
  • Generated *.aop.dart files expose proxy classes (e.g. LoginServiceAopProxy) you can drop in wherever the original class is used.
  • Runtime AopHooks and a global AopRegistry let you attach logging, tracing, guards, or analytics without touching the original implementation.
  • Works with both synchronous and asynchronous (Future) methods and reports invocation details through an AopContext.

Getting started #

Add flutter_aop to your pubspec.yaml and run flutter pub get.

dependencies:
  flutter_aop:
    path: ../flutter_aop # or use your hosted location

dev_dependencies:
  build_runner: ^2.10.4

Every library that wants AOP support must include the generated part file:

import 'package:flutter_aop/flutter_aop.dart';
part 'login_service.aop.dart';

Usage #

  1. Annotate the methods that should trigger hooks.
  2. Declare aspect classes with @Aspect, @Before, @After, or @OnError and include their .aop.dart parts.
  3. Run dart run build_runner build --delete-conflicting-outputs (or flutter pub run ...) to generate both the .aop.dart files and the aggregated flutter_aop_bootstrap.g.dart.
  4. Import the generated runFlutterAopBootstrap() (e.g. import 'package:my_app/flutter_aop_bootstrap.g.dart';) and call it once during startup—this registers every proxy/aspect and internally calls ensureAllAopInitialized().
  5. Wrap your concrete instances with aopWrap(instance) (no need to touch the generated proxy class directly).
import 'package:flutter_aop/flutter_aop.dart';
import 'package:my_app/flutter_aop_bootstrap.g.dart'; // generated by build_runner
part 'login_service.aop.dart';

class LoginService {
  @Aop(tag: 'auth', description: 'track login')
  Future<void> login(String id, String password) async {
    // original implementation
  }
}

@Aspect(tag: 'auth')
class LoggingAspect {
  const LoggingAspect();

  @Before()
  void logBefore(AopContext context) =>
      print('Entering ${context.methodName} -> ${context.positionalArguments}');

  @After()
  void logAfter(AopContext context) =>
      print('Result for ${context.methodName}: ${context.result}');

  @OnError()
  void logError(AopContext context) => print('Error: ${context.error}');
}

Future<void> main() async {
  runFlutterAopBootstrap(); // call once during startup
  final service = aopWrap(LoginService());
  await service.login('gmail', '1234');
}

Need global hooks? Register them once anywhere in your app:

AopRegistry.instance.register(
  AopHooks(before: (ctx) => debugPrint('[${ctx.annotation.tag}] ${ctx.methodName}')),
  tag: 'auth',
);

Using tags keeps large projects organized—you decide which hook handles which annotated method.

Example project #

The example/ directory contains a tiny console-style demo that wires hooks into a LoginService, registers aspects via GetIt/injectable, and prints every lifecycle event.

cd example
flutter pub get
dart run build_runner build --delete-conflicting-outputs
dart run lib/main.dart

Open example/lib/login_service.dart to see the annotated class, the generated example/lib/login_service.aop.dart proxy, and example/lib/flutter_aop_bootstrap.g.dart for the aggregated bootstrap file.

Dependency injection (GetIt/Injectable) #

When using GetIt together with injectable, register wrapped services in a module and simply call the generated bootstrapper before getIt.init():

final getIt = GetIt.instance;

@InjectableInit()
Future<void> configureDependencies() async {
  runFlutterAopBootstrap();
  getIt.init();
}

@module
abstract class ServiceModule {
  @lazySingleton
  LoginService loginService() => aopWrap(LoginService());
}

The example app (example/lib/di.dart) shows the full setup, including how to resolve the proxied service from GetIt.

Generated output #

Running build_runner creates *.aop.dart part files next to your source:

lib/
 ├─ login_service.dart
 └─ login_service.aop.dart  <-- generated

Every class with at least one @Aop method receives a proxy and registers itself inside AopProxyRegistry. You rarely need the proxy class directly—just call aopWrap(Service()) (or AopProxyRegistry.instance.wrap(Service(), hooks: ...)) to receive the instrumented instance. All generated bootstrap functions are aggregated into flutter_aop_bootstrap.g.dart; call runFlutterAopBootstrap() to execute them once and ensureAllAopInitialized() is invoked automatically at the end.

Tips #

  • Hooks for synchronous methods must be synchronous too. If you need async work (e.g. writing to storage), mark the original method async so the proxy can await your hook.
  • positionalArguments and namedArguments inside AopContext give you the exact values passed to the method.
  • The optional description field is never used by the runtime but is emitted in generated comments—handy when you read the .aop.dart file.

Running the generator #

dart run build_runner build --delete-conflicting-outputs

Use watch while developing to regenerate proxies/bootstraps when files change:

dart run build_runner watch

Contributing #

Issues and ideas are welcome! File bugs or feature requests in the repository issue tracker. If you send a PR, please include tests (flutter test) and run flutter pub run build_runner build so the generated code stays in sync.

0
likes
150
points
19
downloads

Publisher

unverified uploader

Weekly Downloads

Annotation driven proxies to add lightweight AOP style hooks to Flutter code.

Homepage

Documentation

API reference

License

MIT (license)

Dependencies

analyzer, build, flutter, glob, meta, source_gen

More

Packages that depend on flutter_aop