Hello, Currently I am using get_it for DI. But now I have a situation where I want the user to select a server to which they want to connect from the app. Based on the selection I need to change the baseUrl and use that throughout the app after the server selection. Do you have any suggestion on how can I achieve that?
What exactly is the problem with that?
I have constructor injected ApiClient
to all my repositories. But now I am worried that I have to create some kind of wrapper on top of that ApiClient
which will provide the current selected ApiClient
and update all my repositories. So I was wondering if there is some trick I can use to avoid this big migration.
Can you show a bit of your code? Do you currently inject that api client everywhere via the constructor?
Why not just registering it in get_it and unregister / and register it again if the user switches the server?
I have a similar case in our app. I tackled it setting an _options Options()
object inside ApiClient
with a method to modify it, and passing it with the dio Api call methods. However, @escamoteur s suggestion is probably simpler.
Apart from widget! I have done the constructor injection in other classes like Repository, Usecase etc. I have a di.dart file which hold all the dependency construction code using get_it
.
I think your idea of register/unregister make sense! Let me give it a try and see what happens. Thanks again
I’ve solved this problem in the past. Are you using bloc, by chance?
unregistering/reregistering in get_it is the easiest solution.
I did solve it with register/unregister in get_it. But curious to know the solution using bloc as well.
Thank you! I was able to solve the problem with this approach. get_it
is just great <3
My use case might have been a bit different from yours. Let me set the stage.
My apps are architected such that an Api
is consumed by a bloc:
class MyBloc extends Bloc<MyEvent, MyState> {
final MyApi api;
MyBloc(this.api) : super(const Initial()) {
on<MyEvent>((event, emit) async {
final result = await api.doSomething();
});
}
}
My Api
is generally pretty simple, then:
class MyApi {
Future<bool> doSomething() async {
return Future.value(true);
}
}
In my case, I’m registering MyApi
in get_it
, then injecting it into the BlocProvider
. This works really well and separates the bloc from the API, allowing me to write really clean unit tests.
Then, one day, a client asked me to come up with a solution where, when logging into a specific account, the app would return hard-coded/debug data. Easy enough… I just need to write a new implementation of the API:
class MyDebugApi implements MyApi {
@override
Future<bool> doSomething() async {
return Future.value(false);
}
}
Then, I simply unregister MyApi
from get_it
and register MyDebugApi
in its place. The problem was that the bloc didn’t pick up on this change, since it had already been initialized. So, how do I tell it to toss out the old API and use the new one instead?
I ended up using one of Arcane’s features, ArcaneEnvironment
*. This let me quickly swap between my normal and debug environments. Once I could detect that change, I was able to create a ValueKey
from it (while also mixing in a simple IdService
that gave me a unique session ID):
final ValueKey key = ValueKey(
"${context.watch<ArcaneEnvironment>().state.name}-${IdService.I.sessionId.value}",
);
By providing this key to my bloc provider, any time I swapped environments the bloc would reinitialize, pulling the new API from get_it
.
Again, this scenario is pretty specific to my use-case, but it did take a bit of noodling to figure out how to make it work properly.
* Disclaimer: I am the author of the arcane_framework
package.
as you are already using get_it, why not simply use get_it for the blocs too or why know triggering a rebuild that would create a new bloc_provider in that case?
I don’t always want my bloc to be a singleton. For example, if I had some theoretical list/details setup where one bloc managed fetching a list and another managed the actual details, I would want to instantiate my list bloc at a higher level (and maintain state as I navigate around, probably), while the details bloc might be provided on the details screen (and could therefore be disposed of and recreated when I choose a new item from the list to view.)
It’s not a perfect explanation, but I hope you get the idea.
In fact, I used to register my blocs in get_it
, but have since moved away from doing that. I think accessing them through BuildContext
(as was the original intent) simply makes more sense, especially when you take into consideration how they’ll be invoked later. (That is to say, each feature should generally have its own bloc, and only that feature talks to the bloc… so there’s no need to register the bloc in get_it
, as you’ll only always be accessing it from a place in the widget tree that you have direct access via context
.)
I understand it but for that get_it has scopes which allow exactly to control the livetime.
Hmm. I’ll have to go back and read the docs.