• Dependency Injection is a way of making the dependencies of an object available via another object, and these dependencies are usually known as services.

    These services can be blocks of code, containing different functionalities that can be easily reused in different parts of your project.

    Dependencies are functionalities required by parts of your project to run successfully. In many cases, you’d import files and use classes or methods inside them, that way the Classes using those imports are dependent on them.

    • DI is helpful for a better development experience.
    • Adding a bit of structure to your projects.
    • Great for mocking objects when writing tests.

    In dart, the most basic way to handle dependency injection is bypassing services to a class through the constructor.

    class MyClass {

    //MyClass is dependent on OtherClass
    OtherClass service;

    MyClass(this.service)
    }

    This method might not seem bad at first (there’s nothing wrong with it), but it becomes very problematic to pass values deep down widget trees and things can get messy quickly when handling many dependencies.

    Dependency Injection Options For Flutter

    There are many DI methods and packages we can use in our flutter apps and we’ll be taking a look at a few in this post:

    • Inherited Widget — comes with flutter out of the box.
    • IOC dart package — an easy-to-use package available on pub.dev.
    • get It dart package — can be found here on pub.dev.

    Inherited Widgets

    Inherited Widgets allow you to pass data down widget trees easily. An example of an inherited widget is shown:

    import 'package:flutter/material.dart';

    class InheritedHomeWidget extends InheritedWidget {
    InheritedHomeWidget({Key key, this.child}) : super(key: key, child: child);

    final Widget child;

    static InheritedHomeWidget of(BuildContext context) {
    return (context.inheritFromWidgetOfExactType(InheritedHomeWidget)
    as InheritedHomeWidget);
    }

    @override
    bool updateShouldNotify(InheritedHomeWidget oldWidget) {
    return true;
    }
    }

    This is an auto-generated Inherited Widget from the flutter VS code extension, let’s take a look at what the code contains.

    • A widget called child (can be named anything) is passed in, this is where you put your sub widget tree.
    • The next block of code looks up the widget tree, finds the closest InheritedWidget , and registers the BuildContext with that widget so that it can rebuild the tree when that widget is changed in any way.
    • updateShouldNotify() returns a boolean that states whether the subtree widgets should be rebuilt when a change occurs to the InheritedWidget.

    Use In The Widgets

    To make your dependencies available in this widget tree, you can maybe send the dependencies via the Inherited Widget’s constructor, or use a getter, or any way that works for your use case, you will still be able to use it as deep in the tree as you want.

    InheritedHomeWidget({Key key, this.child, this.homeText})
          : super(key: key, child: child);
    
    final Widget child;
    //add the new dependency
    final String homeText;

    Wrap your entire widget tree with the Inherited Widget you created, if you want your dependencies to be propagated down the whole tree. In this case, I’ll simply pass a text into the constructor, this would likely be an instance of a service.

    class FunApp extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
    return InheritedHomeWidget(
    homeText: 'This is printed on the screen',
    child: MaterialApp(
    title: 'Inherited Widget',
    home: FunHomePage(),
    ),
    );
    }
    }

    You can then Use that text down the tree by calling .of(context) on the inherited widget, like so:

    class FunHomePage extends StatelessWidget {
      const FunHomePage({Key key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        var homeText = InheritedHomeWidget.of(context).homeText;
        return Container(
          color: Colors.white,
          child: Center(
            child: Text(homeText),
          ),
        );
      }
    }

    IOC package

    IOC stands for Inversion Of Control, and it’s a pattern that states that services/dependencies should be created in a separate class, and is the underlying principle behind Dependency Injection.

    To use this package, you first need to add it to your pubspec.yaml file like this:

    dependencies:
      ioc: ^0.3.0

    Next, create a dart file to bind all your services. I call mine ioc_locator.dart, you can call it whatever you want. The content of this file is:

    import 'package:ioc/ioc.dart';
    
    void iocLocator() {
      Ioc().bind('service1', (ioc) => InfoService());
    } 
    • Here, I’m binding the class(service) InfoService to a string "service1" which acts sort of like a key, and it is dynamic.Ioc().bind(key, (ioc) => Service());
    • The InfoService is another very simple class that just has a string in it called infoText.

    Now, you want to run the iocLocator() function in the main file before your app runs, so import it and add it above your runApp() function like so:

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

    You can then use the data from that service in your widgets and files, I’ll attempt to print out the value of infoText.

    import 'package:flutter/material.dart';
    import 'package:ioc/ioc.dart';
    
    class IocView extends StatelessWidget {
      final infoService = Ioc().use('service1');
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Center(
            child: Text(infoService.infoText),
          ),
        );
      }
    }

    Get It

    Get it is one of the more popular ways to handle DI in flutter apps. You can also register your dependencies as singletons, lazy singletons or factory.

    • A singleton will always return the same instance of that service.
    • A lazy Singleton will create the object on the first instance when it is called. This is useful when you have a service that takes time to start and should only start when it is needed.
    • A Factory will return a new instance of the service anytime it is called.

    Usage

    First, add get_it to your pubspec.yaml file:

    get_it: ^3.1.0

    Now, you can create a file to register all your objects, I’ll call mine service_locator.dart, and place a single function in it called getServices().

    import 'package:get_it/get_it.dart';
    
    GetIt getIt = GetIt.instance;
    
    void getServices() {
      getIt.registerFactory(() => InfoService());
      getIt.registerSingleton(() => MyService());
      getIt.registerLazySingleton(() => OtherService());
    }

    As of version 2.0.0, get_it was remade into a singleton, you now get the same instance of get_it with GetIt.instance or GetIt.I.

    Now place the getServices() function before running the app in your main.dart file:

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

    To use the registered objects in your widgets, simply call locator.get<Type>() or locator<Type>(), where type, in our case, is InfoService.

    class InfoView extends StatelessWidget {
      final infoService = getIt.get<InfoService>();
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Center(
            child: Text(infoService.infoText),
          ),
        );
      }
    }

     

0 Years in
Operation
0 Loyal
Clients
0 Successful
Projects

Words from our clients

 

Tell Us About Your Project

We’ve done lot’s of work, Let’s Check some from here