Dependency Injection (DI) In Flutter

Dependency Injection (DI) In Flutter :

  • Supplies your objects with other objects that they depend on.
  • Three forms of the accepted dependency injection in Flutter.
  1. InheritedWidgets
  2. get_it
  3. provider

For example :

classLoginService {
Apiapi;
}

LoginService depends on the Api object.The usual way to get to a dependency into a class is through the constructor.

Dependency Injection (DI) In Flutter

Inherited Widget

  • Inherited widget effectively allows you to provide access, through the BuildContext, to all it’s properties, to every widget in it’s subtree. It’s common in Flutter and is used for the Theme, MediaQueries and everything else the base app provides. This is how an empty InheritedWidget looks.
import'package:flutter/material.dart';

classInheritedInjectionextendsInheritedWidget{
final Widget child;

InheritedInjection({Key key,this.child}):super(key: key, child: child);

staticInheritedInjectionof(BuildContext context){
return(context.inheritFromWidgetOfExactType(InheritedInjection)asInheritedInjection);
}

@override
  bool updateShouldNotify(InheritedInjectionoldWidget){
returntrue;
}
}

Usage :

  • The way inherited widgets are used is by wrapping the tree you want in the inherited widget. we want this widget to be supplied to our entire app so we’ll wrap the MaterialApp with the InheritedInjection widget.
classMyAppextendsStatelessWidget{
@override
  Widget build(BuildContext context){
returnInheritedInjection(
      child:MaterialApp(
          title:'Flutter Demo',
          theme:ThemeData(
primarySwatch:Colors.blue,
),
          home:HomeView()),
);
}
}
  • The way we access the inherited widget in our code is by using the .of call and passing the context. We can now update our HomeView and remove the AppInfo passed through the constructor as well as the class variable we kept. We can now make our HomeView look like this.
classHomeViewextendsStatelessWidget{
HomeView({Key key}):super(key: key);

@override
  Widget build(BuildContext context){
varappInfo=InheritedInjection.of(context).appInfo;
returnScaffold(
      body:Center(
        child:Text(appInfo.welcomeMessage),
),
);
}
}
  • Now anywhere in the app where you want to use your AppInfo object all you’ll do is.
varappInfo = InheritedInjection.of(context).appInfo;

Pros

  • This is how everything in Flutter is built.
  • Forces one directional data flow.

Cons

  • Boilerplate around instance tracking. Meaning when you want a new instance everytime you request the type you have to set that up. The same with if you want a singleton.
  • Injecting into objects where the context is not available is almost impossible. You have to clutter up the InheritedWidget itself with all the setup and manually inject objects where the context is not available.
  • Very verbose

Get_it

  • No need to pass anything constructors just to access the data.
  • Get it is what is known as a simple service locator. Traditionally you register your your types against an interface and provide the concrete implementation to it. This way you benefit from developing against an interface which also makes unit testing easier because you can provide test specific implementations.

pubspec

get_it

GetIt locator = GetIt.instance;
  • If you really, REALLY need more than one GetIt instance please set allowMultipleInstancesto true to signal you know what you are doing.

GetIt locator = GetIt.asNewInstance();

GetIt.allowMultipleInstances=true;

  • Factory: When you request an instance of the type from the service provider you’ll get a new instance every time.
  • Singleton: Singletons can be registered in two ways. Provide an implementation upon registration or provide a lamda that will be invoked the first time your instance is requested (LazySingleton). The Locator keeps a single instance of your registered type and will always return you that instance.

Pros

  • Can request type anywhere using the global locator
  • Instance tracking is automatically taken care of by registering types as Factories or Singleton
  • Can register types against interfaces and abstract your architecture from the
  • implementation details
  • Compact setup code with minimal boiler plate

Cons

  • Disposing is not a top priority in the framework
  • Loose coding guidelines that can lead to badly written software
  • Global object usage which is the start of multi-directional data flow which is the opposite of what Flutter promotes

Provider

  • It has specialised provider types like StreamProvider, ChangeNotifierProvider, ListenableProvider that can be used to architect your entire app.

pubspec

provider

  • Similarly to the InheritedWidget, the Provider injected value is only available in it’s subtree. To get the value everywhere we will wrap our entire app in a provider.
classMyAppextendsStatelessWidget{
@override
  Widget build(BuildContext context){
returnProvider(
      builder:(context)=>AppInfo(),
      child:MaterialApp(
        title:'Flutter Demo',
        theme:ThemeData(
primarySwatch:Colors.blue,
),
        home:Scaffold(),
),
);
}
}
  • When you want to access the value from the provider all you have to do it.
@override
Widget build(BuildContext context){
varappInfo=Provider.of<AppInfo>(context);
returnScaffold(
body:Center(
child:Text(appInfo.welcomeMessage),
),
);
}

Pros

  • Great for StateManagement, my number one choice
  • Forces one directional data flow
  • The SpecialtyProviders removes loooots of boilerplate code
  • Using the Consumers for your providers is neat and clean
  • Supplies a way to dispose all provider objects

Cons

  • Injecting into objects that doesn’t have a BuildContext requires lots of ProxyProvider stringing

home_view.dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

import 'login_service.dart';
import 'locator.dart';
import 'user_service.dart';

class HomeView extends StatelessWidget {
  HomeView({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: MyList()
    );
  }
}

class MyList extends StatelessWidget {
  const MyList({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return PostItem();
  }
}

class PostItem extends StatelessWidget {
  const PostItem({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return PostMenu();
  }
}

class PostMenu extends StatelessWidget {
  const PostMenu({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return PostAction();
  }
}

class PostAction extends StatelessWidget {
  const PostAction({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return LikeButton();
  }
}

class LikeButton extends StatelessWidget {
  const LikeButton({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // We have access to it anywhere in the app with this simple call
    var appInfo = Provider.of<LoginService>(context);

    var userService = locator<UserService>();
    var appInfoService = locator<LoginService>();
    return Container(
      child: Text(userService.userName + appInfoService.welcomeMessage),
    );
  }
}

login_service.dart

class LoginService {
  String get welcomeMessage => 'Hello from FilledStacks';
}

user_service.dart

class UserService {
  String userName = "filledstacks";
}

locator.dart

import 'package:get_it/get_it.dart';

import 'login_service.dart';
import 'home_view.dart';
import 'user_service.dart';
GetIt locator = GetIt.asNewInstance();

void setupLocator() {

  GetIt.allowMultipleInstances=true;

  locator.registerSingleton(UserService());
  locator.registerFactory(() => LoginService());
}

pubspec.yaml

name: flutter_injection
description: A new Flutter application.
version: 1.0.0+1

environment:
  sdk: ">=2.1.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^0.1.2
  get_it:
  provider:

dev_dependencies:
  flutter_test:
    sdk: flutter

flutter:
  uses-material-design: true

main.dart

import 'package:flutter_injection/home_view.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'login_service.dart';
import 'locator.dart';

void main() {
  setupLocator();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Provider(
      builder: (context) => LoginService(),
      child: MaterialApp(
        title: 'Dependency Injection',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: Scaffold(
          body: HomeView(),
        ),
      ),
    );
  }
}

The flutter tutorial  is a website that bring you the latest and amazing resources of code. All the languages codes are included in this website. The languages like flutter, android, java,kotlin etc.with the help of this languages any user can develop the beautiful application

For more information about Flutter. visit www.fluttertutorial.in