How to use dependecy injection in flutter?

Viewed 8592

I wan to use di. in flutter and I add this https://pub.dartlang.org/packages/di packages my project and I started to read this https://webdev.dartlang.org/angular/guide/dependency-injection article, but I don't fully understand.

So it's ok: use @Injectable() annotation on services class(e.g: MyServices), but how to inject other class? For example I would like to similar:

class MyClass{
   //some variable
   var asd = MyService.asd; //need inject var.
                            //maybe use injector.get(MyService).asd; 
                            //but how add injector? (I don't add across constructor)

   MyService.oneMethod;//need inject method
}

main(){
    var injector = new ModuleInjector([new Module()
       ..bind(MyService)
    ]);
}

The point is, I don't want to use a constructor. I want to directly use injector. This is possible in flutter/dart?

6 Answers

Hello what about something like this? Very simple implementation, Injector itself is singleton and also added classes into it. Of course can be extended very easily. If you are looking for something more sophisticated check this package: https://pub.dartlang.org/packages/flutter_simple_dependency_injection

void main() {  
  Injector injector = Injector();
  injector.add(() => Person('Filip'));
  injector.add(() => City('New York'));

  Person person =  injector.get<Person>(); 
  City city =  injector.get<City>();

  print(person.name);
  print(city.name);
}

class Person {
  String name;

  Person(this.name);
}

class City {
  String name;

  City(this.name);
}


typedef T CreateInstanceFn<T>();

class Injector {
  static final Injector _singleton =  Injector._internal();
  final _factories = Map<String, dynamic>();

  factory Injector() {
    return _singleton;
  }

  Injector._internal();

  String _generateKey<T>(T type) {
    return '${type.toString()}_instance';
  }

  void add<T>(CreateInstanceFn<T> createInstance) {
    final typeKey = _generateKey(T);
    _factories[typeKey] = createInstance();
  }

  T get<T>() {
    final typeKey = _generateKey(T);
    T instance = _factories[typeKey];
    if (instance == null) {
      print('Cannot find instance for type $typeKey');
    }

    return instance;
  }
}

I was facing the same issue here so I created a dart package specifically for Flutter. It relies on factory methods rather than the mirrors api so it works in flutter. While still using a familiar IOC pattern. Hope this helps!

https://pub.dartlang.org/packages/flutter_simple_dependency_injection

import 'package:flutter_simple_dependency_injection/injector.dart';

void main() {
  final injector = Injector.getInjector();
  injector.map(Logger, (i) => new Logger(), isSingleton: true);
  injector.map(String, (i) => "https://api.com/", key: "apiUrl");
  injector.map(SomeService, (i) => new SomeService(i.get(Logger), i.get(String, "apiUrl")));

  injector.get(SomeService).doSomething();
  // passing in the [SomeService] as a generic parameter strongly types the return object.
  injector.get<SomeService>(SomeService).doSomething();
}

class Logger {
  void log(String message) => print(message); 
}

class SomeService {
  final Logger _logger;
  final String _apiUrl;

  SomeService(this._logger, this._apiUrl);

  void doSomething() {
    _logger.log("Doing something with the api at '$_apiUrl'");
  }
}

This package ioc_container is a Dart Ioc Container. Technically, it's not an injector because it doesn't automate injection with anything like reflection. However, it does simplify and organize the process of injecting dependencies within your factories. Here is the code.

Code here

import 'package:meta/meta.dart';

class ServiceDefinition<T> {
  bool isSingleton;
  T Function(IocContainer container) factory;

  ServiceDefinition(this.isSingleton, this.factory);
}

class IocContainer {
  @visibleForTesting
  final Map<Type, ServiceDefinition> serviceDefinitionsByType;
  @visibleForTesting
  final Map<Type, Object> singletons;

  IocContainer(this.serviceDefinitionsByType, this.singletons);

  ///Get an instance of your dependency
  T get<T>() => singletons.containsKey(T)
      ? singletons[T] as T
      : serviceDefinitionsByType.containsKey(T)
          ? (serviceDefinitionsByType[T]!.isSingleton
              ? singletons.putIfAbsent(
                  T,
                  () =>
                      serviceDefinitionsByType[T]!.factory(this) as Object) as T
              : serviceDefinitionsByType[T]!.factory(this))
          : throw Exception('Service not found');
}

///A builder for creating an [IocContainer].
class IocContainerBuilder {
  final Map<Type, ServiceDefinition> _serviceDefinitionsByType = {};

  ///Throw an error if a service is added more than once. Set this to true when
  ///you want to add mocks to set of services for a test.
  final bool allowOverrides;

  IocContainerBuilder({this.allowOverrides = false});

  ///Add a factory to the container.
  void addServiceDefinition<T>(

      ///Add a factory and whether or not this service is a singleton
      ServiceDefinition<T> serviceDefinition) {
    if (_serviceDefinitionsByType.containsKey(T)) {
      if (allowOverrides) {
        _serviceDefinitionsByType.remove(T);
      } else {
        throw Exception('Service already exists');
      }
    }

    _serviceDefinitionsByType.putIfAbsent(T, () => serviceDefinition);
  }

  ///Create an [IocContainer] from the [IocContainerBuilder].
  ///This will create an instance of each singleton service and store it
  ///in an immutable list unless you specify [isLazy] as true.
  IocContainer toContainer(
      {

      ///If this is true the services will be created when they are requested
      ///and this container will not technically be immutable.
      bool isLazy = false}) {
    if (!isLazy) {
      final singletons = <Type, Object>{};
      final tempContainer = IocContainer(_serviceDefinitionsByType, singletons);
      _serviceDefinitionsByType.forEach((type, serviceDefinition) {
        if (serviceDefinition.isSingleton) {
          singletons.putIfAbsent(
              type, () => serviceDefinition.factory(tempContainer));
        }
      });

      return IocContainer(
          Map<Type, ServiceDefinition>.unmodifiable(_serviceDefinitionsByType),
          Map<Type, Object>.unmodifiable(singletons));
    }
    return IocContainer(
        Map<Type, ServiceDefinition>.unmodifiable(_serviceDefinitionsByType),
        <Type, Object>{});
  }
}

extension Extensions on IocContainerBuilder {
  ///Add a singleton object dependency to the container.
  void addSingletonService<T>(T service) =>
      addServiceDefinition(ServiceDefinition(true, (i) => service));

  void addSingleton<T>(T Function(IocContainer container) factory) =>
      addServiceDefinition(ServiceDefinition(true, factory));

  void add<T>(T Function(IocContainer container) factory) =>
      addServiceDefinition(ServiceDefinition(false, factory));
}

Related