Committed by
GitHub
Merge pull request #519 from roipeker/master
GetInstance cleanup
Showing
6 changed files
with
329 additions
and
94 deletions
1 | import 'package:get/src/core/get_interface.dart'; | 1 | import 'package:get/src/core/get_interface.dart'; |
2 | + | ||
2 | import 'get_instance.dart'; | 3 | import 'get_instance.dart'; |
3 | 4 | ||
4 | extension Inst on GetInterface { | 5 | extension Inst on GetInterface { |
@@ -10,13 +11,36 @@ extension Inst on GetInterface { | @@ -10,13 +11,36 @@ extension Inst on GetInterface { | ||
10 | {String tag, bool permanent = false}) async => | 11 | {String tag, bool permanent = false}) async => |
11 | GetInstance().putAsync<S>(builder, tag: tag, permanent: permanent); | 12 | GetInstance().putAsync<S>(builder, tag: tag, permanent: permanent); |
12 | 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 | + /// | ||
13 | void create<S>(FcBuilderFunc<S> builder, | 25 | void create<S>(FcBuilderFunc<S> builder, |
14 | {String name, bool permanent = true}) => | 26 | {String name, bool permanent = true}) => |
15 | GetInstance().create<S>(builder, name: name, permanent: permanent); | 27 | GetInstance().create<S>(builder, name: name, permanent: permanent); |
16 | 28 | ||
17 | - S find<S>({String tag, FcBuilderFunc<S> instance}) => | ||
18 | - GetInstance().find<S>(tag: tag, instance: instance); | ||
19 | - | 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 | + /// | ||
33 | + S find<S>({String tag}) => GetInstance().find<S>(tag: tag); | ||
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() |
1 | -import 'package:flutter/cupertino.dart'; | ||
2 | import 'package:get/src/core/log.dart'; | 1 | import 'package:get/src/core/log.dart'; |
3 | import 'package:get/src/navigation/root/smart_management.dart'; | 2 | import 'package:get/src/navigation/root/smart_management.dart'; |
4 | import 'package:get/src/state_manager/rx/rx_interface.dart'; | 3 | import 'package:get/src/state_manager/rx/rx_interface.dart'; |
@@ -11,28 +10,26 @@ class GetConfig { | @@ -11,28 +10,26 @@ class GetConfig { | ||
11 | static String currentRoute; | 10 | static String currentRoute; |
12 | } | 11 | } |
13 | 12 | ||
14 | -class Lazy { | ||
15 | - Lazy(this.builder, this.fenix); | ||
16 | - bool fenix; | ||
17 | - FcBuilderFunc builder; | ||
18 | -} | ||
19 | - | ||
20 | class GetInstance { | 13 | class GetInstance { |
21 | - factory GetInstance() { | ||
22 | - if (_getInstance == null) _getInstance = GetInstance._(); | ||
23 | - return _getInstance; | ||
24 | - } | 14 | + factory GetInstance() => _getInstance ??= GetInstance._(); |
25 | const GetInstance._(); | 15 | const GetInstance._(); |
26 | static GetInstance _getInstance; | 16 | static GetInstance _getInstance; |
27 | 17 | ||
28 | - static Map<dynamic, dynamic> _singl = {}; | ||
29 | - static Map<dynamic, Lazy> _factory = {}; | 18 | + /// Holds references to every registered Instance when using |
19 | + /// Get.[put] | ||
20 | + static Map<String, _FcBuilder> _singl = {}; | ||
21 | + | ||
22 | + /// Holds a reference to every registered callback when using | ||
23 | + /// Get.[lazyPut] | ||
24 | + static Map<String, _Lazy> _factory = {}; | ||
25 | + | ||
30 | static Map<String, String> _routesKey = {}; | 26 | static Map<String, String> _routesKey = {}; |
31 | 27 | ||
28 | + static GetQueue _queue = GetQueue(); | ||
29 | + | ||
32 | void lazyPut<S>(FcBuilderFunc builder, {String tag, bool fenix = false}) { | 30 | void lazyPut<S>(FcBuilderFunc builder, {String tag, bool fenix = false}) { |
33 | String key = _getKey(S, tag); | 31 | String key = _getKey(S, tag); |
34 | - | ||
35 | - _factory.putIfAbsent(key, () => Lazy(builder, fenix)); | 32 | + _factory.putIfAbsent(key, () => _Lazy(builder, fenix)); |
36 | } | 33 | } |
37 | 34 | ||
38 | Future<S> putAsync<S>(FcBuilderFuncAsync<S> builder, | 35 | Future<S> putAsync<S>(FcBuilderFuncAsync<S> builder, |
@@ -40,7 +37,14 @@ class GetInstance { | @@ -40,7 +37,14 @@ class GetInstance { | ||
40 | return put<S>(await builder(), tag: tag, permanent: permanent); | 37 | return put<S>(await builder(), tag: tag, permanent: permanent); |
41 | } | 38 | } |
42 | 39 | ||
43 | - /// Inject class on Get Instance Manager | 40 | + /// Injects an instance <[S]> in memory to be globally accessible. |
41 | + /// | ||
42 | + /// No need to define the generic type <[S]> as it's inferred from the [dependency] | ||
43 | + /// | ||
44 | + /// - [dependency] The Instance to be injected. | ||
45 | + /// - [tag] optionally, use a [tag] as an "id" to create multiple records of the same Type<[S]> | ||
46 | + /// - [permanent] keeps the Instance in memory, not following [GetConfig.smartManagement] | ||
47 | + /// rules. | ||
44 | S put<S>( | 48 | S put<S>( |
45 | S dependency, { | 49 | S dependency, { |
46 | String tag, | 50 | String tag, |
@@ -55,12 +59,16 @@ class GetInstance { | @@ -55,12 +59,16 @@ class GetInstance { | ||
55 | return find<S>(tag: tag); | 59 | return find<S>(tag: tag); |
56 | } | 60 | } |
57 | 61 | ||
58 | - /// Create a new instance from builder class | ||
59 | - /// Example | ||
60 | - /// create(() => Repl()); | 62 | + /// Creates a new Class Instance [S] from the builder callback[S]. |
63 | + /// Every time [find]<[S]>() is used, it calls the builder method to generate | ||
64 | + /// a new Instance [S]. | ||
65 | + /// | ||
66 | + /// Example: | ||
67 | + /// | ||
68 | + /// ```create(() => Repl()); | ||
61 | /// Repl a = find(); | 69 | /// Repl a = find(); |
62 | /// Repl b = find(); | 70 | /// Repl b = find(); |
63 | - /// print(a==b); (false) | 71 | + /// print(a==b); (false)``` |
64 | void create<S>( | 72 | void create<S>( |
65 | FcBuilderFunc<S> builder, { | 73 | FcBuilderFunc<S> builder, { |
66 | String name, | 74 | String name, |
@@ -70,6 +78,7 @@ class GetInstance { | @@ -70,6 +78,7 @@ class GetInstance { | ||
70 | isSingleton: false, name: name, builder: builder, permanent: permanent); | 78 | isSingleton: false, name: name, builder: builder, permanent: permanent); |
71 | } | 79 | } |
72 | 80 | ||
81 | + /// Injects the Instance [S] builder into the [_singleton] HashMap. | ||
73 | void _insert<S>({ | 82 | void _insert<S>({ |
74 | bool isSingleton, | 83 | bool isSingleton, |
75 | String name, | 84 | String name, |
@@ -77,14 +86,16 @@ class GetInstance { | @@ -77,14 +86,16 @@ class GetInstance { | ||
77 | FcBuilderFunc<S> builder, | 86 | FcBuilderFunc<S> builder, |
78 | }) { | 87 | }) { |
79 | assert(builder != null); | 88 | assert(builder != null); |
80 | - String key = _getKey(S, name); | ||
81 | - | 89 | + final key = _getKey(S, name); |
82 | _singl.putIfAbsent( | 90 | _singl.putIfAbsent( |
83 | - key, () => FcBuilder<S>(isSingleton, builder, permanent, false)); | 91 | + key, () => _FcBuilder<S>(isSingleton, builder, permanent, false)); |
84 | } | 92 | } |
85 | 93 | ||
94 | + /// Clears from memory registered Instances associated with [routeName] when | ||
95 | + /// using [GetConfig.smartManagement] as [SmartManagement.full] or [SmartManagement.keepFactory] | ||
96 | + /// Meant for internal usage of [GetPageRoute] and [GetDialogRoute] | ||
86 | Future<void> removeDependencyByRoute(String routeName) async { | 97 | Future<void> removeDependencyByRoute(String routeName) async { |
87 | - List<String> keysToRemove = []; | 98 | + final keysToRemove = <String>[]; |
88 | _routesKey.forEach((key, value) { | 99 | _routesKey.forEach((key, value) { |
89 | if (value == routeName) { | 100 | if (value == routeName) { |
90 | keysToRemove.add(key); | 101 | keysToRemove.add(key); |
@@ -100,35 +111,43 @@ class GetInstance { | @@ -100,35 +111,43 @@ class GetInstance { | ||
100 | keysToRemove.clear(); | 111 | keysToRemove.clear(); |
101 | } | 112 | } |
102 | 113 | ||
103 | - bool initDependencies<S>({String name}) { | ||
104 | - String key = _getKey(S, name); | 114 | + /// Initializes the dependencies for a Class Instance [S] (or tag), |
115 | + /// If its a Controller, it starts the lifecycle process. | ||
116 | + /// Optionally associating the current Route to the lifetime of the instance, | ||
117 | + /// if [GetConfig.smartManagement] is marked as [SmartManagement.full] or | ||
118 | + /// [GetConfig.keepFactory] | ||
119 | + bool _initDependencies<S>({String name}) { | ||
120 | + final key = _getKey(S, name); | ||
105 | bool isInit = _singl[key].isInit; | 121 | bool isInit = _singl[key].isInit; |
106 | if (!isInit) { | 122 | if (!isInit) { |
107 | - startController<S>(tag: name); | 123 | + _startController<S>(tag: name); |
108 | _singl[key].isInit = true; | 124 | _singl[key].isInit = true; |
109 | if (GetConfig.smartManagement != SmartManagement.onlyBuilder) { | 125 | if (GetConfig.smartManagement != SmartManagement.onlyBuilder) { |
110 | - registerRouteInstance<S>(tag: name); | 126 | + _registerRouteInstance<S>(tag: name); |
111 | } | 127 | } |
112 | } | 128 | } |
113 | return true; | 129 | return true; |
114 | } | 130 | } |
115 | 131 | ||
116 | - void registerRouteInstance<S>({String tag}) { | 132 | + /// Links a Class instance [S] (or [tag]) to the current route. |
133 | + /// Requires usage of [GetMaterialApp]. | ||
134 | + void _registerRouteInstance<S>({String tag}) { | ||
117 | _routesKey.putIfAbsent(_getKey(S, tag), () => GetConfig.currentRoute); | 135 | _routesKey.putIfAbsent(_getKey(S, tag), () => GetConfig.currentRoute); |
118 | } | 136 | } |
119 | 137 | ||
138 | + /// Finds and returns a Instance<[S]> (or [tag]) without further processing. | ||
120 | S findByType<S>(Type type, {String tag}) { | 139 | S findByType<S>(Type type, {String tag}) { |
121 | String key = _getKey(type, tag); | 140 | String key = _getKey(type, tag); |
122 | return _singl[key].getDependency() as S; | 141 | return _singl[key].getDependency() as S; |
123 | } | 142 | } |
124 | 143 | ||
125 | - void startController<S>({String tag}) { | ||
126 | - String key = _getKey(S, tag); | 144 | + /// Initializes the controller |
145 | + void _startController<S>({String tag}) { | ||
146 | + final key = _getKey(S, tag); | ||
127 | final i = _singl[key].getDependency(); | 147 | final i = _singl[key].getDependency(); |
128 | - | ||
129 | if (i is DisposableInterface) { | 148 | if (i is DisposableInterface) { |
130 | i.onStart(); | 149 | i.onStart(); |
131 | - GetConfig.log('$key has been initialized'); | 150 | + GetConfig.log('"$key" has been initialized'); |
132 | } | 151 | } |
133 | } | 152 | } |
134 | 153 | ||
@@ -153,30 +172,35 @@ class GetInstance { | @@ -153,30 +172,35 @@ class GetInstance { | ||
153 | // } | 172 | // } |
154 | // } | 173 | // } |
155 | 174 | ||
156 | - /// Find a instance from required class | ||
157 | - S find<S>({String tag, FcBuilderFunc<S> instance}) { | 175 | + /// Finds the registered type <[S]> (or [tag]) |
176 | + /// In case of using Get.[create] to register a type <[S]> or [tag], it will create an instance | ||
177 | + /// each time you call [find]. | ||
178 | + /// If the registered type <[S]> (or [tag]) is a Controller, it will initialize | ||
179 | + /// it's lifecycle. | ||
180 | + S find<S>({String tag}) { | ||
158 | String key = _getKey(S, tag); | 181 | String key = _getKey(S, tag); |
159 | - | ||
160 | if (isRegistered<S>(tag: tag)) { | 182 | if (isRegistered<S>(tag: tag)) { |
161 | - FcBuilder builder = _singl[key] as FcBuilder; | 183 | + _FcBuilder builder = _singl[key] as _FcBuilder; |
162 | if (builder == null) { | 184 | if (builder == null) { |
163 | if (tag == null) { | 185 | if (tag == null) { |
164 | - throw "class ${S.toString()} is not register"; | 186 | + throw 'Class "$S" is not register'; |
165 | } else { | 187 | } else { |
166 | - throw "class ${S.toString()} with tag '$tag' is not register"; | 188 | + throw 'Class "$S" with tag "$tag" is not register'; |
167 | } | 189 | } |
168 | } | 190 | } |
169 | - initDependencies<S>(name: tag); | 191 | + _initDependencies<S>(name: tag); |
170 | 192 | ||
171 | return _singl[key].getDependency() as S; | 193 | return _singl[key].getDependency() as S; |
172 | } else { | 194 | } else { |
173 | if (!_factory.containsKey(key)) | 195 | if (!_factory.containsKey(key)) |
174 | - throw " $S not found. You need call put<$S>($S()) before"; | 196 | + throw '"$S" not found. You need to call "Get.put<$S>($S())"'; |
197 | + | ||
198 | + // TODO: This message is not clear | ||
199 | + GetConfig.log('"$S" instance was created at that time'); | ||
175 | 200 | ||
176 | - GetConfig.log('$S instance was created at that time'); | ||
177 | S _value = put<S>(_factory[key].builder() as S); | 201 | S _value = put<S>(_factory[key].builder() as S); |
178 | 202 | ||
179 | - initDependencies<S>(name: tag); | 203 | + _initDependencies<S>(name: tag); |
180 | 204 | ||
181 | if (GetConfig.smartManagement != SmartManagement.keepFactory && | 205 | if (GetConfig.smartManagement != SmartManagement.keepFactory && |
182 | !_factory[key].fenix) { | 206 | !_factory[key].fenix) { |
@@ -191,6 +215,12 @@ class GetInstance { | @@ -191,6 +215,12 @@ class GetInstance { | ||
191 | return name == null ? type.toString() : type.toString() + name; | 215 | return name == null ? type.toString() : type.toString() + name; |
192 | } | 216 | } |
193 | 217 | ||
218 | + /// Clears all registered instances (and/or tags). | ||
219 | + /// Even the persistent ones. | ||
220 | + /// | ||
221 | + /// [clearFactory] clears the callbacks registered by [lazyPut] | ||
222 | + /// [clearRouteBindings] clears Instances associated with routes. | ||
223 | + /// | ||
194 | bool reset({bool clearFactory = true, bool clearRouteBindings = true}) { | 224 | bool reset({bool clearFactory = true, bool clearRouteBindings = true}) { |
195 | if (clearFactory) _factory.clear(); | 225 | if (clearFactory) _factory.clear(); |
196 | if (clearRouteBindings) _routesKey.clear(); | 226 | if (clearRouteBindings) _routesKey.clear(); |
@@ -198,33 +228,27 @@ class GetInstance { | @@ -198,33 +228,27 @@ class GetInstance { | ||
198 | return true; | 228 | return true; |
199 | } | 229 | } |
200 | 230 | ||
201 | - static GetQueue queue = GetQueue(); | ||
202 | - | ||
203 | // Future<bool> delete<S>({String tag, String key, bool force = false}) async { | 231 | // Future<bool> delete<S>({String tag, String key, bool force = false}) async { |
204 | // final s = await queue | 232 | // final s = await queue |
205 | // .add<bool>(() async => dele<S>(tag: tag, key: key, force: force)); | 233 | // .add<bool>(() async => dele<S>(tag: tag, key: key, force: force)); |
206 | // return s; | 234 | // return s; |
207 | // } | 235 | // } |
208 | 236 | ||
209 | - /// Delete class instance on [S] and clean memory | 237 | + /// Delete registered Class Instance [S] (or [tag]) and, closes any open |
238 | + /// controllers [DisposableInterface], cleans up the memory | ||
210 | Future<bool> delete<S>({String tag, String key, bool force = false}) async { | 239 | Future<bool> delete<S>({String tag, String key, bool force = false}) async { |
211 | - String newKey; | ||
212 | - if (key == null) { | ||
213 | - newKey = _getKey(S, tag); | ||
214 | - } else { | ||
215 | - newKey = key; | ||
216 | - } | 240 | + final newKey = key ?? _getKey(S, tag); |
217 | 241 | ||
218 | - return queue.add<bool>(() async { | 242 | + return _queue.add<bool>(() async { |
219 | if (!_singl.containsKey(newKey)) { | 243 | if (!_singl.containsKey(newKey)) { |
220 | - GetConfig.log('Instance $newKey already been removed.', isError: true); | 244 | + GetConfig.log('Instance "$newKey" already removed.', isError: true); |
221 | return false; | 245 | return false; |
222 | } | 246 | } |
223 | 247 | ||
224 | - FcBuilder builder = _singl[newKey] as FcBuilder; | 248 | + _FcBuilder builder = _singl[newKey] as _FcBuilder; |
225 | if (builder.permanent && !force) { | 249 | if (builder.permanent && !force) { |
226 | GetConfig.log( | 250 | GetConfig.log( |
227 | - '[$newKey] has been marked as permanent, SmartManagement is not authorized to delete it.', | 251 | + '"$newKey" has been marked as permanent, SmartManagement is not authorized to delete it.', |
228 | isError: true); | 252 | isError: true); |
229 | return false; | 253 | return false; |
230 | } | 254 | } |
@@ -235,24 +259,24 @@ class GetInstance { | @@ -235,24 +259,24 @@ class GetInstance { | ||
235 | } | 259 | } |
236 | if (i is DisposableInterface) { | 260 | if (i is DisposableInterface) { |
237 | await i.onClose(); | 261 | await i.onClose(); |
238 | - GetConfig.log('onClose of $newKey called'); | 262 | + GetConfig.log('"$newKey" onClose() called'); |
239 | } | 263 | } |
240 | 264 | ||
241 | _singl.removeWhere((oldKey, value) => (oldKey == newKey)); | 265 | _singl.removeWhere((oldKey, value) => (oldKey == newKey)); |
242 | if (_singl.containsKey(newKey)) { | 266 | if (_singl.containsKey(newKey)) { |
243 | - GetConfig.log('error on remove object $newKey', isError: true); | 267 | + GetConfig.log('Error removing object "$newKey"', isError: true); |
244 | } else { | 268 | } else { |
245 | - GetConfig.log('$newKey deleted from memory'); | 269 | + GetConfig.log('"$newKey" deleted from memory'); |
246 | } | 270 | } |
247 | // _routesKey?.remove(key); | 271 | // _routesKey?.remove(key); |
248 | return true; | 272 | return true; |
249 | }); | 273 | }); |
250 | } | 274 | } |
251 | 275 | ||
252 | - /// check if instance is registered | 276 | + /// Check if a Class instance [S] (or [tag]) is registered. |
253 | bool isRegistered<S>({String tag}) => _singl.containsKey(_getKey(S, tag)); | 277 | bool isRegistered<S>({String tag}) => _singl.containsKey(_getKey(S, tag)); |
254 | 278 | ||
255 | - /// check if instance is prepared | 279 | + /// Check if Class instance [S] (or [tag]) is prepared to be used. |
256 | bool isPrepared<S>({String tag}) => _factory.containsKey(_getKey(S, tag)); | 280 | bool isPrepared<S>({String tag}) => _factory.containsKey(_getKey(S, tag)); |
257 | } | 281 | } |
258 | 282 | ||
@@ -260,23 +284,37 @@ typedef FcBuilderFunc<S> = S Function(); | @@ -260,23 +284,37 @@ typedef FcBuilderFunc<S> = S Function(); | ||
260 | 284 | ||
261 | typedef FcBuilderFuncAsync<S> = Future<S> Function(); | 285 | typedef FcBuilderFuncAsync<S> = Future<S> Function(); |
262 | 286 | ||
263 | -class FcBuilder<S> { | 287 | +/// Internal class to register instances with Get.[put]<[S]>(). |
288 | +class _FcBuilder<S> { | ||
289 | + /// Marks the Builder as a single instance. | ||
290 | + /// For reusing [dependency] instead of [builderFunc] | ||
264 | bool isSingleton; | 291 | bool isSingleton; |
265 | - FcBuilderFunc builderFunc; | 292 | + |
293 | + /// Stores the actual object instance when [isSingleton]=true. | ||
266 | S dependency; | 294 | S dependency; |
295 | + | ||
296 | + /// Generates (and regenerates) the instance when [isSingleton]=false. | ||
297 | + /// Usually used by factory methods | ||
298 | + FcBuilderFunc<S> builderFunc; | ||
299 | + | ||
300 | + /// Flag to persist the instance in memory, | ||
301 | + /// without considering [GetConfig.smartManagement] | ||
267 | bool permanent = false; | 302 | bool permanent = false; |
303 | + | ||
268 | bool isInit = false; | 304 | bool isInit = false; |
269 | 305 | ||
270 | - FcBuilder(this.isSingleton, this.builderFunc, this.permanent, this.isInit); | 306 | + _FcBuilder(this.isSingleton, this.builderFunc, this.permanent, this.isInit); |
271 | 307 | ||
308 | + /// Gets the actual instance by it's [builderFunc] or the persisted instance. | ||
272 | S getDependency() { | 309 | S getDependency() { |
273 | - if (isSingleton) { | ||
274 | - if (dependency == null) { | ||
275 | - dependency = builderFunc() as S; | ||
276 | - } | ||
277 | - return dependency; | ||
278 | - } else { | ||
279 | - return builderFunc() as S; | ||
280 | - } | 310 | + return isSingleton ? dependency ??= builderFunc() : builderFunc(); |
281 | } | 311 | } |
282 | } | 312 | } |
313 | + | ||
314 | +/// Internal class to register a future instance with [lazyPut], | ||
315 | +/// keeps a reference to the callback to be called. | ||
316 | +class _Lazy { | ||
317 | + bool fenix; | ||
318 | + FcBuilderFunc builder; | ||
319 | + _Lazy(this.builder, this.fenix); | ||
320 | +} |
@@ -10,6 +10,7 @@ class GetPageMatch { | @@ -10,6 +10,7 @@ class GetPageMatch { | ||
10 | 10 | ||
11 | class ParseRouteTree { | 11 | class ParseRouteTree { |
12 | final List<ParseRouteTreeNode> _nodes = <ParseRouteTreeNode>[]; | 12 | final List<ParseRouteTreeNode> _nodes = <ParseRouteTreeNode>[]; |
13 | + | ||
13 | // bool _hasDefaultRoute = false; | 14 | // bool _hasDefaultRoute = false; |
14 | 15 | ||
15 | void addRoute(GetPage route) { | 16 | void addRoute(GetPage route) { |
@@ -59,11 +60,14 @@ class ParseRouteTree { | @@ -59,11 +60,14 @@ class ParseRouteTree { | ||
59 | if (usePath.startsWith("/")) { | 60 | if (usePath.startsWith("/")) { |
60 | usePath = path.substring(1); | 61 | usePath = path.substring(1); |
61 | } | 62 | } |
62 | - List<String> components = usePath.split("/"); | 63 | + |
64 | + // should take off url parameters first.. | ||
65 | + final uri = Uri.tryParse(usePath); | ||
66 | +// List<String> components = usePath.split("/"); | ||
67 | + List<String> components = uri.pathSegments; | ||
63 | if (path == Navigator.defaultRouteName) { | 68 | if (path == Navigator.defaultRouteName) { |
64 | components = ["/"]; | 69 | components = ["/"]; |
65 | } | 70 | } |
66 | - | ||
67 | Map<ParseRouteTreeNode, ParseRouteTreeNodeMatch> nodeMatches = | 71 | Map<ParseRouteTreeNode, ParseRouteTreeNodeMatch> nodeMatches = |
68 | <ParseRouteTreeNode, ParseRouteTreeNodeMatch>{}; | 72 | <ParseRouteTreeNode, ParseRouteTreeNodeMatch>{}; |
69 | List<ParseRouteTreeNode> nodesToCheck = _nodes; | 73 | List<ParseRouteTreeNode> nodesToCheck = _nodes; |
@@ -103,6 +107,10 @@ class ParseRouteTree { | @@ -103,6 +107,10 @@ class ParseRouteTree { | ||
103 | ParseRouteTreeNodeMatch parentMatch = nodeMatches[node.parent]; | 107 | ParseRouteTreeNodeMatch parentMatch = nodeMatches[node.parent]; |
104 | ParseRouteTreeNodeMatch match = | 108 | ParseRouteTreeNodeMatch match = |
105 | ParseRouteTreeNodeMatch.fromMatch(parentMatch, node); | 109 | ParseRouteTreeNodeMatch.fromMatch(parentMatch, node); |
110 | + | ||
111 | + // TODO: find a way to clean this implementation. | ||
112 | + match.parameters.addAll(uri.queryParameters); | ||
113 | + | ||
106 | if (node.isParameter()) { | 114 | if (node.isParameter()) { |
107 | String paramKey = node.part.substring(1); | 115 | String paramKey = node.part.substring(1); |
108 | match.parameters[paramKey] = pathPart; | 116 | match.parameters[paramKey] = pathPart; |
1 | +/// [Bindings] should be extended or implemented. | ||
2 | +/// When using [GetMaterialApp], all [GetPage]s and navigation methods (like Get.to()) | ||
3 | +/// have a [binding] property that takes an instance of Bindings to manage the | ||
4 | +/// dependencies() (via [Get.put()]) for the Route you are opening. | ||
1 | abstract class Bindings { | 5 | abstract class Bindings { |
2 | void dependencies(); | 6 | void dependencies(); |
3 | } | 7 | } |
4 | 8 | ||
9 | +/// Simplifies Bindings generation from a single callback. | ||
10 | +/// To avoid the creation of a custom Binding instance per route. | ||
11 | +/// | ||
12 | +/// Example: | ||
13 | +/// ``` | ||
14 | +/// GetPage( | ||
15 | +/// name: '/', | ||
16 | +/// page: () => Home(), | ||
17 | +/// binding: BindingsBuilder(() => Get.put(HomeController())), | ||
18 | +/// ), | ||
19 | +/// ```` | ||
20 | +class BindingsBuilder extends Bindings { | ||
21 | + /// Register your dependencies in the [builder] callback. | ||
22 | + final Function() builder; | ||
23 | + | ||
24 | + BindingsBuilder(this.builder); | ||
25 | + | ||
26 | + @override | ||
27 | + void dependencies() { | ||
28 | + builder(); | ||
29 | + } | ||
30 | +} | ||
31 | + | ||
5 | // abstract class INavigation {} | 32 | // abstract class INavigation {} |
6 | // typedef Snack = Function(); | 33 | // typedef Snack = Function(); |
7 | // typedef Modal = Function(); | 34 | // typedef Modal = Function(); |
1 | import 'dart:async'; | 1 | import 'dart:async'; |
2 | import 'dart:collection'; | 2 | import 'dart:collection'; |
3 | + | ||
3 | import 'rx_interface.dart'; | 4 | import 'rx_interface.dart'; |
4 | 5 | ||
6 | +RxInterface getObs; | ||
7 | + | ||
8 | +typedef bool Condition(); | ||
9 | + | ||
5 | class _RxImpl<T> implements RxInterface<T> { | 10 | class _RxImpl<T> implements RxInterface<T> { |
6 | StreamController<T> subject = StreamController<T>.broadcast(); | 11 | StreamController<T> subject = StreamController<T>.broadcast(); |
7 | HashMap<Stream<T>, StreamSubscription> _subscriptions = | 12 | HashMap<Stream<T>, StreamSubscription> _subscriptions = |
@@ -15,14 +20,26 @@ class _RxImpl<T> implements RxInterface<T> { | @@ -15,14 +20,26 @@ class _RxImpl<T> implements RxInterface<T> { | ||
15 | return _value; | 20 | return _value; |
16 | } | 21 | } |
17 | 22 | ||
18 | - bool get canUpdate { | ||
19 | - return _subscriptions.length > 0; | 23 | + /// Common to all Types [T], this operator overloading is using for |
24 | + /// assignment, same as rx.value | ||
25 | + /// | ||
26 | + /// Example: | ||
27 | + /// ``` | ||
28 | + /// var counter = 0.obs ; | ||
29 | + /// counter >>= 3; // same as counter.value=3; | ||
30 | + /// print(counter); // calls .toString() now | ||
31 | + /// ``` | ||
32 | + /// | ||
33 | + /// WARNING: still WIP, needs testing! | ||
34 | + _RxImpl<T> operator >>(T val) { | ||
35 | + subject.add(value = val); | ||
36 | + return this; | ||
20 | } | 37 | } |
21 | 38 | ||
39 | + bool get canUpdate => _subscriptions.isNotEmpty; | ||
40 | + | ||
22 | T call([T v]) { | 41 | T call([T v]) { |
23 | - if (v != null) { | ||
24 | - this.value = v; | ||
25 | - } | 42 | + if (v != null) this.value = v; |
26 | return this.value; | 43 | return this.value; |
27 | } | 44 | } |
28 | 45 | ||
@@ -33,15 +50,24 @@ class _RxImpl<T> implements RxInterface<T> { | @@ -33,15 +50,24 @@ class _RxImpl<T> implements RxInterface<T> { | ||
33 | 50 | ||
34 | String get string => value.toString(); | 51 | String get string => value.toString(); |
35 | 52 | ||
36 | - close() { | ||
37 | - _subscriptions.forEach((observable, subscription) { | ||
38 | - subscription.cancel(); | ||
39 | - }); | 53 | + @override |
54 | + String toString() => value.toString(); | ||
55 | + | ||
56 | + /// This equality override works for _RxImpl instances and the internal values. | ||
57 | + bool operator ==(o) { | ||
58 | + // Todo, find a common implementation for the hashCode of different Types. | ||
59 | + if (o is T) return _value == o; | ||
60 | + if (o is _RxImpl<T>) return _value == o.value; | ||
61 | + return false; | ||
62 | + } | ||
63 | + | ||
64 | + void close() { | ||
65 | + _subscriptions.forEach((observable, subscription) => subscription.cancel()); | ||
40 | _subscriptions.clear(); | 66 | _subscriptions.clear(); |
41 | subject.close(); | 67 | subject.close(); |
42 | } | 68 | } |
43 | 69 | ||
44 | - addListener(Stream<T> rxGetx) { | 70 | + void addListener(Stream<T> rxGetx) { |
45 | if (_subscriptions.containsKey(rxGetx)) { | 71 | if (_subscriptions.containsKey(rxGetx)) { |
46 | return; | 72 | return; |
47 | } | 73 | } |
@@ -286,6 +312,16 @@ class RxList<E> extends Iterable<E> implements RxInterface<List<E>> { | @@ -286,6 +312,16 @@ class RxList<E> extends Iterable<E> implements RxInterface<List<E>> { | ||
286 | subject.add(_list); | 312 | subject.add(_list); |
287 | } | 313 | } |
288 | 314 | ||
315 | + /// Special override to push() element(s) in a reactive way | ||
316 | + /// inside the List, | ||
317 | + RxList<E> operator +(val) { | ||
318 | + if (val is Iterable) | ||
319 | + subject.add(_list..addAll(val)); | ||
320 | + else | ||
321 | + subject.add(_list..add(val)); | ||
322 | + return this; | ||
323 | + } | ||
324 | + | ||
289 | E operator [](int index) { | 325 | E operator [](int index) { |
290 | return value[index]; | 326 | return value[index]; |
291 | } | 327 | } |
@@ -428,10 +464,6 @@ class RxList<E> extends Iterable<E> implements RxInterface<List<E>> { | @@ -428,10 +464,6 @@ class RxList<E> extends Iterable<E> implements RxInterface<List<E>> { | ||
428 | List<E> _list = <E>[]; | 464 | List<E> _list = <E>[]; |
429 | } | 465 | } |
430 | 466 | ||
431 | -RxInterface getObs; | ||
432 | - | ||
433 | -typedef bool Condition(); | ||
434 | - | ||
435 | class RxBool extends _RxImpl<bool> { | 467 | class RxBool extends _RxImpl<bool> { |
436 | RxBool([bool initial]) { | 468 | RxBool([bool initial]) { |
437 | _value = initial; | 469 | _value = initial; |
@@ -442,24 +474,109 @@ class RxDouble extends _RxImpl<double> { | @@ -442,24 +474,109 @@ class RxDouble extends _RxImpl<double> { | ||
442 | RxDouble([double initial]) { | 474 | RxDouble([double initial]) { |
443 | _value = initial; | 475 | _value = initial; |
444 | } | 476 | } |
477 | + | ||
478 | + RxDouble operator +(double val) { | ||
479 | + subject.add(value += val); | ||
480 | + return this; | ||
481 | + } | ||
482 | + | ||
483 | + RxDouble operator -(double val) { | ||
484 | + subject.add(value -= val); | ||
485 | + return this; | ||
486 | + } | ||
487 | + | ||
488 | + RxDouble operator /(double val) { | ||
489 | + subject.add(value /= val); | ||
490 | + return this; | ||
491 | + } | ||
492 | + | ||
493 | + RxDouble operator *(double val) { | ||
494 | + subject.add(value *= val); | ||
495 | + return this; | ||
496 | + } | ||
445 | } | 497 | } |
446 | 498 | ||
447 | class RxNum extends _RxImpl<num> { | 499 | class RxNum extends _RxImpl<num> { |
448 | RxNum([num initial]) { | 500 | RxNum([num initial]) { |
449 | _value = initial; | 501 | _value = initial; |
450 | } | 502 | } |
503 | + | ||
504 | + RxNum operator >>(num val) { | ||
505 | + subject.add(value = val); | ||
506 | + return this; | ||
507 | + } | ||
508 | + | ||
509 | + RxNum operator +(num val) { | ||
510 | + subject.add(value += val); | ||
511 | + return this; | ||
512 | + } | ||
513 | + | ||
514 | + RxNum operator -(num val) { | ||
515 | + subject.add(value -= val); | ||
516 | + return this; | ||
517 | + } | ||
518 | + | ||
519 | + RxNum operator /(num val) { | ||
520 | + subject.add(value /= val); | ||
521 | + return this; | ||
522 | + } | ||
523 | + | ||
524 | + RxNum operator *(num val) { | ||
525 | + subject.add(value *= val); | ||
526 | + return this; | ||
527 | + } | ||
451 | } | 528 | } |
452 | 529 | ||
453 | class RxString extends _RxImpl<String> { | 530 | class RxString extends _RxImpl<String> { |
454 | RxString([String initial]) { | 531 | RxString([String initial]) { |
455 | _value = initial; | 532 | _value = initial; |
456 | } | 533 | } |
534 | + | ||
535 | + RxString operator >>(String val) { | ||
536 | + subject.add(value = val); | ||
537 | + return this; | ||
538 | + } | ||
539 | + | ||
540 | + RxString operator +(String val) { | ||
541 | + subject.add(value += val); | ||
542 | + return this; | ||
543 | + } | ||
544 | + | ||
545 | + RxString operator *(int val) { | ||
546 | + subject.add(value *= val); | ||
547 | + return this; | ||
548 | + } | ||
457 | } | 549 | } |
458 | 550 | ||
459 | class RxInt extends _RxImpl<int> { | 551 | class RxInt extends _RxImpl<int> { |
460 | RxInt([int initial]) { | 552 | RxInt([int initial]) { |
461 | _value = initial; | 553 | _value = initial; |
462 | } | 554 | } |
555 | + | ||
556 | + RxInt operator >>(int val) { | ||
557 | + subject.add(value = val); | ||
558 | + return this; | ||
559 | + } | ||
560 | + | ||
561 | + RxInt operator +(int val) { | ||
562 | + subject.add(value += val); | ||
563 | + return this; | ||
564 | + } | ||
565 | + | ||
566 | + RxInt operator -(int val) { | ||
567 | + subject.add(value -= val); | ||
568 | + return this; | ||
569 | + } | ||
570 | + | ||
571 | + RxInt operator /(int val) { | ||
572 | + subject.add(value ~/= val); | ||
573 | + return this; | ||
574 | + } | ||
575 | + | ||
576 | + RxInt operator *(int val) { | ||
577 | + subject.add(value *= val); | ||
578 | + return this; | ||
579 | + } | ||
463 | } | 580 | } |
464 | 581 | ||
465 | class Rx<T> extends _RxImpl<T> { | 582 | class Rx<T> extends _RxImpl<T> { |
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