catalyst_builder 1.3.0
catalyst_builder: ^1.3.0 copied to clipboard

A dependency injection provider builder for dart. Easy to use and lightweight af.

Catalyst Builder #

A dependency injection provider builder for dart.

Background #

Since Catalyst is only for Dart and Flutter Catalyst supports Flutter, but a mess to configure I decided to do something cooler.

Catalyst Builder is a dependency injection provider builder for both, Dart and Flutter. It's easy to use and dependency injection is almost done automatically. You only have to decorate your services with @Service and the build_runner will create a service provider for you.

Installation #

Warning, I am offering this package at an early stage. It may not perform as expected.

Add this to your pubspec.yaml and run pub get or flutter pub get:

dependencies:
  catalyst_builder: ^1.0.0

dev_dependencies:
  build_runner: ^2.0.1

Usage #

Decorate your services with @Service:

@Service()
class MyService {}

Then run pub run build_runner build or flutter pub run build_runner build.
You can also run pub run build_runner watch or flutter pub run build_runner watch to update the provider automatically as you perform changes.

You should see a new file service_provider.dart after the build. Import it to use the service provider.

import 'default_service_provider.dart';

void main() {
  var provider = DefaultServiceProvider();
  provider.boot();

  var myService1 = provider.resolve<MyService>();

  // Inferred types are also supported
  MyService myService2 = provider.resolve();
}

Advanced usage #

Service lifetime #

You can specify the lifetime of the service in the annotation.

@Service(
  lifetime: ServiceLifetime.singleton, // default
)
class SingletonService {}

@Service(
  lifetime: ServiceLifetime.transient,
)
class TransientService {}
LifetimeDescription
SingletonThe instance is stored in the provider. You'll always receive the same instance.
TransientEverytime you call resolve or tryResolve you'll receive a fresh instance.

Exposing #

You can expose the service with another type in the service provider. This is useful if you want to depend on an interface instead of an implementation.


abstract class Transport {
  void transferData(String data);
}

@Service(exposeAs: Transport)
class ConsoleTransport implements Transport {
  @override
  void transferData(String data) {}
}

abstract class ChatProvider {
  Transport transport;

  Future<void> sendChatMessage(String message);
}

@Service(exposeAs: ChatProvider)
class CoolChatProvider implements ChatProvider {
  @override
  Transport transport;

  CoolChatProvider(this.transport);

  @override
  Future<void> sendChatMessage(String message) {}
}

void main() {
  var provider = DefaultServiceProvider();

  var chatService = provider.resolve<ChatProvider>();

  print(chatService is CoolChatProvider); // true
  print(chatService.transport is ConsoleTransport); // true
}

Preloading services #

By default, a service will be instantiated when it's requested. In some cases you need to create an instance after booting the service provider. For example a database connection or a background service that checks the connectivity.

To preload services you can use the @Preload annotation. This annotation is only available for singleton services. Example:

@Service()
@Preload()
class MyService {
  MyService() {
    print('Service was created');
  }
}

void main() {
  ServiceProvider provider;
  provider.boot(); // prints "Service was created" 
  provider.resolve<MyService>(); // Nothing printed
}

Inject parameters by name #

The service provider will try to lookup values for non-existent services in the parameters map. By default, the lookup is done based on the name of the parameter. For example:

@Service()
class MyService {
  String username;

  MyService(this.username);
}

void main() {
  ServiceProvider provider;
  provider['username'] = 'Test';
  provider.boot();

  var myService = provider.resolve<MyService>();

  print(myService.username); // Test 
}

In many cases you've generic terms like 'key' or 'name'. If you've many services with the same name, you'll get in trouble.

You can use the @Parameter('param name') annotation to solve this problem:

@Service()
class MyService {
  String username;

  MyService(@Parameter('senderUserName') this.username);
}

void main() {
  ServiceProvider provider;
  provider['senderUserName'] = 'Test 2';
  provider.boot();

  var myService = provider.resolve<MyService>();

  print(myService.username); // Test 2 
}

Service Maps (v1.2.0+) #

If you depend on a third party package, you can not easily add the @Service to classes inside the package. For this, there is a @ServiceMap annotation, that accepts a map of services.

@ServiceMap(services: {
  ManuallyWiredServiceImplementation: Service(
    lifetime: ServiceLifetime.transient, // optional
    exposeAs: ManuallyWiredService, // optional
  ),
})
void main() {}

Configuration #

To customize the builder, create a build.yaml beside your pubsepc.yaml with this content:

targets:
  $default:
    auto_apply_builders: true
    builders:
      catalyst_builder|buildServiceProvider:
        options:
          providerClassName: 'DefaultServiceProvider' # class name of the provider
          outputName: 'default_service_provider.dart' # file name of the provider. (Can also contain /)
          includePackageDependencies: false # True if services from dependencies should be added to your service provider (v1.1.0+)
3
likes
130
pub points
60%
popularity

Publisher

mintware.de

A dependency injection provider builder for dart. Easy to use and lightweight af.

Homepage

Documentation

API reference

License

MIT (LICENSE)

Dependencies

analyzer, build, build_runner_core, code_builder, dart_style, glob, path

More

Packages that depend on catalyst_builder