Jonatas

added GetNotifier, improve structure, init refator from scratch

@@ -5,7 +5,7 @@ import 'package:get/get.dart'; @@ -5,7 +5,7 @@ import 'package:get/get.dart';
5 5
6 import '../controllers/home_controller.dart'; 6 import '../controllers/home_controller.dart';
7 7
8 -class CountryView extends GetWidget<HomeController> { 8 +class CountryView extends GetView<HomeController> {
9 @override 9 @override
10 Widget build(BuildContext context) { 10 Widget build(BuildContext context) {
11 return Container( 11 return Container(
@@ -29,7 +29,7 @@ dependencies: @@ -29,7 +29,7 @@ dependencies:
29 get: 29 get:
30 path: ../ 30 path: ../
31 dio: ^3.0.9 31 dio: ^3.0.9
32 - get_test: ^3.13.2 32 + get_test: ^3.13.3
33 33
34 dependency_overrides: 34 dependency_overrides:
35 get: 35 get:
  1 +import 'dart:io';
1 import 'dart:math'; 2 import 'dart:math';
2 import 'package:flutter/material.dart'; 3 import 'package:flutter/material.dart';
3 import 'package:flutter_test/flutter_test.dart'; 4 import 'package:flutter_test/flutter_test.dart';
@@ -25,6 +26,7 @@ class MockRepository implements IHomeRepository { @@ -25,6 +26,7 @@ class MockRepository implements IHomeRepository {
25 } 26 }
26 27
27 void main() { 28 void main() {
  29 + setUpAll(() => HttpOverrides.global = null);
28 final binding = BindingsBuilder(() { 30 final binding = BindingsBuilder(() {
29 Get.lazyPut<IHomeRepository>(() => MockRepository()); 31 Get.lazyPut<IHomeRepository>(() => MockRepository());
30 Get.lazyPut<HomeController>( 32 Get.lazyPut<HomeController>(
@@ -11,6 +11,10 @@ class GetInstance { @@ -11,6 +11,10 @@ class GetInstance {
11 11
12 static GetInstance _getInstance; 12 static GetInstance _getInstance;
13 13
  14 + T call<T>() {
  15 + return find<T>();
  16 + }
  17 +
14 /// Holds references to every registered Instance when using 18 /// Holds references to every registered Instance when using
15 /// [Get.put()] 19 /// [Get.put()]
16 static final Map<String, _InstanceBuilderFactory> _singl = {}; 20 static final Map<String, _InstanceBuilderFactory> _singl = {};
@@ -60,6 +64,20 @@ class GetInstance { @@ -60,6 +64,20 @@ class GetInstance {
60 _factory.putIfAbsent(key, () => _Lazy(builder, fenix)); 64 _factory.putIfAbsent(key, () => _Lazy(builder, fenix));
61 } 65 }
62 66
  67 + void injector<S>(
  68 + InjectorBuilderCallback<S> fn, {
  69 + String tag,
  70 + bool fenix = false,
  71 + // bool permanent = false,
  72 + }) {
  73 + lazyPut(
  74 + () => fn(this),
  75 + tag: tag,
  76 + fenix: fenix,
  77 + // permanent: permanent,
  78 + );
  79 + }
  80 +
63 /// async version of [Get.put()]. 81 /// async version of [Get.put()].
64 /// Awaits for the resolution of the Future from [builder()] parameter and 82 /// Awaits for the resolution of the Future from [builder()] parameter and
65 /// stores the Instance returned. 83 /// stores the Instance returned.
@@ -85,7 +103,7 @@ class GetInstance { @@ -85,7 +103,7 @@ class GetInstance {
85 S dependency, { 103 S dependency, {
86 String tag, 104 String tag,
87 bool permanent = false, 105 bool permanent = false,
88 - InstanceBuilderCallback<S> builder, 106 + @deprecated InstanceBuilderCallback<S> builder,
89 }) { 107 }) {
90 _insert( 108 _insert(
91 isSingleton: true, 109 isSingleton: true,
@@ -201,12 +219,6 @@ class GetInstance { @@ -201,12 +219,6 @@ class GetInstance {
201 _routesKey.putIfAbsent(_getKey(S, tag), () => Get.reference); 219 _routesKey.putIfAbsent(_getKey(S, tag), () => Get.reference);
202 } 220 }
203 221
204 - /// Finds and returns a Instance<[S]> (or [tag]) without further processing.  
205 - S findByType<S>(Type type, {String tag}) {  
206 - final key = _getKey(type, tag);  
207 - return _singl[key].getDependency() as S;  
208 - }  
209 -  
210 /// Initializes the controller 222 /// Initializes the controller
211 S _startController<S>({String tag}) { 223 S _startController<S>({String tag}) {
212 final key = _getKey(S, tag); 224 final key = _getKey(S, tag);
@@ -216,9 +228,9 @@ class GetInstance { @@ -216,9 +228,9 @@ class GetInstance {
216 i.onStart(); 228 i.onStart();
217 Get.log('"$key" has been initialized'); 229 Get.log('"$key" has been initialized');
218 } 230 }
219 - if (!_singl[key].isSingleton && i.onClose != null) { 231 + if (!_singl[key].isSingleton && i.onDelete != null) {
220 _routesByCreate[Get.reference] ??= HashSet<Function>(); 232 _routesByCreate[Get.reference] ??= HashSet<Function>();
221 - _routesByCreate[Get.reference].add(i.onClose); 233 + _routesByCreate[Get.reference].add(i.onDelete);
222 } 234 }
223 } 235 }
224 return i; 236 return i;
@@ -349,7 +361,7 @@ class GetInstance { @@ -349,7 +361,7 @@ class GetInstance {
349 return false; 361 return false;
350 } 362 }
351 if (i is GetLifeCycle) { 363 if (i is GetLifeCycle) {
352 - i.onClose(); 364 + i.onDelete();
353 Get.log('"$newKey" onClose() called'); 365 Get.log('"$newKey" onClose() called');
354 } 366 }
355 367
@@ -375,6 +387,8 @@ class GetInstance { @@ -375,6 +387,8 @@ class GetInstance {
375 387
376 typedef InstanceBuilderCallback<S> = S Function(); 388 typedef InstanceBuilderCallback<S> = S Function();
377 389
  390 +typedef InjectorBuilderCallback<S> = S Function(GetInstance);
  391 +
378 typedef AsyncInstanceBuilderCallback<S> = Future<S> Function(); 392 typedef AsyncInstanceBuilderCallback<S> = Future<S> Function();
379 393
380 /// Internal class to register instances with Get.[put]<[S]>(). 394 /// Internal class to register instances with Get.[put]<[S]>().
@@ -413,6 +427,7 @@ class _InstanceBuilderFactory<S> { @@ -413,6 +427,7 @@ class _InstanceBuilderFactory<S> {
413 /// keeps a reference to the callback to be called. 427 /// keeps a reference to the callback to be called.
414 class _Lazy { 428 class _Lazy {
415 bool fenix; 429 bool fenix;
  430 + bool permanent = false;
416 InstanceBuilderCallback builder; 431 InstanceBuilderCallback builder;
417 432
418 _Lazy(this.builder, this.fenix); 433 _Lazy(this.builder, this.fenix);
@@ -11,13 +11,15 @@ class _InternalFinalCallback<T> { @@ -11,13 +11,15 @@ class _InternalFinalCallback<T> {
11 T call() => callback.call(); 11 T call() => callback.call();
12 } 12 }
13 13
14 -abstract class GetLifeCycle { 14 +mixin GetLifeCycle {
15 /// Called at the exact moment the widget is allocated in memory. 15 /// Called at the exact moment the widget is allocated in memory.
16 /// It uses an internal "callable" type, to avoid any @overrides in subclases. 16 /// It uses an internal "callable" type, to avoid any @overrides in subclases.
17 /// This method should be internal and is required to define the 17 /// This method should be internal and is required to define the
18 /// lifetime cycle of the subclass. 18 /// lifetime cycle of the subclass.
19 final onStart = _InternalFinalCallback<void>(); 19 final onStart = _InternalFinalCallback<void>();
20 20
  21 + final onDelete = _InternalFinalCallback<void>();
  22 +
21 /// Called immediately after the widget is allocated in memory. 23 /// Called immediately after the widget is allocated in memory.
22 /// You might use this to initialize something for the controller. 24 /// You might use this to initialize something for the controller.
23 void onInit() {} 25 void onInit() {}
  1 +import 'dart:ui';
  2 +
1 import 'package:flutter/material.dart'; 3 import 'package:flutter/material.dart';
2 import 'package:flutter/scheduler.dart'; 4 import 'package:flutter/scheduler.dart';
3 import '../../get_core/get_core.dart'; 5 import '../../get_core/get_core.dart';
@@ -11,510 +13,279 @@ import 'routes/transitions_type.dart'; @@ -11,510 +13,279 @@ import 'routes/transitions_type.dart';
11 13
12 //TODO: Split this class on "Snackbar" "Dialog" "bottomSheet" 14 //TODO: Split this class on "Snackbar" "Dialog" "bottomSheet"
13 //and "navigation" extensions 15 //and "navigation" extensions
14 -extension GetNavigation on GetInterface {  
15 - /// **Navigation.push()** shortcut.<br><br>  
16 - ///  
17 - /// Pushes a new [page] to the stack  
18 - ///  
19 - /// It has the advantage of not needing context,  
20 - /// so you can call from your business logic  
21 - ///  
22 - /// You can set a custom [transition], and a transition [duration].  
23 - ///  
24 - /// You can send any type of value to the other route in the [arguments].  
25 - ///  
26 - /// Just like native routing in Flutter, you can push a route  
27 - /// as a [fullscreenDialog],  
28 - ///  
29 - /// [id] is for when you are using nested navigation,  
30 - /// as explained in documentation  
31 - ///  
32 - /// If you want the same behavior of ios that pops a route when the user drag,  
33 - /// you can set [popGesture] to true  
34 - ///  
35 - /// If you're using the [Bindings] api, you must define it here  
36 - ///  
37 - /// By default, GetX will prevent you from push a route that you already in,  
38 - /// if you want to push anyway, set [preventDuplicates] to false  
39 - Future<T> to<T>(  
40 - Widget page, {  
41 - bool opaque,  
42 - Transition transition,  
43 - Curve curve,  
44 - Duration duration,  
45 - int id,  
46 - bool fullscreenDialog = false,  
47 - dynamic arguments,  
48 - Bindings binding,  
49 - bool preventDuplicates = true,  
50 - bool popGesture,  
51 - }) {  
52 - var routeName = "/${page.runtimeType.toString()}";  
53 - if (preventDuplicates && routeName == currentRoute) {  
54 - return null;  
55 - }  
56 - return global(id)?.currentState?.push(  
57 - GetPageRoute(  
58 - opaque: opaque ?? true,  
59 - page: () => page,  
60 - routeName: routeName,  
61 - settings: RouteSettings(  
62 - // name: forceRouteName ? '${a.runtimeType}' : '',  
63 - arguments: arguments,  
64 - ),  
65 - popGesture: popGesture ?? defaultPopGesture,  
66 - transition: transition ?? defaultTransition,  
67 - curve: curve ?? defaultTransitionCurve,  
68 - fullscreenDialog: fullscreenDialog,  
69 - binding: binding,  
70 - transitionDuration: duration ?? defaultTransitionDuration,  
71 - ),  
72 - );  
73 - }  
74 16
75 - /// **Navigation.pushNamed()** shortcut.<br><br>  
76 - ///  
77 - /// Pushes a new named [page] to the stack.  
78 - ///  
79 - /// It has the advantage of not needing context, so you can call  
80 - /// from your business logic.  
81 - ///  
82 - /// You can send any type of value to the other route in the [arguments].  
83 - ///  
84 - /// [id] is for when you are using nested navigation,  
85 - /// as explained in documentation  
86 - ///  
87 - /// By default, GetX will prevent you from push a route that you already in,  
88 - /// if you want to push anyway, set [preventDuplicates] to false  
89 - ///  
90 - /// Note: Always put a slash on the route ('/page1'), to avoid unnexpected errors  
91 - Future<T> toNamed<T>(  
92 - String page, {  
93 - dynamic arguments,  
94 - int id,  
95 - bool preventDuplicates = true,  
96 - }) {  
97 - if (preventDuplicates && page == currentRoute) {  
98 - return null;  
99 - }  
100 - return global(id)?.currentState?.pushNamed(page, arguments: arguments);  
101 - } 17 +extension ExtensionSnackbar on GetInterface {
  18 + void rawSnackbar({
  19 + String title,
  20 + String message,
  21 + Widget titleText,
  22 + Widget messageText,
  23 + Widget icon,
  24 + bool instantInit = true,
  25 + bool shouldIconPulse = true,
  26 + double maxWidth,
  27 + EdgeInsets margin = const EdgeInsets.all(0.0),
  28 + EdgeInsets padding = const EdgeInsets.all(16),
  29 + double borderRadius = 0.0,
  30 + Color borderColor,
  31 + double borderWidth = 1.0,
  32 + Color backgroundColor = const Color(0xFF303030),
  33 + Color leftBarIndicatorColor,
  34 + List<BoxShadow> boxShadows,
  35 + Gradient backgroundGradient,
  36 + FlatButton mainButton,
  37 + OnTap onTap,
  38 + Duration duration = const Duration(seconds: 3),
  39 + bool isDismissible = true,
  40 + SnackDismissDirection dismissDirection = SnackDismissDirection.VERTICAL,
  41 + bool showProgressIndicator = false,
  42 + AnimationController progressIndicatorController,
  43 + Color progressIndicatorBackgroundColor,
  44 + Animation<Color> progressIndicatorValueColor,
  45 + SnackPosition snackPosition = SnackPosition.BOTTOM,
  46 + SnackStyle snackStyle = SnackStyle.FLOATING,
  47 + Curve forwardAnimationCurve = Curves.easeOutCirc,
  48 + Curve reverseAnimationCurve = Curves.easeOutCirc,
  49 + Duration animationDuration = const Duration(seconds: 1),
  50 + SnackbarStatusCallback snackbarStatus,
  51 + double barBlur = 0.0,
  52 + double overlayBlur = 0.0,
  53 + Color overlayColor,
  54 + Form userInputForm,
  55 + }) async {
  56 + final getBar = GetBar(
  57 + snackbarStatus: snackbarStatus,
  58 + title: title,
  59 + message: message,
  60 + titleText: titleText,
  61 + messageText: messageText,
  62 + snackPosition: snackPosition,
  63 + borderRadius: borderRadius,
  64 + margin: margin,
  65 + duration: duration,
  66 + barBlur: barBlur,
  67 + backgroundColor: backgroundColor,
  68 + icon: icon,
  69 + shouldIconPulse: shouldIconPulse,
  70 + maxWidth: maxWidth,
  71 + padding: padding,
  72 + borderColor: borderColor,
  73 + borderWidth: borderWidth,
  74 + leftBarIndicatorColor: leftBarIndicatorColor,
  75 + boxShadows: boxShadows,
  76 + backgroundGradient: backgroundGradient,
  77 + mainButton: mainButton,
  78 + onTap: onTap,
  79 + isDismissible: isDismissible,
  80 + dismissDirection: dismissDirection,
  81 + showProgressIndicator: showProgressIndicator ?? false,
  82 + progressIndicatorController: progressIndicatorController,
  83 + progressIndicatorBackgroundColor: progressIndicatorBackgroundColor,
  84 + progressIndicatorValueColor: progressIndicatorValueColor,
  85 + snackStyle: snackStyle,
  86 + forwardAnimationCurve: forwardAnimationCurve,
  87 + reverseAnimationCurve: reverseAnimationCurve,
  88 + animationDuration: animationDuration,
  89 + overlayBlur: overlayBlur,
  90 + overlayColor: overlayColor,
  91 + userInputForm: userInputForm,
  92 + );
102 93
103 - /// **Navigation.pushReplacementNamed()** shortcut.<br><br>  
104 - ///  
105 - /// Pop the current named [page] in the stack and push a new one in its place  
106 - ///  
107 - /// It has the advantage of not needing context, so you can call  
108 - /// from your business logic.  
109 - ///  
110 - /// You can send any type of value to the other route in the [arguments].  
111 - ///  
112 - /// [id] is for when you are using nested navigation,  
113 - /// as explained in documentation  
114 - ///  
115 - /// By default, GetX will prevent you from push a route that you already in,  
116 - /// if you want to push anyway, set [preventDuplicates] to false  
117 - ///  
118 - /// Note: Always put a slash on the route ('/page1'), to avoid unnexpected errors  
119 - Future<T> offNamed<T>(  
120 - String page, {  
121 - dynamic arguments,  
122 - int id,  
123 - bool preventDuplicates = true,  
124 - }) {  
125 - if (preventDuplicates && page == currentRoute) {  
126 - return null; 94 + if (instantInit) {
  95 + getBar.show();
  96 + } else {
  97 + SchedulerBinding.instance.addPostFrameCallback((_) {
  98 + getBar.show();
  99 + });
127 } 100 }
128 - return global(id)  
129 - ?.currentState  
130 - ?.pushReplacementNamed(page, arguments: arguments);  
131 } 101 }
132 102
133 - /// **Navigation.popUntil()** shortcut.<br><br>  
134 - ///  
135 - /// Calls pop several times in the stack until [predicate] returns true  
136 - ///  
137 - /// [id] is for when you are using nested navigation,  
138 - /// as explained in documentation  
139 - ///  
140 - /// [predicate] can be used like this:  
141 - /// `Get.until((route) => Get.currentRoute == '/home')`so when you get to home page,  
142 - ///  
143 - /// or also like this:  
144 - /// `Get.until((route) => !Get.isDialogOpen())`, to make sure the  
145 - /// dialog is closed  
146 - void until(RoutePredicate predicate, {int id}) {  
147 - // if (key.currentState.mounted) // add this if appear problems on future with route navigate  
148 - // when widget don't mounted  
149 - return global(id)?.currentState?.popUntil(predicate); 103 + Future<T> showSnackbar<T>(GetBar snackbar) {
  104 + return key?.currentState?.push(SnackRoute<T>(snack: snackbar));
150 } 105 }
151 106
152 - /// **Navigation.pushAndRemoveUntil()** shortcut.<br><br>  
153 - ///  
154 - /// Push the given [page], and then pop several pages in the stack until  
155 - /// [predicate] returns true  
156 - ///  
157 - /// [id] is for when you are using nested navigation,  
158 - /// as explained in documentation  
159 - ///  
160 - /// Obs: unlike other get methods, this one you need to send a function  
161 - /// that returns the widget to the page argument, like this:  
162 - /// Get.offUntil( () => HomePage() )  
163 - ///  
164 - /// [predicate] can be used like this:  
165 - /// `Get.until((route) => Get.currentRoute == '/home')`so when you get to home page,  
166 - ///  
167 - /// or also like this:  
168 - /// `Get.until((route) => !Get.isDialogOpen())`, to make sure the dialog  
169 - /// is closed  
170 - Future<T> offUntil<T>(Route<T> page, RoutePredicate predicate, {int id}) {  
171 - // if (key.currentState.mounted) // add this if appear problems on future with route navigate  
172 - // when widget don't mounted  
173 - return global(id)?.currentState?.pushAndRemoveUntil(page, predicate);  
174 - } 107 + void snackbar(
  108 + String title,
  109 + String message, {
  110 + Color colorText,
  111 + Duration duration,
175 112
176 - /// **Navigation.pushNamedAndRemoveUntil()** shortcut.<br><br>  
177 - ///  
178 - /// Push the given named [page], and then pop several pages in the stack  
179 - /// until [predicate] returns true  
180 - ///  
181 - /// You can send any type of value to the other route in the [arguments].  
182 - ///  
183 - /// [id] is for when you are using nested navigation,  
184 - /// as explained in documentation  
185 - ///  
186 - /// [predicate] can be used like this:  
187 - /// `Get.until((route) => Get.currentRoute == '/home')`so when you get to home page,  
188 - /// or also like  
189 - /// `Get.until((route) => !Get.isDialogOpen())`, to make sure the dialog  
190 - /// is closed  
191 - ///  
192 - /// Note: Always put a slash on the route ('/page1'), to avoid unnexpected errors  
193 - Future<T> offNamedUntil<T>(  
194 - String page,  
195 - RoutePredicate predicate, {  
196 - int id,  
197 - dynamic arguments,  
198 - }) {  
199 - return global(id)  
200 - ?.currentState  
201 - ?.pushNamedAndRemoveUntil(page, predicate, arguments: arguments); 113 + /// with instantInit = false you can put snackbar on initState
  114 + bool instantInit = true,
  115 + SnackPosition snackPosition,
  116 + Widget titleText,
  117 + Widget messageText,
  118 + Widget icon,
  119 + bool shouldIconPulse,
  120 + double maxWidth,
  121 + EdgeInsets margin,
  122 + EdgeInsets padding,
  123 + double borderRadius,
  124 + Color borderColor,
  125 + double borderWidth,
  126 + Color backgroundColor,
  127 + Color leftBarIndicatorColor,
  128 + List<BoxShadow> boxShadows,
  129 + Gradient backgroundGradient,
  130 + FlatButton mainButton,
  131 + OnTap onTap,
  132 + bool isDismissible,
  133 + bool showProgressIndicator,
  134 + SnackDismissDirection dismissDirection,
  135 + AnimationController progressIndicatorController,
  136 + Color progressIndicatorBackgroundColor,
  137 + Animation<Color> progressIndicatorValueColor,
  138 + SnackStyle snackStyle,
  139 + Curve forwardAnimationCurve,
  140 + Curve reverseAnimationCurve,
  141 + Duration animationDuration,
  142 + double barBlur,
  143 + double overlayBlur,
  144 + SnackbarStatusCallback snackbarStatus,
  145 + Color overlayColor,
  146 + Form userInputForm,
  147 + }) async {
  148 + final getBar = GetBar(
  149 + snackbarStatus: snackbarStatus,
  150 + titleText: (title == null)
  151 + ? null
  152 + : titleText ??
  153 + Text(
  154 + title,
  155 + style: TextStyle(
  156 + color: colorText ?? iconColor ?? Colors.black,
  157 + fontWeight: FontWeight.w800,
  158 + fontSize: 16,
  159 + ),
  160 + ),
  161 + messageText: messageText ??
  162 + Text(
  163 + message,
  164 + style: TextStyle(
  165 + color: colorText ?? iconColor ?? Colors.black,
  166 + fontWeight: FontWeight.w300,
  167 + fontSize: 14,
  168 + ),
  169 + ),
  170 + snackPosition: snackPosition ?? SnackPosition.TOP,
  171 + borderRadius: borderRadius ?? 15,
  172 + margin: margin ?? EdgeInsets.symmetric(horizontal: 10),
  173 + duration: duration ?? Duration(seconds: 3),
  174 + barBlur: barBlur ?? 7.0,
  175 + backgroundColor: backgroundColor ?? Colors.grey.withOpacity(0.2),
  176 + icon: icon,
  177 + shouldIconPulse: shouldIconPulse ?? true,
  178 + maxWidth: maxWidth,
  179 + padding: padding ?? EdgeInsets.all(16),
  180 + borderColor: borderColor,
  181 + borderWidth: borderWidth,
  182 + leftBarIndicatorColor: leftBarIndicatorColor,
  183 + boxShadows: boxShadows,
  184 + backgroundGradient: backgroundGradient,
  185 + mainButton: mainButton,
  186 + onTap: onTap,
  187 + isDismissible: isDismissible ?? true,
  188 + dismissDirection: dismissDirection ?? SnackDismissDirection.VERTICAL,
  189 + showProgressIndicator: showProgressIndicator ?? false,
  190 + progressIndicatorController: progressIndicatorController,
  191 + progressIndicatorBackgroundColor: progressIndicatorBackgroundColor,
  192 + progressIndicatorValueColor: progressIndicatorValueColor,
  193 + snackStyle: snackStyle ?? SnackStyle.FLOATING,
  194 + forwardAnimationCurve: forwardAnimationCurve ?? Curves.easeOutCirc,
  195 + reverseAnimationCurve: reverseAnimationCurve ?? Curves.easeOutCirc,
  196 + animationDuration: animationDuration ?? Duration(seconds: 1),
  197 + overlayBlur: overlayBlur ?? 0.0,
  198 + overlayColor: overlayColor ?? Colors.transparent,
  199 + userInputForm: userInputForm);
  200 +
  201 + if (instantInit) {
  202 + showSnackbar(getBar);
  203 + } else {
  204 + routing.isSnackbar = true;
  205 + SchedulerBinding.instance.addPostFrameCallback((_) {
  206 + showSnackbar(getBar);
  207 + });
  208 + }
202 } 209 }
  210 +}
203 211
204 - /// **Navigation.popAndPushNamed()** shortcut.<br><br>  
205 - ///  
206 - /// Pop the current named page and pushes a new [page] to the stack  
207 - /// in its place  
208 - ///  
209 - /// You can send any type of value to the other route in the [arguments].  
210 - /// It is very similar to `offNamed()` but use a different approach  
211 - ///  
212 - /// The `offNamed()` pop a page, and goes to the next. The  
213 - /// `offAndToNamed()` goes to the next page, and removes the previous one.  
214 - /// The route transition animation is different.  
215 - Future<T> offAndToNamed<T>(  
216 - String page, {  
217 - dynamic arguments,  
218 - int id,  
219 - dynamic result, 212 +extension ExtensionDialog on GetInterface {
  213 + /// Show a dialog.
  214 + /// You can pass a [transitionDuration] and/or [transitionCurve],
  215 + /// overriding the defaults when the dialog shows up and closes.
  216 + /// When the dialog closes, uses those animations in reverse.
  217 + Future<T> dialog<T>(
  218 + Widget widget, {
  219 + bool barrierDismissible = true,
  220 + Color barrierColor,
  221 + bool useSafeArea = true,
  222 + bool useRootNavigator = true,
  223 + RouteSettings routeSettings,
  224 + Duration transitionDuration,
  225 + Curve transitionCurve,
220 }) { 226 }) {
221 - return global(id)  
222 - ?.currentState  
223 - ?.popAndPushNamed(page, arguments: arguments, result: result);  
224 - } 227 + assert(widget != null);
  228 + assert(barrierDismissible != null);
  229 + assert(useSafeArea != null);
  230 + assert(useRootNavigator != null);
  231 + assert(debugCheckHasMaterialLocalizations(context));
225 232
226 - /// **Navigation.removeRoute()** shortcut.<br><br>  
227 - ///  
228 - /// Remove a specific [route] from the stack  
229 - ///  
230 - /// [id] is for when you are using nested navigation,  
231 - /// as explained in documentation  
232 - void removeRoute(Route<dynamic> route, {int id}) {  
233 - return global(id)?.currentState?.removeRoute(route); 233 + final theme = Theme.of(context, shadowThemeOnly: true);
  234 + return generalDialog(
  235 + pageBuilder: (buildContext, animation, secondaryAnimation) {
  236 + final pageChild = widget;
  237 + Widget dialog = Builder(builder: (context) {
  238 + return theme != null
  239 + ? Theme(data: theme, child: pageChild)
  240 + : pageChild;
  241 + });
  242 + if (useSafeArea) {
  243 + dialog = SafeArea(child: dialog);
  244 + }
  245 + return dialog;
  246 + },
  247 + barrierDismissible: barrierDismissible,
  248 + barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
  249 + barrierColor: barrierColor ?? Colors.black54,
  250 + transitionDuration: transitionDuration ?? defaultDialogTransitionDuration,
  251 + transitionBuilder: (context, animation, secondaryAnimation, child) {
  252 + return FadeTransition(
  253 + opacity: CurvedAnimation(
  254 + parent: animation,
  255 + curve: transitionCurve ?? defaultDialogTransitionCurve,
  256 + ),
  257 + child: child,
  258 + );
  259 + },
  260 + useRootNavigator: useRootNavigator,
  261 + routeSettings: routeSettings,
  262 + );
234 } 263 }
235 264
236 - /// **Navigation.pushNamedAndRemoveUntil()** shortcut.<br><br>  
237 - ///  
238 - /// Push a named [page] and pop several pages in the stack  
239 - /// until [predicate] returns true. [predicate] is optional  
240 - ///  
241 - /// It has the advantage of not needing context, so you can  
242 - /// call from your business logic.  
243 - ///  
244 - /// You can send any type of value to the other route in the [arguments].  
245 - ///  
246 - /// [predicate] can be used like this:  
247 - /// `Get.until((route) => Get.currentRoute == '/home')`so when you get to home page,  
248 - /// or also like  
249 - /// `Get.until((route) => !Get.isDialogOpen())`, to make sure the dialog  
250 - /// is closed  
251 - ///  
252 - /// [id] is for when you are using nested navigation,  
253 - /// as explained in documentation  
254 - ///  
255 - /// Note: Always put a slash on the route ('/page1'), to avoid unexpected errors  
256 - Future<T> offAllNamed<T>(  
257 - String newRouteName, {  
258 - RoutePredicate predicate,  
259 - dynamic arguments,  
260 - int id, 265 + /// Api from showGeneralDialog with no context
  266 + Future<T> generalDialog<T>({
  267 + @required RoutePageBuilder pageBuilder,
  268 + bool barrierDismissible = false,
  269 + String barrierLabel,
  270 + Color barrierColor = const Color(0x80000000),
  271 + Duration transitionDuration = const Duration(milliseconds: 200),
  272 + RouteTransitionsBuilder transitionBuilder,
  273 + bool useRootNavigator = true,
  274 + RouteSettings routeSettings,
261 }) { 275 }) {
262 - return global(id)?.currentState?.pushNamedAndRemoveUntil(  
263 - newRouteName,  
264 - predicate ?? (_) => false,  
265 - arguments: arguments,  
266 - );  
267 - }  
268 -  
269 - /// Returns true if a Snackbar, Dialog or BottomSheet is currently OPEN  
270 - bool get isOverlaysOpen =>  
271 - (isSnackbarOpen || isDialogOpen || isBottomSheetOpen);  
272 -  
273 - /// Returns true if there is no Snackbar, Dialog or BottomSheet open  
274 - bool get isOverlaysClosed =>  
275 - (!isSnackbarOpen && !isDialogOpen && !isBottomSheetOpen);  
276 -  
277 - /// **Navigation.popUntil()** shortcut.<br><br>  
278 - ///  
279 - /// Pop the current page, snackbar, dialog or bottomsheet in the stack  
280 - ///  
281 - /// if your set [closeOverlays] to true, Get.back() will close the  
282 - /// currently open snackbar/dialog/bottomsheet AND the current page  
283 - ///  
284 - /// [id] is for when you are using nested navigation,  
285 - /// as explained in documentation  
286 - ///  
287 - /// It has the advantage of not needing context, so you can call  
288 - /// from your business logic.  
289 - void back({  
290 - dynamic result,  
291 - bool closeOverlays = false,  
292 - bool canPop = true,  
293 - int id,  
294 - }) {  
295 - if (closeOverlays && isOverlaysOpen) {  
296 - navigator?.popUntil((route) {  
297 - return (isOverlaysClosed);  
298 - });  
299 - }  
300 - if (canPop) {  
301 - if (global(id)?.currentState?.canPop() == true) {  
302 - global(id)?.currentState?.pop(result);  
303 - }  
304 - } else {  
305 - global(id)?.currentState?.pop(result);  
306 - }  
307 - }  
308 -  
309 - /// **Navigation.popUntil()** (with predicate) shortcut .<br><br>  
310 - ///  
311 - /// Close as many routes as defined by [times]  
312 - ///  
313 - /// [id] is for when you are using nested navigation,  
314 - /// as explained in documentation  
315 - void close(int times, [int id]) {  
316 - if ((times == null) || (times < 1)) {  
317 - times = 1;  
318 - }  
319 - var count = 0;  
320 - var back = global(id)?.currentState?.popUntil((route) => count++ == times);  
321 -  
322 - return back;  
323 - }  
324 -  
325 - /// **Navigation.pushReplacement()** shortcut .<br><br>  
326 - ///  
327 - /// Pop the current page and pushes a new [page] to the stack  
328 - ///  
329 - /// It has the advantage of not needing context,  
330 - /// so you can call from your business logic  
331 - ///  
332 - /// You can set a custom [transition], define a Tween [curve],  
333 - /// and a transition [duration].  
334 - ///  
335 - /// You can send any type of value to the other route in the [arguments].  
336 - ///  
337 - /// Just like native routing in Flutter, you can push a route  
338 - /// as a [fullscreenDialog],  
339 - ///  
340 - /// [id] is for when you are using nested navigation,  
341 - /// as explained in documentation  
342 - ///  
343 - /// If you want the same behavior of ios that pops a route when the user drag,  
344 - /// you can set [popGesture] to true  
345 - ///  
346 - /// If you're using the [Bindings] api, you must define it here  
347 - ///  
348 - /// By default, GetX will prevent you from push a route that you already in,  
349 - /// if you want to push anyway, set [preventDuplicates] to false  
350 - Future<T> off<T>(  
351 - Widget page, {  
352 - bool opaque = false,  
353 - Transition transition,  
354 - Curve curve,  
355 - bool popGesture,  
356 - int id,  
357 - dynamic arguments,  
358 - Bindings binding,  
359 - bool fullscreenDialog = false,  
360 - bool preventDuplicates = true,  
361 - Duration duration,  
362 - }) {  
363 - var routeName = "/${page.runtimeType.toString()}";  
364 - if (preventDuplicates && routeName == currentRoute) {  
365 - return null;  
366 - }  
367 - return global(id)?.currentState?.pushReplacement(GetPageRoute(  
368 - opaque: opaque ?? true,  
369 - page: () => page,  
370 - binding: binding,  
371 - settings: RouteSettings(arguments: arguments),  
372 - routeName: routeName,  
373 - fullscreenDialog: fullscreenDialog,  
374 - popGesture: popGesture ?? defaultPopGesture,  
375 - transition: transition ?? defaultTransition,  
376 - curve: curve ?? defaultTransitionCurve,  
377 - transitionDuration: duration ?? defaultTransitionDuration));  
378 - }  
379 -  
380 - /// **Navigation.pushAndRemoveUntil()** shortcut .<br><br>  
381 - ///  
382 - /// Push a [page] and pop several pages in the stack  
383 - /// until [predicate] returns true. [predicate] is optional  
384 - ///  
385 - /// It has the advantage of not needing context,  
386 - /// so you can call from your business logic  
387 - ///  
388 - /// You can set a custom [transition], a [curve] and a transition [duration].  
389 - ///  
390 - /// You can send any type of value to the other route in the [arguments].  
391 - ///  
392 - /// Just like native routing in Flutter, you can push a route  
393 - /// as a [fullscreenDialog],  
394 - ///  
395 - /// [predicate] can be used like this:  
396 - /// `Get.until((route) => Get.currentRoute == '/home')`so when you get to home page,  
397 - /// or also like  
398 - /// `Get.until((route) => !Get.isDialogOpen())`, to make sure the dialog  
399 - /// is closed  
400 - ///  
401 - /// [id] is for when you are using nested navigation,  
402 - /// as explained in documentation  
403 - ///  
404 - /// If you want the same behavior of ios that pops a route when the user drag,  
405 - /// you can set [popGesture] to true  
406 - ///  
407 - /// If you're using the [Bindings] api, you must define it here  
408 - ///  
409 - /// By default, GetX will prevent you from push a route that you already in,  
410 - /// if you want to push anyway, set [preventDuplicates] to false  
411 - Future<T> offAll<T>(  
412 - Widget page, {  
413 - RoutePredicate predicate,  
414 - bool opaque = false,  
415 - bool popGesture,  
416 - int id,  
417 - dynamic arguments,  
418 - Bindings binding,  
419 - bool fullscreenDialog = false,  
420 - Transition transition,  
421 - Curve curve,  
422 - Duration duration,  
423 - }) {  
424 - var routeName = "/${page.runtimeType.toString()}";  
425 -  
426 - return global(id)?.currentState?.pushAndRemoveUntil(  
427 - GetPageRoute(  
428 - opaque: opaque ?? true,  
429 - popGesture: popGesture ?? defaultPopGesture,  
430 - page: () => page,  
431 - binding: binding,  
432 - settings: RouteSettings(arguments: arguments),  
433 - fullscreenDialog: fullscreenDialog,  
434 - routeName: routeName,  
435 - transition: transition ?? defaultTransition,  
436 - curve: curve ?? defaultTransitionCurve,  
437 - transitionDuration: duration ?? defaultTransitionDuration,  
438 - ),  
439 - predicate ?? (route) => false);  
440 - }  
441 -  
442 - /// Show a dialog.  
443 - /// You can pass a [transitionDuration] and/or [transitionCurve],  
444 - /// overriding the defaults when the dialog shows up and closes.  
445 - /// When the dialog closes, uses those animations in reverse.  
446 - Future<T> dialog<T>(  
447 - Widget widget, {  
448 - bool barrierDismissible = true,  
449 - Color barrierColor,  
450 - bool useSafeArea = true,  
451 - bool useRootNavigator = true,  
452 - RouteSettings routeSettings,  
453 - Duration transitionDuration,  
454 - Curve transitionCurve,  
455 - }) {  
456 - assert(widget != null);  
457 - assert(barrierDismissible != null);  
458 - assert(useSafeArea != null);  
459 - assert(useRootNavigator != null);  
460 - assert(debugCheckHasMaterialLocalizations(context));  
461 -  
462 - final theme = Theme.of(context, shadowThemeOnly: true);  
463 - return generalDialog(  
464 - pageBuilder: (buildContext, animation, secondaryAnimation) {  
465 - final pageChild = widget;  
466 - Widget dialog = Builder(builder: (context) {  
467 - return theme != null  
468 - ? Theme(data: theme, child: pageChild)  
469 - : pageChild;  
470 - });  
471 - if (useSafeArea) {  
472 - dialog = SafeArea(child: dialog);  
473 - }  
474 - return dialog;  
475 - },  
476 - barrierDismissible: barrierDismissible,  
477 - barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,  
478 - barrierColor: barrierColor ?? Colors.black54,  
479 - transitionDuration: transitionDuration ?? defaultDialogTransitionDuration,  
480 - transitionBuilder: (context, animation, secondaryAnimation, child) {  
481 - return FadeTransition(  
482 - opacity: CurvedAnimation(  
483 - parent: animation,  
484 - curve: transitionCurve ?? defaultDialogTransitionCurve,  
485 - ),  
486 - child: child,  
487 - );  
488 - },  
489 - useRootNavigator: useRootNavigator,  
490 - routeSettings: routeSettings,  
491 - );  
492 - }  
493 -  
494 - /// Api from showGeneralDialog with no context  
495 - Future<T> generalDialog<T>({  
496 - @required RoutePageBuilder pageBuilder,  
497 - bool barrierDismissible = false,  
498 - String barrierLabel,  
499 - Color barrierColor = const Color(0x80000000),  
500 - Duration transitionDuration = const Duration(milliseconds: 200),  
501 - RouteTransitionsBuilder transitionBuilder,  
502 - bool useRootNavigator = true,  
503 - RouteSettings routeSettings,  
504 - }) {  
505 - assert(pageBuilder != null);  
506 - assert(useRootNavigator != null);  
507 - assert(!barrierDismissible || barrierLabel != null);  
508 - return Navigator.of(overlayContext, rootNavigator: useRootNavigator)  
509 - .push<T>(GetDialogRoute<T>(  
510 - pageBuilder: pageBuilder,  
511 - barrierDismissible: barrierDismissible,  
512 - barrierLabel: barrierLabel,  
513 - barrierColor: barrierColor,  
514 - transitionDuration: transitionDuration,  
515 - transitionBuilder: transitionBuilder,  
516 - settings: routeSettings,  
517 - )); 276 + assert(pageBuilder != null);
  277 + assert(useRootNavigator != null);
  278 + assert(!barrierDismissible || barrierLabel != null);
  279 + return Navigator.of(overlayContext, rootNavigator: useRootNavigator)
  280 + .push<T>(GetDialogRoute<T>(
  281 + pageBuilder: pageBuilder,
  282 + barrierDismissible: barrierDismissible,
  283 + barrierLabel: barrierLabel,
  284 + barrierColor: barrierColor,
  285 + transitionDuration: transitionDuration,
  286 + transitionBuilder: transitionBuilder,
  287 + settings: routeSettings,
  288 + ));
518 } 289 }
519 290
520 /// Custom UI Dialog. 291 /// Custom UI Dialog.
@@ -623,7 +394,9 @@ extension GetNavigation on GetInterface { @@ -623,7 +394,9 @@ extension GetNavigation on GetInterface {
623 barrierDismissible: barrierDismissible, 394 barrierDismissible: barrierDismissible,
624 ); 395 );
625 } 396 }
  397 +}
626 398
  399 +extension ExtensionBottomSheet on GetInterface {
627 Future<T> bottomSheet<T>( 400 Future<T> bottomSheet<T>(
628 Widget bottomsheet, { 401 Widget bottomsheet, {
629 Color backgroundColor, 402 Color backgroundColor,
@@ -667,198 +440,434 @@ extension GetNavigation on GetInterface { @@ -667,198 +440,434 @@ extension GetNavigation on GetInterface {
667 enableDrag: enableDrag, 440 enableDrag: enableDrag,
668 )); 441 ));
669 } 442 }
  443 +}
670 444
671 - void rawSnackbar({  
672 - String title,  
673 - String message,  
674 - Widget titleText,  
675 - Widget messageText,  
676 - Widget icon,  
677 - bool instantInit = true,  
678 - bool shouldIconPulse = true,  
679 - double maxWidth,  
680 - EdgeInsets margin = const EdgeInsets.all(0.0),  
681 - EdgeInsets padding = const EdgeInsets.all(16),  
682 - double borderRadius = 0.0,  
683 - Color borderColor,  
684 - double borderWidth = 1.0,  
685 - Color backgroundColor = const Color(0xFF303030),  
686 - Color leftBarIndicatorColor,  
687 - List<BoxShadow> boxShadows,  
688 - Gradient backgroundGradient,  
689 - FlatButton mainButton,  
690 - OnTap onTap,  
691 - Duration duration = const Duration(seconds: 3),  
692 - bool isDismissible = true,  
693 - SnackDismissDirection dismissDirection = SnackDismissDirection.VERTICAL,  
694 - bool showProgressIndicator = false,  
695 - AnimationController progressIndicatorController,  
696 - Color progressIndicatorBackgroundColor,  
697 - Animation<Color> progressIndicatorValueColor,  
698 - SnackPosition snackPosition = SnackPosition.BOTTOM,  
699 - SnackStyle snackStyle = SnackStyle.FLOATING,  
700 - Curve forwardAnimationCurve = Curves.easeOutCirc,  
701 - Curve reverseAnimationCurve = Curves.easeOutCirc,  
702 - Duration animationDuration = const Duration(seconds: 1),  
703 - SnackbarStatusCallback snackbarStatus,  
704 - double barBlur = 0.0,  
705 - double overlayBlur = 0.0,  
706 - Color overlayColor,  
707 - Form userInputForm,  
708 - }) async {  
709 - final getBar = GetBar(  
710 - snackbarStatus: snackbarStatus,  
711 - title: title,  
712 - message: message,  
713 - titleText: titleText,  
714 - messageText: messageText,  
715 - snackPosition: snackPosition,  
716 - borderRadius: borderRadius,  
717 - margin: margin,  
718 - duration: duration,  
719 - barBlur: barBlur,  
720 - backgroundColor: backgroundColor,  
721 - icon: icon,  
722 - shouldIconPulse: shouldIconPulse,  
723 - maxWidth: maxWidth,  
724 - padding: padding,  
725 - borderColor: borderColor,  
726 - borderWidth: borderWidth,  
727 - leftBarIndicatorColor: leftBarIndicatorColor,  
728 - boxShadows: boxShadows,  
729 - backgroundGradient: backgroundGradient,  
730 - mainButton: mainButton,  
731 - onTap: onTap,  
732 - isDismissible: isDismissible,  
733 - dismissDirection: dismissDirection,  
734 - showProgressIndicator: showProgressIndicator ?? false,  
735 - progressIndicatorController: progressIndicatorController,  
736 - progressIndicatorBackgroundColor: progressIndicatorBackgroundColor,  
737 - progressIndicatorValueColor: progressIndicatorValueColor,  
738 - snackStyle: snackStyle,  
739 - forwardAnimationCurve: forwardAnimationCurve,  
740 - reverseAnimationCurve: reverseAnimationCurve,  
741 - animationDuration: animationDuration,  
742 - overlayBlur: overlayBlur,  
743 - overlayColor: overlayColor,  
744 - userInputForm: userInputForm,  
745 - ); 445 +extension GetNavigation on GetInterface {
  446 + /// **Navigation.push()** shortcut.<br><br>
  447 + ///
  448 + /// Pushes a new [page] to the stack
  449 + ///
  450 + /// It has the advantage of not needing context,
  451 + /// so you can call from your business logic
  452 + ///
  453 + /// You can set a custom [transition], and a transition [duration].
  454 + ///
  455 + /// You can send any type of value to the other route in the [arguments].
  456 + ///
  457 + /// Just like native routing in Flutter, you can push a route
  458 + /// as a [fullscreenDialog],
  459 + ///
  460 + /// [id] is for when you are using nested navigation,
  461 + /// as explained in documentation
  462 + ///
  463 + /// If you want the same behavior of ios that pops a route when the user drag,
  464 + /// you can set [popGesture] to true
  465 + ///
  466 + /// If you're using the [Bindings] api, you must define it here
  467 + ///
  468 + /// By default, GetX will prevent you from push a route that you already in,
  469 + /// if you want to push anyway, set [preventDuplicates] to false
  470 + Future<T> to<T>(
  471 + Widget page, {
  472 + bool opaque,
  473 + Transition transition,
  474 + Curve curve,
  475 + Duration duration,
  476 + int id,
  477 + bool fullscreenDialog = false,
  478 + dynamic arguments,
  479 + Bindings binding,
  480 + bool preventDuplicates = true,
  481 + bool popGesture,
  482 + }) {
  483 + var routeName = "/${page.runtimeType.toString()}";
  484 + if (preventDuplicates && routeName == currentRoute) {
  485 + return null;
  486 + }
  487 + return global(id)?.currentState?.push(
  488 + GetPageRoute(
  489 + opaque: opaque ?? true,
  490 + page: () => page,
  491 + routeName: routeName,
  492 + settings: RouteSettings(
  493 + // name: forceRouteName ? '${a.runtimeType}' : '',
  494 + arguments: arguments,
  495 + ),
  496 + popGesture: popGesture ?? defaultPopGesture,
  497 + transition: transition ?? defaultTransition,
  498 + curve: curve ?? defaultTransitionCurve,
  499 + fullscreenDialog: fullscreenDialog,
  500 + binding: binding,
  501 + transitionDuration: duration ?? defaultTransitionDuration,
  502 + ),
  503 + );
  504 + }
746 505
747 - if (instantInit) {  
748 - getBar.show();  
749 - } else {  
750 - SchedulerBinding.instance.addPostFrameCallback((_) {  
751 - getBar.show(); 506 + /// **Navigation.pushNamed()** shortcut.<br><br>
  507 + ///
  508 + /// Pushes a new named [page] to the stack.
  509 + ///
  510 + /// It has the advantage of not needing context, so you can call
  511 + /// from your business logic.
  512 + ///
  513 + /// You can send any type of value to the other route in the [arguments].
  514 + ///
  515 + /// [id] is for when you are using nested navigation,
  516 + /// as explained in documentation
  517 + ///
  518 + /// By default, GetX will prevent you from push a route that you already in,
  519 + /// if you want to push anyway, set [preventDuplicates] to false
  520 + ///
  521 + /// Note: Always put a slash on the route ('/page1'), to avoid unnexpected errors
  522 + Future<T> toNamed<T>(
  523 + String page, {
  524 + dynamic arguments,
  525 + int id,
  526 + bool preventDuplicates = true,
  527 + }) {
  528 + if (preventDuplicates && page == currentRoute) {
  529 + return null;
  530 + }
  531 + return global(id)?.currentState?.pushNamed(page, arguments: arguments);
  532 + }
  533 +
  534 + /// **Navigation.pushReplacementNamed()** shortcut.<br><br>
  535 + ///
  536 + /// Pop the current named [page] in the stack and push a new one in its place
  537 + ///
  538 + /// It has the advantage of not needing context, so you can call
  539 + /// from your business logic.
  540 + ///
  541 + /// You can send any type of value to the other route in the [arguments].
  542 + ///
  543 + /// [id] is for when you are using nested navigation,
  544 + /// as explained in documentation
  545 + ///
  546 + /// By default, GetX will prevent you from push a route that you already in,
  547 + /// if you want to push anyway, set [preventDuplicates] to false
  548 + ///
  549 + /// Note: Always put a slash on the route ('/page1'), to avoid unnexpected errors
  550 + Future<T> offNamed<T>(
  551 + String page, {
  552 + dynamic arguments,
  553 + int id,
  554 + bool preventDuplicates = true,
  555 + }) {
  556 + if (preventDuplicates && page == currentRoute) {
  557 + return null;
  558 + }
  559 + return global(id)
  560 + ?.currentState
  561 + ?.pushReplacementNamed(page, arguments: arguments);
  562 + }
  563 +
  564 + /// **Navigation.popUntil()** shortcut.<br><br>
  565 + ///
  566 + /// Calls pop several times in the stack until [predicate] returns true
  567 + ///
  568 + /// [id] is for when you are using nested navigation,
  569 + /// as explained in documentation
  570 + ///
  571 + /// [predicate] can be used like this:
  572 + /// `Get.until((route) => Get.currentRoute == '/home')`so when you get to home page,
  573 + ///
  574 + /// or also like this:
  575 + /// `Get.until((route) => !Get.isDialogOpen())`, to make sure the
  576 + /// dialog is closed
  577 + void until(RoutePredicate predicate, {int id}) {
  578 + // if (key.currentState.mounted) // add this if appear problems on future with route navigate
  579 + // when widget don't mounted
  580 + return global(id)?.currentState?.popUntil(predicate);
  581 + }
  582 +
  583 + /// **Navigation.pushAndRemoveUntil()** shortcut.<br><br>
  584 + ///
  585 + /// Push the given [page], and then pop several pages in the stack until
  586 + /// [predicate] returns true
  587 + ///
  588 + /// [id] is for when you are using nested navigation,
  589 + /// as explained in documentation
  590 + ///
  591 + /// Obs: unlike other get methods, this one you need to send a function
  592 + /// that returns the widget to the page argument, like this:
  593 + /// Get.offUntil( () => HomePage() )
  594 + ///
  595 + /// [predicate] can be used like this:
  596 + /// `Get.until((route) => Get.currentRoute == '/home')`so when you get to home page,
  597 + ///
  598 + /// or also like this:
  599 + /// `Get.until((route) => !Get.isDialogOpen())`, to make sure the dialog
  600 + /// is closed
  601 + Future<T> offUntil<T>(Route<T> page, RoutePredicate predicate, {int id}) {
  602 + // if (key.currentState.mounted) // add this if appear problems on future with route navigate
  603 + // when widget don't mounted
  604 + return global(id)?.currentState?.pushAndRemoveUntil(page, predicate);
  605 + }
  606 +
  607 + /// **Navigation.pushNamedAndRemoveUntil()** shortcut.<br><br>
  608 + ///
  609 + /// Push the given named [page], and then pop several pages in the stack
  610 + /// until [predicate] returns true
  611 + ///
  612 + /// You can send any type of value to the other route in the [arguments].
  613 + ///
  614 + /// [id] is for when you are using nested navigation,
  615 + /// as explained in documentation
  616 + ///
  617 + /// [predicate] can be used like this:
  618 + /// `Get.until((route) => Get.currentRoute == '/home')`so when you get to home page,
  619 + /// or also like
  620 + /// `Get.until((route) => !Get.isDialogOpen())`, to make sure the dialog
  621 + /// is closed
  622 + ///
  623 + /// Note: Always put a slash on the route ('/page1'), to avoid unnexpected errors
  624 + Future<T> offNamedUntil<T>(
  625 + String page,
  626 + RoutePredicate predicate, {
  627 + int id,
  628 + dynamic arguments,
  629 + }) {
  630 + return global(id)
  631 + ?.currentState
  632 + ?.pushNamedAndRemoveUntil(page, predicate, arguments: arguments);
  633 + }
  634 +
  635 + /// **Navigation.popAndPushNamed()** shortcut.<br><br>
  636 + ///
  637 + /// Pop the current named page and pushes a new [page] to the stack
  638 + /// in its place
  639 + ///
  640 + /// You can send any type of value to the other route in the [arguments].
  641 + /// It is very similar to `offNamed()` but use a different approach
  642 + ///
  643 + /// The `offNamed()` pop a page, and goes to the next. The
  644 + /// `offAndToNamed()` goes to the next page, and removes the previous one.
  645 + /// The route transition animation is different.
  646 + Future<T> offAndToNamed<T>(
  647 + String page, {
  648 + dynamic arguments,
  649 + int id,
  650 + dynamic result,
  651 + }) {
  652 + return global(id)
  653 + ?.currentState
  654 + ?.popAndPushNamed(page, arguments: arguments, result: result);
  655 + }
  656 +
  657 + /// **Navigation.removeRoute()** shortcut.<br><br>
  658 + ///
  659 + /// Remove a specific [route] from the stack
  660 + ///
  661 + /// [id] is for when you are using nested navigation,
  662 + /// as explained in documentation
  663 + void removeRoute(Route<dynamic> route, {int id}) {
  664 + return global(id)?.currentState?.removeRoute(route);
  665 + }
  666 +
  667 + /// **Navigation.pushNamedAndRemoveUntil()** shortcut.<br><br>
  668 + ///
  669 + /// Push a named [page] and pop several pages in the stack
  670 + /// until [predicate] returns true. [predicate] is optional
  671 + ///
  672 + /// It has the advantage of not needing context, so you can
  673 + /// call from your business logic.
  674 + ///
  675 + /// You can send any type of value to the other route in the [arguments].
  676 + ///
  677 + /// [predicate] can be used like this:
  678 + /// `Get.until((route) => Get.currentRoute == '/home')`so when you get to home page,
  679 + /// or also like
  680 + /// `Get.until((route) => !Get.isDialogOpen())`, to make sure the dialog
  681 + /// is closed
  682 + ///
  683 + /// [id] is for when you are using nested navigation,
  684 + /// as explained in documentation
  685 + ///
  686 + /// Note: Always put a slash on the route ('/page1'), to avoid unexpected errors
  687 + Future<T> offAllNamed<T>(
  688 + String newRouteName, {
  689 + RoutePredicate predicate,
  690 + dynamic arguments,
  691 + int id,
  692 + }) {
  693 + return global(id)?.currentState?.pushNamedAndRemoveUntil(
  694 + newRouteName,
  695 + predicate ?? (_) => false,
  696 + arguments: arguments,
  697 + );
  698 + }
  699 +
  700 + /// Returns true if a Snackbar, Dialog or BottomSheet is currently OPEN
  701 + bool get isOverlaysOpen =>
  702 + (isSnackbarOpen || isDialogOpen || isBottomSheetOpen);
  703 +
  704 + /// Returns true if there is no Snackbar, Dialog or BottomSheet open
  705 + bool get isOverlaysClosed =>
  706 + (!isSnackbarOpen && !isDialogOpen && !isBottomSheetOpen);
  707 +
  708 + /// **Navigation.popUntil()** shortcut.<br><br>
  709 + ///
  710 + /// Pop the current page, snackbar, dialog or bottomsheet in the stack
  711 + ///
  712 + /// if your set [closeOverlays] to true, Get.back() will close the
  713 + /// currently open snackbar/dialog/bottomsheet AND the current page
  714 + ///
  715 + /// [id] is for when you are using nested navigation,
  716 + /// as explained in documentation
  717 + ///
  718 + /// It has the advantage of not needing context, so you can call
  719 + /// from your business logic.
  720 + void back({
  721 + dynamic result,
  722 + bool closeOverlays = false,
  723 + bool canPop = true,
  724 + int id,
  725 + }) {
  726 + if (closeOverlays && isOverlaysOpen) {
  727 + navigator?.popUntil((route) {
  728 + return (isOverlaysClosed);
752 }); 729 });
753 } 730 }
  731 + if (canPop) {
  732 + if (global(id)?.currentState?.canPop() == true) {
  733 + global(id)?.currentState?.pop(result);
  734 + }
  735 + } else {
  736 + global(id)?.currentState?.pop(result);
  737 + }
754 } 738 }
755 739
756 - Future<T> showSnackbar<T>(GetBar snackbar) {  
757 - return key?.currentState?.push(SnackRoute<T>(snack: snackbar)); 740 + /// **Navigation.popUntil()** (with predicate) shortcut .<br><br>
  741 + ///
  742 + /// Close as many routes as defined by [times]
  743 + ///
  744 + /// [id] is for when you are using nested navigation,
  745 + /// as explained in documentation
  746 + void close(int times, [int id]) {
  747 + if ((times == null) || (times < 1)) {
  748 + times = 1;
  749 + }
  750 + var count = 0;
  751 + var back = global(id)?.currentState?.popUntil((route) => count++ == times);
  752 +
  753 + return back;
758 } 754 }
759 755
760 - void snackbar(  
761 - String title,  
762 - String message, {  
763 - Color colorText, 756 + /// **Navigation.pushReplacement()** shortcut .<br><br>
  757 + ///
  758 + /// Pop the current page and pushes a new [page] to the stack
  759 + ///
  760 + /// It has the advantage of not needing context,
  761 + /// so you can call from your business logic
  762 + ///
  763 + /// You can set a custom [transition], define a Tween [curve],
  764 + /// and a transition [duration].
  765 + ///
  766 + /// You can send any type of value to the other route in the [arguments].
  767 + ///
  768 + /// Just like native routing in Flutter, you can push a route
  769 + /// as a [fullscreenDialog],
  770 + ///
  771 + /// [id] is for when you are using nested navigation,
  772 + /// as explained in documentation
  773 + ///
  774 + /// If you want the same behavior of ios that pops a route when the user drag,
  775 + /// you can set [popGesture] to true
  776 + ///
  777 + /// If you're using the [Bindings] api, you must define it here
  778 + ///
  779 + /// By default, GetX will prevent you from push a route that you already in,
  780 + /// if you want to push anyway, set [preventDuplicates] to false
  781 + Future<T> off<T>(
  782 + Widget page, {
  783 + bool opaque = false,
  784 + Transition transition,
  785 + Curve curve,
  786 + bool popGesture,
  787 + int id,
  788 + dynamic arguments,
  789 + Bindings binding,
  790 + bool fullscreenDialog = false,
  791 + bool preventDuplicates = true,
764 Duration duration, 792 Duration duration,
  793 + }) {
  794 + var routeName = "/${page.runtimeType.toString()}";
  795 + if (preventDuplicates && routeName == currentRoute) {
  796 + return null;
  797 + }
  798 + return global(id)?.currentState?.pushReplacement(GetPageRoute(
  799 + opaque: opaque ?? true,
  800 + page: () => page,
  801 + binding: binding,
  802 + settings: RouteSettings(arguments: arguments),
  803 + routeName: routeName,
  804 + fullscreenDialog: fullscreenDialog,
  805 + popGesture: popGesture ?? defaultPopGesture,
  806 + transition: transition ?? defaultTransition,
  807 + curve: curve ?? defaultTransitionCurve,
  808 + transitionDuration: duration ?? defaultTransitionDuration));
  809 + }
765 810
766 - /// with instantInit = false you can put snackbar on initState  
767 - bool instantInit = true,  
768 - SnackPosition snackPosition,  
769 - Widget titleText,  
770 - Widget messageText,  
771 - Widget icon,  
772 - bool shouldIconPulse,  
773 - double maxWidth,  
774 - EdgeInsets margin,  
775 - EdgeInsets padding,  
776 - double borderRadius,  
777 - Color borderColor,  
778 - double borderWidth,  
779 - Color backgroundColor,  
780 - Color leftBarIndicatorColor,  
781 - List<BoxShadow> boxShadows,  
782 - Gradient backgroundGradient,  
783 - FlatButton mainButton,  
784 - OnTap onTap,  
785 - bool isDismissible,  
786 - bool showProgressIndicator,  
787 - SnackDismissDirection dismissDirection,  
788 - AnimationController progressIndicatorController,  
789 - Color progressIndicatorBackgroundColor,  
790 - Animation<Color> progressIndicatorValueColor,  
791 - SnackStyle snackStyle,  
792 - Curve forwardAnimationCurve,  
793 - Curve reverseAnimationCurve,  
794 - Duration animationDuration,  
795 - double barBlur,  
796 - double overlayBlur,  
797 - SnackbarStatusCallback snackbarStatus,  
798 - Color overlayColor,  
799 - Form userInputForm,  
800 - }) async {  
801 - final getBar = GetBar(  
802 - snackbarStatus: snackbarStatus,  
803 - titleText: (title == null)  
804 - ? null  
805 - : titleText ??  
806 - Text(  
807 - title,  
808 - style: TextStyle(  
809 - color: colorText ?? iconColor ?? Colors.black,  
810 - fontWeight: FontWeight.w800,  
811 - fontSize: 16,  
812 - ),  
813 - ),  
814 - messageText: messageText ??  
815 - Text(  
816 - message,  
817 - style: TextStyle(  
818 - color: colorText ?? iconColor ?? Colors.black,  
819 - fontWeight: FontWeight.w300,  
820 - fontSize: 14,  
821 - ),  
822 - ),  
823 - snackPosition: snackPosition ?? SnackPosition.TOP,  
824 - borderRadius: borderRadius ?? 15,  
825 - margin: margin ?? EdgeInsets.symmetric(horizontal: 10),  
826 - duration: duration ?? Duration(seconds: 3),  
827 - barBlur: barBlur ?? 7.0,  
828 - backgroundColor: backgroundColor ?? Colors.grey.withOpacity(0.2),  
829 - icon: icon,  
830 - shouldIconPulse: shouldIconPulse ?? true,  
831 - maxWidth: maxWidth,  
832 - padding: padding ?? EdgeInsets.all(16),  
833 - borderColor: borderColor,  
834 - borderWidth: borderWidth,  
835 - leftBarIndicatorColor: leftBarIndicatorColor,  
836 - boxShadows: boxShadows,  
837 - backgroundGradient: backgroundGradient,  
838 - mainButton: mainButton,  
839 - onTap: onTap,  
840 - isDismissible: isDismissible ?? true,  
841 - dismissDirection: dismissDirection ?? SnackDismissDirection.VERTICAL,  
842 - showProgressIndicator: showProgressIndicator ?? false,  
843 - progressIndicatorController: progressIndicatorController,  
844 - progressIndicatorBackgroundColor: progressIndicatorBackgroundColor,  
845 - progressIndicatorValueColor: progressIndicatorValueColor,  
846 - snackStyle: snackStyle ?? SnackStyle.FLOATING,  
847 - forwardAnimationCurve: forwardAnimationCurve ?? Curves.easeOutCirc,  
848 - reverseAnimationCurve: reverseAnimationCurve ?? Curves.easeOutCirc,  
849 - animationDuration: animationDuration ?? Duration(seconds: 1),  
850 - overlayBlur: overlayBlur ?? 0.0,  
851 - overlayColor: overlayColor ?? Colors.transparent,  
852 - userInputForm: userInputForm); 811 + /// **Navigation.pushAndRemoveUntil()** shortcut .<br><br>
  812 + ///
  813 + /// Push a [page] and pop several pages in the stack
  814 + /// until [predicate] returns true. [predicate] is optional
  815 + ///
  816 + /// It has the advantage of not needing context,
  817 + /// so you can call from your business logic
  818 + ///
  819 + /// You can set a custom [transition], a [curve] and a transition [duration].
  820 + ///
  821 + /// You can send any type of value to the other route in the [arguments].
  822 + ///
  823 + /// Just like native routing in Flutter, you can push a route
  824 + /// as a [fullscreenDialog],
  825 + ///
  826 + /// [predicate] can be used like this:
  827 + /// `Get.until((route) => Get.currentRoute == '/home')`so when you get to home page,
  828 + /// or also like
  829 + /// `Get.until((route) => !Get.isDialogOpen())`, to make sure the dialog
  830 + /// is closed
  831 + ///
  832 + /// [id] is for when you are using nested navigation,
  833 + /// as explained in documentation
  834 + ///
  835 + /// If you want the same behavior of ios that pops a route when the user drag,
  836 + /// you can set [popGesture] to true
  837 + ///
  838 + /// If you're using the [Bindings] api, you must define it here
  839 + ///
  840 + /// By default, GetX will prevent you from push a route that you already in,
  841 + /// if you want to push anyway, set [preventDuplicates] to false
  842 + Future<T> offAll<T>(
  843 + Widget page, {
  844 + RoutePredicate predicate,
  845 + bool opaque = false,
  846 + bool popGesture,
  847 + int id,
  848 + dynamic arguments,
  849 + Bindings binding,
  850 + bool fullscreenDialog = false,
  851 + Transition transition,
  852 + Curve curve,
  853 + Duration duration,
  854 + }) {
  855 + var routeName = "/${page.runtimeType.toString()}";
853 856
854 - if (instantInit) {  
855 - showSnackbar(getBar);  
856 - } else {  
857 - routing.isSnackbar = true;  
858 - SchedulerBinding.instance.addPostFrameCallback((_) {  
859 - showSnackbar(getBar);  
860 - });  
861 - } 857 + return global(id)?.currentState?.pushAndRemoveUntil(
  858 + GetPageRoute(
  859 + opaque: opaque ?? true,
  860 + popGesture: popGesture ?? defaultPopGesture,
  861 + page: () => page,
  862 + binding: binding,
  863 + settings: RouteSettings(arguments: arguments),
  864 + fullscreenDialog: fullscreenDialog,
  865 + routeName: routeName,
  866 + transition: transition ?? defaultTransition,
  867 + curve: curve ?? defaultTransitionCurve,
  868 + transitionDuration: duration ?? defaultTransitionDuration,
  869 + ),
  870 + predicate ?? (route) => false);
862 } 871 }
863 872
864 void addPages(List<GetPage> getPages) { 873 void addPages(List<GetPage> getPages) {
@@ -1024,7 +1033,36 @@ Since version 2.8 it is possible to access the properties @@ -1024,7 +1033,36 @@ Since version 2.8 it is possible to access the properties
1024 return _theme; 1033 return _theme;
1025 } 1034 }
1026 1035
1027 - WidgetsBinding get engine => WidgetsBinding.instance; 1036 + ///The current [WidgetsBinding]
  1037 + WidgetsBinding get engine {
  1038 + if (WidgetsBinding.instance == null) {
  1039 + WidgetsFlutterBinding();
  1040 + }
  1041 + return WidgetsBinding.instance;
  1042 + }
  1043 +
  1044 + ///The window to which this binding is bound.
  1045 + Window get window => engine.window;
  1046 +
  1047 + ///The number of device pixels for each logical pixel.
  1048 + double get pixelRatio => window.devicePixelRatio;
  1049 +
  1050 + ///The horizontal extent of this size.
  1051 + double get width => window.physicalSize.width / pixelRatio;
  1052 +
  1053 + ///The vertical extent of this size
  1054 + double get height => window.physicalSize.height / pixelRatio;
  1055 +
  1056 + ///The distance from the top edge to the first unpadded pixel,
  1057 + ///in physical pixels.
  1058 + double get statusBarHeight => window.padding.top;
  1059 +
  1060 + ///The distance from the bottom edge to the first unpadded pixel,
  1061 + ///in physical pixels.
  1062 + double get bottomBarHeight => window.padding.bottom;
  1063 +
  1064 + ///The system-reported text scale.
  1065 + double get textScaleFactor => window.textScaleFactor;
1028 1066
1029 /// give access to TextTheme.of(context) 1067 /// give access to TextTheme.of(context)
1030 TextTheme get textTheme => theme?.textTheme; 1068 TextTheme get textTheme => theme?.textTheme;
@@ -1036,8 +1074,7 @@ Since version 2.8 it is possible to access the properties @@ -1036,8 +1074,7 @@ Since version 2.8 it is possible to access the properties
1036 bool get isDarkMode => (theme.brightness == Brightness.dark); 1074 bool get isDarkMode => (theme.brightness == Brightness.dark);
1037 1075
1038 /// Check if dark mode theme is enable on platform on android Q+ 1076 /// Check if dark mode theme is enable on platform on android Q+
1039 - bool get isPlatformDarkMode =>  
1040 - (mediaQuery.platformBrightness == Brightness.dark); 1077 + bool get isPlatformDarkMode => (window.platformBrightness == Brightness.dark);
1041 1078
1042 /// give access to Theme.of(context).iconTheme.color 1079 /// give access to Theme.of(context).iconTheme.color
1043 Color get iconColor => theme?.iconTheme?.color; 1080 Color get iconColor => theme?.iconTheme?.color;
@@ -1045,11 +1082,11 @@ Since version 2.8 it is possible to access the properties @@ -1045,11 +1082,11 @@ Since version 2.8 it is possible to access the properties
1045 /// give access to FocusScope.of(context) 1082 /// give access to FocusScope.of(context)
1046 FocusNode get focusScope => FocusManager.instance.primaryFocus; 1083 FocusNode get focusScope => FocusManager.instance.primaryFocus;
1047 1084
1048 - /// give access to Immutable MediaQuery.of(context).size.height  
1049 - double get height => MediaQuery.of(context).size.height; 1085 + // /// give access to Immutable MediaQuery.of(context).size.height
  1086 + // double get height => MediaQuery.of(context).size.height;
1050 1087
1051 - /// give access to Immutable MediaQuery.of(context).size.width  
1052 - double get width => MediaQuery.of(context).size.width; 1088 + // /// give access to Immutable MediaQuery.of(context).size.width
  1089 + // double get width => MediaQuery.of(context).size.width;
1053 1090
1054 GlobalKey<NavigatorState> get key => getxController?.key; 1091 GlobalKey<NavigatorState> get key => getxController?.key;
1055 1092
1 import 'dart:async'; 1 import 'dart:async';
2 import 'dart:collection'; 2 import 'dart:collection';
3 -import 'package:flutter/foundation.dart';  
4 -  
5 import '../rx_core/rx_interface.dart'; 3 import '../rx_core/rx_interface.dart';
6 part 'rx_num.dart'; 4 part 'rx_num.dart';
7 5
@@ -229,81 +227,6 @@ class Rx<T> extends _RxImpl<T> { @@ -229,81 +227,6 @@ class Rx<T> extends _RxImpl<T> {
229 dynamic toJson() => (value as dynamic)?.toJson(); 227 dynamic toJson() => (value as dynamic)?.toJson();
230 } 228 }
231 229
232 -enum RxStatus { loading, error, success }  
233 -  
234 -/// It's Experimental class, the Api can be change  
235 -abstract class RxState<T> extends _RxImpl<T> {  
236 - RxState(T initial) : super(initial) {  
237 - _fillEmptyStatus();  
238 - }  
239 -  
240 - RxStatus _status;  
241 -  
242 - bool get isNullOrEmpty {  
243 - if (_value == null) return true;  
244 - dynamic val = _value;  
245 - var result = false;  
246 - if (val is Iterable) {  
247 - result = val.isEmpty;  
248 - } else if (val is String) {  
249 - result = val.isEmpty;  
250 - } else if (val is Map) {  
251 - result = val.isEmpty;  
252 - }  
253 - return result;  
254 - }  
255 -  
256 - void _fillEmptyStatus() {  
257 - _status = isNullOrEmpty ? RxStatus.loading : RxStatus.success;  
258 - }  
259 -  
260 - RxStatus get status {  
261 - return _status;  
262 - }  
263 -  
264 - bool get isLoading => _status == RxStatus.loading;  
265 - bool get hasError => _status == RxStatus.error;  
266 - bool get hasData => _status == RxStatus.success;  
267 -  
268 - @protected  
269 - void refresh() {  
270 - subject.add(_value);  
271 - }  
272 -  
273 - @protected  
274 - void update(void fn(T val)) {  
275 - fn(_value);  
276 - subject.add(_value);  
277 - }  
278 -  
279 - @protected  
280 - T call([T v]) {  
281 - if (v != null) value = v;  
282 - return value;  
283 - }  
284 -  
285 - @protected  
286 - set value(T val) {  
287 - if (_value == val && !firstRebuild) return;  
288 - firstRebuild = false;  
289 - _value = val;  
290 - subject.add(_value);  
291 - }  
292 -  
293 - @protected  
294 - void change(T newState, {RxStatus status}) {  
295 - if (status != null) {  
296 - _status = status;  
297 - }  
298 - if (newState != _value) {  
299 - value = newState;  
300 - }  
301 - }  
302 -  
303 - @override  
304 - dynamic toJson() => (value as dynamic)?.toJson();  
305 -}  
306 -  
307 extension StringExtension on String { 230 extension StringExtension on String {
308 /// Returns a `RxString` with [this] `String` as initial value. 231 /// Returns a `RxString` with [this] `String` as initial value.
309 RxString get obs => RxString(this); 232 RxString get obs => RxString(this);
@@ -2,6 +2,7 @@ library get_state_manager; @@ -2,6 +2,7 @@ library get_state_manager;
2 2
3 export 'src/rx_flutter/rx_disposable.dart'; 3 export 'src/rx_flutter/rx_disposable.dart';
4 export 'src/rx_flutter/rx_getx_widget.dart'; 4 export 'src/rx_flutter/rx_getx_widget.dart';
  5 +export 'src/rx_flutter/rx_notifier.dart';
5 export 'src/rx_flutter/rx_obx_widget.dart'; 6 export 'src/rx_flutter/rx_obx_widget.dart';
6 export 'src/rx_flutter/rx_ticket_provider_mixin.dart'; 7 export 'src/rx_flutter/rx_ticket_provider_mixin.dart';
7 export 'src/simple/get_state.dart'; 8 export 'src/simple/get_state.dart';
@@ -9,23 +9,37 @@ import '../../../get_instance/src/lifecycle.dart'; @@ -9,23 +9,37 @@ import '../../../get_instance/src/lifecycle.dart';
9 /// it is Get.reset(). 9 /// it is Get.reset().
10 abstract class GetxService extends DisposableInterface with GetxServiceMixin {} 10 abstract class GetxService extends DisposableInterface with GetxServiceMixin {}
11 11
12 -abstract class DisposableInterface extends GetLifeCycle { 12 +abstract class DisposableInterface with GetLifeCycle {
13 bool _initialized = false; 13 bool _initialized = false;
14 14
15 /// Checks whether the controller has already been initialized. 15 /// Checks whether the controller has already been initialized.
16 bool get initialized => _initialized; 16 bool get initialized => _initialized;
17 17
  18 + bool _isClosed = false;
  19 +
  20 + /// Checks whether the controller has already been closed.
  21 + bool get isClosed => _isClosed;
  22 +
18 DisposableInterface() { 23 DisposableInterface() {
19 onStart.callback = _onStart; 24 onStart.callback = _onStart;
  25 + onDelete.callback = _onDelete;
20 } 26 }
21 27
22 // Internal callback that starts the cycle of this controller. 28 // Internal callback that starts the cycle of this controller.
23 void _onStart() { 29 void _onStart() {
  30 + if (_initialized) return;
24 onInit(); 31 onInit();
25 _initialized = true; 32 _initialized = true;
26 SchedulerBinding.instance?.addPostFrameCallback((_) => onReady()); 33 SchedulerBinding.instance?.addPostFrameCallback((_) => onReady());
27 } 34 }
28 35
  36 + // Internal callback that starts the cycle of this controller.
  37 + void _onDelete() {
  38 + if (_isClosed) return;
  39 + _isClosed = true;
  40 + onClose();
  41 + }
  42 +
29 /// Called immediately after the widget is allocated in memory. 43 /// Called immediately after the widget is allocated in memory.
30 /// You might use this to initialize something for the controller. 44 /// You might use this to initialize something for the controller.
31 @override 45 @override
  1 +import 'package:flutter/foundation.dart';
  2 +import 'package:flutter/material.dart';
  3 +import 'package:flutter/scheduler.dart';
  4 +import '../../../instance_manager.dart';
  5 +import '../../get_state_manager.dart';
  6 +import '../simple/list_notifier.dart';
  7 +
  8 +class Value<T> extends ListNotifier implements ValueListenable<T> {
  9 + Value(this._value);
  10 +
  11 + T get value {
  12 + notifyChildrens();
  13 + return _value;
  14 + }
  15 +
  16 + @override
  17 + String toString() => value.toString();
  18 +
  19 + T _value;
  20 +
  21 + set value(T newValue) {
  22 + if (_value == newValue) return;
  23 + _value = newValue;
  24 + updater();
  25 + }
  26 +
  27 + void update(void fn(T value)) {
  28 + fn(value);
  29 + updater();
  30 + }
  31 +}
  32 +
  33 +extension ReactiveT<T> on T {
  34 + Value<T> get reactive => Value<T>(this);
  35 +}
  36 +
  37 +typedef Condition = bool Function();
  38 +
  39 +abstract class GetNotifier<T> extends Value<T> with GetLifeCycle {
  40 + GetNotifier(T initial) : super(initial) {
  41 + onStart.callback = _onStart;
  42 + onDelete.callback = _onDelete;
  43 + _fillEmptyStatus();
  44 + }
  45 +
  46 + bool _initialized = false;
  47 +
  48 + /// Checks whether the controller has already been initialized.
  49 + bool get initialized => _initialized;
  50 +
  51 + bool _isClosed = false;
  52 +
  53 + /// Checks whether the controller has already been closed.
  54 + bool get isClosed => _isClosed;
  55 +
  56 + // Internal callback that starts the cycle of this controller.
  57 + void _onStart() {
  58 + if (_initialized) return;
  59 + onInit();
  60 + _initialized = true;
  61 + SchedulerBinding.instance?.addPostFrameCallback((_) => onReady());
  62 + }
  63 +
  64 + // Internal callback that starts the cycle of this controller.
  65 + void _onDelete() {
  66 + if (_isClosed) return;
  67 + _isClosed = true;
  68 + onClose();
  69 + }
  70 +
  71 + RxStatus _status;
  72 +
  73 + bool get isNullOrEmpty {
  74 + if (_value == null) return true;
  75 + dynamic val = _value;
  76 + var result = false;
  77 + if (val is Iterable) {
  78 + result = val.isEmpty;
  79 + } else if (val is String) {
  80 + result = val.isEmpty;
  81 + } else if (val is Map) {
  82 + result = val.isEmpty;
  83 + }
  84 + return result;
  85 + }
  86 +
  87 + void _fillEmptyStatus() {
  88 + _status = isNullOrEmpty ? RxStatus.loading() : RxStatus.success();
  89 + }
  90 +
  91 + RxStatus get status {
  92 + // notifyChildrens();
  93 + return _status;
  94 + }
  95 +
  96 + Widget call(NotifierBuilder<T> widget, {Widget onError, Widget onLoading}) {
  97 + return SimpleBuilder(builder: (_) {
  98 + if (status.isLoading) {
  99 + return onLoading ?? CircularProgressIndicator();
  100 + } else if (status.isError) {
  101 + return onError ?? Text('A error occured');
  102 + } else {
  103 + if (widget == null) throw 'Widget cannot be null';
  104 + return widget(value);
  105 + }
  106 + });
  107 + }
  108 +
  109 + @protected
  110 + void change(T newState, {RxStatus status}) {
  111 + if (status != null) {
  112 + _status = status;
  113 + }
  114 + if (newState != _value) {
  115 + value = newState;
  116 + }
  117 + }
  118 +
  119 + dynamic toJson() => (value as dynamic)?.toJson();
  120 +}
  121 +
  122 +class RxStatus {
  123 + final bool isLoading;
  124 + final bool isError;
  125 + final bool isSuccess;
  126 + final String errorMessage;
  127 + RxStatus._({
  128 + this.isLoading,
  129 + this.isError,
  130 + this.isSuccess,
  131 + this.errorMessage,
  132 + });
  133 +
  134 + factory RxStatus.loading() {
  135 + return RxStatus._(
  136 + isLoading: true,
  137 + isError: false,
  138 + isSuccess: false,
  139 + );
  140 + }
  141 +
  142 + factory RxStatus.success() {
  143 + return RxStatus._(
  144 + isLoading: false,
  145 + isError: false,
  146 + isSuccess: true,
  147 + );
  148 + }
  149 +
  150 + factory RxStatus.error([String message]) {
  151 + return RxStatus._(
  152 + isLoading: false,
  153 + isError: true,
  154 + isSuccess: false,
  155 + errorMessage: message,
  156 + );
  157 + }
  158 +}
  159 +
  160 +typedef NotifierBuilder<T> = Widget Function(T state);
  161 +
@@ -4,8 +4,6 @@ import '../../../get_core/get_core.dart'; @@ -4,8 +4,6 @@ import '../../../get_core/get_core.dart';
4 import '../../../get_instance/src/get_instance.dart'; 4 import '../../../get_instance/src/get_instance.dart';
5 import '../../get_state_manager.dart'; 5 import '../../get_state_manager.dart';
6 6
7 -import 'simple_builder.dart';  
8 -  
9 // Changed to VoidCallback. 7 // Changed to VoidCallback.
10 //typedef Disposer = void Function(); 8 //typedef Disposer = void Function();
11 9
@@ -34,12 +32,12 @@ mixin GetStateUpdaterMixin<T extends StatefulWidget> on State<T> { @@ -34,12 +32,12 @@ mixin GetStateUpdaterMixin<T extends StatefulWidget> on State<T> {
34 } 32 }
35 33
36 class GetxController extends DisposableInterface { 34 class GetxController extends DisposableInterface {
37 - final _updaters = HashSet<GetStateUpdate>(); 35 + final _updaters = <GetStateUpdate>[];
38 36
39 // final _updatersIds = HashMap<String, StateSetter>(); //<old> 37 // final _updatersIds = HashMap<String, StateSetter>(); //<old>
40 final _updatersIds = HashMap<String, GetStateUpdate>(); 38 final _updatersIds = HashMap<String, GetStateUpdate>();
41 39
42 - final _updatersGroupIds = HashMap<String, HashSet<GetStateUpdate>>(); 40 + final _updatersGroupIds = HashMap<String, List<GetStateUpdate>>();
43 41
44 /// Rebuilds [GetBuilder] each time you call [update()]; 42 /// Rebuilds [GetBuilder] each time you call [update()];
45 /// Can take a List of [ids], that will only update the matching 43 /// Can take a List of [ids], that will only update the matching
@@ -81,7 +79,7 @@ class GetxController extends DisposableInterface { @@ -81,7 +79,7 @@ class GetxController extends DisposableInterface {
81 VoidCallback addListenerId(String key, GetStateUpdate listener) { 79 VoidCallback addListenerId(String key, GetStateUpdate listener) {
82 // _printCurrentIds(); 80 // _printCurrentIds();
83 if (_updatersIds.containsKey(key)) { 81 if (_updatersIds.containsKey(key)) {
84 - _updatersGroupIds[key] ??= HashSet<GetStateUpdate>.identity(); 82 + _updatersGroupIds[key] ??= <GetStateUpdate>[];
85 _updatersGroupIds[key].add(listener); 83 _updatersGroupIds[key].add(listener);
86 return () { 84 return () {
87 _updatersGroupIds[key].remove(listener); 85 _updatersGroupIds[key].remove(listener);
@@ -254,22 +252,22 @@ class _GetBuilderState<T extends GetxController> extends State<GetBuilder<T>> @@ -254,22 +252,22 @@ class _GetBuilderState<T extends GetxController> extends State<GetBuilder<T>>
254 /// This is a experimental feature. 252 /// This is a experimental feature.
255 /// Meant to be used with SimpleBuilder, it auto-registers the variable 253 /// Meant to be used with SimpleBuilder, it auto-registers the variable
256 /// like Rx() does with Obx(). 254 /// like Rx() does with Obx().
257 -class Value<T> extends GetxController {  
258 - Value([this._value]);  
259 -  
260 - T _value;  
261 -  
262 - T get value {  
263 - TaskManager.instance.notify(_updaters);  
264 - return _value;  
265 - }  
266 -  
267 - set value(T newValue) {  
268 - if (_value == newValue) return;  
269 - _value = newValue;  
270 - update();  
271 - }  
272 -} 255 +// class Value<T> extends GetxController {
  256 +// Value([this._value]);
  257 +
  258 +// T _value;
  259 +
  260 +// T get value {
  261 +// TaskManager.instance.notify(_updaters);
  262 +// return _value;
  263 +// }
  264 +
  265 +// set value(T newValue) {
  266 +// if (_value == newValue) return;
  267 +// _value = newValue;
  268 +// update();
  269 +// }
  270 +// }
273 271
274 /// It's Experimental class, the Api can be change 272 /// It's Experimental class, the Api can be change
275 abstract class GetState<T> extends GetxController { 273 abstract class GetState<T> extends GetxController {
@@ -40,7 +40,7 @@ abstract class GetView<T> extends StatelessWidget { @@ -40,7 +40,7 @@ abstract class GetView<T> extends StatelessWidget {
40 } 40 }
41 41
42 abstract class GetWidget<T extends DisposableInterface> 42 abstract class GetWidget<T extends DisposableInterface>
43 - extends GetStatelessWidget { 43 + extends StatelessWidget {
44 GetWidget({Key key}) : super(key: key); 44 GetWidget({Key key}) : super(key: key);
45 45
46 final Set<T> _value = <T>{}; 46 final Set<T> _value = <T>{};
@@ -103,7 +103,7 @@ class GetStatelessElement extends ComponentElement { @@ -103,7 +103,7 @@ class GetStatelessElement extends ComponentElement {
103 103
104 @override 104 @override
105 void unmount() { 105 void unmount() {
106 - widget?.controller?.onClose(); 106 + widget?.controller?.onDelete();
107 super.unmount(); 107 super.unmount();
108 } 108 }
109 } 109 }
@@ -146,3 +146,5 @@ @@ -146,3 +146,5 @@
146 // return widget.builder(controller.state); 146 // return widget.builder(controller.state);
147 // } 147 // }
148 //} 148 //}
  149 +
  150 +
  1 +import 'package:flutter/foundation.dart';
  2 +
  3 +import 'simple_builder.dart';
  4 +
  5 +class ListNotifier implements Listenable {
  6 + List<VoidCallback> _listeners = <VoidCallback>[];
  7 +
  8 + void updater() {
  9 + assert(_debugAssertNotDisposed());
  10 + for (var element in _listeners) {
  11 + element();
  12 + }
  13 + }
  14 +
  15 + bool _debugAssertNotDisposed() {
  16 + assert(() {
  17 + if (_listeners == null) {
  18 + throw FlutterError('''A $runtimeType was used after being disposed.\n
  19 +'Once you have called dispose() on a $runtimeType, it can no longer be used.''');
  20 + }
  21 + return true;
  22 + }());
  23 + return true;
  24 + }
  25 +
  26 + @protected
  27 + void notifyChildrens() {
  28 + TaskManager.instance.notify(_listeners);
  29 + }
  30 +
  31 + bool get hasListeners {
  32 + assert(_debugAssertNotDisposed());
  33 + return _listeners.isNotEmpty;
  34 + }
  35 +
  36 + @override
  37 + void addListener(VoidCallback listener) {
  38 + assert(_debugAssertNotDisposed());
  39 + _listeners.add(listener);
  40 + }
  41 +
  42 + @override
  43 + void removeListener(VoidCallback listener) {
  44 + assert(_debugAssertNotDisposed());
  45 + _listeners.remove(listener);
  46 + }
  47 +
  48 + @mustCallSuper
  49 + void dispose() {
  50 + assert(_debugAssertNotDisposed());
  51 + _listeners = null;
  52 + }
  53 +}
1 import 'dart:async'; 1 import 'dart:async';
2 -import 'dart:collection';  
3 import 'package:flutter/widgets.dart'; 2 import 'package:flutter/widgets.dart';
4 import 'get_state.dart'; 3 import 'get_state.dart';
5 4
@@ -88,7 +87,7 @@ class SimpleBuilder extends StatefulWidget { @@ -88,7 +87,7 @@ class SimpleBuilder extends StatefulWidget {
88 87
89 class _SimpleBuilderState extends State<SimpleBuilder> 88 class _SimpleBuilderState extends State<SimpleBuilder>
90 with GetStateUpdaterMixin { 89 with GetStateUpdaterMixin {
91 - final HashSet<VoidCallback> disposers = HashSet<VoidCallback>(); 90 + final disposers = <VoidCallback>[];
92 91
93 @override 92 @override
94 void dispose() { 93 void dispose() {
@@ -116,13 +115,11 @@ class TaskManager { @@ -116,13 +115,11 @@ class TaskManager {
116 115
117 static TaskManager get instance => _instance ??= TaskManager._(); 116 static TaskManager get instance => _instance ??= TaskManager._();
118 117
119 -// StateSetter _setter;//<old>  
120 GetStateUpdate _setter; 118 GetStateUpdate _setter;
121 119
122 - HashSet<VoidCallback> _remove; 120 + List<VoidCallback> _remove;
123 121
124 -// void notify(HashSet<StateSetter> _updaters) { //<old>  
125 - void notify(HashSet<GetStateUpdate> _updaters) { 122 + void notify(List<GetStateUpdate> _updaters) {
126 if (_setter != null) { 123 if (_setter != null) {
127 if (!_updaters.contains(_setter)) { 124 if (!_updaters.contains(_setter)) {
128 _updaters.add(_setter); 125 _updaters.add(_setter);
@@ -132,8 +129,7 @@ class TaskManager { @@ -132,8 +129,7 @@ class TaskManager {
132 } 129 }
133 130
134 Widget exchange( 131 Widget exchange(
135 - HashSet<VoidCallback> disposers,  
136 -// StateSetter setState, //<old> 132 + List<VoidCallback> disposers,
137 GetStateUpdate setState, 133 GetStateUpdate setState,
138 Widget Function(BuildContext) builder, 134 Widget Function(BuildContext) builder,
139 BuildContext context, 135 BuildContext context,
@@ -9,20 +9,29 @@ class Mock { @@ -9,20 +9,29 @@ class Mock {
9 } 9 }
10 } 10 }
11 11
12 -class Controller {}  
13 -  
14 -class DisposableController extends GetLifeCycle { 12 +class DisposableController with GetLifeCycle {
15 DisposableController() { 13 DisposableController() {
16 onStart.callback = _onStart; 14 onStart.callback = _onStart;
  15 + onDelete.callback = _onDelete;
17 } 16 }
18 17
19 // Internal callback that starts the cycle of this controller. 18 // Internal callback that starts the cycle of this controller.
20 void _onStart() { 19 void _onStart() {
  20 + if (initialized) return;
21 onInit(); 21 onInit();
22 } 22 }
23 23
  24 + // Internal callback that starts the cycle of this controller.
  25 + void _onDelete() {
  26 + if (isClosed) return;
  27 + isClosed = true;
  28 + onClose();
  29 + }
  30 +
24 bool initialized = false; 31 bool initialized = false;
25 32
  33 + bool isClosed = false;
  34 +
26 void onInit() async { 35 void onInit() async {
27 initialized = true; 36 initialized = true;
28 } 37 }
@@ -53,6 +62,17 @@ void main() { @@ -53,6 +62,17 @@ void main() {
53 Get.reset(); 62 Get.reset();
54 }); 63 });
55 64
  65 + test('Get start and delete called just one time', () async {
  66 + Get..put(Controller())..put(Controller());
  67 +
  68 + final controller = Get.find<Controller>();
  69 + expect(controller.init, 1);
  70 +
  71 + Get..delete<Controller>()..delete<Controller>();
  72 + expect(controller.close, 1);
  73 + Get.reset();
  74 + });
  75 +
56 test('Get.put tag test', () async { 76 test('Get.put tag test', () async {
57 final instance = Get.put<Controller>(Controller(), tag: 'one'); 77 final instance = Get.put<Controller>(Controller(), tag: 'one');
58 final instance2 = Get.put<Controller>(Controller(), tag: 'two'); 78 final instance2 = Get.put<Controller>(Controller(), tag: 'two');
@@ -128,3 +148,19 @@ void main() { @@ -128,3 +148,19 @@ void main() {
128 }); 148 });
129 }); 149 });
130 } 150 }
  151 +
  152 +class Controller extends DisposableController {
  153 + int init = 0;
  154 + int close = 0;
  155 + @override
  156 + void onInit() {
  157 + init++;
  158 + super.onInit();
  159 + }
  160 +
  161 + @override
  162 + void onClose() {
  163 + close++;
  164 + super.onClose();
  165 + }
  166 +}