Injectfy: A Simple Dependency Injection Library for Dart

A minimalistic and easy-to-use dependency injection library for managing singletons and factories in Dart.

Features

  • Register singletons and factories to manage dependencies.
  • Automatically resolve and inject dependencies.
  • Easily unregister dependencies.
  • Cache frequently accessed instances for better performance.
  • Useful for both production and testing (with mock support).

Installation

Add the following dependency to your pubspec.yaml:

dependencies:
  injectfy: ^1.0.0

Then, run:

flutter pub get

Getting Started

Basic Usage

import 'package:injectfy/injectfy.dart';

void main() {
  // Registering a singleton
  Injectfy.registerSingleton<SomeService>(() => SomeService());

  // Registering a factory
  Injectfy.registerFactory<ClientsRepository>(() => ClientsRepositoryImpl());

  // Resolving dependencies
  final someService = Injectfy.get<SomeService>();
  final clientsRepository = Injectfy.get<ClientsRepository>();

  print(someService);  // Instance of SomeService
  print(clientsRepository);  // Instance of ClientsRepositoryImpl
}

Registering a Singleton

The singleton will be instantiated only once and reused every time it's requested.

class SomeService {
  void doSomething() => print("Doing something...");
}

void main() {
  // Registering a singleton
  Injectfy.registerSingleton<SomeService>(() => SomeService());

  // Resolving the singleton
  final service1 = Injectfy.get<SomeService>();
  final service2 = Injectfy.get<SomeService>();

  // Both variables will point to the same instance
  print(identical(service1, service2));  // Output: true
}

Registering a Factory

A factory allows creating a new instance of the dependency every time it is requested.

class UserRepository {}

void main() {
  // Registering a factory
  Injectfy.registerFactory<UserRepository>(() => UserRepository());

  // Resolving the factory
  final repo1 = Injectfy.get<UserRepository>();
  final repo2 = Injectfy.get<UserRepository>();

  // Different instances are created each time
  print(identical(repo1, repo2));  // Output: false
}

Automatically Resolving Dependencies

You can automatically resolve dependencies method if instance call.

class SomeService {
  final SomeDependency _someDependency;

  SomeService(this._someDependency);
}

void main() {
  final injectfy = Injectfy.instance;
  // Registering a dependency
  Injectfy.registerSingleton<SomeService>(() => SomeService(injectfy()));
}

Unregistering a Dependency

You can unregister a previously registered dependency if it's no longer needed.

void main() {
  Injectfy.registerSingleton<SomeService>(() => SomeService());

  // Unregistering the dependency
  Injectfy.instance.unregister<SomeService>();

  try {
    final service = Injectfy.get<SomeService>();
  } catch (e) {
    print(e);  // Output: Dependency of type SomeService not found. Please ensure it is registered before calling.
  }
}

Registering Mock Dependencies (Useful for Testing)

For testing purposes, you can register mock objects in place of actual dependencies.

void main() {
  final mockService = SomeService();
  Injectfy.registerMock<SomeService>(mockService);

  final service = Injectfy.get<SomeService>();
  print(service == mockService);  // Output: true
}

API Reference

Injectfy

Method Description
registerSingleton<T> Registers a singleton for the specified type T.
registerFactory<T> Registers a factory for the specified type T.
get<T> Resolves and returns the instance of type T.
call<T> Automatically resolves and returns the instance of type T.
unregister<T> Unregisters the dependency of type T.
registerMock<T> Registers a mock object in place of a real dependency. Useful for testing.

T Function()

This is the type of the factory function you provide when registering dependencies. It creates a new instance of the dependency when called.

Example of registerSingleton and registerFactory

  • Singleton:
Injectfy.registerSingleton<SomeService>(() => SomeService());

This ensures that only one instance of SomeService is created and reused each time it's requested.

  • Factory:
Injectfy.registerFactory<SomeService>(() => SomeService());

This creates a new instance of SomeService every time it's requested.

Testing

You can test your application by registering mock dependencies for testing purposes.

Example of Testing with Mocks

import 'package:test/test.dart';

void main() {
  test('Singleton works correctly', () {
    Injectfy.registerSingleton<SomeService>(() => SomeService());

    final service1 = Injectfy.get<SomeService>();
    final service2 = Injectfy.get<SomeService>();

    expect(identical(service1, service2), true);
  });

  test('Factory works correctly', () {
    Injectfy.registerFactory<UserRepository>(() => UserRepository("John"));

    final repo1 = Injectfy.get<UserRepository>();
    final repo2 = Injectfy.get<UserRepository>();

    expect(identical(repo1, repo2), false);
  });

  test('Mock registration works correctly', () {
    final mockService = SomeService();
    Injectfy.registerMock<SomeService>(mockService);

    final service = Injectfy.get<SomeService>();

    expect(service, mockService);
  });
}

Contributing

Contributions are welcome! Please open issues or submit pull requests on the GitHub repository.

License

This library is licensed under the MIT License. See the LICENSE file for details.

Libraries

injectfy