Closed JCKodel closed 2 weeks ago
Have you checked watch_it the companion package to get_it for state management which contains a pushScope method for exactly this? Am 24. Okt. 2024, 23:24 +0100 schrieb J.C.Ködel @.***>:
I was trying to create a helper widget that would:
- Push a scope with dependencies that will be used from now on
- Automatically initialize dependencies that require async (or not) initialization
So I wrote this: Widget build(BuildContext context) { return DependencyContext( singletons: [ SomeClassThatImplementsIInitializable(), ], transients: [ () => OtherClassThatImplementsIInitializable(), ], ); } IInitializable contains a FutureOr
initialize();. But this is impossible for some reasons:
- There is no way to register instances or factories without specifying the type.
- There is no way of getting all instances of singleton registrations, so I can check if they implements IInitializable and run await instance.initialize().
- There is no way of hooking transient (factory) instantations.
It would be nice if:
- We could register classes without specifying the type (that could be either by getting the runtype type implicitly (such as GetIt.I.registerSingleton(SomeSingleton()) will register a
). Or if we could specify the type as a parameter, such as GetIt.registerSingletonAs(singleton: SomeSingleton(), type: SomeSingleton). - Get.I.getAll() will build all transient instances but, in this case, I just want to initialize the singletons. It would be nice to have more control of what is registered (types, instances, if they are singletons or transient, etc.)
- It would be nice to register a hook whenever some factory is built: GetIt.I.registerFactory
(() => SomeClass(), onCreated: (instance) => instance.initializeAsync()). I know we can do all those things using the registration methods, async singletons, etc. but this scenario is for a higher-level widget that would simplify the creation of a new context (that widget will push a new scope, register what needs to be registered, initialize what needs to be initialized (and waiting for them using a FutureBuilder, if any is required), and disposing when the widget is no longer in scope. — Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you are subscribed to this thread.Message ID: @.***>
Actually you don't have to specify a type when registering a singleton if the passed instance should be registered with its type and not with an interface.
Why would you want to register a factory without type? How would you call it later then? What do you mean with get All should not create the instances.
Please try to explain more about you want to achieve. I think I haven't fully understood it yet. You can also reach out on my slack. (see Readme) Am 24. Okt. 2024, 23:24 +0100 schrieb J.C.Ködel @.***>:
I was trying to create a helper widget that would:
- Push a scope with dependencies that will be used from now on
- Automatically initialize dependencies that require async (or not) initialization
So I wrote this: Widget build(BuildContext context) { return DependencyContext( singletons: [ SomeClassThatImplementsIInitializable(), ], transients: [ () => OtherClassThatImplementsIInitializable(), ], ); } IInitializable contains a FutureOr
initialize();. But this is impossible for some reasons:
- There is no way to register instances or factories without specifying the type.
- There is no way of getting all instances of singleton registrations, so I can check if they implements IInitializable and run await instance.initialize().
- There is no way of hooking transient (factory) instantations.
It would be nice if:
- We could register classes without specifying the type (that could be either by getting the runtype type implicitly (such as GetIt.I.registerSingleton(SomeSingleton()) will register a
). Or if we could specify the type as a parameter, such as GetIt.registerSingletonAs(singleton: SomeSingleton(), type: SomeSingleton). - Get.I.getAll() will build all transient instances but, in this case, I just want to initialize the singletons. It would be nice to have more control of what is registered (types, instances, if they are singletons or transient, etc.)
- It would be nice to register a hook whenever some factory is built: GetIt.I.registerFactory
(() => SomeClass(), onCreated: (instance) => instance.initializeAsync()). I know we can do all those things using the registration methods, async singletons, etc. but this scenario is for a higher-level widget that would simplify the creation of a new context (that widget will push a new scope, register what needs to be registered, initialize what needs to be initialized (and waiting for them using a FutureBuilder, if any is required), and disposing when the widget is no longer in scope. — Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you are subscribed to this thread.Message ID: @.***>
Have you checked watch_it the companion package to get_it for state management which contains a pushScope method for exactly this?
Yes. I am using it. Can't live without it!
I think what best describes what I'm trying to accomplish is something like Provider, but using GetIt scopes instead.
So, in my runApp()
, I would have something like this:
runApp(
DependencyContext(
transients: [
() => DataRepository() as IDataRepository,
() => Logger() as ILogger,
],
singletons: [
AuthViewModel(),
],
busyBuilder: (context) => CircularProgressIndicator.adaptive(),
child: const MainApp(),
),
);
That DependencyContext
would have to:
1) Create a new GetIt scope, registering those factories and singletons (for(final factory in transients) GetIt.I.registerFactory(factory);
won't work. It needs a type).
2) Check if those singletons are IInitializable
and, if they are, add all initializables in a Future.wait<void>([])
and provide it for a FutureBuilder
(if no initializable, just returns the child
).
3) Dispose the scope as soon as DependencyContext
is disposed (it is a Stateful widget).
The way GetIt is now, this is impossible. What I ended up with is:
runApp(
Context(
name: "Authentication",
registration: (getIt) => getIt
..registerSingletonAsync(
() => Repository.singleton("https://my.server"),
)
..registerSingletonAsync(
() => AuthViewModel.singleton(
defaultThemeSettings: ThemeSettings(
seedColorValue: Colors.pink.value,
themeVariant: ThemeVariant.tonalSpot,
),
),
),
child: const MainApp(),
),
);
Those .singleton
are async initializers, such as:
final class Repository {
...
static Future<Repository> singleton(String endpoint) async {
final instance = Repository._(endpoint);
await instance.initialize();
return instance;
}
Works as intended, but it's a bit verbose (although it gives me a lot of power in how things are registered).
What I intend is to write as little as possible and do what needs to be done using the decorator pattern (basically, indicating a class is initializable by implementing an interface). Dispose is already handled by GetIt, but initialization, only by signal ready or async singleton registration. I could use signal ready to accomplish that, but I'm still unable to register dependencies that are in a List
.
Why would you want to register a factory without type? How would you call it later then?
Hmmm. I think Dart generics aren't powerful enough to let you know what type a methods returns =\ This could be solved in my case by one of these (not pretty):
final transients = [
(() => Repository.singleton("https://xxx")) as IRepository Function()
];
final transients = {
IRepository: () => Repository.singleton("https://xxx"),
}
What do you mean with get All should not create the instances
What I want is: list all singletons registered and, for each one of them, if they implements IInitializable
, call the IInitializable.initialize()
.
getAll
, would trigger all transient instances, according to the documentation, which is not good for this scenario. Maybe a getAllSingletons()
that returns all singletons registered as an extra method? That would not have any side effect because (I think) they are all instantiated (except the lazy ones, right?))
GetIt is a superb piece of software that already do all of these. I'm just trying to make it more declarative with as little code as possible, with as many automations I can do (so I won't accidentally forget any dispose, etc.)
Will look into your ideas more tomorrow. Are you from Germany too? Am 25. Okt. 2024, 00:25 +0100 schrieb J.C.Ködel @.***>:
Have you checked watch_it the companion package to get_it for state management which contains a pushScope method for exactly this? Yes. I am using it. Can't live without it! I think what best describes what I'm trying to accomplish is something like Provider, but using GetIt scopes instead. So, in my runApp(), I would have something like this: runApp( DependencyContext( transients: [ () => DataRepository() as IDataRepository, () => Logger() as ILogger, ], singletons: [ AuthViewModel(), ], busyBuilder: (context) => CircularProgressIndicator.adaptive(), child: const MainApp(), ), ); That DependencyContext would have to:
- Create a new GetIt scope, registering those factories and singletons (for(final factory in transients) GetIt.I.registerFactory(factory); won't work. It needs a type).
- Check if those singletons are IInitializable and, if they are, add all initializables in a Future.wait
([]) and provide it for a FutureBuilder (if no initializable, just returns the child). The way GetIt is now, this is impossible. What I ended up with is: runApp( Context( name: "Authentication", registration: (getIt) => getIt ..registerSingletonAsync( () => Repository.singleton("https://my.server"), ) ..registerSingletonAsync( () => AuthViewModel.singleton( defaultThemeSettings: ThemeSettings( seedColorValue: Colors.pink.value, themeVariant: ThemeVariant.tonalSpot, ), ), ), child: const MainApp(), ), ); Those .singleton are async initializers, such as: final class Repository { ...
static Future
singleton(String endpoint) async { final instance = Repository._(endpoint); await instance.initialize();
return instance; } Works as intended, but it's a bit verbose (although it gives me a lot of power in how things are registered). What I intend is to write as little as possible and do what needs to be done using the decorator pattern (basically, indicating a class is initializable by implementing an interface). Dispose is already handled by GetIt, but initialization, only by signal ready or async singleton registration. I could use signal ready to accomplish that, but I'm still unable to register dependencies that are in a List.
Why would you want to register a factory without type? How would you call it later then? Hmmm. I think Dart generics aren't powerful enough to let you know what type a methods returns =\ This could be solved in my case by one of these (not pretty): final transients = [ (() => Repository.singleton("https://xxx")) as IRepository Function() ];
final transients = { IRepository: () => Repository.singleton("https://xxx"), }
What do you mean with get All should not create the instances What I want is: list all singletons registered and, for each one of them, if they implements IInitializable, call the IInitializable.initialize(). getAll, would trigger all transient instances, according to the documentation, which is not good for this scenario. Maybe a getAllSingletons() that returns all singletons registered as an extra method? That would not have any side effect because (I think) they are all instantiated (except the lazy ones, right?)) GetIt is a superb piece of software that already do all of these. I'm just trying to make it more declarative with as little code as possible, with as many automations I can do (so I won't accidentally forget any dispose, etc.) — Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you commented.Message ID: @.***>
Thank you.
I'm from Brazil [<O>]
(a.k.a. Life in Hard Mode)
Ah, I was guessing because of the ö in your name. Am 25. Okt. 2024, 00:48 +0100 schrieb J.C.Ködel @.***>:
Thank you. I'm from Brazil [
] (a.k.a. Life in Hard Mode) — Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you commented.Message ID: @.***>
You guessed it (almost) right. Grandmother was german =)
On Thu, Oct 24, 2024 at 8:54 PM escamoteur @.***> wrote:
Ah, I was guessing because of the ö in your name. Am 25. Okt. 2024, 00:48 +0100 schrieb J.C.Ködel @.***>:
Thank you. I'm from Brazil [
] (a.k.a. Life in Hard Mode) — Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you commented.Message ID: @.***> — Reply to this email directly, view it on GitHub https://github.com/fluttercommunity/get_it/issues/386#issuecomment-2436534075, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAC4TS4LL5FSXANVIVJJ553Z5GCDVAVCNFSM6AAAAABQSBK5GCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDIMZWGUZTIMBXGU . You are receiving this because you authored the thread.Message ID: @.***>
ok, I now found time to look more closely on it. On one side I like the idea that Singletons that implement IInitialize would automatically get initialized like doing a regiserAsync. On the other side I'm less and less using implicit behaviour that is just based on implementing a certain interface as it is not easy to follow when you read the someone else's code, which is why I don't use the Disposable interface in our project. In general it's a pity that the Dart team did not define certain default interfaces like IDisposable. I typically don't use static methods for async registrations but a normal method:
class AsyncSingleon{
Future<AsyncSingleton> init(){
... do was it needed
return this;
}
I'm currently not convinced that it is worth investing a lot of time in this as it merely reduced some lines of code but doesn't add new functionality. I leave it open, maybe we get some more input from other users
I was trying to create a helper widget that would:
1) Push a scope with dependencies that will be used from now on 2) Automatically initialize dependencies that require async (or not) initialization
So I wrote this:
IInitializable
contains aFutureOr<void> initialize();
.But this is impossible for some reasons:
1) There is no way to register instances or factories without specifying the type. 2) There is no way of getting all instances of singleton registrations, so I can check if they implements IInitializable and run
await instance.initialize()
. 3) There is no way of hooking transient (factory) instantations.It would be nice if:
1) We could register classes without specifying the type (that could be either by getting the runtype type implicitly (such as
GetIt.I.registerSingleton(SomeSingleton())
will register a<SomeSingleton>
). Or if we could specify the type as a parameter, such asGetIt.registerSingletonAs(singleton: SomeSingleton(), type: SomeSingleton)
. 2)Get.I.getAll()
will build all transient instances but, in this case, I just want to initialize the singletons. It would be nice to have more control of what is registered (types, instances, if they are singletons or transient, etc.) 3) It would be nice to register a hook whenever some factory is built:GetIt.I.registerFactory<ISomeInterface>(() => SomeClass(), onCreated: (instance) => instance.initializeAsync())
.I know we can do all those things using the registration methods, async singletons, etc. but this scenario is for a higher-level widget that would simplify the creation of a new context (that widget will push a new scope, register what needs to be registered, initialize what needs to be initialized (and waiting for them using a FutureBuilder, if any is required), and disposing when the widget is no longer in scope.