GetIt Scopes

Another tool for the toolbox. Image by Maria_Domnina

I’m using Suragch’s “Flutter state management for minimalists” method in my Flutter apps, as I found it simple and intuitive. However, GetIt is defined at app startup. What happens if you want to change it afterwards?

For example, in my app I need to connect to a device. So I have a DataManager class that gets data from a DataService. DataService can be Device1DataService (get data from device type 1) or MockDataService (simulate data).

Some DataServices

Ideally, my DataManager should simply get the registered DataService:

Dataservice service = getIt<DataService>();

But in in my app a user can choose to simulate the data, and then the default DataService should change to MockDataService.

I can, of course, register each separately and add a simulate parameter in SharedPreferences:

//in DataManager
prefs = await SharedPreferences.getInstance();
simulate = prefs.getBool("simulate",false);
DataService service = getIt<DataService>();
if (simulate){
service = getIt<MockDataService>();

But this … smells. The entire point of having an abstract DataService parent is that DataManager doesn't need to know which DataServices exist; Now it needs to know that there is a MockDataService, and also if we are in simulate mode. Yuck. On the other hand, a quick check of GetIt showed that in order to change the registration, I need to set allowReassignment=true for the entire getIt instance, which seemed to smell almost as much, as it’s not really recommended.

So it stayed like this for a while, until Device 2 came into being. Now I had Device1DataService, Device2DataService, MockDataService. User chooses at startup what device they are connecting to. Default DataService is MockDataService, before user chooses device.

What now? Adding it to DataManager is not only smelly, but Stinky, with a capital S. So I checked GetIt a little more thoroughly and found Scopes.

This thing of beauty allows to change the scope of registration and re-register what you need. So at startup:

getIt.registerLazySingleton<DataService>(() => MockDataService());

Let’s say user chose Device2. So:

//In choose device page
GetIt.I.pushNewScope(scopeName: "device");
GetIt.I.registerLazySingleton<DataService>(() => Device2DataService());
//In data manager. Initialized after the choose page.
Dataservice service = getIt<DataService>();

If user decides to change device and goes back to the choosing page, we need to go back to base scope, as GetIt doesn’t allow pushing two scopes with the same name:

GetIt.I.popScope(); //OR
GetIt.I.popScopesTill("device");//if you have numerous scopes

And you’re good to go! DataManager doesn’t need to know what DataServices there are, or if we are in simulate mode, and didn’t have to make getIt accept all changes using allowReassignments . Code smell all gone :)

Have you used scopes in GetIt? What was your use case?



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Danielle H

Danielle H

I started programming in LabVIEW and Matlab, and quickly expanded to include Android, Swift, Flutter, Web(PHP, HTML, Javascript), Arduino and Processing.