- 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