I'm implementing a dependency cleanup system in Flutter using get_it and injectable where certain repositories need to be disposed when the user logs out. I've created a Disposable pattern but want to ensure I'm doing it correctly.
I've tried something like this, but does not work
abstract class Disposable {
void dispose();
}
@lazySingleton
class RepoA extends Disposable {
void printHello() {
print('hello');
}
@override
void dispose() {
print('dispose');
}
}
@lazySingleton
class RepoB extends Disposable {
void printHelloFromRepoB() {
print('hello');
}
@override
void dispose() {
print('dispose');
}
}
@injectable
class UseCase {
UseCase(
this.repoB,
this.repoA,
);
final RepoB repoB;
final RepoA repoA;
}
void disposeAllDisposables() {
final disposables =
getIt.getAll<Disposable>(); // Disposable is not registered
for (var disposable in disposables) {
disposable.dispose();
}
}
First of all, Disposable
is a member of the get_it package and the package already knows how to handle classes that extend it, so you shouldn't be reinventing it. (Maybe you only included it here for demonstration purposes, but it still needs to be said.)
Having said that, now on to the frame challenge:
Doing it the way you are describing is certainly possible, but it's cumbersome, overrides a lot of type-safety, and requires manual management of the disposable singletons. Instead, I would recommend instead using get_it's scope system:
// Login logic
void login(String username, String password) {
... // Whatever other login logic you are doing
GetIt.pushNewScope(
scopeName: username,
init: (scopedGI) {
// Note: The dispose parameters are optional if RepoA or RepoB extend Disposable
scopedGi.registerLazySingleton<RepoA>(() => RepoA(), dispose: (repoA) => repoA.dispose());
scopedGi.registerLazySingleton<RepoB>(() => RepoB(), dispose: (repoB) => repoB.dispose());
},
);
}
//Logout logic
void logout(String username) {
... // Whatever other logout logic you are doing
GetIt.popScope();
// or
GetIt.dropScope(username);
}
Note: you can use this approach with injectable, but injectable's support for scopes seems somewhat limited and doesn't support scopes with dynamic names. You could use a static scope name instead (though personally I would prefer the dynamic approach just for the added security of each user having their own scope so there's no unintended crosstalk).
@lazySingleton
@Scope('user')
class RepoA {
void printHello() {
print('hello');
}
@disposeMethod
void dispose() {
print('dispose');
}
}
@lazySingleton
@Scope('user')
class RepoB {
void printHelloFromRepoB() {
print('hello');
}
@disposeMethod
void dispose() {
print('dispose');
}
}
...
// Login logic
void login(String username, String password) {
... // Whatever other login logic you are doing
GetIt.initUserScope();
}
//Logout logic
void logout(String username) {
... // Whatever other logout logic you are doing
GetIt.popScope();
}
(As a personal note, the scope functionality of injectable is poorly documented and has no first-party test or example code that I can find, so I would personally prefer using the vanilla get_it approach here.)