Jonny Borges
Committed by GitHub

Merge pull request #519 from roipeker/master

GetInstance cleanup
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 }