DI - DI

Buy Me A Coffee

Dart & Flutter Packages by DevCetra.com & contributors.

Pub Package MIT License


Summary

This package provides a pragmatic dependency injection (DI) system, coupled with service classes for seamless state management in Dart.

Features

  • Robust FutureOr support for handling both synchronous and asynchronous dependencies and callbacks.
  • Register dependencies by type and groupKey, enabling management of multiple dependencies of the same type.
  • Hierarchical DI with scoped dependencies through child containers.
  • Retrieve dependencies by runtime type or generic type.
  • Factory dependencies and lazy initialization for singleton dependencies.
  • Service classes that automatically handle cleanup when they are unregistered.

For a full feature set, please refer to the API reference.

Quickstart

Creating a DI container:

// Access the global DI instance anywhere in your app.
DI.global;

// Or create your own DI container.
final di = DI();

// Create nested child containers, useful for scoping dependencies in modular apps.
final scopedDi = di.child().child().child(groupKey: DIKey('moduleGroup'));

Registering Dependencies:

// Register the answer to life, the universe and everything.
di.register<int>(42);

// Register an integer under a specific groupKey, useful in environments like testing.
di.register<int>(0, groupKey: DIKey.testGroup);

// Register a Future as a dependency.
di.register(Future.value('Hello, DI!'));
print(di.get<String>()); // Instance of 'Future<String>'

// Register a factory or lazy singleton constructor.
int n = 0;
di.registerLazy<int>(() => n + 1);
di.registerLazy(() => DateTime.now());

Unregistering Dependencies:

// Unregister a specific type.
  di.unregister<int>();
  di.unregisterT(int);

// Unregister all dependencies, resetting the container.
  di.unregisterAll();

// Unregister child containers when they’re no longer needed.
  di.unregisterChild();

Getting Dependencies:

// Retrieve a registered integer dependency.
print(di<int>()); // 42
Type intType = int;
print(di.getT(intType)); // 42

// Retrieve a dependency registered under a specific groupKey.
print(di.get<int>(groupKey: DIKey('testGroup'))); // 0

// Handle asynchronous dependencies.
final greeting = await di.get<String>();
print(greeting); // Hello, DI!

// Retrieve a factory-registered dependency.
final now = di.getFactory<DateTime>();
print(now); // Current timestamp
await Future.delayed(Duration(seconds: 1));
final now1 = di.getFactoryT(DateTime);
print(now1);  // A second later

Real-World Example - UserService:

final class UserService extends Service {
  final _userName = ValueNotifier<String>('Guest');

  // Getter for the UI to consume.
  ValueListenable<String> get userName => _userName;

  @override
  Future<void> onInitService(_) async {
    // Simulate loading user data.
    await Future.delayed(Duration(seconds: 2));
    _userName.value = 'John Doe';
  }

  @override
  void onDispose() {
    _userName.dispose(); // Cleanup resources.
  }
}

// Register the service.
di.registerService(UserService.new);

// Access the service.
final userService = await di.getServiceSingleton<UserService>();
print(userService.userName.value); // John Doe

Handling Synchronous and Asynchronous Services:

Service with Synchronous Initialization and Asynchronous Disposal

final class SyncInitAsyncDisposeService extends Service {
  // di<SyncInitAsyncDisposeService>() will not return a Future.
  @override
  void onInitService(_) {
    // Synchronous initialization logic
  }

  // di.unregister<SyncInitAsyncDisposeService>() will return a Future.
  @override
  Future<void> onDispose() async {
    // Asynchronous cleanup logic
  }
}

// Register and use the service.
di.registerService(SyncInitAsyncDisposeService.new);
final service = di.getServiceSingleton<SyncInitAsyncDisposeService>();
await di.unregister<SyncInitAsyncDisposeService>();

Service with Asynchronous Initialization and Synchronous Disposal

final class AsyncInitSyncDisposeService extends Service {
  // di<AsyncInitSyncDisposeService>() will not return a Future.
  @override
  Future<void> onInitService(_) async {
    await Future.delayed(Duration(seconds: 3));
    // Asynchronous initialization logic
  }

  // di.unregister<AsyncInitSyncDisposeService>() will not return a Future.
  @override
  void onDispose() {
    // Synchronous cleanup logic
  }
}

// Register and use the service.
di.registerService(AsyncInitSyncDisposeService.new);
final service = await di.getServiceSingleton<AsyncInitSyncDisposeService>();
di.unregister<AsyncInitSyncDisposeService>();

Getting the State for Debugging:

// Print the current state of the DI container.
print(di.registry.state);

// Check if a specific type is registered.
print(di.isRegistered<int>()); // true

Installation

Use this package as a dependency by adding it to your pubspec.yaml file (see here).


Contributing and Discussions

This is an open-source project, and we warmly welcome contributions from everyone, regardless of experience level. Whether you're a seasoned developer or just starting out, contributing to this project is a fantastic way to learn, share your knowledge, and make a meaningful impact on the community.

Ways you can contribute:

  • Buy me a coffee: If you'd like to support the project financially, consider buying me a coffee. Your support helps cover the costs of development and keeps the project growing.
  • Share your ideas: Every perspective matters, and your ideas can spark innovation.
  • Report bugs: Help us identify and fix issues to make the project more robust.
  • Suggest improvements or new features: Your ideas can help shape the future of the project.
  • Help clarify documentation: Good documentation is groupKey to accessibility. You can make it easier for others to get started by improving or expanding our documentation.
  • Write articles: Share your knowledge by writing tutorials, guides, or blog posts about your experiences with the project. It's a great way to contribute and help others learn.

No matter how you choose to contribute, your involvement is greatly appreciated and valued!


Chief Maintainer:

📧 Email Robert Mollentze at robmllze@gmail.com

Dontations:

If you're enjoying this package and find it valuable, consider showing your appreciation with a small donation. Every bit helps in supporting future development. You can donate here:

https://www.buymeacoffee.com/robmllze


License

This project is released under the MIT License. See LICENSE for more information.

Libraries

df_di
A pragmatic dependency injection (DI) system, coupled with service classes for seamless state management in Dart.