Flutter fast dependency injection!

Rod Brown
Flutter Community
Published in
6 min readMay 27, 2020

--

Plus some help separating business logic and making your code a little simpler…

Photo by Max Bender on Unsplash

How will this help?

It’s fast to add, easy to use, and comes with extras

Dependency Injection solutions handle some of the big jobs in our Apps, and they typically have a few things in common:

  • First, they instantiate a single version of a service or controller that can be shared throughout our code
  • Second, they require a mechanism to locate or find that service or controller from anywhere within our code.

For example, an AuthService might be needed in the Login, Signup, and Profile pages of our App. To enable Dependency Injection, we would typically need to create our AuthService before any of those pages need it, and then those pages will use some kind of locator or finder to access methods like getuser() on the AuthService.

Our Dependency Injection solution skips the need to write that locator/finder and throws in some additional benefits that might also speed up your App development.

What do I need?

Not too much actually

We’re going to use a package called Get. Why this particular package? According to the package author :

“Get is a microframework for Flutter”

Because it’s a framework it can do much more than just Dependency injection, and some of our speed gains will come using other features on the framework. Plus it’s really easy to use, and according to the documentation it will grant us superpowers, so that’s a plus.

Photo by Analia Baggiano on Unsplash

Start the timer

STEP 1 — add Get into pubspec.yaml and import it into our App.

STEP 2 — change our MaterialApp to GetMaterialApp.

The Get microframework is now available for us.

STEP 3 —add our services.

We’re going to try out Dependency Injection on two services. First we’ll instantiate the TestService immediately.

Second, we’ll do a lazy instantiation on AuthService which means the service won’t be created until it’s needed. It’s generally considered good form to use this method unless you need access to your service immediately.

Either way is fine, and here’s what that looks like.

STEP 4 — we inject our services.

Just as there are two ways to instantiate a service for Injection, Get thas two ways to Inject a Dependency:

  • The first uses a static method at the start of the class to be injected — we’ll use this one to inject TestService
  • The second requires a type declaration<> to qualify the injection — we’ll use this one to inject AuthService.

I find the first method easier to read but the performance is identical.

And we’re done

We now have a fully functioning Dependency Injection solution with two injected services that present their output through a SnackBar in 65 lines!

Here’s a look at a snippet of our code.

Abstract Classes and Testing

Although I’ve use concrete classes in my example, Get also supports abstract classes, perfect for testing or interfacing your code.

Here’s an abstract MyService class extended with our Implementation code:

  • Get.lazyPut<MyService>(() => MyServiceImpl());

And here’s the same abstract service extended with a Mock testing class:

  • Get.lazyPut<MyService>(() => MyServiceMock());

Either way, our call to MyService is the same:

  • MyService.to.multiply(2, 3);

I’ve included a gist of the example here, thanks to Stefan de Vogelaere for his help and example.

What else can I do?

A few added bonuses come with our DI solution

Let’s do a few things to make our code a little simpler to write and read.

Bonus 1 —Help separate our Business Logic

Notice I said help. This isn’t a state management solution (although Get has two), it’s more to help you with some of the things you may want to do from inside of your Business Logic classes.

Say for instance our Business Logic TestService needs to present a DialogBox, a SnackBar, or a BottomSheet? Let’s try it out with that SnackBar we used earlier.

We’ll remove the Builder widget and the following showSnackBar line from the StatefulWidget in our TestPage :

  • Scaffold.of(context).showSnackBar(SnackBar(content: Text(result))

After that we simply add the following line to our TestService:

Get.snackBar("TestService", result)

Did you notice we did that without referencingcontext?

We could also have done that from our StatefulWidget if we didn’t want to move the SnackBar to our Business Logic. It’s still much cleaner than the original code and doesn’t need context or the Builder widget above it.

This is a great enabler for the separation of concerns in architectures like MVVM, BLoC, and MVC.

Bonus 2— Simplify that syntax

Let’s be honest, Flutter can be a little wordy at times, so anything that can simplify those calls to get a value from theme or context is good.

Let’s say we want to get the height of a page. That would be:

  • MediaQuery.of(context).size.height;

With Get, that becomes:

  • Get.height;

Now let’s see if the dark theme is active with:

  • MediaQuery.of(context).platformBrightness == Brightness.dark
    ? true : false;

And with Get:

  • Get.isDarkMode;

It’s cleaner and faster to write. These were just a few of the convenience wrappers Get provides.

Here’s part of our updated code. As you can see our SnackBar looks a little different, and it’s also quite configurable.

So, we’ve created a fully functional App to demonstrate Dependency Injection, Separation of Concerns, and a little code cleaning and we’re still less than 100 lines!

You can find a gist of our code here.

Photo by Diego PH on Unsplash

How is this possible?

Please explain

When we changed from MaterialApp to GetMaterialApp we gave Get stewardship over context and the master widget in our App. From then on we could just call Get from anywhere in our code to make things happen.

When we used Get.put to create our TestService and Get.lazyPut on our AuthService we were actually instantiating them inside of the Get Instance. Once GetMaterialApp was added those services were available anywhere in our app.

Things like Get.length are simply convenience wrappers around the standard Flutter methods that we access through Get.

Get.snackbar works the same way, but offers a more configurable and visually appealing solution than its standard Flutter counterpart.

If you want a slightly better understanding of how to add Get to your project, or you’re interested in managing themes, have a look here.

So how did we do?

Added Dependency Injection with just a few lines of code? Yep.

Found a simple way to remove some of that Business Logic from our Flutter pages? Yep.

Made our code a little easier on the eyes and a little clearer to write? Yep.

Enjoy.

--

--