From 2d4937ce67703f5942a96b5b5a1d8c11aaf007d1 Mon Sep 17 00:00:00 2001 From: Noor Dawod Date: Fri, 1 Jan 2021 12:31:37 +0100 Subject: [PATCH 1/9] Add IDE-friendly editor config file. --- .editorconfig | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..67547bb --- /dev/null +++ b/.editorconfig @@ -0,0 +1,10 @@ +root = true + +[*] +trim_trailing_whitespace = true +end_of_line = lf +insert_final_newline = true +charset = utf-8 +max_line_length = 120 +indent_size = 2 +indent_style = space From 4d4013ce77e922fa353b87a91ee4143f2d8bd77a Mon Sep 17 00:00:00 2001 From: Noor Dawod Date: Fri, 1 Jan 2021 12:40:23 +0100 Subject: [PATCH 2/9] Set maximum line length to 100 characters. --- .editorconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index 67547bb..db364f4 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,6 +5,6 @@ trim_trailing_whitespace = true end_of_line = lf insert_final_newline = true charset = utf-8 -max_line_length = 120 +max_line_length = 100 indent_size = 2 indent_style = space From 51f3e923624d0403434e0a8b7183b2e3ef4fe2ba Mon Sep 17 00:00:00 2001 From: Noor Dawod Date: Fri, 1 Jan 2021 13:10:43 +0100 Subject: [PATCH 3/9] Reformatted code, fixed grammatical errors, fix a crash in isRegistered(). --- lib/get_it.dart | 828 ++++++++++++++++++++++--------------------- lib/get_it_impl.dart | 549 +++++++++++++++------------- 2 files changed, 725 insertions(+), 652 deletions(-) diff --git a/lib/get_it.dart b/lib/get_it.dart index 44a256e..3a956e2 100644 --- a/lib/get_it.dart +++ b/lib/get_it.dart @@ -1,396 +1,432 @@ -library get_it; - -import 'dart:async'; - -import 'package:async/async.dart'; -import 'package:collection/collection.dart' show IterableExtension; - -part 'get_it_impl.dart'; - -/// If your singleton that you register wants to use the manually signalling -/// of its ready state, it can implement this interface class instead of using -/// the [signalsReady] parameter of the registration functions -/// (you don't really have to implement much ;-) ) -abstract class WillSignalReady {} - -/// Signature of the factory function used by non async factories -typedef FactoryFunc = T Function(); - -/// For Factories that expect up to two parameters if you need only one use `void` for the one -/// you don't use -typedef FactoryFuncParam = T Function(P1 param1, P2 param2); - -/// Signature of the factory function used by async factories -typedef FactoryFuncAsync = Future Function(); - -/// Signature for disposing function -/// because closures like `(x){}` have a return type of Null we don't use `FutureOr` -typedef DisposingFunc = FutureOr Function(T param); - -/// Signature for disposing function on scope level -typedef ScopeDisposeFunc = FutureOr Function(); - -/// For async Factories that expect up to two parameters if you need only one use `void` for the one -/// you don't use -typedef FactoryFuncParamAsync = Future Function( - P1 param1, P2 param2); - -class WaitingTimeOutException implements Exception { - /// In case of an timeout while waiting for an instance to get ready - /// This exception is thrown whith information about who is still waiting - /// - /// if you pass the [callee] parameter to [isReady], or define dependent Singletons - /// this maps lists which callees is waiting for whom - final Map> areWaitedBy; - - /// Lists with Types that are still not ready - final List notReadyYet; - - /// Lists with Types that are already ready - final List areReady; - - WaitingTimeOutException(this.areWaitedBy, this.notReadyYet, this.areReady); - // todo : assert(areWaitedBy != null && notReadyYet != null && areReady != null); - - @override - String toString() { - // ignore: avoid_print - print( - 'GetIt: There was a timeout while waiting for an instance to signal ready'); - // ignore: avoid_print - print('The following instance types where waiting for completion'); - for (final entry in areWaitedBy.entries) { - // ignore: avoid_print - print('${entry.value} is waiting for ${entry.key}'); - } - // ignore: avoid_print - print('The following instance types have NOT signalled ready yet'); - for (final entry in notReadyYet) { - // ignore: avoid_print - print(entry); - } - // ignore: avoid_print - print('The following instance types HAVE signalled ready yet'); - for (final entry in areReady) { - // ignore: avoid_print - print(entry); - } - return super.toString(); - } -} - -/// Very simple and easy to use service locator -/// You register your object creation factory or an instance of an object with [registerFactory], -/// [registerSingleton] or [registerLazySingleton] -/// And retrieve the desired object using [get] or call your locator das as function as its a -/// callable class -/// Additionally GetIt offers asynchronous creation functions as well as functions to synchronize -/// the async initialization of multiple Singletons -abstract class GetIt { - static final GetIt _instance = _GetItImplementation(); - - /// access to the Singleton instance of GetIt - static GetIt get instance => _instance; - - /// Short form to access the instance of GetIt - static GetIt get I => _instance; - - /// If you need more than one instance of GetIt you can use [asNewInstance()] - /// You should prefer to use the `instance()` method to access the global instance of [GetIt]. - factory GetIt.asNewInstance() { - return _GetItImplementation(); - } - - /// By default it's not allowed to register a type a second time. - /// If you really need to you can disable the asserts by setting[allowReassignment]= true - bool allowReassignment = false; - - /// retrieves or creates an instance of a registered type [T] depending on the registration - /// function used for this type or based on a name. - /// for factories you can pass up to 2 parameters [param1,param2] they have to match the types - /// given at registration with [registerFactoryParam()] - T get( - {String? instanceName, dynamic param1, dynamic param2}); - - /// Returns an Future of an instance that is created by an async factory or a Singleton that is - /// not ready with its initialization. - /// for async factories you can pass up to 2 parameters [param1,param2] they have to match the types - /// given at registration with [registerFactoryParamAsync()] - Future getAsync( - {String? instanceName, dynamic param1, dynamic param2}); - - /// Callable class so that you can write `GetIt.instance` instead of - /// `GetIt.instance.get` - T call( - {String? instanceName, dynamic param1, dynamic param2}); - - /// registers a type so that a new instance will be created on each call of [get] on that type - /// [T] type to register - /// [factoryfunc] factory function for this type - /// [instanceName] if you provide a value here your factory gets registered with that - /// name instead of a type. This should only be necessary if you need to register more - /// than one instance of one type. Its highly not recommended - void registerFactory(FactoryFunc factoryfunc, - {String? instanceName}); - - /// registers a type so that a new instance will be created on each call of [get] on that type based on - /// up to two parameters provided to [get()] - /// [T] type to register - /// [P1] type of param1 - /// [P2] type of param2 - /// if you use only one parameter pass void here - /// [factoryfunc] factory function for this type that accepts two parameters - /// [instanceName] if you provide a value here your factory gets registered with that - /// name instead of a type. This should only be necessary if you need to register more - /// than one instance of one type. Its highly not recommended - /// - /// example: - /// getIt.registerFactoryParam((s,i) - /// => TestClassParam(param1:s, param2: i)); - /// - /// if you only use one parameter: - /// - /// getIt.registerFactoryParam((s,_) - /// => TestClassParam(param1:s); - void registerFactoryParam( - FactoryFuncParam factoryfunc, - {String? instanceName}); - - /// registers a type so that a new instance will be created on each call of [getAsync] on that type - /// the creation function is executed asynchronously and has to be accessed with [getAsync] - /// [T] type to register - /// [factoryfunc] async factory function for this type - /// [instanceName] if you provide a value here your factory gets registered with that - /// name instead of a type. This should only be necessary if you need to register more - /// than one instance of one type. Its highly not recommended - void registerFactoryAsync(FactoryFuncAsync factoryfunc, - {String? instanceName}); - - /// registers a type so that a new instance will be created on each call of [getAsync] - /// on that type based on up to two parameters provided to [getAsync()] - /// the creation function is executed asynchronously and has to be accessed with [getAsync] - /// [T] type to register - /// [P1] type of param1 - /// [P2] type of param2 - /// if you use only one parameter pass void here - /// [factoryfunc] factory function for this type that accepts two parameters - /// [instanceName] if you provide a value here your factory gets registered with that - /// name instead of a type. This should only be necessary if you need to register more - /// than one instance of one type. Its highly not recommended - /// - /// example: - /// getIt.registerFactoryParam((s,i) async - /// => TestClassParam(param1:s, param2: i)); - /// - /// if you only use one parameter: - /// - /// getIt.registerFactoryParam((s,_) async - /// => TestClassParam(param1:s); - void registerFactoryParamAsync( - FactoryFuncParamAsync factoryfunc, - {String? instanceName}); - - /// registers a type as Singleton by passing an [instance] of that type - /// that will be returned on each call of [get] on that type - /// [T] type to register - /// [instanceName] if you provide a value here your instance gets registered with that - /// name instead of a type. This should only be necessary if you need to register more - /// than one instance of one type. Its highly not recommended - /// If [signalsReady] is set to `true` it means that the future you can get from `allReady()` - /// cannot complete until this this instance was signalled ready by calling [signalsReady(instance)]. - void registerSingleton(T instance, - {String? instanceName, bool? signalsReady, DisposingFunc? dispose}); - - /// registers a type as Singleton by passing an factory function of that type - /// that will be called on each call of [get] on that type - /// [T] type to register - /// [instanceName] if you provide a value here your instance gets registered with that - /// name instead of a type. This should only be necessary if you need to register more - /// than one instance of one type. Its highly not recommended - /// [dependsOn] if this instance depends on other registered Singletons before it can be initilaized - /// you can either orchestrate this manually using [isReady()] or pass a list of the types that the - /// instance depends on here. [factoryFunc] won't get executed till this types are ready. - /// [func] is called - /// If [signalsReady] is set to `true` it means that the future you can get from `allReady()` - /// cannot complete until this this instance was signalled ready by calling [signalsReady(instance)]. - void registerSingletonWithDependencies( - FactoryFunc factoryFunc, - {String? instanceName, - Iterable? dependsOn, - bool? signalsReady, - DisposingFunc? dispose}); - - /// registers a type as Singleton by passing an asynchronous factory function which has to return the instance - /// that will be returned on each call of [get] on that type. - /// Therefore you have to ensure that the instance is ready before you use [get] on it or use [getAsync()] to - /// wait for the completion. - /// You can wait/check if the instance is ready by using [isReady()] and [isReadySync()]. - /// [factoryfunc] is executed immediately if there are no dependencies to other Singletons (see below). - /// As soon as it returns, this instance is marked as ready unless you don't set [signalsReady==true] - /// [instanceName] if you provide a value here your instance gets registered with that - /// name instead of a type. This should only be necessary if you need to register more - /// than one instance of one type. Its highly not recommended - /// [dependsOn] if this instance depends on other registered Singletons before it can be initilaized - /// you can either orchestrate this manually using [isReady()] or pass a list of the types that the - /// instance depends on here. [factoryFunc] won't get executed till this types are ready. - /// If [signalsReady] is set to `true` it means that the future you can get from `allReady()` cannot complete until this - /// this instance was signalled ready by calling [signalsReady(instance)]. In that case no automatic ready signal - /// is made after completion of [factoryfunc] - void registerSingletonAsync(FactoryFuncAsync factoryfunc, - {String? instanceName, - Iterable? dependsOn, - bool? signalsReady, - DisposingFunc? dispose}); - - /// registers a type as Singleton by passing a factory function that will be called - /// on the first call of [get] on that type - /// [T] type to register - /// [factoryfunc] factory function for this type - /// [instanceName] if you provide a value here your factory gets registered with that - /// name instead of a type. This should only be necessary if you need to register more - /// than one instance of one type. Its highly not recommended - /// [registerLazySingleton] does not influence [allReady] however you can wait - /// for and be dependent on a LazySingleton. - void registerLazySingleton(FactoryFunc factoryfunc, - {String? instanceName, DisposingFunc? dispose}); - - /// registers a type as Singleton by passing a async factory function that will be called - /// on the first call of [getAsnc] on that type - /// This is a rather esoteric requirement so you should seldom have the need to use it. - /// This factory function [factoryFunc] isn't called immediately but wait till the first call by - /// [getAsync()] or [isReady()] is made - /// To control if an async Singleton has completed its [factoryFunc] gets a `Completer` passed - /// as parameter that has to be completed to signal that this instance is ready. - /// Therefore you have to ensure that the instance is ready before you use [get] on it or use [getAsync()] to - /// wait for the completion. - /// You can wait/check if the instance is ready by using [isReady()] and [isReadySync()]. - /// [instanceName] if you provide a value here your instance gets registered with that - /// name instead of a type. This should only be necessary if you need to register more - /// than one instance of one type. Its highly not recommended. - /// [registerLazySingletonAsync] does not influence [allReady] however you can wait - /// for and be dependent on a LazySingleton. - void registerLazySingletonAsync( - FactoryFuncAsync factoryFunc, - {String? instanceName, - DisposingFunc? dispose}); - - /// Tests if an [instance] of an object or aType [T] or a name [instanceName] - /// is registered inside GetIt - bool isRegistered({Object? instance, String? instanceName}); - - /// Clears all registered types. Handy when writing unit tests - /// If you provided dispose function when registering they will be called - /// [dispose] if `false` it only resets without calling any dispose - /// functions - /// As dispose funcions can be async, you should await this function. - Future reset({bool dispose = true}); - - /// Clears all registered types for the current scope - /// If you provided dispose function when registering they will be called - /// [dispose] if `false` it only resets without calling any dispose - /// functions - /// As dispose funcions can be async, you should await this function. - Future resetScope({bool dispose = true}); - - /// Creates a new registration scope. If you register types after creating - /// a new scope they will hide any previous registration of the same type. - /// Scopes allow you to manage different live times of your Objects. - /// [scopeName] if you name a scope you can pop all scopes above the named one - /// by using the name. - /// [dispose] function that will be called when you pop this scope. The scope - /// is still valied while it is executed - void pushNewScope({String? scopeName, ScopeDisposeFunc? dispose}); - - /// Disposes all factories/Singletons that have ben registered in this scope - /// and pops (destroys) the scope so that the previous scope gets active again. - /// if you provided dispose functions on registration, they will be called. - /// if you passed a dispose function when you pushed this scope it will be - /// calles before the scope is popped. - /// As dispose funcions can be async, you should await this function. - Future popScope(); - - /// if you have a lot of scopes with names you can pop (see [popScope]) all - /// scopes above the scope with [name] including that scope - /// Scopes are poped in order from the top - /// As dispose funcions can be async, you should await this function. - /// it no scope with [name] exists, nothing is popped and `false` is returned - Future popScopesTill(String name); - - /// Clears the instance of a lazy singleton, - /// being able to call the factory function on the next call - /// of [get] on that type again. - /// you select the lazy Singleton you want to reset by either providing - /// an [instance], its registered type [T] or its registration name. - /// if you need to dispose some resources before the reset, you can - /// provide a [disposingFunction]. This function overrides the disposing - /// you might have provided when registering. - void resetLazySingleton( - {Object? instance, - String? instanceName, - void Function(T)? disposingFunction}); - - /// Unregister an [instance] of an object or a factory/singleton by Type [T] or by name [instanceName] - /// if you need to dispose any resources you can do it using [disposingFunction] function - /// that provides a instance of your class to be disposed. This function overrides the disposing - /// you might have provided when registering. - void unregister( - {Object? instance, - String? instanceName, - void Function(T)? disposingFunction}); - - /// returns a Future that completes if all asynchronously created Singletons and any Singleton that had - /// [signalsReady==true] are ready. - /// This can be used inside a FutureBuilder to change the UI as soon as all initialization - /// is done - /// If you pass a [timeout], an [WaitingTimeOutException] will be thrown if not all Singletons - /// were ready in the given time. The Exception contains details on which Singletons are not ready yet. - /// if [allReady] should not wait for the completion of async Signletons set - /// [ignorePendingAsyncCreation==true] - Future allReady( - {Duration? timeout, bool ignorePendingAsyncCreation = false}); - - /// Returns a Future that completes if the instance of an Singleton, defined by Type [T] or - /// by name [instanceName] or by passing the an existing [instance], is ready - /// If you pass a [timeout], an [WaitingTimeOutException] will be thrown if the instance - /// is not ready in the given time. The Exception contains details on which Singletons are - /// not ready at that time. - /// [callee] optional parameter which makes debugging easier. Pass `this` in here. - Future isReady({ - Object? instance, - String? instanceName, - Duration? timeout, - Object? callee, - }); - - /// Checks if an async Singleton defined by an [instance], a type [T] or an [instanceName] - /// is ready without waiting - bool isReadySync({Object? instance, String? instanceName}); - - /// Returns if all async Singletons are ready without waiting - /// if [allReady] should not wait for the completion of async Signletons set - /// [ignorePendingAsyncCreation==true] - // ignore: avoid_positional_boolean_parameters - bool allReadySync([bool ignorePendingAsyncCreation = false]); - - /// Used to manually signal the ready state of a Singleton. - /// If you want to use this mechanism you have to pass [signalsReady==true] when registering - /// the Singleton. - /// If [instance] has a value GetIt will search for the responsible Singleton - /// and completes all futures that might be waited for by [isReady] - /// If all waiting singletons have signalled ready the future you can get - /// from [allReady] is automatically completed - /// - /// Typically this is used in this way inside the registered objects init - /// method `GetIt.instance.signalReady(this);` - /// - /// if [instance] is `null` and no factory/singleton is waiting to be signalled this - /// will complete the future you got from [allReady], so it can be used to globally - /// giving a ready Signal - /// - /// Both ways are mutual exclusive, meaning either only use the global `signalReady()` and - /// don't register a singleton to signal ready or use any async registrations - /// - /// Or use async registrations methods or let individual instances signal their ready - /// state on their own. - void signalReady(Object? instance); -} +library get_it; + +import 'dart:async'; + +import 'package:async/async.dart'; +import 'package:collection/collection.dart' show IterableExtension; + +part 'get_it_impl.dart'; + +/// If your singleton that you register wants to use the manually signalling +/// of its ready state, it can implement this interface class instead of using +/// the [signalsReady] parameter of the registration functions +/// (you don't really have to implement much ;-) ) +abstract class WillSignalReady {} + +/// Signature of the factory function used by non async factories +typedef FactoryFunc = T Function(); + +/// For Factories that expect up to two parameters if you need only one use `void` for the one +/// you don't use +typedef FactoryFuncParam = T Function(P1 param1, P2 param2); + +/// Signature of the factory function used by async factories +typedef FactoryFuncAsync = Future Function(); + +/// Signature for disposing function +/// because closures like `(x){}` have a return type of Null we don't use `FutureOr` +typedef DisposingFunc = FutureOr Function(T param); + +/// Signature for disposing function on scope level +typedef ScopeDisposeFunc = FutureOr Function(); + +/// For async Factories that expect up to two parameters if you need only one use `void` for the one +/// you don't use +typedef FactoryFuncParamAsync = Future Function(P1 param1, P2 param2); + +class WaitingTimeOutException implements Exception { + /// In case of an timeout while waiting for an instance to get ready + /// This exception is thrown with information about who is still waiting. + /// + /// If you pass the [callee] parameter to [isReady], or define dependent Singletons + /// this maps lists which callees is waiting for whom. + final Map> areWaitedBy; + + /// Lists with Types that are still not ready. + final List notReadyYet; + + /// Lists with Types that are already ready. + final List areReady; + + WaitingTimeOutException(this.areWaitedBy, this.notReadyYet, this.areReady); + // todo : assert(areWaitedBy != null && notReadyYet != null && areReady != null); + + @override + String toString() { + // ignore: avoid_print + print('GetIt: There was a timeout while waiting for an instance to signal ready'); + // ignore: avoid_print + print('The following instance types where waiting for completion'); + for (final entry in areWaitedBy.entries) { + // ignore: avoid_print + print('${entry.value} is waiting for ${entry.key}'); + } + // ignore: avoid_print + print('The following instance types have NOT signalled ready yet'); + for (final entry in notReadyYet) { + // ignore: avoid_print + print(entry); + } + // ignore: avoid_print + print('The following instance types HAVE signalled ready yet'); + for (final entry in areReady) { + // ignore: avoid_print + print(entry); + } + return super.toString(); + } +} + +/// Very simple and easy to use service locator +/// You register your object creation factory or an instance of an object with [registerFactory], +/// [registerSingleton] or [registerLazySingleton] +/// And retrieve the desired object using [get] or call your locator das as function as its a +/// callable class +/// Additionally GetIt offers asynchronous creation functions as well as functions to synchronize +/// the async initialization of multiple Singletons +abstract class GetIt { + static final GetIt _instance = _GetItImplementation(); + + /// access to the Singleton instance of GetIt + static GetIt get instance => _instance; + + /// Short form to access the instance of GetIt + static GetIt get I => _instance; + + /// If you need more than one instance of GetIt you can use [asNewInstance()] + /// You should prefer to use the `instance()` method to access the global instance of [GetIt]. + factory GetIt.asNewInstance() { + return _GetItImplementation(); + } + + /// By default it's not allowed to register a type a second time. + /// If you really need to you can disable the asserts by setting[allowReassignment]= true + bool allowReassignment = false; + + /// retrieves or creates an instance of a registered type [T] depending on the registration + /// function used for this type or based on a name. + /// for factories you can pass up to 2 parameters [param1,param2] they have to match the types + /// given at registration with [registerFactoryParam()] + T get({ + String? instanceName, + dynamic param1, + dynamic param2, + }); + + /// Returns an Future of an instance that is created by an async factory or a Singleton that is + /// not ready with its initialization. + /// for async factories you can pass up to 2 parameters [param1,param2] they have to match the + /// types given at registration with [registerFactoryParamAsync()] + Future getAsync({ + String? instanceName, + dynamic param1, + dynamic param2, + }); + + /// Callable class so that you can write `GetIt.instance` instead of + /// `GetIt.instance.get` + T call({ + String? instanceName, + dynamic param1, + dynamic param2, + }); + + /// registers a type so that a new instance will be created on each call of [get] on that type + /// [T] type to register + /// [factoryFunc] factory function for this type + /// [instanceName] if you provide a value here your factory gets registered with that + /// name instead of a type. This should only be necessary if you need to register more + /// than one instance of one type. Its highly not recommended + void registerFactory( + FactoryFunc factoryFunc, { + String? instanceName, + }); + + /// registers a type so that a new instance will be created on each call of [get] on that type + /// based on up to two parameters provided to [get()] + /// [T] type to register + /// [P1] type of param1 + /// [P2] type of param2 + /// if you use only one parameter pass void here + /// [factoryFunc] factory function for this type that accepts two parameters + /// [instanceName] if you provide a value here your factory gets registered with that + /// name instead of a type. This should only be necessary if you need to register more + /// than one instance of one type. Its highly not recommended + /// + /// example: + /// getIt.registerFactoryParam((s,i) + /// => TestClassParam(param1:s, param2: i)); + /// + /// if you only use one parameter: + /// + /// getIt.registerFactoryParam((s,_) + /// => TestClassParam(param1:s); + void registerFactoryParam( + FactoryFuncParam factoryFunc, { + String? instanceName, + }); + + /// registers a type so that a new instance will be created on each call of [getAsync] on that type + /// the creation function is executed asynchronously and has to be accessed with [getAsync] + /// [T] type to register + /// [factoryFunc] async factory function for this type + /// [instanceName] if you provide a value here your factory gets registered with that + /// name instead of a type. This should only be necessary if you need to register more + /// than one instance of one type. Its highly not recommended + void registerFactoryAsync( + FactoryFuncAsync factoryFunc, { + String? instanceName, + }); + + /// registers a type so that a new instance will be created on each call of [getAsync] + /// on that type based on up to two parameters provided to [getAsync()] + /// the creation function is executed asynchronously and has to be accessed with [getAsync] + /// [T] type to register + /// [P1] type of param1 + /// [P2] type of param2 + /// if you use only one parameter pass void here + /// [factoryFunc] factory function for this type that accepts two parameters + /// [instanceName] if you provide a value here your factory gets registered with that + /// name instead of a type. This should only be necessary if you need to register more + /// than one instance of one type. Its highly not recommended + /// + /// example: + /// getIt.registerFactoryParam((s,i) async + /// => TestClassParam(param1:s, param2: i)); + /// + /// if you only use one parameter: + /// + /// getIt.registerFactoryParam((s,_) async + /// => TestClassParam(param1:s); + void registerFactoryParamAsync( + FactoryFuncParamAsync factoryFunc, { + String? instanceName, + }); + + /// registers a type as Singleton by passing an [instance] of that type + /// that will be returned on each call of [get] on that type + /// [T] type to register + /// [instanceName] if you provide a value here your instance gets registered with that + /// name instead of a type. This should only be necessary if you need to register more + /// than one instance of one type. Its highly not recommended + /// If [signalsReady] is set to `true` it means that the future you can get from `allReady()` + /// cannot complete until this this instance was signalled ready by calling + /// [signalsReady(instance)]. + void registerSingleton( + T instance, { + String? instanceName, + bool? signalsReady, + DisposingFunc? dispose, + }); + + /// registers a type as Singleton by passing an factory function of that type + /// that will be called on each call of [get] on that type + /// [T] type to register + /// [instanceName] if you provide a value here your instance gets registered with that + /// name instead of a type. This should only be necessary if you need to register more + /// than one instance of one type. Its highly not recommended + /// [dependsOn] if this instance depends on other registered Singletons before it can be + /// initialized you can either orchestrate this manually using [isReady()] or pass a list of + /// the types that the instance depends on here. [factoryFunc] won't get executed till this + /// types are ready. [func] is called if [signalsReady] is set to `true` it means that the + /// future you can get from `allReady()` cannot complete until this this instance was + /// signalled ready by calling [signalsReady(instance)]. + void registerSingletonWithDependencies( + FactoryFunc factoryFunc, { + String? instanceName, + Iterable? dependsOn, + bool? signalsReady, + DisposingFunc? dispose, + }); + + /// registers a type as Singleton by passing an asynchronous factory function which has to + /// return the instance that will be returned on each call of [get] on that type. Therefore + /// you have to ensure that the instance is ready before you use [get] on it or use [getAsync()] + /// to wait for the completion. + /// You can wait/check if the instance is ready by using [isReady()] and [isReadySync()]. + /// [factoryFunc] is executed immediately if there are no dependencies to other Singletons + /// (see below). As soon as it returns, this instance is marked as ready unless you don't + /// set [signalsReady==true] [instanceName] if you provide a value here your instance gets + /// registered with that name instead of a type. This should only be necessary if you need to + /// register more than one instance of one type. Its highly not recommended + /// [dependsOn] if this instance depends on other registered Singletons before it can be + /// initialized you can either orchestrate this manually using [isReady()] or pass a list of + /// the types that the instance depends on here. [factoryFunc] won't get executed till this + /// types are ready. If [signalsReady] is set to `true` it means that the future you can get + /// from `allReady()` cannot complete until this this instance was signalled ready by calling + /// [signalsReady(instance)]. In that case no automatic ready signal is made after + /// completion of [factoryFunc] + void registerSingletonAsync( + FactoryFuncAsync factoryFunc, { + String? instanceName, + Iterable? dependsOn, + bool? signalsReady, + DisposingFunc? dispose, + }); + + /// registers a type as Singleton by passing a factory function that will be called + /// on the first call of [get] on that type + /// [T] type to register + /// [factoryFunc] factory function for this type + /// [instanceName] if you provide a value here your factory gets registered with that + /// name instead of a type. This should only be necessary if you need to register more + /// than one instance of one type. Its highly not recommended + /// [registerLazySingleton] does not influence [allReady] however you can wait + /// for and be dependent on a LazySingleton. + void registerLazySingleton( + FactoryFunc factoryFunc, { + String? instanceName, + DisposingFunc? dispose, + }); + + /// registers a type as Singleton by passing a async factory function that will be called + /// on the first call of [getAsnc] on that type + /// This is a rather esoteric requirement so you should seldom have the need to use it. + /// This factory function [factoryFunc] isn't called immediately but wait till the first call by + /// [getAsync()] or [isReady()] is made + /// To control if an async Singleton has completed its [factoryFunc] gets a `Completer` passed + /// as parameter that has to be completed to signal that this instance is ready. + /// Therefore you have to ensure that the instance is ready before you use [get] on it or use + /// [getAsync()] to wait for the completion. + /// You can wait/check if the instance is ready by using [isReady()] and [isReadySync()]. + /// [instanceName] if you provide a value here your instance gets registered with that + /// name instead of a type. This should only be necessary if you need to register more + /// than one instance of one type. Its highly not recommended. + /// [registerLazySingletonAsync] does not influence [allReady] however you can wait + /// for and be dependent on a LazySingleton. + void registerLazySingletonAsync( + FactoryFuncAsync factoryFunc, { + String? instanceName, + DisposingFunc? dispose, + }); + + /// Tests if an [instance] of an object or aType [T] or a name [instanceName] + /// is registered inside GetIt + bool isRegistered({Object? instance, String? instanceName}); + + /// Clears all registered types. Handy when writing unit tests + /// If you provided dispose function when registering they will be called + /// [dispose] if `false` it only resets without calling any dispose + /// functions + /// As dispose functions can be async, you should await this function. + Future reset({bool dispose = true}); + + /// Clears all registered types for the current scope + /// If you provided dispose function when registering they will be called + /// [dispose] if `false` it only resets without calling any dispose + /// functions + /// As dispose functions can be async, you should await this function. + Future resetScope({bool dispose = true}); + + /// Creates a new registration scope. If you register types after creating + /// a new scope they will hide any previous registration of the same type. + /// Scopes allow you to manage different live times of your Objects. + /// [scopeName] if you name a scope you can pop all scopes above the named one + /// by using the name. + /// [dispose] function that will be called when you pop this scope. The scope + /// is still valied while it is executed + void pushNewScope({ + String? scopeName, + ScopeDisposeFunc? dispose, + }); + + /// Disposes all factories/Singletons that have ben registered in this scope + /// and pops (destroys) the scope so that the previous scope gets active again. + /// if you provided dispose functions on registration, they will be called. + /// if you passed a dispose function when you pushed this scope it will be + /// calles before the scope is popped. + /// As dispose functions can be async, you should await this function. + Future popScope(); + + /// if you have a lot of scopes with names you can pop (see [popScope]) all + /// scopes above the scope with [name] including that scope + /// Scopes are popped in order from the top + /// As dispose functions can be async, you should await this function. + /// it no scope with [name] exists, nothing is popped and `false` is returned + Future popScopesTill(String name); + + /// Clears the instance of a lazy singleton, + /// being able to call the factory function on the next call + /// of [get] on that type again. + /// you select the lazy Singleton you want to reset by either providing + /// an [instance], its registered type [T] or its registration name. + /// if you need to dispose some resources before the reset, you can + /// provide a [disposingFunction]. This function overrides the disposing + /// you might have provided when registering. + void resetLazySingleton({ + Object? instance, + String? instanceName, + void Function(T)? disposingFunction, + }); + + /// Unregister an [instance] of an object or a factory/singleton by Type [T] or by name + /// [instanceName] if you need to dispose any resources you can do it using + /// [disposingFunction] function that provides a instance of your class to be disposed. + /// This function overrides the disposing you might have provided when registering. + void unregister({ + Object? instance, + String? instanceName, + void Function(T)? disposingFunction, + }); + + /// returns a Future that completes if all asynchronously created Singletons and any + /// Singleton that had [signalsReady==true] are ready. + /// This can be used inside a FutureBuilder to change the UI as soon as all initialization + /// is done + /// If you pass a [timeout], an [WaitingTimeOutException] will be thrown if not all Singletons + /// were ready in the given time. The Exception contains details on which Singletons are not + /// ready yet. if [allReady] should not wait for the completion of async Singletons set + /// [ignorePendingAsyncCreation==true] + Future allReady({ + Duration? timeout, + bool ignorePendingAsyncCreation = false, + }); + + /// Returns a Future that completes if the instance of an Singleton, defined by Type [T] or + /// by name [instanceName] or by passing the an existing [instance], is ready + /// If you pass a [timeout], an [WaitingTimeOutException] will be thrown if the instance + /// is not ready in the given time. The Exception contains details on which Singletons are + /// not ready at that time. + /// [callee] optional parameter which makes debugging easier. Pass `this` in here. + Future isReady({ + Object? instance, + String? instanceName, + Duration? timeout, + Object? callee, + }); + + /// Checks if an async Singleton defined by an [instance], a type [T] or an [instanceName] + /// is ready without waiting + bool isReadySync({ + Object? instance, + String? instanceName, + }); + + /// Returns if all async Singletons are ready without waiting + /// if [allReady] should not wait for the completion of async Singletons set + /// [ignorePendingAsyncCreation==true] + // ignore: avoid_positional_boolean_parameters + bool allReadySync([bool ignorePendingAsyncCreation = false]); + + /// Used to manually signal the ready state of a Singleton. + /// If you want to use this mechanism you have to pass [signalsReady==true] when registering + /// the Singleton. + /// If [instance] has a value GetIt will search for the responsible Singleton + /// and completes all futures that might be waited for by [isReady] + /// If all waiting singletons have signalled ready the future you can get + /// from [allReady] is automatically completed + /// + /// Typically this is used in this way inside the registered objects init + /// method `GetIt.instance.signalReady(this);` + /// + /// if [instance] is `null` and no factory/singleton is waiting to be signalled this + /// will complete the future you got from [allReady], so it can be used to globally + /// giving a ready Signal + /// + /// Both ways are mutual exclusive, meaning either only use the global `signalReady()` and + /// don't register a singleton to signal ready or use any async registrations + /// + /// Or use async registrations methods or let individual instances signal their ready + /// state on their own. + void signalReady(Object? instance); +} diff --git a/lib/get_it_impl.dart b/lib/get_it_impl.dart index 007e7bc..bb7769e 100644 --- a/lib/get_it_impl.dart +++ b/lib/get_it_impl.dart @@ -62,7 +62,7 @@ class _ServiceFactory { /// to enable Singletons to signal that they are ready (their initialization is finished) late Completer _readyCompleter; - /// the returned future of pending async factory calls or facttory call with dependencies + /// the returned future of pending async factory calls or factory call with dependencies Future? pendingResult; /// If other objects are waiting for this one @@ -75,8 +75,7 @@ class _ServiceFactory { String get debugName => '$instanceName : $registrationType'; - bool get canBeWaitedFor => - shouldSignalReady || pendingResult != null || isAsync; + bool get canBeWaitedFor => shouldSignalReady || pendingResult != null || isAsync; final bool shouldSignalReady; @@ -102,9 +101,7 @@ class _ServiceFactory { /// returns an instance depending on the type of the registration if [async==false] T getObject(dynamic param1, dynamic param2) { - assert( - !(factoryType != _ServiceFactoryType.alwaysNew && - (param1 != null || param2 != null)), + assert(!(factoryType != _ServiceFactoryType.alwaysNew && (param1 != null || param2 != null)), 'You can only pass parameters to factories!'); try { @@ -146,17 +143,17 @@ class _ServiceFactory { } } - /// returns an async instance depending on the type of the registration if [async==true] or if [dependsOn.isnoEmpty]. + /// returns an async instance depending on the type of the registration if [async==true] or + /// if [dependsOn.isnoEmpty]. Future getObjectAsync(dynamic param1, dynamic param2) async { - assert( - !(factoryType != _ServiceFactoryType.alwaysNew && - (param1 != null || param2 != null)), + assert(!(factoryType != _ServiceFactoryType.alwaysNew && (param1 != null || param2 != null)), 'You can only pass parameters to factories!'); throwIfNot( - isAsync || pendingResult != null, - StateError( - 'You can only access registered factories/objects this way if they are created asynchronously')); + isAsync || pendingResult != null, + StateError('You can only access registered factories/objects ' + 'this way if they are created asynchronously'), + ); try { switch (factoryType) { case _ServiceFactoryType.alwaysNew: @@ -169,8 +166,7 @@ class _ServiceFactory { param2 == null || param2.runtimeType == param2Type, 'Incompatible Type passed a param2\n' 'expected: $param2Type actual: ${param2.runtimeType}'); - return asyncCreationFunctionParam!(param1 as P1?, param2 as P2?) - as Future; + return asyncCreationFunctionParam!(param1 as P1?, param2 as P2?) as Future; } else { return asyncCreationFunction!() as Future; } @@ -186,8 +182,7 @@ class _ServiceFactory { // We already have a finished instance return Future.value(instance as R); } else { - if (pendingResult != - null) // an async creation is already in progress + if (pendingResult != null) // an async creation is already in progress { return pendingResult as Future; } @@ -197,7 +192,8 @@ class _ServiceFactory { pendingResult = asyncResult.then((newInstance) { if (!shouldSignalReady) { - ///only complete automatically if the registration wasn't marked with [signalsReady==true] + /// only complete automatically if the registration wasn't marked with + /// [signalsReady==true] _readyCompleter.complete(); } instance = newInstance; @@ -221,8 +217,7 @@ class _ServiceFactory { class _Scope { final String? name; final ScopeDisposeFunc? disposeFunc; - final factoriesByName = - >>{}; + final factoriesByName = >>{}; _Scope({this.name, this.disposeFunc}); @@ -235,8 +230,8 @@ class _Scope { factoriesByName.clear(); } - List<_ServiceFactory> get allFactories => factoriesByName.values - .fold>([], (sum, x) => sum..addAll(x.values)); + List<_ServiceFactory> get allFactories => + factoriesByName.values.fold>([], (sum, x) => sum..addAll(x.values)); Future dispose() async { await disposeFunc?.call(); @@ -259,14 +254,18 @@ class _GetItImplementation implements GetIt { bool allowReassignment = false; /// Is used by several other functions to retrieve the correct [_ServiceFactory] - _ServiceFactory /*!*/ /*!*/ /*!*/ /*!*/ /*!*/ _findFactoryByNameAndType< - T extends Object>(String? instanceName, [Type? type]) { + _ServiceFactory? _findFirstFactoryByNameAndTypeOrNull( + String? instanceName, [ + Type? type, + ]) { /// We use an assert here instead of an `if..throw` because it gets called on every call /// of [get] /// `(const Object() is! T)` tests if [T] is a real type and not Object or dynamic assert( type != null || const Object() is! T, - 'GetIt: The compiler could not infer the type. You have to provide a type and optional a name. Did you accidentally do `var sl=GetIt.instance();` instead of var sl=GetIt.instance;', + 'GetIt: The compiler could not infer the type. You have to provide a type ' + 'and optionally a name. Did you accidentally do `var sl=GetIt.instance();` ' + 'instead of var sl=GetIt.instance;', ); _ServiceFactory? instanceFactory; @@ -288,12 +287,24 @@ class _GetItImplementation implements GetIt { } scopeLevel--; } + + return instanceFactory; + } + + /// Is used by several other functions to retrieve the correct [_ServiceFactory] + _ServiceFactory /*!*/ /*!*/ /*!*/ /*!*/ /*!*/ _findFactoryByNameAndType( + String? instanceName, [ + Type? type, + ]) { + final instanceFactory = _findFirstFactoryByNameAndTypeOrNull(instanceName, type); + assert( - instanceFactory != null, - 'Object/factory with ${instanceName != null ? 'with name $instanceName and ' : ''}' - ' type ${T.toString()} is not registered inside GetIt. ' - '\n(Did you accidentally do GetIt sl=GetIt.instance(); instead of GetIt sl=GetIt.instance;' - '\nDid you forget to register it?)'); + instanceFactory != null, + 'Object/factory with ${instanceName != null ? 'with name $instanceName and ' : ''}' + ' type ${T.toString()} is not registered inside GetIt. ' + '\n(Did you accidentally do GetIt sl=GetIt.instance(); instead of GetIt sl=GetIt.instance;' + '\nDid you forget to register it?)', + ); return instanceFactory!; } @@ -303,26 +314,36 @@ class _GetItImplementation implements GetIt { /// for factories you can pass up to 2 parameters [param1,param2] they have to match the types /// given at registration with [registerFactoryParam()] @override - T get( - {String? instanceName, dynamic param1, dynamic param2}) { + T get({ + String? instanceName, + dynamic param1, + dynamic param2, + }) { final instanceFactory = _findFactoryByNameAndType(instanceName); Object instance = Object; //late if (instanceFactory.isAsync || instanceFactory.pendingResult != null) { /// We use an assert here instead of an `if..throw` for performance reasons assert( - instanceFactory.factoryType == _ServiceFactoryType.constant || - instanceFactory.factoryType == _ServiceFactoryType.lazy, - "You can't use get with an async Factory of ${instanceName ?? T.toString()}."); - assert(instanceFactory.isReady, - 'You tried to access an instance of ${instanceName ?? T.toString()} that was not ready yet'); + instanceFactory.factoryType == _ServiceFactoryType.constant || + instanceFactory.factoryType == _ServiceFactoryType.lazy, + "You can't use get with an async Factory of ${instanceName ?? T.toString()}.", + ); + assert( + instanceFactory.isReady, + 'You tried to access an instance of ${instanceName ?? T.toString()} that is not ready yet', + ); instance = instanceFactory.instance!; } else { instance = instanceFactory.getObject(param1, param2); } - assert(instance is T, - 'Object with name $instanceName has a different type (${instanceFactory.registrationType.toString()}) than the one that is inferred (${T.toString()}) where you call it'); + assert( + instance is T, + 'Object with name $instanceName has a different type ' + '(${instanceFactory.registrationType.toString()}) than the one that is inferred ' + '(${T.toString()}) where you call it', + ); return instance as T; } @@ -330,46 +351,55 @@ class _GetItImplementation implements GetIt { /// Callable class so that you can write `GetIt.instance` instead of /// `GetIt.instance.get` @override - T call( - {String? instanceName, dynamic param1, dynamic param2}) { + T call({ + String? instanceName, + dynamic param1, + dynamic param2, + }) { return get(instanceName: instanceName, param1: param1, param2: param2); } /// Returns an Future of an instance that is created by an async factory or a Singleton that is /// not ready with its initialization. - /// for async factories you can pass up to 2 parameters [param1,param2] they have to match the types - /// given at registration with [registerFactoryParamAsync()] + /// for async factories you can pass up to 2 parameters [param1,param2] they have to match + /// the types given at registration with [registerFactoryParamAsync()] @override - Future getAsync( - {String? instanceName, dynamic param1, dynamic param2}) { + Future getAsync({ + String? instanceName, + dynamic param1, + dynamic param2, + }) { final factoryToGet = _findFactoryByNameAndType(instanceName); return factoryToGet.getObjectAsync(param1, param2); } /// registers a type so that a new instance will be created on each call of [get] on that type /// [T] type to register - /// [func] factory function for this type + /// [factoryFunc] factory function for this type /// [instanceName] if you provide a value here your factory gets registered with that /// name instead of a type. This should only be necessary if you need to register more /// than one instance of one type. Its highly not recommended @override - void registerFactory(FactoryFunc func, - {String? instanceName}) { + void registerFactory( + FactoryFunc factoryFunc, { + String? instanceName, + }) { _register( - type: _ServiceFactoryType.alwaysNew, - instanceName: instanceName, - factoryFunc: func, - isAsync: false, - shouldSignalReady: false); + type: _ServiceFactoryType.alwaysNew, + instanceName: instanceName, + factoryFunc: factoryFunc, + isAsync: false, + shouldSignalReady: false, + ); } - /// registers a type so that a new instance will be created on each call of [get] on that type based on - /// up to two parameters provided to [get()] + /// registers a type so that a new instance will be created on each call of [get] on that + /// type based on up to two parameters provided to [get()] /// [T] type to register /// [P1] type of param1 /// [P2] type of param2 /// if you use only one parameter pass void here - /// [func] factory function for this type that accepts two parameters + /// [factoryFunc] factory function for this type that accepts two parameters /// [instanceName] if you provide a value here your factory gets registered with that /// name instead of a type. This should only be necessary if you need to register more /// than one instance of one type. Its highly not recommended @@ -384,12 +414,13 @@ class _GetItImplementation implements GetIt { /// => TestClassParam(param1:s); @override void registerFactoryParam( - FactoryFuncParam func, - {String? instanceName}) { + FactoryFuncParam factoryFunc, { + String? instanceName, + }) { _register( type: _ServiceFactoryType.alwaysNew, instanceName: instanceName, - factoryFuncParam: func, + factoryFuncParam: factoryFunc, isAsync: false, shouldSignalReady: false); } @@ -397,12 +428,14 @@ class _GetItImplementation implements GetIt { /// We use a separate function for the async registration instead just a new parameter /// so make the intention explicit @override - void registerFactoryAsync(FactoryFuncAsync asyncFunc, - {String? instanceName}) { + void registerFactoryAsync( + FactoryFuncAsync factoryFunc, { + String? instanceName, + }) { _register( type: _ServiceFactoryType.alwaysNew, instanceName: instanceName, - factoryFuncAsync: asyncFunc, + factoryFuncAsync: factoryFunc, isAsync: true, shouldSignalReady: false); } @@ -414,7 +447,7 @@ class _GetItImplementation implements GetIt { /// [P1] type of param1 /// [P2] type of param2 /// if you use only one parameter pass void here - /// [func] factory function for this type that accepts two parameters + /// [factoryFunc] factory function for this type that accepts two parameters /// [instanceName] if you provide a value here your factory gets registered with that /// name instead of a type. This should only be necessary if you need to register more /// than one instance of one type. Its highly not recommended @@ -429,12 +462,13 @@ class _GetItImplementation implements GetIt { /// => TestClassParam(param1:s); @override void registerFactoryParamAsync( - FactoryFuncParamAsync func, - {String? instanceName}) { + FactoryFuncParamAsync factoryFunc, { + String? instanceName, + }) { _register( type: _ServiceFactoryType.alwaysNew, instanceName: instanceName, - factoryFuncParamAsync: func, + factoryFuncParamAsync: factoryFunc, isAsync: true, shouldSignalReady: false); } @@ -442,19 +476,22 @@ class _GetItImplementation implements GetIt { /// registers a type as Singleton by passing a factory function that will be called /// on the first call of [get] on that type /// [T] type to register - /// [func] factory function for this type + /// [factoryFunc] factory function for this type /// [instanceName] if you provide a value here your factory gets registered with that /// name instead of a type. This should only be necessary if you need to register more /// than one instance of one type. Its highly not recommended /// [registerLazySingleton] does not influence [allReady] however you can wait /// for and be dependent on a LazySingleton. @override - void registerLazySingleton(FactoryFunc func, - {String? instanceName, DisposingFunc? dispose}) { + void registerLazySingleton( + FactoryFunc factoryFunc, { + String? instanceName, + DisposingFunc? dispose, + }) { _register( type: _ServiceFactoryType.lazy, instanceName: instanceName, - factoryFunc: func, + factoryFunc: factoryFunc, isAsync: false, shouldSignalReady: false, disposeFunc: dispose, @@ -464,11 +501,11 @@ class _GetItImplementation implements GetIt { /// registers a type as Singleton by passing an [instance] of that type /// that will be returned on each call of [get] on that type /// [T] type to register - /// If [signalsReady] is set to `true` it means that the future you can get from `allReady()` cannot complete until this - /// registration was signalled ready by calling [signalsReady(instance)] - /// [instanceName] if you provide a value here your instance gets registered with that - /// name instead of a type. This should only be necessary if you need to register more - /// than one instance of one type. Its highly not recommended + /// If [signalsReady] is set to `true` it means that the future you can get from `allReady()` + /// cannot complete until this registration was signalled ready by calling + /// [signalsReady(instance)] [instanceName] if you provide a value here your instance gets + /// registered with that name instead of a type. This should only be necessary if you need + /// to register more than one instance of one type. Its highly not recommended @override void registerSingleton( T instance, { @@ -500,7 +537,7 @@ class _GetItImplementation implements GetIt { /// cannot complete until this this instance was signalled ready by calling [signalsReady(instance)]. @override void registerSingletonWithDependencies( - FactoryFunc providerFunc, { + FactoryFunc factoryFunc, { String? instanceName, Iterable? dependsOn, bool? signalsReady, @@ -510,41 +547,43 @@ class _GetItImplementation implements GetIt { type: _ServiceFactoryType.constant, instanceName: instanceName, isAsync: false, - factoryFunc: providerFunc, + factoryFunc: factoryFunc, dependsOn: dependsOn, shouldSignalReady: signalsReady ?? [] is List, disposeFunc: dispose, ); } - /// registers a type as Singleton by passing an asynchronous factory function which has to return the instance - /// that will be returned on each call of [get] on that type. - /// Therefore you have to ensure that the instance is ready before you use [get] on it or use [getAsync()] to - /// wait for the completion. + /// registers a type as Singleton by passing an asynchronous factory function which has to + /// return the instance that will be returned on each call of [get] on that type. + /// Therefore you have to ensure that the instance is ready before you use [get] on it or use + /// [getAsync()] to wait for the completion. /// You can wait/check if the instance is ready by using [isReady()] and [isReadySync()]. - /// [factoryfunc] is executed immediately if there are no dependencies to other Singletons (see below). - /// As soon as it returns, this instance is marked as ready unless you don't set [signalsReady==true] - /// [instanceName] if you provide a value here your instance gets registered with that - /// name instead of a type. This should only be necessary if you need to register more - /// than one instance of one type. Its highly not recommended - /// [dependsOn] if this instance depends on other registered Singletons before it can be initialized - /// you can either orchestrate this manually using [isReady()] or pass a list of the types that the - /// instance depends on here. [factoryFunc] won't get executed till this types are ready. - /// If [signalsReady] is set to `true` it means that the future you can get from `allReady()` cannot complete until this - /// this instance was signalled ready by calling [signalsReady(instance)]. In that case no automatic ready signal - /// is made after completion of [factoryfunc] + /// [factoryFunc] is executed immediately if there are no dependencies to other Singletons + /// (see below). As soon as it returns, this instance is marked as ready unless you don't set + /// [signalsReady==true] [instanceName] if you provide a value here your instance gets + /// registered with that name instead of a type. This should only be necessary if you need + /// to register more than one instance of one type. Its highly not recommended + /// [dependsOn] if this instance depends on other registered Singletons before it can be + /// initialized you can either orchestrate this manually using [isReady()] or pass a list of + /// the types that the instance depends on here. [factoryFunc] won't get executed till this + /// types are ready. If [signalsReady] is set to `true` it means that the future you can get + /// from `allReady()` cannot complete until this this instance was signalled ready by calling + /// [signalsReady(instance)]. In that case no automatic ready signal is made after + /// completion of [factoryFunc] @override void registerSingletonAsync( - FactoryFuncAsync providerFunc, - {String? instanceName, - Iterable? dependsOn, - bool? signalsReady, - DisposingFunc? dispose}) { + FactoryFuncAsync factoryFunc, { + String? instanceName, + Iterable? dependsOn, + bool? signalsReady, + DisposingFunc? dispose, + }) { _register( type: _ServiceFactoryType.constant, instanceName: instanceName, isAsync: true, - factoryFuncAsync: providerFunc, + factoryFuncAsync: factoryFunc, dependsOn: dependsOn, shouldSignalReady: signalsReady ?? [] is List, disposeFunc: dispose, @@ -552,14 +591,14 @@ class _GetItImplementation implements GetIt { } /// registers a type as Singleton by passing a async factory function that will be called - /// on the first call of [getAsnc] on that type + /// on the first call of [getAsync] on that type /// This is a rather esoteric requirement so you should seldom have the need to use it. /// This factory function [providerFunc] isn't called immediately but wait till the first call by /// [getAsync()] or [isReady()] is made /// To control if an async Singleton has completed its [providerFunc] gets a `Completer` passed /// as parameter that has to be completed to signal that this instance is ready. - /// Therefore you have to ensure that the instance is ready before you use [get] on it or use [getAsync()] to - /// wait for the completion. + /// Therefore you have to ensure that the instance is ready before you use [get] on it or + /// use [getAsync()] to wait for the completion. /// You can wait/check if the instance is ready by using [isReady()] and [isReadySync()]. /// [instanceName] if you provide a value here your instance gets registered with that /// name instead of a type. This should only be necessary if you need to register more @@ -567,19 +606,22 @@ class _GetItImplementation implements GetIt { /// [registerLazySingletonAsync] does not influence [allReady] however you can wait /// for and be dependent on a LazySingleton. @override - void registerLazySingletonAsync(FactoryFuncAsync func, - {String? instanceName, DisposingFunc? dispose}) { + void registerLazySingletonAsync( + FactoryFuncAsync factoryFunc, { + String? instanceName, + DisposingFunc? dispose, + }) { _register( isAsync: true, type: _ServiceFactoryType.lazy, instanceName: instanceName, - factoryFuncAsync: func, + factoryFuncAsync: factoryFunc, shouldSignalReady: false, disposeFunc: dispose, ); } - /// Clears all registered types. Handy when writing unit tests + /// Clears all registered types. Handy when writing unit tests. @override Future reset({bool dispose = true}) async { if (dispose) { @@ -607,13 +649,14 @@ class _GetItImplementation implements GetIt { /// [scopeName] if you name a scope you can pop all scopes above the named one /// by using the name. /// [dispose] function that will be called when you pop this scope. The scope - /// is still valied while it is executed + /// is still valid while it is executed @override void pushNewScope({String? scopeName, ScopeDisposeFunc? dispose}) { - assert(scopeName != _baseScopeName, - 'This name is reseved for the real base scope'); - assert(_scopes.firstWhereOrNull((x) => x.name == scopeName) == null, - 'You already have used the scope name $scopeName'); + assert(scopeName != _baseScopeName, 'This name is reserved for the real base scope.'); + assert( + _scopes.firstWhereOrNull((x) => x.name == scopeName) == null, + 'You already have used the scope name $scopeName', + ); _scopes.add(_Scope(name: scopeName, disposeFunc: dispose)); } @@ -622,13 +665,13 @@ class _GetItImplementation implements GetIt { /// if you provided dispose functions on registration, they will be called. /// if you passed a dispose function when you pushed this scope it will be /// calles before the scope is popped. - /// As dispose funcions can be async, you should await this function. + /// As dispose functions can be async, you should await this function. @override Future popScope() async { assert( - _scopes.length > 1, - "You are already on the base scope. you can't pop" - ' this one'); + _scopes.length > 1, + "You are already on the base scope. you can't pop this one", + ); await _currentScope.dispose(); await _currentScope.reset(dispose: true); _scopes.removeLast(); @@ -636,8 +679,8 @@ class _GetItImplementation implements GetIt { /// if you have a lot of scopes with names you can pop (see [popScope]) all scopes above /// the scope with [scopeName] including that scope - /// Scopes are poped in order from the top - /// As dispose funcions can be async, you should await this function. + /// Scopes are popped in order from the top + /// As dispose functions can be async, you should await this function. @override Future popScopesTill(String scopeName) async { assert(scopeName != _baseScopeName, "You can't pop the base scope"); @@ -652,44 +695,53 @@ class _GetItImplementation implements GetIt { return true; } - void _register( - {required _ServiceFactoryType type, - FactoryFunc? factoryFunc, - FactoryFuncParam? factoryFuncParam, - FactoryFuncAsync? factoryFuncAsync, - FactoryFuncParamAsync? factoryFuncParamAsync, - T? instance, - required String? instanceName, - required bool isAsync, - Iterable? dependsOn, - required bool shouldSignalReady, - DisposingFunc? disposeFunc}) { + void _register({ + required _ServiceFactoryType type, + FactoryFunc? factoryFunc, + FactoryFuncParam? factoryFuncParam, + FactoryFuncAsync? factoryFuncAsync, + FactoryFuncParamAsync? factoryFuncParamAsync, + T? instance, + required String? instanceName, + required bool isAsync, + Iterable? dependsOn, + required bool shouldSignalReady, + DisposingFunc? disposeFunc, + }) { throwIfNot( const Object() is! T, - 'GetIt: You have to provide type. Did you accidentally do `var sl=GetIt.instance();` instead of var sl=GetIt.instance;', + 'GetIt: You have to provide type. Did you accidentally do `var sl=GetIt.instance();` ' + 'instead of var sl=GetIt.instance;', ); final factoriesByName = _currentScope.factoriesByName; throwIf( - factoriesByName.containsKey(instanceName) && - factoriesByName[instanceName]!.containsKey(T) && - !allowReassignment, - ArgumentError( - 'Object/factory with ${instanceName != null ? 'with name $instanceName and ' : ''}' - ' type ${T.toString()} is already registered inside GetIt. ')); - - final serviceFactory = _ServiceFactory(type, - creationFunction: factoryFunc, - creationFunctionParam: factoryFuncParam, - asyncCreationFunctionParam: factoryFuncParamAsync, - asyncCreationFunction: factoryFuncAsync, - instance: instance, - isAsync: isAsync, - instanceName: instanceName, - shouldSignalReady: shouldSignalReady, - disposeFunction: disposeFunc); + factoriesByName.containsKey(instanceName) && + factoriesByName[instanceName]!.containsKey(T) && + !allowReassignment, + ArgumentError( + // ignore: missing_whitespace_between_adjacent_strings + 'Object/factory with ${instanceName != null ? 'with name $instanceName and ' : ' '}' + 'type ${T.toString()} is already registered inside GetIt. ', + ), + ); + + final serviceFactory = _ServiceFactory( + type, + creationFunction: factoryFunc, + creationFunctionParam: factoryFuncParam, + asyncCreationFunctionParam: factoryFuncParamAsync, + asyncCreationFunction: factoryFuncAsync, + instance: instance, + isAsync: isAsync, + instanceName: instanceName, + shouldSignalReady: shouldSignalReady, + disposeFunction: disposeFunc, + ); - factoriesByName.putIfAbsent(instanceName, - () => >{}); + factoriesByName.putIfAbsent( + instanceName, + () => >{}, + ); factoriesByName[instanceName]![T] = serviceFactory; // simple Singletons get creates immediately @@ -704,8 +756,7 @@ class _GetItImplementation implements GetIt { // if its an async or an dependent Singleton we start its creation function here after we check if // it is dependent on other registered Singletons. - if ((isAsync || (dependsOn?.isNotEmpty ?? false)) && - type == _ServiceFactoryType.constant) { + if ((isAsync || (dependsOn?.isNotEmpty ?? false)) && type == _ServiceFactoryType.constant) { /// Any client awaiting the completion of this Singleton /// Has to wait for the completion of the Singleton itself as well /// as for the completion of all the Singletons this one depends on @@ -722,8 +773,10 @@ class _GetItImplementation implements GetIt { for (final type in dependsOn!) { final dependentFactory = _findFactoryByNameAndType(null, type); - throwIfNot(dependentFactory.canBeWaitedFor, - ArgumentError('Dependent Type $type is not an async Singleton')); + throwIfNot( + dependentFactory.canBeWaitedFor, + ArgumentError('Dependent Type $type is not an async Singleton'), + ); dependentFactory.objectsWaiting.add(serviceFactory.registrationType); dependentFutureGroup.add(dependentFactory._readyCompleter.future); } @@ -738,12 +791,12 @@ class _GetItImplementation implements GetIt { outerFutureGroup.add(dependentFuture); /// if someone uses getAsync on an async Singleton that has not be started to get created - /// because its dependent on other objects this doesn't work because [pendingResult] is not set in - /// that case. Therefore we have to set [outerFutureGroup] as [pendingResult] + /// because its dependent on other objects this doesn't work because [pendingResult] is + /// not set in that case. Therefore we have to set [outerFutureGroup] as [pendingResult] dependentFuture.then((_) { Future isReadyFuture; if (!isAsync) { - /// SingletonWithDepencencies + /// SingletonWithDependencies serviceFactory.instance = factoryFunc!(); isReadyFuture = Future.value(serviceFactory.instance as T); if (!serviceFactory.shouldSignalReady) { @@ -771,8 +824,7 @@ class _GetItImplementation implements GetIt { /// outerFutureGroup.future returns a Future and not a Future /// As we know that the actual factory function was added last to the FutureGroup /// we just use that one - serviceFactory.pendingResult = - outerFutureGroup.future.then((completedFutures) { + serviceFactory.pendingResult = outerFutureGroup.future.then((completedFutures) { return completedFutures.last as T; }); } @@ -781,44 +833,34 @@ class _GetItImplementation implements GetIt { /// Tests if an [instance] of an object or aType [T] or a name [instanceName] /// is registered inside GetIt @override - bool isRegistered( - {Object? instance, String? instanceName}) { - try { - if (instance != null) { - _findFactoryByInstance(instance); - } else { - _findFactoryByNameAndType(instanceName); - } - // because not being registered isn't an error when you want to check if an object is registered - // ignore: avoid_catching_errors - } on StateError { - return false; - // ignore: avoid_catching_errors - } on AssertionError { - return false; + bool isRegistered({ + Object? instance, + String? instanceName, + }) { + if (instance != null) { + return _findFirstFactoryByInstanceOrNull(instance) != null; + } else { + return _findFirstFactoryByNameAndTypeOrNull(instanceName) != null; } - return true; } /// Unregister an instance of an object or a factory/singleton by Type [T] or by name [instanceName] /// if you need to dispose any resources you can do it using [disposingFunction] function /// that provides a instance of your class to be disposed @override - void unregister( - {Object? instance, - String? instanceName, - void Function(T)? disposingFunction}) { - _ServiceFactory factoryToRemove; - if (instance != null) { - factoryToRemove = _findFactoryByInstance(instance); - } else { - factoryToRemove = _findFactoryByNameAndType(instanceName); - } + void unregister({ + Object? instance, + String? instanceName, + void Function(T)? disposingFunction, + }) { + final factoryToRemove = instance != null + ? _findFactoryByInstance(instance) + : _findFactoryByNameAndType(instanceName); throwIf( - factoryToRemove.objectsWaiting.isNotEmpty, - StateError( - 'There are still other objects waiting for this instance so signal ready')); + factoryToRemove.objectsWaiting.isNotEmpty, + StateError('There are still other objects waiting for this instance so signal ready'), + ); _currentScope.factoriesByName[factoryToRemove.instanceName]! .remove(factoryToRemove.registrationType); @@ -826,7 +868,7 @@ class _GetItImplementation implements GetIt { if (factoryToRemove.instance != null) { disposingFunction?.call(factoryToRemove.instance as T); } else { - factoryToRemove?.dispose(); + factoryToRemove.dispose(); } } @@ -838,10 +880,11 @@ class _GetItImplementation implements GetIt { /// if you need to dispose some resources before the reset, you can /// provide a [disposingFunction] @override - void resetLazySingleton( - {Object? instance, - String? instanceName, - void Function(T)? disposingFunction}) { + void resetLazySingleton({ + Object? instance, + String? instanceName, + void Function(T)? disposingFunction, + }) { _ServiceFactory instanceFactory; if (instance != null) { @@ -850,9 +893,9 @@ class _GetItImplementation implements GetIt { instanceFactory = _findFactoryByNameAndType(instanceName); } throwIfNot( - instanceFactory.factoryType == _ServiceFactoryType.lazy, - StateError( - 'There is no type ${instance.runtimeType} registered as LazySingleton in GetIt')); + instanceFactory.factoryType == _ServiceFactoryType.lazy, + StateError('There is no type ${instance.runtimeType} registered as LazySingleton in GetIt'), + ); if (instanceFactory.instance != null) { if (disposingFunction != null) { @@ -867,25 +910,27 @@ class _GetItImplementation implements GetIt { instanceFactory._readyCompleter = Completer(); } - List<_ServiceFactory> get _allFactories => _scopes - .fold>([], (sum, x) => sum..addAll(x.allFactories)); + List<_ServiceFactory> get _allFactories => _scopes.fold>( + [], + (sum, x) => sum..addAll(x.allFactories), + ); + + _ServiceFactory? _findFirstFactoryByInstanceOrNull(Object instance) { + final registeredFactories = _allFactories.where((x) => identical(x.instance, instance)); + return registeredFactories.isEmpty ? null : registeredFactories.first; + } _ServiceFactory _findFactoryByInstance(Object instance) { - final registeredFactories = - _allFactories.where((x) => identical(x.instance, instance)); + final registeredFactory = _findFirstFactoryByInstanceOrNull(instance); throwIf( - registeredFactories.isEmpty, - StateError( - 'This instance of the type ${instance.runtimeType} is not available in GetIt ' - 'If you have registered it as LazySingleton, are you sure you have used ' - 'it at least once?')); + registeredFactory == null, + StateError('This instance of the type ${instance.runtimeType} is not available in GetIt ' + 'If you have registered it as LazySingleton, are you sure you have used ' + 'it at least once?'), + ); - throwIfNot( - registeredFactories.length == 1, - StateError( - 'One Instance of ${instance.runtimeType} more than once registered in GetIt')); - return registeredFactories.first; + return registeredFactory!; } /// Used to manually signal the ready state of a Singleton. @@ -915,14 +960,17 @@ class _GetItImplementation implements GetIt { registeredInstance = _findFactoryByInstance(instance); throwIfNot( - registeredInstance.shouldSignalReady, - ArgumentError.value(instance, - 'This instance of type ${instance.runtimeType} is not supposed to be signalled.\nDid you forget to set signalsReady==true when registering it?')); + registeredInstance.shouldSignalReady, + ArgumentError.value( + instance, + 'This instance of type ${instance.runtimeType} is not supposed to be ' + 'signalled.\nDid you forget to set signalsReady==true when registering it?'), + ); throwIf( - registeredInstance.isReady, - StateError( - 'This instance of type ${instance.runtimeType} was already signalled')); + registeredInstance.isReady, + StateError('This instance of type ${instance.runtimeType} was already signalled'), + ); registeredInstance._readyCompleter.complete(); registeredInstance.objectsWaiting.clear(); @@ -933,38 +981,36 @@ class _GetItImplementation implements GetIt { /// but aren't signalled we throw an error with details which objects are concerned final notReady = _allFactories .where((x) => - (x.shouldSignalReady) && (!x.isReady) || - (x.pendingResult != null) && (!x.isReady)) + (x.shouldSignalReady) && (!x.isReady) || (x.pendingResult != null) && (!x.isReady)) .map((x) => '${x.registrationType}/${x.instanceName}') .toList(); throwIf( - notReady.isNotEmpty, - StateError( - "You can't signal ready manually if you have registered instances that should signal ready or are asnyc.\n" - // this lint is stupif because it doesn't recognize newlines - // ignore: missing_whitespace_between_adjacent_strings - 'Did you forget to pass an object instance?' - 'This registered types/names: $notReady should signal ready but are not ready')); + notReady.isNotEmpty, + StateError("You can't signal ready manually if you have registered instances that should " + "signal ready or are async.\n" + // this lint is stupid because it doesn't recognize newlines + // ignore: missing_whitespace_between_adjacent_strings + 'Did you forget to pass an object instance?' + 'This registered types/names: $notReady should signal ready but are not ready'), + ); _globalReadyCompleter.complete(); } } - /// returns a Future that completes if all asynchronously created Singletons and any Singleton that had - /// [signalsReady==true] are ready. + /// returns a Future that completes if all asynchronously created Singletons and any + /// Singleton that had [signalsReady==true] are ready. /// This can be used inside a FutureBuilder to change the UI as soon as all initialization - /// is done - /// If you pass a [timeout], an [WaitingTimeOutException] will be thrown if not all Singletons - /// were ready in the given time. The Exception contains details on which Singletons are not ready yet. + /// is done. If you pass a [timeout], an [WaitingTimeOutException] will be thrown if not all + /// Singletons were ready in the given time. The Exception contains details on which + /// Singletons are not ready yet. @override - Future allReady( - {Duration? timeout, bool ignorePendingAsyncCreation = false}) { + Future allReady({Duration? timeout, bool ignorePendingAsyncCreation = false}) { final futures = FutureGroup(); _allFactories .where((x) => (x.isAsync && !ignorePendingAsyncCreation || - (!x.isAsync && - x.pendingResult != null) || // Singletons with dependencies + (!x.isAsync && x.pendingResult != null) || // Singletons with dependencies x.shouldSignalReady) && !x.isReady && x.factoryType == _ServiceFactoryType.constant) @@ -987,9 +1033,7 @@ class _GetItImplementation implements GetIt { final notReadyTypes = _allFactories .where((x) => (x.isAsync && !ignorePendingAsyncCreation || - (!x.isAsync && - x.pendingResult != - null) || // Singletons with dependencies + (!x.isAsync && x.pendingResult != null) || // Singletons with dependencies x.shouldSignalReady) && !x.isReady && x.factoryType == _ServiceFactoryType.constant || @@ -1029,20 +1073,17 @@ class _GetItImplementation implements GetIt { x.objectsWaiting.isNotEmpty) .map>>( (isWaitedFor) => MapEntry( - isWaitedFor.debugName, - isWaitedFor.objectsWaiting - .map((waitedByType) => waitedByType.toString()) - .toList()), + isWaitedFor.debugName, + isWaitedFor.objectsWaiting.map((waitedByType) => waitedByType.toString()).toList(), + ), ), ); final notReady = allFactories - .where((x) => - (x.shouldSignalReady || x.pendingResult != null) && !x.isReady) + .where((x) => (x.shouldSignalReady || x.pendingResult != null) && !x.isReady) .map((f) => f.debugName) .toList(); final areReady = allFactories - .where((x) => - (x.shouldSignalReady || x.pendingResult != null) && x.isReady) + .where((x) => (x.shouldSignalReady || x.pendingResult != null) && x.isReady) .map((f) => f.debugName) .toList(); @@ -1069,19 +1110,17 @@ class _GetItImplementation implements GetIt { factoryToCheck = _findFactoryByNameAndType(instanceName); } throwIfNot( - factoryToCheck.canBeWaitedFor && - factoryToCheck.factoryType != _ServiceFactoryType.alwaysNew, + factoryToCheck.canBeWaitedFor && factoryToCheck.factoryType != _ServiceFactoryType.alwaysNew, ArgumentError( - 'You only can use this function on Singletons that are async, that are marked as dependen ' - 'or that are marked with "signalsReady==true"'), + 'You only can use this function on Singletons that are async, that are marked as ' + 'dependent or that are marked with "signalsReady==true"'), ); factoryToCheck.objectsWaiting.add(callee.runtimeType); if (factoryToCheck.isAsync && factoryToCheck.factoryType == _ServiceFactoryType.lazy && factoryToCheck.instance == null) { if (timeout != null) { - return factoryToCheck.getObjectAsync(null, null).timeout(timeout, - onTimeout: () { + return factoryToCheck.getObjectAsync(null, null).timeout(timeout, onTimeout: () { _throwTimeoutError(); return null; }); @@ -1090,8 +1129,7 @@ class _GetItImplementation implements GetIt { } } if (timeout != null) { - return factoryToCheck._readyCompleter.future - .timeout(timeout, onTimeout: _throwTimeoutError); + return factoryToCheck._readyCompleter.future.timeout(timeout, onTimeout: _throwTimeoutError); } else { return factoryToCheck._readyCompleter.future; } @@ -1108,11 +1146,10 @@ class _GetItImplementation implements GetIt { factoryToCheck = _findFactoryByNameAndType(instanceName); } throwIfNot( - factoryToCheck.canBeWaitedFor && - factoryToCheck.factoryType != _ServiceFactoryType.alwaysNew, - ArgumentError( - 'You only can use this function on async Singletons or Singletons ' - 'that have ben marked with "signalsReady" or that they depend on others')); + factoryToCheck.canBeWaitedFor && factoryToCheck.factoryType != _ServiceFactoryType.alwaysNew, + ArgumentError('You only can use this function on async Singletons or Singletons ' + 'that have ben marked with "signalsReady" or that they depend on others'), + ); return factoryToCheck.isReady; } } From 8f2dcdb1dbc0928b07af0160e59efe352cdaad3e Mon Sep 17 00:00:00 2001 From: Noor Dawod Date: Fri, 1 Jan 2021 13:11:04 +0100 Subject: [PATCH 4/9] Reformatted code with 100 characters per line. --- example/lib/main.dart | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 453c98e..3cd4c89 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -6,8 +6,7 @@ import 'package:get_it_example/app_model.dart'; GetIt getIt = GetIt.instance; void main() { - getIt.registerSingleton(AppModelImplementation(), - signalsReady: true); + getIt.registerSingleton(AppModelImplementation(), signalsReady: true); runApp(MyApp()); } @@ -40,9 +39,7 @@ class _MyHomePageState extends State { void initState() { // Access the instance of the registered AppModel // As we don't know for sure if AppModel is already ready we use getAsync - getIt - .isReady() - .then((_) => getIt().addListener(update)); + getIt.isReady().then((_) => getIt().addListener(update)); // Alternative // getIt.get().addListener(update); From 88859b9b831bab86908772fb99d8d1705680bbea Mon Sep 17 00:00:00 2001 From: Noor Dawod Date: Fri, 1 Jan 2021 13:20:15 +0100 Subject: [PATCH 5/9] Bump up version. --- pubspec.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index 44bc557..e029b04 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,8 +1,8 @@ name: get_it description: Simple direct Service Locator that allows to decouple the interface from a concrete implementation and to access the concrete implementation from everywhere in your App" -version: 6.0.0-nullsafety.1 +version: 6.0.0-nullsafety.2 maintainer: Thomas Burkhart (@escamoteur) -authors: +authors: - Flutter Community - Thomas Burkhart @@ -13,7 +13,7 @@ environment: dependencies: async: ^2.5.0-nullsafety.3 - collection: 1.15.0-nullsafety.5 + collection: 1.15.0-nullsafety.5 meta: ^1.3.0-nullsafety.6 dev_dependencies: From 75ae0801137a079ef5307bbd4dff3d301bbc1a0e Mon Sep 17 00:00:00 2001 From: Noor Dawod Date: Fri, 1 Jan 2021 17:19:33 +0100 Subject: [PATCH 6/9] Fix double spaces in error message. --- lib/get_it_impl.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/get_it_impl.dart b/lib/get_it_impl.dart index bb7769e..613a271 100644 --- a/lib/get_it_impl.dart +++ b/lib/get_it_impl.dart @@ -300,8 +300,9 @@ class _GetItImplementation implements GetIt { assert( instanceFactory != null, - 'Object/factory with ${instanceName != null ? 'with name $instanceName and ' : ''}' - ' type ${T.toString()} is not registered inside GetIt. ' + // ignore: missing_whitespace_between_adjacent_strings + 'Object/factory with ${instanceName != null ? 'with name $instanceName and ' : ' '}' + 'type ${T.toString()} is not registered inside GetIt. ' '\n(Did you accidentally do GetIt sl=GetIt.instance(); instead of GetIt sl=GetIt.instance;' '\nDid you forget to register it?)', ); From 74fbe585c957a326493e1405e7ac8da9fc3a3168 Mon Sep 17 00:00:00 2001 From: Noor Dawod Date: Fri, 8 Jan 2021 00:37:34 +0100 Subject: [PATCH 7/9] Reformat the code using "flutter format", set line to 80 characters. --- .editorconfig | 2 +- example/lib/main.dart | 7 ++- lib/get_it.dart | 19 +++++-- lib/get_it_impl.dart | 121 +++++++++++++++++++++++++++--------------- 4 files changed, 99 insertions(+), 50 deletions(-) diff --git a/.editorconfig b/.editorconfig index db364f4..ea8bb05 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,6 +5,6 @@ trim_trailing_whitespace = true end_of_line = lf insert_final_newline = true charset = utf-8 -max_line_length = 100 +max_line_length = 80 indent_size = 2 indent_style = space diff --git a/example/lib/main.dart b/example/lib/main.dart index 3cd4c89..453c98e 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -6,7 +6,8 @@ import 'package:get_it_example/app_model.dart'; GetIt getIt = GetIt.instance; void main() { - getIt.registerSingleton(AppModelImplementation(), signalsReady: true); + getIt.registerSingleton(AppModelImplementation(), + signalsReady: true); runApp(MyApp()); } @@ -39,7 +40,9 @@ class _MyHomePageState extends State { void initState() { // Access the instance of the registered AppModel // As we don't know for sure if AppModel is already ready we use getAsync - getIt.isReady().then((_) => getIt().addListener(update)); + getIt + .isReady() + .then((_) => getIt().addListener(update)); // Alternative // getIt.get().addListener(update); diff --git a/lib/get_it.dart b/lib/get_it.dart index 3a956e2..854af97 100644 --- a/lib/get_it.dart +++ b/lib/get_it.dart @@ -18,7 +18,10 @@ typedef FactoryFunc = T Function(); /// For Factories that expect up to two parameters if you need only one use `void` for the one /// you don't use -typedef FactoryFuncParam = T Function(P1 param1, P2 param2); +typedef FactoryFuncParam = T Function( + P1 param1, + P2 param2, +); /// Signature of the factory function used by async factories typedef FactoryFuncAsync = Future Function(); @@ -32,7 +35,10 @@ typedef ScopeDisposeFunc = FutureOr Function(); /// For async Factories that expect up to two parameters if you need only one use `void` for the one /// you don't use -typedef FactoryFuncParamAsync = Future Function(P1 param1, P2 param2); +typedef FactoryFuncParamAsync = Future Function( + P1 param1, + P2 param2, +); class WaitingTimeOutException implements Exception { /// In case of an timeout while waiting for an instance to get ready @@ -48,13 +54,18 @@ class WaitingTimeOutException implements Exception { /// Lists with Types that are already ready. final List areReady; - WaitingTimeOutException(this.areWaitedBy, this.notReadyYet, this.areReady); + WaitingTimeOutException( + this.areWaitedBy, + this.notReadyYet, + this.areReady, + ); // todo : assert(areWaitedBy != null && notReadyYet != null && areReady != null); @override String toString() { // ignore: avoid_print - print('GetIt: There was a timeout while waiting for an instance to signal ready'); + print( + 'GetIt: There was a timeout while waiting for an instance to signal ready'); // ignore: avoid_print print('The following instance types where waiting for completion'); for (final entry in areWaitedBy.entries) { diff --git a/lib/get_it_impl.dart b/lib/get_it_impl.dart index 613a271..a052fae 100644 --- a/lib/get_it_impl.dart +++ b/lib/get_it_impl.dart @@ -75,7 +75,8 @@ class _ServiceFactory { String get debugName => '$instanceName : $registrationType'; - bool get canBeWaitedFor => shouldSignalReady || pendingResult != null || isAsync; + bool get canBeWaitedFor => + shouldSignalReady || pendingResult != null || isAsync; final bool shouldSignalReady; @@ -101,7 +102,9 @@ class _ServiceFactory { /// returns an instance depending on the type of the registration if [async==false] T getObject(dynamic param1, dynamic param2) { - assert(!(factoryType != _ServiceFactoryType.alwaysNew && (param1 != null || param2 != null)), + assert( + !(factoryType != _ServiceFactoryType.alwaysNew && + (param1 != null || param2 != null)), 'You can only pass parameters to factories!'); try { @@ -146,7 +149,9 @@ class _ServiceFactory { /// returns an async instance depending on the type of the registration if [async==true] or /// if [dependsOn.isnoEmpty]. Future getObjectAsync(dynamic param1, dynamic param2) async { - assert(!(factoryType != _ServiceFactoryType.alwaysNew && (param1 != null || param2 != null)), + assert( + !(factoryType != _ServiceFactoryType.alwaysNew && + (param1 != null || param2 != null)), 'You can only pass parameters to factories!'); throwIfNot( @@ -166,7 +171,8 @@ class _ServiceFactory { param2 == null || param2.runtimeType == param2Type, 'Incompatible Type passed a param2\n' 'expected: $param2Type actual: ${param2.runtimeType}'); - return asyncCreationFunctionParam!(param1 as P1?, param2 as P2?) as Future; + return asyncCreationFunctionParam!(param1 as P1?, param2 as P2?) + as Future; } else { return asyncCreationFunction!() as Future; } @@ -182,12 +188,13 @@ class _ServiceFactory { // We already have a finished instance return Future.value(instance as R); } else { - if (pendingResult != null) // an async creation is already in progress + if (pendingResult != + null) // an async creation is already in progress { return pendingResult as Future; } - /// Seems this is really the first access to this async Signleton + /// Seems this is really the first access to this async Singleton final asyncResult = asyncCreationFunction!(); pendingResult = asyncResult.then((newInstance) { @@ -217,7 +224,8 @@ class _ServiceFactory { class _Scope { final String? name; final ScopeDisposeFunc? disposeFunc; - final factoriesByName = >>{}; + final factoriesByName = + >>{}; _Scope({this.name, this.disposeFunc}); @@ -230,8 +238,8 @@ class _Scope { factoriesByName.clear(); } - List<_ServiceFactory> get allFactories => - factoriesByName.values.fold>([], (sum, x) => sum..addAll(x.values)); + List<_ServiceFactory> get allFactories => factoriesByName.values + .fold>([], (sum, x) => sum..addAll(x.values)); Future dispose() async { await disposeFunc?.call(); @@ -254,7 +262,8 @@ class _GetItImplementation implements GetIt { bool allowReassignment = false; /// Is used by several other functions to retrieve the correct [_ServiceFactory] - _ServiceFactory? _findFirstFactoryByNameAndTypeOrNull( + _ServiceFactory? + _findFirstFactoryByNameAndTypeOrNull( String? instanceName, [ Type? type, ]) { @@ -292,11 +301,13 @@ class _GetItImplementation implements GetIt { } /// Is used by several other functions to retrieve the correct [_ServiceFactory] - _ServiceFactory /*!*/ /*!*/ /*!*/ /*!*/ /*!*/ _findFactoryByNameAndType( + _ServiceFactory /*!*/ /*!*/ /*!*/ /*!*/ /*!*/ _findFactoryByNameAndType< + T extends Object>( String? instanceName, [ Type? type, ]) { - final instanceFactory = _findFirstFactoryByNameAndTypeOrNull(instanceName, type); + final instanceFactory = + _findFirstFactoryByNameAndTypeOrNull(instanceName, type); assert( instanceFactory != null, @@ -379,7 +390,7 @@ class _GetItImplementation implements GetIt { /// [factoryFunc] factory function for this type /// [instanceName] if you provide a value here your factory gets registered with that /// name instead of a type. This should only be necessary if you need to register more - /// than one instance of one type. Its highly not recommended + /// than one instance of one type. It's highly not recommended @override void registerFactory( FactoryFunc factoryFunc, { @@ -403,7 +414,7 @@ class _GetItImplementation implements GetIt { /// [factoryFunc] factory function for this type that accepts two parameters /// [instanceName] if you provide a value here your factory gets registered with that /// name instead of a type. This should only be necessary if you need to register more - /// than one instance of one type. Its highly not recommended + /// than one instance of one type. It's highly not recommended /// /// example: /// getIt.registerFactoryParam((s,i) @@ -451,7 +462,7 @@ class _GetItImplementation implements GetIt { /// [factoryFunc] factory function for this type that accepts two parameters /// [instanceName] if you provide a value here your factory gets registered with that /// name instead of a type. This should only be necessary if you need to register more - /// than one instance of one type. Its highly not recommended + /// than one instance of one type. It's highly not recommended /// /// example: /// getIt.registerFactoryParam((s,i) async @@ -480,7 +491,7 @@ class _GetItImplementation implements GetIt { /// [factoryFunc] factory function for this type /// [instanceName] if you provide a value here your factory gets registered with that /// name instead of a type. This should only be necessary if you need to register more - /// than one instance of one type. Its highly not recommended + /// than one instance of one type. It's highly not recommended /// [registerLazySingleton] does not influence [allReady] however you can wait /// for and be dependent on a LazySingleton. @override @@ -506,7 +517,7 @@ class _GetItImplementation implements GetIt { /// cannot complete until this registration was signalled ready by calling /// [signalsReady(instance)] [instanceName] if you provide a value here your instance gets /// registered with that name instead of a type. This should only be necessary if you need - /// to register more than one instance of one type. Its highly not recommended + /// to register more than one instance of one type. It's highly not recommended @override void registerSingleton( T instance, { @@ -529,7 +540,7 @@ class _GetItImplementation implements GetIt { /// [T] type to register /// [instanceName] if you provide a value here your instance gets registered with that /// name instead of a type. This should only be necessary if you need to register more - /// than one instance of one type. Its highly not recommended + /// than one instance of one type. It's highly not recommended /// [dependsOn] if this instance depends on other registered Singletons before it can be initialized /// you can either orchestrate this manually using [isReady()] or pass a list of the types that the /// instance depends on here. [factoryFunc] won't get executed till this types are ready. @@ -564,7 +575,7 @@ class _GetItImplementation implements GetIt { /// (see below). As soon as it returns, this instance is marked as ready unless you don't set /// [signalsReady==true] [instanceName] if you provide a value here your instance gets /// registered with that name instead of a type. This should only be necessary if you need - /// to register more than one instance of one type. Its highly not recommended + /// to register more than one instance of one type. It's highly not recommended /// [dependsOn] if this instance depends on other registered Singletons before it can be /// initialized you can either orchestrate this manually using [isReady()] or pass a list of /// the types that the instance depends on here. [factoryFunc] won't get executed till this @@ -603,7 +614,7 @@ class _GetItImplementation implements GetIt { /// You can wait/check if the instance is ready by using [isReady()] and [isReadySync()]. /// [instanceName] if you provide a value here your instance gets registered with that /// name instead of a type. This should only be necessary if you need to register more - /// than one instance of one type. Its highly not recommended. + /// than one instance of one type. It's highly not recommended. /// [registerLazySingletonAsync] does not influence [allReady] however you can wait /// for and be dependent on a LazySingleton. @override @@ -653,7 +664,8 @@ class _GetItImplementation implements GetIt { /// is still valid while it is executed @override void pushNewScope({String? scopeName, ScopeDisposeFunc? dispose}) { - assert(scopeName != _baseScopeName, 'This name is reserved for the real base scope.'); + assert(scopeName != _baseScopeName, + 'This name is reserved for the real base scope.'); assert( _scopes.firstWhereOrNull((x) => x.name == scopeName) == null, 'You already have used the scope name $scopeName', @@ -757,7 +769,8 @@ class _GetItImplementation implements GetIt { // if its an async or an dependent Singleton we start its creation function here after we check if // it is dependent on other registered Singletons. - if ((isAsync || (dependsOn?.isNotEmpty ?? false)) && type == _ServiceFactoryType.constant) { + if ((isAsync || (dependsOn?.isNotEmpty ?? false)) && + type == _ServiceFactoryType.constant) { /// Any client awaiting the completion of this Singleton /// Has to wait for the completion of the Singleton itself as well /// as for the completion of all the Singletons this one depends on @@ -825,7 +838,8 @@ class _GetItImplementation implements GetIt { /// outerFutureGroup.future returns a Future and not a Future /// As we know that the actual factory function was added last to the FutureGroup /// we just use that one - serviceFactory.pendingResult = outerFutureGroup.future.then((completedFutures) { + serviceFactory.pendingResult = + outerFutureGroup.future.then((completedFutures) { return completedFutures.last as T; }); } @@ -860,7 +874,8 @@ class _GetItImplementation implements GetIt { throwIf( factoryToRemove.objectsWaiting.isNotEmpty, - StateError('There are still other objects waiting for this instance so signal ready'), + StateError( + 'There are still other objects waiting for this instance so signal ready'), ); _currentScope.factoriesByName[factoryToRemove.instanceName]! @@ -895,7 +910,8 @@ class _GetItImplementation implements GetIt { } throwIfNot( instanceFactory.factoryType == _ServiceFactoryType.lazy, - StateError('There is no type ${instance.runtimeType} registered as LazySingleton in GetIt'), + StateError( + 'There is no type ${instance.runtimeType} registered as LazySingleton in GetIt'), ); if (instanceFactory.instance != null) { @@ -911,13 +927,15 @@ class _GetItImplementation implements GetIt { instanceFactory._readyCompleter = Completer(); } - List<_ServiceFactory> get _allFactories => _scopes.fold>( + List<_ServiceFactory> get _allFactories => + _scopes.fold>( [], (sum, x) => sum..addAll(x.allFactories), ); _ServiceFactory? _findFirstFactoryByInstanceOrNull(Object instance) { - final registeredFactories = _allFactories.where((x) => identical(x.instance, instance)); + final registeredFactories = + _allFactories.where((x) => identical(x.instance, instance)); return registeredFactories.isEmpty ? null : registeredFactories.first; } @@ -926,7 +944,8 @@ class _GetItImplementation implements GetIt { throwIf( registeredFactory == null, - StateError('This instance of the type ${instance.runtimeType} is not available in GetIt ' + StateError( + 'This instance of the type ${instance.runtimeType} is not available in GetIt ' 'If you have registered it as LazySingleton, are you sure you have used ' 'it at least once?'), ); @@ -970,7 +989,8 @@ class _GetItImplementation implements GetIt { throwIf( registeredInstance.isReady, - StateError('This instance of type ${instance.runtimeType} was already signalled'), + StateError( + 'This instance of type ${instance.runtimeType} was already signalled'), ); registeredInstance._readyCompleter.complete(); @@ -982,12 +1002,14 @@ class _GetItImplementation implements GetIt { /// but aren't signalled we throw an error with details which objects are concerned final notReady = _allFactories .where((x) => - (x.shouldSignalReady) && (!x.isReady) || (x.pendingResult != null) && (!x.isReady)) + (x.shouldSignalReady) && (!x.isReady) || + (x.pendingResult != null) && (!x.isReady)) .map((x) => '${x.registrationType}/${x.instanceName}') .toList(); throwIf( notReady.isNotEmpty, - StateError("You can't signal ready manually if you have registered instances that should " + StateError( + "You can't signal ready manually if you have registered instances that should " "signal ready or are async.\n" // this lint is stupid because it doesn't recognize newlines // ignore: missing_whitespace_between_adjacent_strings @@ -1006,12 +1028,14 @@ class _GetItImplementation implements GetIt { /// Singletons were ready in the given time. The Exception contains details on which /// Singletons are not ready yet. @override - Future allReady({Duration? timeout, bool ignorePendingAsyncCreation = false}) { + Future allReady( + {Duration? timeout, bool ignorePendingAsyncCreation = false}) { final futures = FutureGroup(); _allFactories .where((x) => (x.isAsync && !ignorePendingAsyncCreation || - (!x.isAsync && x.pendingResult != null) || // Singletons with dependencies + (!x.isAsync && + x.pendingResult != null) || // Singletons with dependencies x.shouldSignalReady) && !x.isReady && x.factoryType == _ServiceFactoryType.constant) @@ -1027,14 +1051,16 @@ class _GetItImplementation implements GetIt { } /// Returns if all async Singletons are ready without waiting - /// if [allReady] should not wait for the completion of async Signletons set + /// if [allReady] should not wait for the completion of async Singletons set /// [ignorePendingAsyncCreation==true] @override bool allReadySync([bool ignorePendingAsyncCreation = false]) { final notReadyTypes = _allFactories .where((x) => (x.isAsync && !ignorePendingAsyncCreation || - (!x.isAsync && x.pendingResult != null) || // Singletons with dependencies + (!x.isAsync && + x.pendingResult != + null) || // Singletons with dependencies x.shouldSignalReady) && !x.isReady && x.factoryType == _ServiceFactoryType.constant || @@ -1075,16 +1101,20 @@ class _GetItImplementation implements GetIt { .map>>( (isWaitedFor) => MapEntry( isWaitedFor.debugName, - isWaitedFor.objectsWaiting.map((waitedByType) => waitedByType.toString()).toList(), + isWaitedFor.objectsWaiting + .map((waitedByType) => waitedByType.toString()) + .toList(), ), ), ); final notReady = allFactories - .where((x) => (x.shouldSignalReady || x.pendingResult != null) && !x.isReady) + .where((x) => + (x.shouldSignalReady || x.pendingResult != null) && !x.isReady) .map((f) => f.debugName) .toList(); final areReady = allFactories - .where((x) => (x.shouldSignalReady || x.pendingResult != null) && x.isReady) + .where((x) => + (x.shouldSignalReady || x.pendingResult != null) && x.isReady) .map((f) => f.debugName) .toList(); @@ -1111,7 +1141,8 @@ class _GetItImplementation implements GetIt { factoryToCheck = _findFactoryByNameAndType(instanceName); } throwIfNot( - factoryToCheck.canBeWaitedFor && factoryToCheck.factoryType != _ServiceFactoryType.alwaysNew, + factoryToCheck.canBeWaitedFor && + factoryToCheck.factoryType != _ServiceFactoryType.alwaysNew, ArgumentError( 'You only can use this function on Singletons that are async, that are marked as ' 'dependent or that are marked with "signalsReady==true"'), @@ -1121,7 +1152,8 @@ class _GetItImplementation implements GetIt { factoryToCheck.factoryType == _ServiceFactoryType.lazy && factoryToCheck.instance == null) { if (timeout != null) { - return factoryToCheck.getObjectAsync(null, null).timeout(timeout, onTimeout: () { + return factoryToCheck.getObjectAsync(null, null).timeout(timeout, + onTimeout: () { _throwTimeoutError(); return null; }); @@ -1130,7 +1162,8 @@ class _GetItImplementation implements GetIt { } } if (timeout != null) { - return factoryToCheck._readyCompleter.future.timeout(timeout, onTimeout: _throwTimeoutError); + return factoryToCheck._readyCompleter.future + .timeout(timeout, onTimeout: _throwTimeoutError); } else { return factoryToCheck._readyCompleter.future; } @@ -1147,8 +1180,10 @@ class _GetItImplementation implements GetIt { factoryToCheck = _findFactoryByNameAndType(instanceName); } throwIfNot( - factoryToCheck.canBeWaitedFor && factoryToCheck.factoryType != _ServiceFactoryType.alwaysNew, - ArgumentError('You only can use this function on async Singletons or Singletons ' + factoryToCheck.canBeWaitedFor && + factoryToCheck.factoryType != _ServiceFactoryType.alwaysNew, + ArgumentError( + 'You only can use this function on async Singletons or Singletons ' 'that have ben marked with "signalsReady" or that they depend on others'), ); return factoryToCheck.isReady; From 3200852d498429aa400e873b2c4fc87fbdfde686 Mon Sep 17 00:00:00 2001 From: escamoteur Date: Sun, 10 Jan 2021 15:58:36 +0100 Subject: [PATCH 8/9] added mor meaning full check --- lib/get_it_impl.dart | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/get_it_impl.dart b/lib/get_it_impl.dart index a052fae..8b07f91 100644 --- a/lib/get_it_impl.dart +++ b/lib/get_it_impl.dart @@ -786,9 +786,12 @@ class _GetItImplementation implements GetIt { final dependentFutureGroup = FutureGroup(); for (final type in dependsOn!) { - final dependentFactory = _findFactoryByNameAndType(null, type); + final dependentFactory = + _findFirstFactoryByNameAndTypeOrNull(instanceName, type); + throwIf(dependentFactory == null, + ArgumentError('Dependent Type $type is not registered in GetIt')); throwIfNot( - dependentFactory.canBeWaitedFor, + dependentFactory!.canBeWaitedFor, ArgumentError('Dependent Type $type is not an async Singleton'), ); dependentFactory.objectsWaiting.add(serviceFactory.registrationType); From ad6b7cf8dd0c7f70fdd6352a2495941c758c043c Mon Sep 17 00:00:00 2001 From: escamoteur Date: Sun, 10 Jan 2021 16:25:18 +0100 Subject: [PATCH 9/9] bugfix --- lib/get_it_impl.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/get_it_impl.dart b/lib/get_it_impl.dart index 8b07f91..3c07a45 100644 --- a/lib/get_it_impl.dart +++ b/lib/get_it_impl.dart @@ -301,13 +301,12 @@ class _GetItImplementation implements GetIt { } /// Is used by several other functions to retrieve the correct [_ServiceFactory] - _ServiceFactory /*!*/ /*!*/ /*!*/ /*!*/ /*!*/ _findFactoryByNameAndType< - T extends Object>( + _ServiceFactory _findFactoryByNameAndType( String? instanceName, [ Type? type, ]) { final instanceFactory = - _findFirstFactoryByNameAndTypeOrNull(instanceName, type); + _findFirstFactoryByNameAndTypeOrNull(instanceName, type); assert( instanceFactory != null,