Jonatas

added GetNotifier, improve structure, init refator from scratch

... ... @@ -5,7 +5,7 @@ import 'package:get/get.dart';
import '../controllers/home_controller.dart';
class CountryView extends GetWidget<HomeController> {
class CountryView extends GetView<HomeController> {
@override
Widget build(BuildContext context) {
return Container(
... ...
... ... @@ -29,7 +29,7 @@ dependencies:
get:
path: ../
dio: ^3.0.9
get_test: ^3.13.2
get_test: ^3.13.3
dependency_overrides:
get:
... ...
import 'dart:io';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
... ... @@ -25,6 +26,7 @@ class MockRepository implements IHomeRepository {
}
void main() {
setUpAll(() => HttpOverrides.global = null);
final binding = BindingsBuilder(() {
Get.lazyPut<IHomeRepository>(() => MockRepository());
Get.lazyPut<HomeController>(
... ...
... ... @@ -11,6 +11,10 @@ class GetInstance {
static GetInstance _getInstance;
T call<T>() {
return find<T>();
}
/// Holds references to every registered Instance when using
/// [Get.put()]
static final Map<String, _InstanceBuilderFactory> _singl = {};
... ... @@ -60,6 +64,20 @@ class GetInstance {
_factory.putIfAbsent(key, () => _Lazy(builder, fenix));
}
void injector<S>(
InjectorBuilderCallback<S> fn, {
String tag,
bool fenix = false,
// bool permanent = false,
}) {
lazyPut(
() => fn(this),
tag: tag,
fenix: fenix,
// permanent: permanent,
);
}
/// async version of [Get.put()].
/// Awaits for the resolution of the Future from [builder()] parameter and
/// stores the Instance returned.
... ... @@ -85,7 +103,7 @@ class GetInstance {
S dependency, {
String tag,
bool permanent = false,
InstanceBuilderCallback<S> builder,
@deprecated InstanceBuilderCallback<S> builder,
}) {
_insert(
isSingleton: true,
... ... @@ -201,12 +219,6 @@ class GetInstance {
_routesKey.putIfAbsent(_getKey(S, tag), () => Get.reference);
}
/// Finds and returns a Instance<[S]> (or [tag]) without further processing.
S findByType<S>(Type type, {String tag}) {
final key = _getKey(type, tag);
return _singl[key].getDependency() as S;
}
/// Initializes the controller
S _startController<S>({String tag}) {
final key = _getKey(S, tag);
... ... @@ -216,9 +228,9 @@ class GetInstance {
i.onStart();
Get.log('"$key" has been initialized');
}
if (!_singl[key].isSingleton && i.onClose != null) {
if (!_singl[key].isSingleton && i.onDelete != null) {
_routesByCreate[Get.reference] ??= HashSet<Function>();
_routesByCreate[Get.reference].add(i.onClose);
_routesByCreate[Get.reference].add(i.onDelete);
}
}
return i;
... ... @@ -349,7 +361,7 @@ class GetInstance {
return false;
}
if (i is GetLifeCycle) {
i.onClose();
i.onDelete();
Get.log('"$newKey" onClose() called');
}
... ... @@ -375,6 +387,8 @@ class GetInstance {
typedef InstanceBuilderCallback<S> = S Function();
typedef InjectorBuilderCallback<S> = S Function(GetInstance);
typedef AsyncInstanceBuilderCallback<S> = Future<S> Function();
/// Internal class to register instances with Get.[put]<[S]>().
... ... @@ -413,6 +427,7 @@ class _InstanceBuilderFactory<S> {
/// keeps a reference to the callback to be called.
class _Lazy {
bool fenix;
bool permanent = false;
InstanceBuilderCallback builder;
_Lazy(this.builder, this.fenix);
... ...
... ... @@ -11,13 +11,15 @@ class _InternalFinalCallback<T> {
T call() => callback.call();
}
abstract class GetLifeCycle {
mixin GetLifeCycle {
/// Called at the exact moment the widget is allocated in memory.
/// It uses an internal "callable" type, to avoid any @overrides in subclases.
/// This method should be internal and is required to define the
/// lifetime cycle of the subclass.
final onStart = _InternalFinalCallback<void>();
final onDelete = _InternalFinalCallback<void>();
/// Called immediately after the widget is allocated in memory.
/// You might use this to initialize something for the controller.
void onInit() {}
... ...
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import '../../get_core/get_core.dart';
... ... @@ -11,510 +13,279 @@ import 'routes/transitions_type.dart';
//TODO: Split this class on "Snackbar" "Dialog" "bottomSheet"
//and "navigation" extensions
extension GetNavigation on GetInterface {
/// **Navigation.push()** shortcut.<br><br>
///
/// Pushes a new [page] to the stack
///
/// It has the advantage of not needing context,
/// so you can call from your business logic
///
/// You can set a custom [transition], and a transition [duration].
///
/// You can send any type of value to the other route in the [arguments].
///
/// Just like native routing in Flutter, you can push a route
/// as a [fullscreenDialog],
///
/// [id] is for when you are using nested navigation,
/// as explained in documentation
///
/// If you want the same behavior of ios that pops a route when the user drag,
/// you can set [popGesture] to true
///
/// If you're using the [Bindings] api, you must define it here
///
/// By default, GetX will prevent you from push a route that you already in,
/// if you want to push anyway, set [preventDuplicates] to false
Future<T> to<T>(
Widget page, {
bool opaque,
Transition transition,
Curve curve,
Duration duration,
int id,
bool fullscreenDialog = false,
dynamic arguments,
Bindings binding,
bool preventDuplicates = true,
bool popGesture,
}) {
var routeName = "/${page.runtimeType.toString()}";
if (preventDuplicates && routeName == currentRoute) {
return null;
}
return global(id)?.currentState?.push(
GetPageRoute(
opaque: opaque ?? true,
page: () => page,
routeName: routeName,
settings: RouteSettings(
// name: forceRouteName ? '${a.runtimeType}' : '',
arguments: arguments,
),
popGesture: popGesture ?? defaultPopGesture,
transition: transition ?? defaultTransition,
curve: curve ?? defaultTransitionCurve,
fullscreenDialog: fullscreenDialog,
binding: binding,
transitionDuration: duration ?? defaultTransitionDuration,
),
);
}
/// **Navigation.pushNamed()** shortcut.<br><br>
///
/// Pushes a new named [page] to the stack.
///
/// It has the advantage of not needing context, so you can call
/// from your business logic.
///
/// You can send any type of value to the other route in the [arguments].
///
/// [id] is for when you are using nested navigation,
/// as explained in documentation
///
/// By default, GetX will prevent you from push a route that you already in,
/// if you want to push anyway, set [preventDuplicates] to false
///
/// Note: Always put a slash on the route ('/page1'), to avoid unnexpected errors
Future<T> toNamed<T>(
String page, {
dynamic arguments,
int id,
bool preventDuplicates = true,
}) {
if (preventDuplicates && page == currentRoute) {
return null;
}
return global(id)?.currentState?.pushNamed(page, arguments: arguments);
}
extension ExtensionSnackbar on GetInterface {
void rawSnackbar({
String title,
String message,
Widget titleText,
Widget messageText,
Widget icon,
bool instantInit = true,
bool shouldIconPulse = true,
double maxWidth,
EdgeInsets margin = const EdgeInsets.all(0.0),
EdgeInsets padding = const EdgeInsets.all(16),
double borderRadius = 0.0,
Color borderColor,
double borderWidth = 1.0,
Color backgroundColor = const Color(0xFF303030),
Color leftBarIndicatorColor,
List<BoxShadow> boxShadows,
Gradient backgroundGradient,
FlatButton mainButton,
OnTap onTap,
Duration duration = const Duration(seconds: 3),
bool isDismissible = true,
SnackDismissDirection dismissDirection = SnackDismissDirection.VERTICAL,
bool showProgressIndicator = false,
AnimationController progressIndicatorController,
Color progressIndicatorBackgroundColor,
Animation<Color> progressIndicatorValueColor,
SnackPosition snackPosition = SnackPosition.BOTTOM,
SnackStyle snackStyle = SnackStyle.FLOATING,
Curve forwardAnimationCurve = Curves.easeOutCirc,
Curve reverseAnimationCurve = Curves.easeOutCirc,
Duration animationDuration = const Duration(seconds: 1),
SnackbarStatusCallback snackbarStatus,
double barBlur = 0.0,
double overlayBlur = 0.0,
Color overlayColor,
Form userInputForm,
}) async {
final getBar = GetBar(
snackbarStatus: snackbarStatus,
title: title,
message: message,
titleText: titleText,
messageText: messageText,
snackPosition: snackPosition,
borderRadius: borderRadius,
margin: margin,
duration: duration,
barBlur: barBlur,
backgroundColor: backgroundColor,
icon: icon,
shouldIconPulse: shouldIconPulse,
maxWidth: maxWidth,
padding: padding,
borderColor: borderColor,
borderWidth: borderWidth,
leftBarIndicatorColor: leftBarIndicatorColor,
boxShadows: boxShadows,
backgroundGradient: backgroundGradient,
mainButton: mainButton,
onTap: onTap,
isDismissible: isDismissible,
dismissDirection: dismissDirection,
showProgressIndicator: showProgressIndicator ?? false,
progressIndicatorController: progressIndicatorController,
progressIndicatorBackgroundColor: progressIndicatorBackgroundColor,
progressIndicatorValueColor: progressIndicatorValueColor,
snackStyle: snackStyle,
forwardAnimationCurve: forwardAnimationCurve,
reverseAnimationCurve: reverseAnimationCurve,
animationDuration: animationDuration,
overlayBlur: overlayBlur,
overlayColor: overlayColor,
userInputForm: userInputForm,
);
/// **Navigation.pushReplacementNamed()** shortcut.<br><br>
///
/// Pop the current named [page] in the stack and push a new one in its place
///
/// It has the advantage of not needing context, so you can call
/// from your business logic.
///
/// You can send any type of value to the other route in the [arguments].
///
/// [id] is for when you are using nested navigation,
/// as explained in documentation
///
/// By default, GetX will prevent you from push a route that you already in,
/// if you want to push anyway, set [preventDuplicates] to false
///
/// Note: Always put a slash on the route ('/page1'), to avoid unnexpected errors
Future<T> offNamed<T>(
String page, {
dynamic arguments,
int id,
bool preventDuplicates = true,
}) {
if (preventDuplicates && page == currentRoute) {
return null;
if (instantInit) {
getBar.show();
} else {
SchedulerBinding.instance.addPostFrameCallback((_) {
getBar.show();
});
}
return global(id)
?.currentState
?.pushReplacementNamed(page, arguments: arguments);
}
/// **Navigation.popUntil()** shortcut.<br><br>
///
/// Calls pop several times in the stack until [predicate] returns true
///
/// [id] is for when you are using nested navigation,
/// as explained in documentation
///
/// [predicate] can be used like this:
/// `Get.until((route) => Get.currentRoute == '/home')`so when you get to home page,
///
/// or also like this:
/// `Get.until((route) => !Get.isDialogOpen())`, to make sure the
/// dialog is closed
void until(RoutePredicate predicate, {int id}) {
// if (key.currentState.mounted) // add this if appear problems on future with route navigate
// when widget don't mounted
return global(id)?.currentState?.popUntil(predicate);
Future<T> showSnackbar<T>(GetBar snackbar) {
return key?.currentState?.push(SnackRoute<T>(snack: snackbar));
}
/// **Navigation.pushAndRemoveUntil()** shortcut.<br><br>
///
/// Push the given [page], and then pop several pages in the stack until
/// [predicate] returns true
///
/// [id] is for when you are using nested navigation,
/// as explained in documentation
///
/// Obs: unlike other get methods, this one you need to send a function
/// that returns the widget to the page argument, like this:
/// Get.offUntil( () => HomePage() )
///
/// [predicate] can be used like this:
/// `Get.until((route) => Get.currentRoute == '/home')`so when you get to home page,
///
/// or also like this:
/// `Get.until((route) => !Get.isDialogOpen())`, to make sure the dialog
/// is closed
Future<T> offUntil<T>(Route<T> page, RoutePredicate predicate, {int id}) {
// if (key.currentState.mounted) // add this if appear problems on future with route navigate
// when widget don't mounted
return global(id)?.currentState?.pushAndRemoveUntil(page, predicate);
}
void snackbar(
String title,
String message, {
Color colorText,
Duration duration,
/// **Navigation.pushNamedAndRemoveUntil()** shortcut.<br><br>
///
/// Push the given named [page], and then pop several pages in the stack
/// until [predicate] returns true
///
/// You can send any type of value to the other route in the [arguments].
///
/// [id] is for when you are using nested navigation,
/// as explained in documentation
///
/// [predicate] can be used like this:
/// `Get.until((route) => Get.currentRoute == '/home')`so when you get to home page,
/// or also like
/// `Get.until((route) => !Get.isDialogOpen())`, to make sure the dialog
/// is closed
///
/// Note: Always put a slash on the route ('/page1'), to avoid unnexpected errors
Future<T> offNamedUntil<T>(
String page,
RoutePredicate predicate, {
int id,
dynamic arguments,
}) {
return global(id)
?.currentState
?.pushNamedAndRemoveUntil(page, predicate, arguments: arguments);
/// with instantInit = false you can put snackbar on initState
bool instantInit = true,
SnackPosition snackPosition,
Widget titleText,
Widget messageText,
Widget icon,
bool shouldIconPulse,
double maxWidth,
EdgeInsets margin,
EdgeInsets padding,
double borderRadius,
Color borderColor,
double borderWidth,
Color backgroundColor,
Color leftBarIndicatorColor,
List<BoxShadow> boxShadows,
Gradient backgroundGradient,
FlatButton mainButton,
OnTap onTap,
bool isDismissible,
bool showProgressIndicator,
SnackDismissDirection dismissDirection,
AnimationController progressIndicatorController,
Color progressIndicatorBackgroundColor,
Animation<Color> progressIndicatorValueColor,
SnackStyle snackStyle,
Curve forwardAnimationCurve,
Curve reverseAnimationCurve,
Duration animationDuration,
double barBlur,
double overlayBlur,
SnackbarStatusCallback snackbarStatus,
Color overlayColor,
Form userInputForm,
}) async {
final getBar = GetBar(
snackbarStatus: snackbarStatus,
titleText: (title == null)
? null
: titleText ??
Text(
title,
style: TextStyle(
color: colorText ?? iconColor ?? Colors.black,
fontWeight: FontWeight.w800,
fontSize: 16,
),
),
messageText: messageText ??
Text(
message,
style: TextStyle(
color: colorText ?? iconColor ?? Colors.black,
fontWeight: FontWeight.w300,
fontSize: 14,
),
),
snackPosition: snackPosition ?? SnackPosition.TOP,
borderRadius: borderRadius ?? 15,
margin: margin ?? EdgeInsets.symmetric(horizontal: 10),
duration: duration ?? Duration(seconds: 3),
barBlur: barBlur ?? 7.0,
backgroundColor: backgroundColor ?? Colors.grey.withOpacity(0.2),
icon: icon,
shouldIconPulse: shouldIconPulse ?? true,
maxWidth: maxWidth,
padding: padding ?? EdgeInsets.all(16),
borderColor: borderColor,
borderWidth: borderWidth,
leftBarIndicatorColor: leftBarIndicatorColor,
boxShadows: boxShadows,
backgroundGradient: backgroundGradient,
mainButton: mainButton,
onTap: onTap,
isDismissible: isDismissible ?? true,
dismissDirection: dismissDirection ?? SnackDismissDirection.VERTICAL,
showProgressIndicator: showProgressIndicator ?? false,
progressIndicatorController: progressIndicatorController,
progressIndicatorBackgroundColor: progressIndicatorBackgroundColor,
progressIndicatorValueColor: progressIndicatorValueColor,
snackStyle: snackStyle ?? SnackStyle.FLOATING,
forwardAnimationCurve: forwardAnimationCurve ?? Curves.easeOutCirc,
reverseAnimationCurve: reverseAnimationCurve ?? Curves.easeOutCirc,
animationDuration: animationDuration ?? Duration(seconds: 1),
overlayBlur: overlayBlur ?? 0.0,
overlayColor: overlayColor ?? Colors.transparent,
userInputForm: userInputForm);
if (instantInit) {
showSnackbar(getBar);
} else {
routing.isSnackbar = true;
SchedulerBinding.instance.addPostFrameCallback((_) {
showSnackbar(getBar);
});
}
}
}
/// **Navigation.popAndPushNamed()** shortcut.<br><br>
///
/// Pop the current named page and pushes a new [page] to the stack
/// in its place
///
/// You can send any type of value to the other route in the [arguments].
/// It is very similar to `offNamed()` but use a different approach
///
/// The `offNamed()` pop a page, and goes to the next. The
/// `offAndToNamed()` goes to the next page, and removes the previous one.
/// The route transition animation is different.
Future<T> offAndToNamed<T>(
String page, {
dynamic arguments,
int id,
dynamic result,
extension ExtensionDialog on GetInterface {
/// Show a dialog.
/// You can pass a [transitionDuration] and/or [transitionCurve],
/// overriding the defaults when the dialog shows up and closes.
/// When the dialog closes, uses those animations in reverse.
Future<T> dialog<T>(
Widget widget, {
bool barrierDismissible = true,
Color barrierColor,
bool useSafeArea = true,
bool useRootNavigator = true,
RouteSettings routeSettings,
Duration transitionDuration,
Curve transitionCurve,
}) {
return global(id)
?.currentState
?.popAndPushNamed(page, arguments: arguments, result: result);
}
assert(widget != null);
assert(barrierDismissible != null);
assert(useSafeArea != null);
assert(useRootNavigator != null);
assert(debugCheckHasMaterialLocalizations(context));
/// **Navigation.removeRoute()** shortcut.<br><br>
///
/// Remove a specific [route] from the stack
///
/// [id] is for when you are using nested navigation,
/// as explained in documentation
void removeRoute(Route<dynamic> route, {int id}) {
return global(id)?.currentState?.removeRoute(route);
final theme = Theme.of(context, shadowThemeOnly: true);
return generalDialog(
pageBuilder: (buildContext, animation, secondaryAnimation) {
final pageChild = widget;
Widget dialog = Builder(builder: (context) {
return theme != null
? Theme(data: theme, child: pageChild)
: pageChild;
});
if (useSafeArea) {
dialog = SafeArea(child: dialog);
}
return dialog;
},
barrierDismissible: barrierDismissible,
barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
barrierColor: barrierColor ?? Colors.black54,
transitionDuration: transitionDuration ?? defaultDialogTransitionDuration,
transitionBuilder: (context, animation, secondaryAnimation, child) {
return FadeTransition(
opacity: CurvedAnimation(
parent: animation,
curve: transitionCurve ?? defaultDialogTransitionCurve,
),
child: child,
);
},
useRootNavigator: useRootNavigator,
routeSettings: routeSettings,
);
}
/// **Navigation.pushNamedAndRemoveUntil()** shortcut.<br><br>
///
/// Push a named [page] and pop several pages in the stack
/// until [predicate] returns true. [predicate] is optional
///
/// It has the advantage of not needing context, so you can
/// call from your business logic.
///
/// You can send any type of value to the other route in the [arguments].
///
/// [predicate] can be used like this:
/// `Get.until((route) => Get.currentRoute == '/home')`so when you get to home page,
/// or also like
/// `Get.until((route) => !Get.isDialogOpen())`, to make sure the dialog
/// is closed
///
/// [id] is for when you are using nested navigation,
/// as explained in documentation
///
/// Note: Always put a slash on the route ('/page1'), to avoid unexpected errors
Future<T> offAllNamed<T>(
String newRouteName, {
RoutePredicate predicate,
dynamic arguments,
int id,
/// Api from showGeneralDialog with no context
Future<T> generalDialog<T>({
@required RoutePageBuilder pageBuilder,
bool barrierDismissible = false,
String barrierLabel,
Color barrierColor = const Color(0x80000000),
Duration transitionDuration = const Duration(milliseconds: 200),
RouteTransitionsBuilder transitionBuilder,
bool useRootNavigator = true,
RouteSettings routeSettings,
}) {
return global(id)?.currentState?.pushNamedAndRemoveUntil(
newRouteName,
predicate ?? (_) => false,
arguments: arguments,
);
}
/// Returns true if a Snackbar, Dialog or BottomSheet is currently OPEN
bool get isOverlaysOpen =>
(isSnackbarOpen || isDialogOpen || isBottomSheetOpen);
/// Returns true if there is no Snackbar, Dialog or BottomSheet open
bool get isOverlaysClosed =>
(!isSnackbarOpen && !isDialogOpen && !isBottomSheetOpen);
/// **Navigation.popUntil()** shortcut.<br><br>
///
/// Pop the current page, snackbar, dialog or bottomsheet in the stack
///
/// if your set [closeOverlays] to true, Get.back() will close the
/// currently open snackbar/dialog/bottomsheet AND the current page
///
/// [id] is for when you are using nested navigation,
/// as explained in documentation
///
/// It has the advantage of not needing context, so you can call
/// from your business logic.
void back({
dynamic result,
bool closeOverlays = false,
bool canPop = true,
int id,
}) {
if (closeOverlays && isOverlaysOpen) {
navigator?.popUntil((route) {
return (isOverlaysClosed);
});
}
if (canPop) {
if (global(id)?.currentState?.canPop() == true) {
global(id)?.currentState?.pop(result);
}
} else {
global(id)?.currentState?.pop(result);
}
}
/// **Navigation.popUntil()** (with predicate) shortcut .<br><br>
///
/// Close as many routes as defined by [times]
///
/// [id] is for when you are using nested navigation,
/// as explained in documentation
void close(int times, [int id]) {
if ((times == null) || (times < 1)) {
times = 1;
}
var count = 0;
var back = global(id)?.currentState?.popUntil((route) => count++ == times);
return back;
}
/// **Navigation.pushReplacement()** shortcut .<br><br>
///
/// Pop the current page and pushes a new [page] to the stack
///
/// It has the advantage of not needing context,
/// so you can call from your business logic
///
/// You can set a custom [transition], define a Tween [curve],
/// and a transition [duration].
///
/// You can send any type of value to the other route in the [arguments].
///
/// Just like native routing in Flutter, you can push a route
/// as a [fullscreenDialog],
///
/// [id] is for when you are using nested navigation,
/// as explained in documentation
///
/// If you want the same behavior of ios that pops a route when the user drag,
/// you can set [popGesture] to true
///
/// If you're using the [Bindings] api, you must define it here
///
/// By default, GetX will prevent you from push a route that you already in,
/// if you want to push anyway, set [preventDuplicates] to false
Future<T> off<T>(
Widget page, {
bool opaque = false,
Transition transition,
Curve curve,
bool popGesture,
int id,
dynamic arguments,
Bindings binding,
bool fullscreenDialog = false,
bool preventDuplicates = true,
Duration duration,
}) {
var routeName = "/${page.runtimeType.toString()}";
if (preventDuplicates && routeName == currentRoute) {
return null;
}
return global(id)?.currentState?.pushReplacement(GetPageRoute(
opaque: opaque ?? true,
page: () => page,
binding: binding,
settings: RouteSettings(arguments: arguments),
routeName: routeName,
fullscreenDialog: fullscreenDialog,
popGesture: popGesture ?? defaultPopGesture,
transition: transition ?? defaultTransition,
curve: curve ?? defaultTransitionCurve,
transitionDuration: duration ?? defaultTransitionDuration));
}
/// **Navigation.pushAndRemoveUntil()** shortcut .<br><br>
///
/// Push a [page] and pop several pages in the stack
/// until [predicate] returns true. [predicate] is optional
///
/// It has the advantage of not needing context,
/// so you can call from your business logic
///
/// You can set a custom [transition], a [curve] and a transition [duration].
///
/// You can send any type of value to the other route in the [arguments].
///
/// Just like native routing in Flutter, you can push a route
/// as a [fullscreenDialog],
///
/// [predicate] can be used like this:
/// `Get.until((route) => Get.currentRoute == '/home')`so when you get to home page,
/// or also like
/// `Get.until((route) => !Get.isDialogOpen())`, to make sure the dialog
/// is closed
///
/// [id] is for when you are using nested navigation,
/// as explained in documentation
///
/// If you want the same behavior of ios that pops a route when the user drag,
/// you can set [popGesture] to true
///
/// If you're using the [Bindings] api, you must define it here
///
/// By default, GetX will prevent you from push a route that you already in,
/// if you want to push anyway, set [preventDuplicates] to false
Future<T> offAll<T>(
Widget page, {
RoutePredicate predicate,
bool opaque = false,
bool popGesture,
int id,
dynamic arguments,
Bindings binding,
bool fullscreenDialog = false,
Transition transition,
Curve curve,
Duration duration,
}) {
var routeName = "/${page.runtimeType.toString()}";
return global(id)?.currentState?.pushAndRemoveUntil(
GetPageRoute(
opaque: opaque ?? true,
popGesture: popGesture ?? defaultPopGesture,
page: () => page,
binding: binding,
settings: RouteSettings(arguments: arguments),
fullscreenDialog: fullscreenDialog,
routeName: routeName,
transition: transition ?? defaultTransition,
curve: curve ?? defaultTransitionCurve,
transitionDuration: duration ?? defaultTransitionDuration,
),
predicate ?? (route) => false);
}
/// Show a dialog.
/// You can pass a [transitionDuration] and/or [transitionCurve],
/// overriding the defaults when the dialog shows up and closes.
/// When the dialog closes, uses those animations in reverse.
Future<T> dialog<T>(
Widget widget, {
bool barrierDismissible = true,
Color barrierColor,
bool useSafeArea = true,
bool useRootNavigator = true,
RouteSettings routeSettings,
Duration transitionDuration,
Curve transitionCurve,
}) {
assert(widget != null);
assert(barrierDismissible != null);
assert(useSafeArea != null);
assert(useRootNavigator != null);
assert(debugCheckHasMaterialLocalizations(context));
final theme = Theme.of(context, shadowThemeOnly: true);
return generalDialog(
pageBuilder: (buildContext, animation, secondaryAnimation) {
final pageChild = widget;
Widget dialog = Builder(builder: (context) {
return theme != null
? Theme(data: theme, child: pageChild)
: pageChild;
});
if (useSafeArea) {
dialog = SafeArea(child: dialog);
}
return dialog;
},
barrierDismissible: barrierDismissible,
barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
barrierColor: barrierColor ?? Colors.black54,
transitionDuration: transitionDuration ?? defaultDialogTransitionDuration,
transitionBuilder: (context, animation, secondaryAnimation, child) {
return FadeTransition(
opacity: CurvedAnimation(
parent: animation,
curve: transitionCurve ?? defaultDialogTransitionCurve,
),
child: child,
);
},
useRootNavigator: useRootNavigator,
routeSettings: routeSettings,
);
}
/// Api from showGeneralDialog with no context
Future<T> generalDialog<T>({
@required RoutePageBuilder pageBuilder,
bool barrierDismissible = false,
String barrierLabel,
Color barrierColor = const Color(0x80000000),
Duration transitionDuration = const Duration(milliseconds: 200),
RouteTransitionsBuilder transitionBuilder,
bool useRootNavigator = true,
RouteSettings routeSettings,
}) {
assert(pageBuilder != null);
assert(useRootNavigator != null);
assert(!barrierDismissible || barrierLabel != null);
return Navigator.of(overlayContext, rootNavigator: useRootNavigator)
.push<T>(GetDialogRoute<T>(
pageBuilder: pageBuilder,
barrierDismissible: barrierDismissible,
barrierLabel: barrierLabel,
barrierColor: barrierColor,
transitionDuration: transitionDuration,
transitionBuilder: transitionBuilder,
settings: routeSettings,
));
assert(pageBuilder != null);
assert(useRootNavigator != null);
assert(!barrierDismissible || barrierLabel != null);
return Navigator.of(overlayContext, rootNavigator: useRootNavigator)
.push<T>(GetDialogRoute<T>(
pageBuilder: pageBuilder,
barrierDismissible: barrierDismissible,
barrierLabel: barrierLabel,
barrierColor: barrierColor,
transitionDuration: transitionDuration,
transitionBuilder: transitionBuilder,
settings: routeSettings,
));
}
/// Custom UI Dialog.
... ... @@ -623,7 +394,9 @@ extension GetNavigation on GetInterface {
barrierDismissible: barrierDismissible,
);
}
}
extension ExtensionBottomSheet on GetInterface {
Future<T> bottomSheet<T>(
Widget bottomsheet, {
Color backgroundColor,
... ... @@ -667,198 +440,434 @@ extension GetNavigation on GetInterface {
enableDrag: enableDrag,
));
}
}
void rawSnackbar({
String title,
String message,
Widget titleText,
Widget messageText,
Widget icon,
bool instantInit = true,
bool shouldIconPulse = true,
double maxWidth,
EdgeInsets margin = const EdgeInsets.all(0.0),
EdgeInsets padding = const EdgeInsets.all(16),
double borderRadius = 0.0,
Color borderColor,
double borderWidth = 1.0,
Color backgroundColor = const Color(0xFF303030),
Color leftBarIndicatorColor,
List<BoxShadow> boxShadows,
Gradient backgroundGradient,
FlatButton mainButton,
OnTap onTap,
Duration duration = const Duration(seconds: 3),
bool isDismissible = true,
SnackDismissDirection dismissDirection = SnackDismissDirection.VERTICAL,
bool showProgressIndicator = false,
AnimationController progressIndicatorController,
Color progressIndicatorBackgroundColor,
Animation<Color> progressIndicatorValueColor,
SnackPosition snackPosition = SnackPosition.BOTTOM,
SnackStyle snackStyle = SnackStyle.FLOATING,
Curve forwardAnimationCurve = Curves.easeOutCirc,
Curve reverseAnimationCurve = Curves.easeOutCirc,
Duration animationDuration = const Duration(seconds: 1),
SnackbarStatusCallback snackbarStatus,
double barBlur = 0.0,
double overlayBlur = 0.0,
Color overlayColor,
Form userInputForm,
}) async {
final getBar = GetBar(
snackbarStatus: snackbarStatus,
title: title,
message: message,
titleText: titleText,
messageText: messageText,
snackPosition: snackPosition,
borderRadius: borderRadius,
margin: margin,
duration: duration,
barBlur: barBlur,
backgroundColor: backgroundColor,
icon: icon,
shouldIconPulse: shouldIconPulse,
maxWidth: maxWidth,
padding: padding,
borderColor: borderColor,
borderWidth: borderWidth,
leftBarIndicatorColor: leftBarIndicatorColor,
boxShadows: boxShadows,
backgroundGradient: backgroundGradient,
mainButton: mainButton,
onTap: onTap,
isDismissible: isDismissible,
dismissDirection: dismissDirection,
showProgressIndicator: showProgressIndicator ?? false,
progressIndicatorController: progressIndicatorController,
progressIndicatorBackgroundColor: progressIndicatorBackgroundColor,
progressIndicatorValueColor: progressIndicatorValueColor,
snackStyle: snackStyle,
forwardAnimationCurve: forwardAnimationCurve,
reverseAnimationCurve: reverseAnimationCurve,
animationDuration: animationDuration,
overlayBlur: overlayBlur,
overlayColor: overlayColor,
userInputForm: userInputForm,
);
extension GetNavigation on GetInterface {
/// **Navigation.push()** shortcut.<br><br>
///
/// Pushes a new [page] to the stack
///
/// It has the advantage of not needing context,
/// so you can call from your business logic
///
/// You can set a custom [transition], and a transition [duration].
///
/// You can send any type of value to the other route in the [arguments].
///
/// Just like native routing in Flutter, you can push a route
/// as a [fullscreenDialog],
///
/// [id] is for when you are using nested navigation,
/// as explained in documentation
///
/// If you want the same behavior of ios that pops a route when the user drag,
/// you can set [popGesture] to true
///
/// If you're using the [Bindings] api, you must define it here
///
/// By default, GetX will prevent you from push a route that you already in,
/// if you want to push anyway, set [preventDuplicates] to false
Future<T> to<T>(
Widget page, {
bool opaque,
Transition transition,
Curve curve,
Duration duration,
int id,
bool fullscreenDialog = false,
dynamic arguments,
Bindings binding,
bool preventDuplicates = true,
bool popGesture,
}) {
var routeName = "/${page.runtimeType.toString()}";
if (preventDuplicates && routeName == currentRoute) {
return null;
}
return global(id)?.currentState?.push(
GetPageRoute(
opaque: opaque ?? true,
page: () => page,
routeName: routeName,
settings: RouteSettings(
// name: forceRouteName ? '${a.runtimeType}' : '',
arguments: arguments,
),
popGesture: popGesture ?? defaultPopGesture,
transition: transition ?? defaultTransition,
curve: curve ?? defaultTransitionCurve,
fullscreenDialog: fullscreenDialog,
binding: binding,
transitionDuration: duration ?? defaultTransitionDuration,
),
);
}
if (instantInit) {
getBar.show();
} else {
SchedulerBinding.instance.addPostFrameCallback((_) {
getBar.show();
/// **Navigation.pushNamed()** shortcut.<br><br>
///
/// Pushes a new named [page] to the stack.
///
/// It has the advantage of not needing context, so you can call
/// from your business logic.
///
/// You can send any type of value to the other route in the [arguments].
///
/// [id] is for when you are using nested navigation,
/// as explained in documentation
///
/// By default, GetX will prevent you from push a route that you already in,
/// if you want to push anyway, set [preventDuplicates] to false
///
/// Note: Always put a slash on the route ('/page1'), to avoid unnexpected errors
Future<T> toNamed<T>(
String page, {
dynamic arguments,
int id,
bool preventDuplicates = true,
}) {
if (preventDuplicates && page == currentRoute) {
return null;
}
return global(id)?.currentState?.pushNamed(page, arguments: arguments);
}
/// **Navigation.pushReplacementNamed()** shortcut.<br><br>
///
/// Pop the current named [page] in the stack and push a new one in its place
///
/// It has the advantage of not needing context, so you can call
/// from your business logic.
///
/// You can send any type of value to the other route in the [arguments].
///
/// [id] is for when you are using nested navigation,
/// as explained in documentation
///
/// By default, GetX will prevent you from push a route that you already in,
/// if you want to push anyway, set [preventDuplicates] to false
///
/// Note: Always put a slash on the route ('/page1'), to avoid unnexpected errors
Future<T> offNamed<T>(
String page, {
dynamic arguments,
int id,
bool preventDuplicates = true,
}) {
if (preventDuplicates && page == currentRoute) {
return null;
}
return global(id)
?.currentState
?.pushReplacementNamed(page, arguments: arguments);
}
/// **Navigation.popUntil()** shortcut.<br><br>
///
/// Calls pop several times in the stack until [predicate] returns true
///
/// [id] is for when you are using nested navigation,
/// as explained in documentation
///
/// [predicate] can be used like this:
/// `Get.until((route) => Get.currentRoute == '/home')`so when you get to home page,
///
/// or also like this:
/// `Get.until((route) => !Get.isDialogOpen())`, to make sure the
/// dialog is closed
void until(RoutePredicate predicate, {int id}) {
// if (key.currentState.mounted) // add this if appear problems on future with route navigate
// when widget don't mounted
return global(id)?.currentState?.popUntil(predicate);
}
/// **Navigation.pushAndRemoveUntil()** shortcut.<br><br>
///
/// Push the given [page], and then pop several pages in the stack until
/// [predicate] returns true
///
/// [id] is for when you are using nested navigation,
/// as explained in documentation
///
/// Obs: unlike other get methods, this one you need to send a function
/// that returns the widget to the page argument, like this:
/// Get.offUntil( () => HomePage() )
///
/// [predicate] can be used like this:
/// `Get.until((route) => Get.currentRoute == '/home')`so when you get to home page,
///
/// or also like this:
/// `Get.until((route) => !Get.isDialogOpen())`, to make sure the dialog
/// is closed
Future<T> offUntil<T>(Route<T> page, RoutePredicate predicate, {int id}) {
// if (key.currentState.mounted) // add this if appear problems on future with route navigate
// when widget don't mounted
return global(id)?.currentState?.pushAndRemoveUntil(page, predicate);
}
/// **Navigation.pushNamedAndRemoveUntil()** shortcut.<br><br>
///
/// Push the given named [page], and then pop several pages in the stack
/// until [predicate] returns true
///
/// You can send any type of value to the other route in the [arguments].
///
/// [id] is for when you are using nested navigation,
/// as explained in documentation
///
/// [predicate] can be used like this:
/// `Get.until((route) => Get.currentRoute == '/home')`so when you get to home page,
/// or also like
/// `Get.until((route) => !Get.isDialogOpen())`, to make sure the dialog
/// is closed
///
/// Note: Always put a slash on the route ('/page1'), to avoid unnexpected errors
Future<T> offNamedUntil<T>(
String page,
RoutePredicate predicate, {
int id,
dynamic arguments,
}) {
return global(id)
?.currentState
?.pushNamedAndRemoveUntil(page, predicate, arguments: arguments);
}
/// **Navigation.popAndPushNamed()** shortcut.<br><br>
///
/// Pop the current named page and pushes a new [page] to the stack
/// in its place
///
/// You can send any type of value to the other route in the [arguments].
/// It is very similar to `offNamed()` but use a different approach
///
/// The `offNamed()` pop a page, and goes to the next. The
/// `offAndToNamed()` goes to the next page, and removes the previous one.
/// The route transition animation is different.
Future<T> offAndToNamed<T>(
String page, {
dynamic arguments,
int id,
dynamic result,
}) {
return global(id)
?.currentState
?.popAndPushNamed(page, arguments: arguments, result: result);
}
/// **Navigation.removeRoute()** shortcut.<br><br>
///
/// Remove a specific [route] from the stack
///
/// [id] is for when you are using nested navigation,
/// as explained in documentation
void removeRoute(Route<dynamic> route, {int id}) {
return global(id)?.currentState?.removeRoute(route);
}
/// **Navigation.pushNamedAndRemoveUntil()** shortcut.<br><br>
///
/// Push a named [page] and pop several pages in the stack
/// until [predicate] returns true. [predicate] is optional
///
/// It has the advantage of not needing context, so you can
/// call from your business logic.
///
/// You can send any type of value to the other route in the [arguments].
///
/// [predicate] can be used like this:
/// `Get.until((route) => Get.currentRoute == '/home')`so when you get to home page,
/// or also like
/// `Get.until((route) => !Get.isDialogOpen())`, to make sure the dialog
/// is closed
///
/// [id] is for when you are using nested navigation,
/// as explained in documentation
///
/// Note: Always put a slash on the route ('/page1'), to avoid unexpected errors
Future<T> offAllNamed<T>(
String newRouteName, {
RoutePredicate predicate,
dynamic arguments,
int id,
}) {
return global(id)?.currentState?.pushNamedAndRemoveUntil(
newRouteName,
predicate ?? (_) => false,
arguments: arguments,
);
}
/// Returns true if a Snackbar, Dialog or BottomSheet is currently OPEN
bool get isOverlaysOpen =>
(isSnackbarOpen || isDialogOpen || isBottomSheetOpen);
/// Returns true if there is no Snackbar, Dialog or BottomSheet open
bool get isOverlaysClosed =>
(!isSnackbarOpen && !isDialogOpen && !isBottomSheetOpen);
/// **Navigation.popUntil()** shortcut.<br><br>
///
/// Pop the current page, snackbar, dialog or bottomsheet in the stack
///
/// if your set [closeOverlays] to true, Get.back() will close the
/// currently open snackbar/dialog/bottomsheet AND the current page
///
/// [id] is for when you are using nested navigation,
/// as explained in documentation
///
/// It has the advantage of not needing context, so you can call
/// from your business logic.
void back({
dynamic result,
bool closeOverlays = false,
bool canPop = true,
int id,
}) {
if (closeOverlays && isOverlaysOpen) {
navigator?.popUntil((route) {
return (isOverlaysClosed);
});
}
if (canPop) {
if (global(id)?.currentState?.canPop() == true) {
global(id)?.currentState?.pop(result);
}
} else {
global(id)?.currentState?.pop(result);
}
}
Future<T> showSnackbar<T>(GetBar snackbar) {
return key?.currentState?.push(SnackRoute<T>(snack: snackbar));
/// **Navigation.popUntil()** (with predicate) shortcut .<br><br>
///
/// Close as many routes as defined by [times]
///
/// [id] is for when you are using nested navigation,
/// as explained in documentation
void close(int times, [int id]) {
if ((times == null) || (times < 1)) {
times = 1;
}
var count = 0;
var back = global(id)?.currentState?.popUntil((route) => count++ == times);
return back;
}
void snackbar(
String title,
String message, {
Color colorText,
/// **Navigation.pushReplacement()** shortcut .<br><br>
///
/// Pop the current page and pushes a new [page] to the stack
///
/// It has the advantage of not needing context,
/// so you can call from your business logic
///
/// You can set a custom [transition], define a Tween [curve],
/// and a transition [duration].
///
/// You can send any type of value to the other route in the [arguments].
///
/// Just like native routing in Flutter, you can push a route
/// as a [fullscreenDialog],
///
/// [id] is for when you are using nested navigation,
/// as explained in documentation
///
/// If you want the same behavior of ios that pops a route when the user drag,
/// you can set [popGesture] to true
///
/// If you're using the [Bindings] api, you must define it here
///
/// By default, GetX will prevent you from push a route that you already in,
/// if you want to push anyway, set [preventDuplicates] to false
Future<T> off<T>(
Widget page, {
bool opaque = false,
Transition transition,
Curve curve,
bool popGesture,
int id,
dynamic arguments,
Bindings binding,
bool fullscreenDialog = false,
bool preventDuplicates = true,
Duration duration,
}) {
var routeName = "/${page.runtimeType.toString()}";
if (preventDuplicates && routeName == currentRoute) {
return null;
}
return global(id)?.currentState?.pushReplacement(GetPageRoute(
opaque: opaque ?? true,
page: () => page,
binding: binding,
settings: RouteSettings(arguments: arguments),
routeName: routeName,
fullscreenDialog: fullscreenDialog,
popGesture: popGesture ?? defaultPopGesture,
transition: transition ?? defaultTransition,
curve: curve ?? defaultTransitionCurve,
transitionDuration: duration ?? defaultTransitionDuration));
}
/// with instantInit = false you can put snackbar on initState
bool instantInit = true,
SnackPosition snackPosition,
Widget titleText,
Widget messageText,
Widget icon,
bool shouldIconPulse,
double maxWidth,
EdgeInsets margin,
EdgeInsets padding,
double borderRadius,
Color borderColor,
double borderWidth,
Color backgroundColor,
Color leftBarIndicatorColor,
List<BoxShadow> boxShadows,
Gradient backgroundGradient,
FlatButton mainButton,
OnTap onTap,
bool isDismissible,
bool showProgressIndicator,
SnackDismissDirection dismissDirection,
AnimationController progressIndicatorController,
Color progressIndicatorBackgroundColor,
Animation<Color> progressIndicatorValueColor,
SnackStyle snackStyle,
Curve forwardAnimationCurve,
Curve reverseAnimationCurve,
Duration animationDuration,
double barBlur,
double overlayBlur,
SnackbarStatusCallback snackbarStatus,
Color overlayColor,
Form userInputForm,
}) async {
final getBar = GetBar(
snackbarStatus: snackbarStatus,
titleText: (title == null)
? null
: titleText ??
Text(
title,
style: TextStyle(
color: colorText ?? iconColor ?? Colors.black,
fontWeight: FontWeight.w800,
fontSize: 16,
),
),
messageText: messageText ??
Text(
message,
style: TextStyle(
color: colorText ?? iconColor ?? Colors.black,
fontWeight: FontWeight.w300,
fontSize: 14,
),
),
snackPosition: snackPosition ?? SnackPosition.TOP,
borderRadius: borderRadius ?? 15,
margin: margin ?? EdgeInsets.symmetric(horizontal: 10),
duration: duration ?? Duration(seconds: 3),
barBlur: barBlur ?? 7.0,
backgroundColor: backgroundColor ?? Colors.grey.withOpacity(0.2),
icon: icon,
shouldIconPulse: shouldIconPulse ?? true,
maxWidth: maxWidth,
padding: padding ?? EdgeInsets.all(16),
borderColor: borderColor,
borderWidth: borderWidth,
leftBarIndicatorColor: leftBarIndicatorColor,
boxShadows: boxShadows,
backgroundGradient: backgroundGradient,
mainButton: mainButton,
onTap: onTap,
isDismissible: isDismissible ?? true,
dismissDirection: dismissDirection ?? SnackDismissDirection.VERTICAL,
showProgressIndicator: showProgressIndicator ?? false,
progressIndicatorController: progressIndicatorController,
progressIndicatorBackgroundColor: progressIndicatorBackgroundColor,
progressIndicatorValueColor: progressIndicatorValueColor,
snackStyle: snackStyle ?? SnackStyle.FLOATING,
forwardAnimationCurve: forwardAnimationCurve ?? Curves.easeOutCirc,
reverseAnimationCurve: reverseAnimationCurve ?? Curves.easeOutCirc,
animationDuration: animationDuration ?? Duration(seconds: 1),
overlayBlur: overlayBlur ?? 0.0,
overlayColor: overlayColor ?? Colors.transparent,
userInputForm: userInputForm);
/// **Navigation.pushAndRemoveUntil()** shortcut .<br><br>
///
/// Push a [page] and pop several pages in the stack
/// until [predicate] returns true. [predicate] is optional
///
/// It has the advantage of not needing context,
/// so you can call from your business logic
///
/// You can set a custom [transition], a [curve] and a transition [duration].
///
/// You can send any type of value to the other route in the [arguments].
///
/// Just like native routing in Flutter, you can push a route
/// as a [fullscreenDialog],
///
/// [predicate] can be used like this:
/// `Get.until((route) => Get.currentRoute == '/home')`so when you get to home page,
/// or also like
/// `Get.until((route) => !Get.isDialogOpen())`, to make sure the dialog
/// is closed
///
/// [id] is for when you are using nested navigation,
/// as explained in documentation
///
/// If you want the same behavior of ios that pops a route when the user drag,
/// you can set [popGesture] to true
///
/// If you're using the [Bindings] api, you must define it here
///
/// By default, GetX will prevent you from push a route that you already in,
/// if you want to push anyway, set [preventDuplicates] to false
Future<T> offAll<T>(
Widget page, {
RoutePredicate predicate,
bool opaque = false,
bool popGesture,
int id,
dynamic arguments,
Bindings binding,
bool fullscreenDialog = false,
Transition transition,
Curve curve,
Duration duration,
}) {
var routeName = "/${page.runtimeType.toString()}";
if (instantInit) {
showSnackbar(getBar);
} else {
routing.isSnackbar = true;
SchedulerBinding.instance.addPostFrameCallback((_) {
showSnackbar(getBar);
});
}
return global(id)?.currentState?.pushAndRemoveUntil(
GetPageRoute(
opaque: opaque ?? true,
popGesture: popGesture ?? defaultPopGesture,
page: () => page,
binding: binding,
settings: RouteSettings(arguments: arguments),
fullscreenDialog: fullscreenDialog,
routeName: routeName,
transition: transition ?? defaultTransition,
curve: curve ?? defaultTransitionCurve,
transitionDuration: duration ?? defaultTransitionDuration,
),
predicate ?? (route) => false);
}
void addPages(List<GetPage> getPages) {
... ... @@ -1024,7 +1033,36 @@ Since version 2.8 it is possible to access the properties
return _theme;
}
WidgetsBinding get engine => WidgetsBinding.instance;
///The current [WidgetsBinding]
WidgetsBinding get engine {
if (WidgetsBinding.instance == null) {
WidgetsFlutterBinding();
}
return WidgetsBinding.instance;
}
///The window to which this binding is bound.
Window get window => engine.window;
///The number of device pixels for each logical pixel.
double get pixelRatio => window.devicePixelRatio;
///The horizontal extent of this size.
double get width => window.physicalSize.width / pixelRatio;
///The vertical extent of this size
double get height => window.physicalSize.height / pixelRatio;
///The distance from the top edge to the first unpadded pixel,
///in physical pixels.
double get statusBarHeight => window.padding.top;
///The distance from the bottom edge to the first unpadded pixel,
///in physical pixels.
double get bottomBarHeight => window.padding.bottom;
///The system-reported text scale.
double get textScaleFactor => window.textScaleFactor;
/// give access to TextTheme.of(context)
TextTheme get textTheme => theme?.textTheme;
... ... @@ -1036,8 +1074,7 @@ Since version 2.8 it is possible to access the properties
bool get isDarkMode => (theme.brightness == Brightness.dark);
/// Check if dark mode theme is enable on platform on android Q+
bool get isPlatformDarkMode =>
(mediaQuery.platformBrightness == Brightness.dark);
bool get isPlatformDarkMode => (window.platformBrightness == Brightness.dark);
/// give access to Theme.of(context).iconTheme.color
Color get iconColor => theme?.iconTheme?.color;
... ... @@ -1045,11 +1082,11 @@ Since version 2.8 it is possible to access the properties
/// give access to FocusScope.of(context)
FocusNode get focusScope => FocusManager.instance.primaryFocus;
/// give access to Immutable MediaQuery.of(context).size.height
double get height => MediaQuery.of(context).size.height;
// /// give access to Immutable MediaQuery.of(context).size.height
// double get height => MediaQuery.of(context).size.height;
/// give access to Immutable MediaQuery.of(context).size.width
double get width => MediaQuery.of(context).size.width;
// /// give access to Immutable MediaQuery.of(context).size.width
// double get width => MediaQuery.of(context).size.width;
GlobalKey<NavigatorState> get key => getxController?.key;
... ...
import 'dart:async';
import 'dart:collection';
import 'package:flutter/foundation.dart';
import '../rx_core/rx_interface.dart';
part 'rx_num.dart';
... ... @@ -229,81 +227,6 @@ class Rx<T> extends _RxImpl<T> {
dynamic toJson() => (value as dynamic)?.toJson();
}
enum RxStatus { loading, error, success }
/// It's Experimental class, the Api can be change
abstract class RxState<T> extends _RxImpl<T> {
RxState(T initial) : super(initial) {
_fillEmptyStatus();
}
RxStatus _status;
bool get isNullOrEmpty {
if (_value == null) return true;
dynamic val = _value;
var result = false;
if (val is Iterable) {
result = val.isEmpty;
} else if (val is String) {
result = val.isEmpty;
} else if (val is Map) {
result = val.isEmpty;
}
return result;
}
void _fillEmptyStatus() {
_status = isNullOrEmpty ? RxStatus.loading : RxStatus.success;
}
RxStatus get status {
return _status;
}
bool get isLoading => _status == RxStatus.loading;
bool get hasError => _status == RxStatus.error;
bool get hasData => _status == RxStatus.success;
@protected
void refresh() {
subject.add(_value);
}
@protected
void update(void fn(T val)) {
fn(_value);
subject.add(_value);
}
@protected
T call([T v]) {
if (v != null) value = v;
return value;
}
@protected
set value(T val) {
if (_value == val && !firstRebuild) return;
firstRebuild = false;
_value = val;
subject.add(_value);
}
@protected
void change(T newState, {RxStatus status}) {
if (status != null) {
_status = status;
}
if (newState != _value) {
value = newState;
}
}
@override
dynamic toJson() => (value as dynamic)?.toJson();
}
extension StringExtension on String {
/// Returns a `RxString` with [this] `String` as initial value.
RxString get obs => RxString(this);
... ...
... ... @@ -2,6 +2,7 @@ library get_state_manager;
export 'src/rx_flutter/rx_disposable.dart';
export 'src/rx_flutter/rx_getx_widget.dart';
export 'src/rx_flutter/rx_notifier.dart';
export 'src/rx_flutter/rx_obx_widget.dart';
export 'src/rx_flutter/rx_ticket_provider_mixin.dart';
export 'src/simple/get_state.dart';
... ...
... ... @@ -9,23 +9,37 @@ import '../../../get_instance/src/lifecycle.dart';
/// it is Get.reset().
abstract class GetxService extends DisposableInterface with GetxServiceMixin {}
abstract class DisposableInterface extends GetLifeCycle {
abstract class DisposableInterface with GetLifeCycle {
bool _initialized = false;
/// Checks whether the controller has already been initialized.
bool get initialized => _initialized;
bool _isClosed = false;
/// Checks whether the controller has already been closed.
bool get isClosed => _isClosed;
DisposableInterface() {
onStart.callback = _onStart;
onDelete.callback = _onDelete;
}
// Internal callback that starts the cycle of this controller.
void _onStart() {
if (_initialized) return;
onInit();
_initialized = true;
SchedulerBinding.instance?.addPostFrameCallback((_) => onReady());
}
// Internal callback that starts the cycle of this controller.
void _onDelete() {
if (_isClosed) return;
_isClosed = true;
onClose();
}
/// Called immediately after the widget is allocated in memory.
/// You might use this to initialize something for the controller.
@override
... ...
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import '../../../instance_manager.dart';
import '../../get_state_manager.dart';
import '../simple/list_notifier.dart';
class Value<T> extends ListNotifier implements ValueListenable<T> {
Value(this._value);
T get value {
notifyChildrens();
return _value;
}
@override
String toString() => value.toString();
T _value;
set value(T newValue) {
if (_value == newValue) return;
_value = newValue;
updater();
}
void update(void fn(T value)) {
fn(value);
updater();
}
}
extension ReactiveT<T> on T {
Value<T> get reactive => Value<T>(this);
}
typedef Condition = bool Function();
abstract class GetNotifier<T> extends Value<T> with GetLifeCycle {
GetNotifier(T initial) : super(initial) {
onStart.callback = _onStart;
onDelete.callback = _onDelete;
_fillEmptyStatus();
}
bool _initialized = false;
/// Checks whether the controller has already been initialized.
bool get initialized => _initialized;
bool _isClosed = false;
/// Checks whether the controller has already been closed.
bool get isClosed => _isClosed;
// Internal callback that starts the cycle of this controller.
void _onStart() {
if (_initialized) return;
onInit();
_initialized = true;
SchedulerBinding.instance?.addPostFrameCallback((_) => onReady());
}
// Internal callback that starts the cycle of this controller.
void _onDelete() {
if (_isClosed) return;
_isClosed = true;
onClose();
}
RxStatus _status;
bool get isNullOrEmpty {
if (_value == null) return true;
dynamic val = _value;
var result = false;
if (val is Iterable) {
result = val.isEmpty;
} else if (val is String) {
result = val.isEmpty;
} else if (val is Map) {
result = val.isEmpty;
}
return result;
}
void _fillEmptyStatus() {
_status = isNullOrEmpty ? RxStatus.loading() : RxStatus.success();
}
RxStatus get status {
// notifyChildrens();
return _status;
}
Widget call(NotifierBuilder<T> widget, {Widget onError, Widget onLoading}) {
return SimpleBuilder(builder: (_) {
if (status.isLoading) {
return onLoading ?? CircularProgressIndicator();
} else if (status.isError) {
return onError ?? Text('A error occured');
} else {
if (widget == null) throw 'Widget cannot be null';
return widget(value);
}
});
}
@protected
void change(T newState, {RxStatus status}) {
if (status != null) {
_status = status;
}
if (newState != _value) {
value = newState;
}
}
dynamic toJson() => (value as dynamic)?.toJson();
}
class RxStatus {
final bool isLoading;
final bool isError;
final bool isSuccess;
final String errorMessage;
RxStatus._({
this.isLoading,
this.isError,
this.isSuccess,
this.errorMessage,
});
factory RxStatus.loading() {
return RxStatus._(
isLoading: true,
isError: false,
isSuccess: false,
);
}
factory RxStatus.success() {
return RxStatus._(
isLoading: false,
isError: false,
isSuccess: true,
);
}
factory RxStatus.error([String message]) {
return RxStatus._(
isLoading: false,
isError: true,
isSuccess: false,
errorMessage: message,
);
}
}
typedef NotifierBuilder<T> = Widget Function(T state);
... ...
... ... @@ -4,8 +4,6 @@ import '../../../get_core/get_core.dart';
import '../../../get_instance/src/get_instance.dart';
import '../../get_state_manager.dart';
import 'simple_builder.dart';
// Changed to VoidCallback.
//typedef Disposer = void Function();
... ... @@ -34,12 +32,12 @@ mixin GetStateUpdaterMixin<T extends StatefulWidget> on State<T> {
}
class GetxController extends DisposableInterface {
final _updaters = HashSet<GetStateUpdate>();
final _updaters = <GetStateUpdate>[];
// final _updatersIds = HashMap<String, StateSetter>(); //<old>
final _updatersIds = HashMap<String, GetStateUpdate>();
final _updatersGroupIds = HashMap<String, HashSet<GetStateUpdate>>();
final _updatersGroupIds = HashMap<String, List<GetStateUpdate>>();
/// Rebuilds [GetBuilder] each time you call [update()];
/// Can take a List of [ids], that will only update the matching
... ... @@ -81,7 +79,7 @@ class GetxController extends DisposableInterface {
VoidCallback addListenerId(String key, GetStateUpdate listener) {
// _printCurrentIds();
if (_updatersIds.containsKey(key)) {
_updatersGroupIds[key] ??= HashSet<GetStateUpdate>.identity();
_updatersGroupIds[key] ??= <GetStateUpdate>[];
_updatersGroupIds[key].add(listener);
return () {
_updatersGroupIds[key].remove(listener);
... ... @@ -254,22 +252,22 @@ class _GetBuilderState<T extends GetxController> extends State<GetBuilder<T>>
/// This is a experimental feature.
/// Meant to be used with SimpleBuilder, it auto-registers the variable
/// like Rx() does with Obx().
class Value<T> extends GetxController {
Value([this._value]);
T _value;
T get value {
TaskManager.instance.notify(_updaters);
return _value;
}
set value(T newValue) {
if (_value == newValue) return;
_value = newValue;
update();
}
}
// class Value<T> extends GetxController {
// Value([this._value]);
// T _value;
// T get value {
// TaskManager.instance.notify(_updaters);
// return _value;
// }
// set value(T newValue) {
// if (_value == newValue) return;
// _value = newValue;
// update();
// }
// }
/// It's Experimental class, the Api can be change
abstract class GetState<T> extends GetxController {
... ...
... ... @@ -40,7 +40,7 @@ abstract class GetView<T> extends StatelessWidget {
}
abstract class GetWidget<T extends DisposableInterface>
extends GetStatelessWidget {
extends StatelessWidget {
GetWidget({Key key}) : super(key: key);
final Set<T> _value = <T>{};
... ... @@ -103,7 +103,7 @@ class GetStatelessElement extends ComponentElement {
@override
void unmount() {
widget?.controller?.onClose();
widget?.controller?.onDelete();
super.unmount();
}
}
... ...
... ... @@ -146,3 +146,5 @@
// return widget.builder(controller.state);
// }
//}
... ...
import 'package:flutter/foundation.dart';
import 'simple_builder.dart';
class ListNotifier implements Listenable {
List<VoidCallback> _listeners = <VoidCallback>[];
void updater() {
assert(_debugAssertNotDisposed());
for (var element in _listeners) {
element();
}
}
bool _debugAssertNotDisposed() {
assert(() {
if (_listeners == null) {
throw FlutterError('''A $runtimeType was used after being disposed.\n
'Once you have called dispose() on a $runtimeType, it can no longer be used.''');
}
return true;
}());
return true;
}
@protected
void notifyChildrens() {
TaskManager.instance.notify(_listeners);
}
bool get hasListeners {
assert(_debugAssertNotDisposed());
return _listeners.isNotEmpty;
}
@override
void addListener(VoidCallback listener) {
assert(_debugAssertNotDisposed());
_listeners.add(listener);
}
@override
void removeListener(VoidCallback listener) {
assert(_debugAssertNotDisposed());
_listeners.remove(listener);
}
@mustCallSuper
void dispose() {
assert(_debugAssertNotDisposed());
_listeners = null;
}
}
... ...
import 'dart:async';
import 'dart:collection';
import 'package:flutter/widgets.dart';
import 'get_state.dart';
... ... @@ -88,7 +87,7 @@ class SimpleBuilder extends StatefulWidget {
class _SimpleBuilderState extends State<SimpleBuilder>
with GetStateUpdaterMixin {
final HashSet<VoidCallback> disposers = HashSet<VoidCallback>();
final disposers = <VoidCallback>[];
@override
void dispose() {
... ... @@ -116,13 +115,11 @@ class TaskManager {
static TaskManager get instance => _instance ??= TaskManager._();
// StateSetter _setter;//<old>
GetStateUpdate _setter;
HashSet<VoidCallback> _remove;
List<VoidCallback> _remove;
// void notify(HashSet<StateSetter> _updaters) { //<old>
void notify(HashSet<GetStateUpdate> _updaters) {
void notify(List<GetStateUpdate> _updaters) {
if (_setter != null) {
if (!_updaters.contains(_setter)) {
_updaters.add(_setter);
... ... @@ -132,8 +129,7 @@ class TaskManager {
}
Widget exchange(
HashSet<VoidCallback> disposers,
// StateSetter setState, //<old>
List<VoidCallback> disposers,
GetStateUpdate setState,
Widget Function(BuildContext) builder,
BuildContext context,
... ...
... ... @@ -9,20 +9,29 @@ class Mock {
}
}
class Controller {}
class DisposableController extends GetLifeCycle {
class DisposableController with GetLifeCycle {
DisposableController() {
onStart.callback = _onStart;
onDelete.callback = _onDelete;
}
// Internal callback that starts the cycle of this controller.
void _onStart() {
if (initialized) return;
onInit();
}
// Internal callback that starts the cycle of this controller.
void _onDelete() {
if (isClosed) return;
isClosed = true;
onClose();
}
bool initialized = false;
bool isClosed = false;
void onInit() async {
initialized = true;
}
... ... @@ -53,6 +62,17 @@ void main() {
Get.reset();
});
test('Get start and delete called just one time', () async {
Get..put(Controller())..put(Controller());
final controller = Get.find<Controller>();
expect(controller.init, 1);
Get..delete<Controller>()..delete<Controller>();
expect(controller.close, 1);
Get.reset();
});
test('Get.put tag test', () async {
final instance = Get.put<Controller>(Controller(), tag: 'one');
final instance2 = Get.put<Controller>(Controller(), tag: 'two');
... ... @@ -128,3 +148,19 @@ void main() {
});
});
}
class Controller extends DisposableController {
int init = 0;
int close = 0;
@override
void onInit() {
init++;
super.onInit();
}
@override
void onClose() {
close++;
super.onClose();
}
}
... ...