Dependency Injection with Flutter

You know dependency injection? You love dependency injection!
Unfortunately, Flutter don’t provide any built-in DI feature.

For this, I created last year the flutter_catalyst package with is a port of the catalyst package which is only supported for D…

You know dependency injection? You love dependency injection!
Unfortunately, Flutter don’t provide any built-in DI feature.

For this, I created last year the flutter_catalyst package with is a port of the catalyst package which is only supported for Dart native.

flutter_catalyst was a good starting point for me to implement DI in my Flutter apps but in large projects it’s a mess to configure.

In the last two months I created a new package catalyst_builder which supports all platforms and is easy to configure.

This package uses the build_runner which performs tasks when you run it.
catalyst_builder has a build_runner task that reads annotations from your dart files and generate a service provider for DI.



Setup

Run flutter pub add catalyst_builder or add the package to your pubspec.yaml

# pubspec.yaml
dependencies:
  catalyst_builder: ^1.0.1

Since we use the build_runner you need to add this to your dev_dependencies:

# pubspec.yaml

dev_dependencies:
  build_runner: ^2.0.4

Create a build.yaml beside your pubspec.yaml. This file contains the configuration for the service provider (output file name and provider class name)

targets:
  $default:
    auto_apply_builders: true
    builders:
      catalyst_builder|buildServiceProvider:
        options:
          providerClassName: 'AppServiceProvider'
          outputName: 'app_service_provider.dart'

Run flutter pub get to install the packages

Now run flutter pub pub run build_runner watch --delete-conflicting-outputs which watches for changes and create the service provider dart file



Usage

You can declare every class as a service with the @Service annotation from the catalyst_builder package:

@Service()
class MyService {
   final String username = 'TestUser';
}

Ensure that flutter pub pub run build_runner watch --delete-conflicting-outputs is running. You should see now a app_service_provider.dart file that you can include in your project.

Create the service provider and retrieve the service from it:

var myProvider = AppServiceProvider();
myProvider.boot(); // This is important

var myService = myProvider.resolve<MyService>();
//  also works: MyService myService = myProvider.resolve();
print(myService.username); // prints TestUser

Thats all for a simple service.



Nested services a.k.a. Dependency Injection

In the real world you’ve services that depend on other services that depend on configuration parameters etc.

catalyst_builder also supports this scenario:

@Service()
class ServiceA {}

@Service()
class ServiceB {
    final ServiceA serviceA;
    ServiceB(this.ServiceA);
}

class ServiceC {} 

@Service()
class ServiceD {
    final ServiceC serviceC;
    ServiceD(@Parameter('otherService') this.ServiceC);
}


void main() {
    var myProvider = AppServiceProvider();
    myProvider.boot();

    // This works:
    var serviceB = myProvider.resolve<ServiceB>();

    // This not because ServiceC is not known as a service:
    var serviceD = myProvider.resolve<ServiceD>();

    // But this works, because the provider contains a 
    // parameter with the same name as the required argument:
    myProvider.parameters['serviceC'] = ServiceC();
    var serviceD = myProvider.resolve<ServiceD>();

    // This also works, because the provider contains a 
    // parameter with the name which is given in the 
    // Parameter annotation.
    myProvider.parameters['otherService'] = ServiceC();
    var serviceD = myProvider.resolve<ServiceD>();
}



Service lifetime

By default, all services are singeltons. You will get the same instance everytime you call resolve<T>.

You can specify the lifetime with the lifetime argument in the @Service annotation:

/// Transient services are always recreated
@Service(lifetime: ServiceLifetime.transient)
class TransientService {}

/// Default is singleton
@Service(lifetime: ServiceLifetime.singleton)
class SingletonService {}



Code Against Interfaces, Not Implementations.

Every programmer would tell you that you shouldn’t depend on implementations but interfaces.

Also this is possible with the exposeAs Property in the @Service annotation. Expose as will return the implementation if you request the type that you provide as exposeAs. This also works for nested services.

// interface
abstract class BaseService {}

// implementation
@Service(exposeAs: BaseService)
class MyService implements BaseService {}



Preloading services

Some services are background services (connectivity checks for example).
Decorate this services with @Preload() to create a instance of the service while boot()-ing the provider.

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

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



Flutter specific tips:

  • Screens (widgets) should be always transient services.
  • You can use resolve<T> in the router:
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      initialRoute: '/',
      routes: {
        '/': (_) => container.resolve<HomeScreen>(),
      },
    );
  }
}

Hope you like and use the package 😉


Print Share Comment Cite Upload Translate
APA
Julian Finkler | Sciencx (2024-03-28T08:36:26+00:00) » Dependency Injection with Flutter. Retrieved from https://www.scien.cx/2021/06/18/dependency-injection-with-flutter/.
MLA
" » Dependency Injection with Flutter." Julian Finkler | Sciencx - Friday June 18, 2021, https://www.scien.cx/2021/06/18/dependency-injection-with-flutter/
HARVARD
Julian Finkler | Sciencx Friday June 18, 2021 » Dependency Injection with Flutter., viewed 2024-03-28T08:36:26+00:00,<https://www.scien.cx/2021/06/18/dependency-injection-with-flutter/>
VANCOUVER
Julian Finkler | Sciencx - » Dependency Injection with Flutter. [Internet]. [Accessed 2024-03-28T08:36:26+00:00]. Available from: https://www.scien.cx/2021/06/18/dependency-injection-with-flutter/
CHICAGO
" » Dependency Injection with Flutter." Julian Finkler | Sciencx - Accessed 2024-03-28T08:36:26+00:00. https://www.scien.cx/2021/06/18/dependency-injection-with-flutter/
IEEE
" » Dependency Injection with Flutter." Julian Finkler | Sciencx [Online]. Available: https://www.scien.cx/2021/06/18/dependency-injection-with-flutter/. [Accessed: 2024-03-28T08:36:26+00:00]
rf:citation
» Dependency Injection with Flutter | Julian Finkler | Sciencx | https://www.scien.cx/2021/06/18/dependency-injection-with-flutter/ | 2024-03-28T08:36:26+00:00
https://github.com/addpipe/simple-recorderjs-demo