- Added some comment docs to several methods in GetInstance() and extension_instance.dart (WIP)
- Added a fix (a little bit hacky) on DisposableInterface (rx_interface.dart), to prevent method overriding in subclasses, preventing devs to break the lifecycle of Controllers. This compensates the lack of `final` methods in Dart, or the inability to limit the scope of methods to internal packages.
Showing
3 changed files
with
108 additions
and
26 deletions
| @@ -11,12 +11,36 @@ extension Inst on GetInterface { | @@ -11,12 +11,36 @@ extension Inst on GetInterface { | ||
| 11 | {String tag, bool permanent = false}) async => | 11 | {String tag, bool permanent = false}) async => |
| 12 | GetInstance().putAsync<S>(builder, tag: tag, permanent: permanent); | 12 | GetInstance().putAsync<S>(builder, tag: tag, permanent: permanent); |
| 13 | 13 | ||
| 14 | + /// Creates a new Instance<[S]> from the <[S]>[builder] callback. | ||
| 15 | + /// Every time [find]<[S]>() is used, it calls the builder method to generate | ||
| 16 | + /// a new Instance [S]. | ||
| 17 | + /// | ||
| 18 | + /// Example: | ||
| 19 | + /// | ||
| 20 | + /// ```create(() => Repl()); | ||
| 21 | + /// Repl a = find(); | ||
| 22 | + /// Repl b = find(); | ||
| 23 | + /// print(a==b); (false)``` | ||
| 24 | + /// | ||
| 14 | void create<S>(FcBuilderFunc<S> builder, | 25 | void create<S>(FcBuilderFunc<S> builder, |
| 15 | {String name, bool permanent = true}) => | 26 | {String name, bool permanent = true}) => |
| 16 | GetInstance().create<S>(builder, name: name, permanent: permanent); | 27 | GetInstance().create<S>(builder, name: name, permanent: permanent); |
| 17 | 28 | ||
| 29 | + /// Finds a instance of the required Class<[S]> (or [tag]) | ||
| 30 | + /// In the case of using Get.[create], it will create an instance | ||
| 31 | + /// each time you call [find] | ||
| 32 | + /// | ||
| 18 | S find<S>({String tag}) => GetInstance().find<S>(tag: tag); | 33 | S find<S>({String tag}) => GetInstance().find<S>(tag: tag); |
| 19 | 34 | ||
| 35 | + /// Injects a Instance [S] in [GetInstance]. | ||
| 36 | + /// | ||
| 37 | + /// No need to define the generic type <[S]> as it's inferred from the [dependency] | ||
| 38 | + /// | ||
| 39 | + /// - [dependency] The Instance to be injected. | ||
| 40 | + /// - [tag] optionally, use a [tag] as an "id" to create multiple records of the same Type<[S]> | ||
| 41 | + /// - [permanent] keeps the Instance in memory, not following [GetConfig.smartManagement] | ||
| 42 | + /// rules | ||
| 43 | + /// | ||
| 20 | S put<S>(S dependency, | 44 | S put<S>(S dependency, |
| 21 | {String tag, bool permanent = false, FcBuilderFunc<S> builder}) => | 45 | {String tag, bool permanent = false, FcBuilderFunc<S> builder}) => |
| 22 | GetInstance() | 46 | GetInstance() |
| @@ -39,7 +39,15 @@ class GetInstance { | @@ -39,7 +39,15 @@ class GetInstance { | ||
| 39 | return put<S>(await builder(), tag: tag, permanent: permanent); | 39 | return put<S>(await builder(), tag: tag, permanent: permanent); |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | - /// Inject class on Get Instance Manager | 42 | + /// Injects a Instance [S] in [GetInstance]. |
| 43 | + /// | ||
| 44 | + /// No need to define the generic type <[S]> as it's inferred from the [dependency] | ||
| 45 | + /// | ||
| 46 | + /// - [dependency] The Instance to be injected. | ||
| 47 | + /// - [tag] optionally, use a [tag] as an "id" to create multiple records of the same Type<[S]> | ||
| 48 | + /// - [permanent] keeps the Instance in memory, not following [GetConfig.smartManagement] | ||
| 49 | + /// rules. | ||
| 50 | + /// | ||
| 43 | S put<S>( | 51 | S put<S>( |
| 44 | S dependency, { | 52 | S dependency, { |
| 45 | String tag, | 53 | String tag, |
| @@ -54,12 +62,17 @@ class GetInstance { | @@ -54,12 +62,17 @@ class GetInstance { | ||
| 54 | return find<S>(tag: tag); | 62 | return find<S>(tag: tag); |
| 55 | } | 63 | } |
| 56 | 64 | ||
| 57 | - /// Create a new instance from builder class | ||
| 58 | - /// Example | ||
| 59 | - /// create(() => Repl()); | 65 | + /// Creates a new Class Instance [S] from the builder callback[S]. |
| 66 | + /// Every time [find]<[S]>() is used, it calls the builder method to generate | ||
| 67 | + /// a new Instance [S]. | ||
| 68 | + /// | ||
| 69 | + /// Example: | ||
| 70 | + /// | ||
| 71 | + /// ```create(() => Repl()); | ||
| 60 | /// Repl a = find(); | 72 | /// Repl a = find(); |
| 61 | /// Repl b = find(); | 73 | /// Repl b = find(); |
| 62 | - /// print(a==b); (false) | 74 | + /// print(a==b); (false)``` |
| 75 | + /// | ||
| 63 | void create<S>( | 76 | void create<S>( |
| 64 | FcBuilderFunc<S> builder, { | 77 | FcBuilderFunc<S> builder, { |
| 65 | String name, | 78 | String name, |
| @@ -69,6 +82,8 @@ class GetInstance { | @@ -69,6 +82,8 @@ class GetInstance { | ||
| 69 | isSingleton: false, name: name, builder: builder, permanent: permanent); | 82 | isSingleton: false, name: name, builder: builder, permanent: permanent); |
| 70 | } | 83 | } |
| 71 | 84 | ||
| 85 | + /// Injects the Instance [S] builder into the [_singleton] HashMap. | ||
| 86 | + /// | ||
| 72 | void _insert<S>({ | 87 | void _insert<S>({ |
| 73 | bool isSingleton, | 88 | bool isSingleton, |
| 74 | String name, | 89 | String name, |
| @@ -76,14 +91,17 @@ class GetInstance { | @@ -76,14 +91,17 @@ class GetInstance { | ||
| 76 | FcBuilderFunc<S> builder, | 91 | FcBuilderFunc<S> builder, |
| 77 | }) { | 92 | }) { |
| 78 | assert(builder != null); | 93 | assert(builder != null); |
| 79 | - String key = _getKey(S, name); | ||
| 80 | - | 94 | + final key = _getKey(S, name); |
| 81 | _singl.putIfAbsent( | 95 | _singl.putIfAbsent( |
| 82 | key, () => FcBuilder<S>(isSingleton, builder, permanent, false)); | 96 | key, () => FcBuilder<S>(isSingleton, builder, permanent, false)); |
| 83 | } | 97 | } |
| 84 | 98 | ||
| 99 | + /// Clears from memory registered Instances associated with [routeName] when | ||
| 100 | + /// using [GetConfig.smartManagement] as [SmartManagement.full] or [SmartManagement.keepFactory] | ||
| 101 | + /// Meant for internal usage of [GetPageRoute] and [GetDialogRoute] | ||
| 102 | + /// | ||
| 85 | Future<void> removeDependencyByRoute(String routeName) async { | 103 | Future<void> removeDependencyByRoute(String routeName) async { |
| 86 | - List<String> keysToRemove = []; | 104 | + final keysToRemove = <String>[]; |
| 87 | _routesKey.forEach((key, value) { | 105 | _routesKey.forEach((key, value) { |
| 88 | if (value == routeName) { | 106 | if (value == routeName) { |
| 89 | keysToRemove.add(key); | 107 | keysToRemove.add(key); |
| @@ -99,32 +117,41 @@ class GetInstance { | @@ -99,32 +117,41 @@ class GetInstance { | ||
| 99 | keysToRemove.clear(); | 117 | keysToRemove.clear(); |
| 100 | } | 118 | } |
| 101 | 119 | ||
| 102 | - bool initDependencies<S>({String name}) { | ||
| 103 | - String key = _getKey(S, name); | 120 | + /// Initializes the dependencies for a Class Instance [S] (or tag), |
| 121 | + /// If its a Controller, it starts the lifecycle process. | ||
| 122 | + /// Optionally associating the current Route to the lifetime of the instance, | ||
| 123 | + /// if [GetConfig.smartManagement] is marked as [SmartManagement.full] or | ||
| 124 | + /// [GetConfig.keepFactory] | ||
| 125 | + /// | ||
| 126 | + bool _initDependencies<S>({String name}) { | ||
| 127 | + final key = _getKey(S, name); | ||
| 104 | bool isInit = _singl[key].isInit; | 128 | bool isInit = _singl[key].isInit; |
| 105 | if (!isInit) { | 129 | if (!isInit) { |
| 106 | - startController<S>(tag: name); | 130 | + _startController<S>(tag: name); |
| 107 | _singl[key].isInit = true; | 131 | _singl[key].isInit = true; |
| 108 | if (GetConfig.smartManagement != SmartManagement.onlyBuilder) { | 132 | if (GetConfig.smartManagement != SmartManagement.onlyBuilder) { |
| 109 | - registerRouteInstance<S>(tag: name); | 133 | + _registerRouteInstance<S>(tag: name); |
| 110 | } | 134 | } |
| 111 | } | 135 | } |
| 112 | return true; | 136 | return true; |
| 113 | } | 137 | } |
| 114 | 138 | ||
| 115 | - void registerRouteInstance<S>({String tag}) { | 139 | + /// Links a Class instance [S] (or [tag]) to the current route. |
| 140 | + /// Requires usage of [GetMaterialApp]. | ||
| 141 | + /// | ||
| 142 | + void _registerRouteInstance<S>({String tag}) { | ||
| 116 | _routesKey.putIfAbsent(_getKey(S, tag), () => GetConfig.currentRoute); | 143 | _routesKey.putIfAbsent(_getKey(S, tag), () => GetConfig.currentRoute); |
| 117 | } | 144 | } |
| 118 | 145 | ||
| 146 | + /// Finds and returns a Class instance [S] (or tag) without further processing. | ||
| 119 | S findByType<S>(Type type, {String tag}) { | 147 | S findByType<S>(Type type, {String tag}) { |
| 120 | String key = _getKey(type, tag); | 148 | String key = _getKey(type, tag); |
| 121 | return _singl[key].getDependency() as S; | 149 | return _singl[key].getDependency() as S; |
| 122 | } | 150 | } |
| 123 | 151 | ||
| 124 | - void startController<S>({String tag}) { | ||
| 125 | - String key = _getKey(S, tag); | 152 | + void _startController<S>({String tag}) { |
| 153 | + final key = _getKey(S, tag); | ||
| 126 | final i = _singl[key].getDependency(); | 154 | final i = _singl[key].getDependency(); |
| 127 | - | ||
| 128 | if (i is DisposableInterface) { | 155 | if (i is DisposableInterface) { |
| 129 | i.onStart(); | 156 | i.onStart(); |
| 130 | GetConfig.log('[GETX] $key has been initialized'); | 157 | GetConfig.log('[GETX] $key has been initialized'); |
| @@ -152,7 +179,10 @@ class GetInstance { | @@ -152,7 +179,10 @@ class GetInstance { | ||
| 152 | // } | 179 | // } |
| 153 | // } | 180 | // } |
| 154 | 181 | ||
| 155 | - /// Find a instance from required class | 182 | + /// Finds a instance of the required Class<[S]> (or [tag]) |
| 183 | + /// In the case of using Get.[create], it will create an instance | ||
| 184 | + /// each time you call [find] | ||
| 185 | + /// | ||
| 156 | S find<S>({String tag}) { | 186 | S find<S>({String tag}) { |
| 157 | String key = _getKey(S, tag); | 187 | String key = _getKey(S, tag); |
| 158 | 188 | ||
| @@ -165,17 +195,17 @@ class GetInstance { | @@ -165,17 +195,17 @@ class GetInstance { | ||
| 165 | throw "class ${S.toString()} with tag '$tag' is not register"; | 195 | throw "class ${S.toString()} with tag '$tag' is not register"; |
| 166 | } | 196 | } |
| 167 | } | 197 | } |
| 168 | - initDependencies<S>(name: tag); | 198 | + _initDependencies<S>(name: tag); |
| 169 | 199 | ||
| 170 | return _singl[key].getDependency() as S; | 200 | return _singl[key].getDependency() as S; |
| 171 | } else { | 201 | } else { |
| 172 | if (!_factory.containsKey(key)) | 202 | if (!_factory.containsKey(key)) |
| 173 | - throw " $S not found. You need call put<$S>($S()) before"; | 203 | + throw "$S not found. You need call put<$S>($S()) before"; |
| 174 | 204 | ||
| 175 | GetConfig.log('[GETX] $S instance was created at that time'); | 205 | GetConfig.log('[GETX] $S instance was created at that time'); |
| 176 | S _value = put<S>(_factory[key].builder() as S); | 206 | S _value = put<S>(_factory[key].builder() as S); |
| 177 | 207 | ||
| 178 | - initDependencies<S>(name: tag); | 208 | + _initDependencies<S>(name: tag); |
| 179 | 209 | ||
| 180 | if (GetConfig.smartManagement != SmartManagement.keepFactory && | 210 | if (GetConfig.smartManagement != SmartManagement.keepFactory && |
| 181 | !_factory[key].fenix) { | 211 | !_factory[key].fenix) { |
| @@ -190,6 +220,12 @@ class GetInstance { | @@ -190,6 +220,12 @@ class GetInstance { | ||
| 190 | return name == null ? type.toString() : type.toString() + name; | 220 | return name == null ? type.toString() : type.toString() + name; |
| 191 | } | 221 | } |
| 192 | 222 | ||
| 223 | + /// Clears all registered instances (and/or tags). | ||
| 224 | + /// Even the persistent ones. | ||
| 225 | + /// | ||
| 226 | + /// [clearFactory] clears the callbacks registered by [lazyPut] | ||
| 227 | + /// [clearRouteBindings] clears Instances associated with routes. | ||
| 228 | + /// | ||
| 193 | bool reset({bool clearFactory = true, bool clearRouteBindings = true}) { | 229 | bool reset({bool clearFactory = true, bool clearRouteBindings = true}) { |
| 194 | if (clearFactory) _factory.clear(); | 230 | if (clearFactory) _factory.clear(); |
| 195 | if (clearRouteBindings) _routesKey.clear(); | 231 | if (clearRouteBindings) _routesKey.clear(); |
| @@ -205,7 +241,8 @@ class GetInstance { | @@ -205,7 +241,8 @@ class GetInstance { | ||
| 205 | // return s; | 241 | // return s; |
| 206 | // } | 242 | // } |
| 207 | 243 | ||
| 208 | - /// Delete class instance on [S] and clean memory | 244 | + /// Delete registered Class Instance [S] (or [tag]) and, closes any open |
| 245 | + /// controllers [DisposableInterface], cleans up the memory | ||
| 209 | Future<bool> delete<S>({String tag, String key, bool force = false}) async { | 246 | Future<bool> delete<S>({String tag, String key, bool force = false}) async { |
| 210 | final newKey = key ?? _getKey(S, tag); | 247 | final newKey = key ?? _getKey(S, tag); |
| 211 | 248 | ||
| @@ -244,10 +281,10 @@ class GetInstance { | @@ -244,10 +281,10 @@ class GetInstance { | ||
| 244 | }); | 281 | }); |
| 245 | } | 282 | } |
| 246 | 283 | ||
| 247 | - /// check if instance is registered | 284 | + /// Check if a Class instance [S] (or [tag]) is registered. |
| 248 | bool isRegistered<S>({String tag}) => _singl.containsKey(_getKey(S, tag)); | 285 | bool isRegistered<S>({String tag}) => _singl.containsKey(_getKey(S, tag)); |
| 249 | 286 | ||
| 250 | - /// check if instance is prepared | 287 | + /// Check if Class instance [S] (or [tag]) is prepared to be used. |
| 251 | bool isPrepared<S>({String tag}) => _factory.containsKey(_getKey(S, tag)); | 288 | bool isPrepared<S>({String tag}) => _factory.containsKey(_getKey(S, tag)); |
| 252 | } | 289 | } |
| 253 | 290 |
| 1 | import 'dart:async'; | 1 | import 'dart:async'; |
| 2 | + | ||
| 2 | import 'package:flutter/scheduler.dart'; | 3 | import 'package:flutter/scheduler.dart'; |
| 3 | import 'package:get/src/state_manager/rx/rx_callbacks.dart'; | 4 | import 'package:get/src/state_manager/rx/rx_callbacks.dart'; |
| 4 | 5 | ||
| @@ -26,10 +27,30 @@ abstract class RxInterface<T> { | @@ -26,10 +27,30 @@ abstract class RxInterface<T> { | ||
| 26 | /// once started, that service will remain in memory, such as Auth control for example. | 27 | /// once started, that service will remain in memory, such as Auth control for example. |
| 27 | abstract class GetxService extends DisposableInterface {} | 28 | abstract class GetxService extends DisposableInterface {} |
| 28 | 29 | ||
| 30 | +/// Special callable class to keep the contract of a regular method, and avoid | ||
| 31 | +/// overrides if you extend the class that uses it, as Dart has no final methods. | ||
| 32 | +/// Used in [DisposableInterface] to avoid the danger of overriding onStart. | ||
| 33 | +/// | ||
| 34 | +class _InternalFinalCallback<T> { | ||
| 35 | + T Function() callback; | ||
| 36 | + _InternalFinalCallback(); | ||
| 37 | + T call() => callback.call(); | ||
| 38 | +} | ||
| 39 | + | ||
| 29 | abstract class DisposableInterface { | 40 | abstract class DisposableInterface { |
| 30 | - /// Called at the exact moment that the widget is allocated in memory. | ||
| 31 | - /// Do not overwrite this method. | ||
| 32 | - void onStart() { | 41 | + /// Called at the exact moment the widget is allocated in memory. |
| 42 | + /// It uses an internal "callable" type, to avoid any @overrides in subclases. | ||
| 43 | + /// This method should be internal and is required to define the lifetime cycle | ||
| 44 | + /// of the subclass. | ||
| 45 | + /// | ||
| 46 | + final onStart = _InternalFinalCallback<void>(); | ||
| 47 | + | ||
| 48 | + DisposableInterface() { | ||
| 49 | + onStart.callback = _onStart; | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + // Internal callback that starts the cycle of this controller. | ||
| 53 | + void _onStart() { | ||
| 33 | onInit(); | 54 | onInit(); |
| 34 | SchedulerBinding.instance?.addPostFrameCallback((_) => onReady()); | 55 | SchedulerBinding.instance?.addPostFrameCallback((_) => onReady()); |
| 35 | } | 56 | } |
-
Please register or login to post a comment