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.
Come Iniziare
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.
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.
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 referencing context?
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.
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.