catalyst_builder 2.2.0 copy "catalyst_builder: ^2.2.0" to clipboard
catalyst_builder: ^2.2.0 copied to clipboard

outdated

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

GitHub license Pub

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 {}
Lifetime Description
Singleton The instance is stored in the provider. You'll always receive the same instance.
Transient Everytime 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() {}

Registering services at runtime (v2.1.0+) #

Sometimes you need to register services at runtime. For this, you can use the register method:

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

  provider.register(
        (provider) => MySelfRegisteredService(provider.resolve()),
  );

  var selfRegistered = provider.resolve<MySelfRegisteredService>();
  selfRegistered.sayHello();
}

Create a sub-provider with additional services / parameters (v2.2.0+) #

In some cases, you want to register services or parameters only for a specific service. The services or parameters should not be placed inside the global service provider. To solve this problem, you can use the enhance method, which accepts an array of additional services and a map of additional parameters. These are only available in the returned ServiceProvider.

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

  var newProvider = provider.enhance(
    parameters: {
      'foo': 'overwritten',
    },
    services: [
      LazyServiceDescriptor(
            (p) => MySelfRegisteredService(p.resolve(), p.parameters['foo']),
        const Service(exposeAs: SelfRegisteredService),
      ),
    ],
  );

  var mySvc = newProvider.resolve<SelfRegisteredService>();
  expect(mySvc.foo, equals('overwritten'));
}

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+)
8
likes
0
pub points
59%
popularity

Publisher

verified publishermintware.de

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

Repository (GitHub)
View/report issues

License

unknown (LICENSE)

Dependencies

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

More

Packages that depend on catalyst_builder