Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
June 18, 2021 05:57 am GMT

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 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.yamldependencies:  catalyst_builder: ^1.0.1

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

# pubspec.yamldev_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 importantvar 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.

// interfaceabstract 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 ;-)


Original Link: https://dev.to/devtronic/dependency-injection-with-flutter-54pl

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To