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