import 'dart:ui' as ui; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import '../../get_core/get_core.dart'; import '../../get_instance/src/bindings_interface.dart'; import '../../get_utils/get_utils.dart'; import '../get_navigation.dart'; import 'dialog/dialog_route.dart'; import 'root/parse_route.dart'; /// It replaces the Flutter Navigator, but needs no context. /// You can to use navigator.push(YourRoute()) rather /// Navigator.push(context, YourRoute()); NavigatorState? get navigator => GetNavigation(Get).key.currentState; extension ExtensionBottomSheet on GetInterface { Future bottomSheet( Widget bottomsheet, { Color? backgroundColor, double? elevation, bool persistent = true, ShapeBorder? shape, Clip? clipBehavior, Color? barrierColor, bool? ignoreSafeArea, bool isScrollControlled = false, bool useRootNavigator = false, bool isDismissible = true, bool enableDrag = true, RouteSettings? settings, Duration? enterBottomSheetDuration, Duration? exitBottomSheetDuration, }) { return Navigator.of(overlayContext!, rootNavigator: useRootNavigator) .push(GetModalBottomSheetRoute( builder: (_) => bottomsheet, isPersistent: persistent, // theme: Theme.of(key.currentContext, shadowThemeOnly: true), theme: Theme.of(key.currentContext!), isScrollControlled: isScrollControlled, barrierLabel: MaterialLocalizations.of(key.currentContext!) .modalBarrierDismissLabel, backgroundColor: backgroundColor ?? Colors.transparent, elevation: elevation, shape: shape, removeTop: ignoreSafeArea ?? true, clipBehavior: clipBehavior, isDismissible: isDismissible, modalBarrierColor: barrierColor, settings: settings, enableDrag: enableDrag, enterBottomSheetDuration: enterBottomSheetDuration ?? const Duration(milliseconds: 250), exitBottomSheetDuration: exitBottomSheetDuration ?? const Duration(milliseconds: 200), )); } } 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 dialog( Widget widget, { bool barrierDismissible = true, Color? barrierColor, bool useSafeArea = true, GlobalKey? navigatorKey, Object? arguments, Duration? transitionDuration, Curve? transitionCurve, String? name, RouteSettings? routeSettings, }) { assert(debugCheckHasMaterialLocalizations(context!)); // final theme = Theme.of(context, shadowThemeOnly: true); final theme = Theme.of(context!); return generalDialog( pageBuilder: (buildContext, animation, secondaryAnimation) { final pageChild = widget; Widget dialog = Builder(builder: (context) { return Theme(data: theme, child: 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, ); }, navigatorKey: navigatorKey, routeSettings: routeSettings ?? RouteSettings(arguments: arguments, name: name), ); } /// Api from showGeneralDialog with no context Future generalDialog({ required RoutePageBuilder pageBuilder, bool barrierDismissible = false, String? barrierLabel, Color barrierColor = const Color(0x80000000), Duration transitionDuration = const Duration(milliseconds: 200), RouteTransitionsBuilder? transitionBuilder, GlobalKey? navigatorKey, RouteSettings? routeSettings, }) { assert(!barrierDismissible || barrierLabel != null); final nav = navigatorKey?.currentState ?? Navigator.of(overlayContext!, rootNavigator: true); //overlay context will always return the root navigator return nav.push( GetDialogRoute( pageBuilder: pageBuilder, barrierDismissible: barrierDismissible, barrierLabel: barrierLabel, barrierColor: barrierColor, transitionDuration: transitionDuration, transitionBuilder: transitionBuilder, settings: routeSettings, ), ); } /// Custom UI Dialog. Future defaultDialog({ String title = "Alert", EdgeInsetsGeometry? titlePadding, TextStyle? titleStyle, Widget? content, EdgeInsetsGeometry? contentPadding, VoidCallback? onConfirm, VoidCallback? onCancel, VoidCallback? onCustom, Color? cancelTextColor, Color? confirmTextColor, String? textConfirm, String? textCancel, String? textCustom, Widget? confirm, Widget? cancel, Widget? custom, Color? backgroundColor, bool barrierDismissible = true, Color? buttonColor, String middleText = "Dialog made in 3 lines of code", TextStyle? middleTextStyle, double radius = 20.0, // ThemeData themeData, List? actions, // onWillPop Scope WillPopCallback? onWillPop, // the navigator used to push the dialog GlobalKey? navigatorKey, }) { var leanCancel = onCancel != null || textCancel != null; var leanConfirm = onConfirm != null || textConfirm != null; actions ??= []; if (cancel != null) { actions.add(cancel); } else { if (leanCancel) { actions.add(TextButton( style: TextButton.styleFrom( tapTargetSize: MaterialTapTargetSize.shrinkWrap, padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), shape: RoundedRectangleBorder( side: BorderSide( color: buttonColor ?? theme.colorScheme.secondary, width: 2, style: BorderStyle.solid), borderRadius: BorderRadius.circular(100)), ), onPressed: () { onCancel?.call(); back(); }, child: Text( textCancel ?? "Cancel", style: TextStyle( color: cancelTextColor ?? theme.colorScheme.secondary), ), )); } } if (confirm != null) { actions.add(confirm); } else { if (leanConfirm) { actions.add(TextButton( style: TextButton.styleFrom( tapTargetSize: MaterialTapTargetSize.shrinkWrap, backgroundColor: buttonColor ?? theme.colorScheme.secondary, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(100)), ), child: Text( textConfirm ?? "Ok", style: TextStyle( color: confirmTextColor ?? theme.colorScheme.onPrimary), ), onPressed: () { onConfirm?.call(); })); } } Widget baseAlertDialog = AlertDialog( titlePadding: titlePadding ?? const EdgeInsets.all(8), contentPadding: contentPadding ?? const EdgeInsets.all(8), backgroundColor: backgroundColor ?? theme.dialogBackgroundColor, shape: RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(radius))), title: Text(title, textAlign: TextAlign.center, style: titleStyle), content: Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ content ?? Text(middleText, textAlign: TextAlign.center, style: middleTextStyle), const SizedBox(height: 16), ButtonTheme( minWidth: 78.0, height: 34.0, child: Wrap( alignment: WrapAlignment.center, spacing: 8, runSpacing: 8, children: actions, ), ) ], ), // actions: actions, // ?? [cancelButton, confirmButton], buttonPadding: EdgeInsets.zero, ); return dialog( onWillPop != null ? WillPopScope( onWillPop: onWillPop, child: baseAlertDialog, ) : baseAlertDialog, barrierDismissible: barrierDismissible, navigatorKey: navigatorKey, ); } } extension ExtensionSnackbar on GetInterface { SnackbarController 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? boxShadows, Gradient? backgroundGradient, Widget? mainButton, OnTap? onTap, Duration? duration = const Duration(seconds: 3), bool isDismissible = true, DismissDirection? dismissDirection, bool showProgressIndicator = false, AnimationController? progressIndicatorController, Color? progressIndicatorBackgroundColor, Animation? 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, }) { final getSnackBar = GetSnackBar( 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, progressIndicatorController: progressIndicatorController, progressIndicatorBackgroundColor: progressIndicatorBackgroundColor, progressIndicatorValueColor: progressIndicatorValueColor, snackStyle: snackStyle, forwardAnimationCurve: forwardAnimationCurve, reverseAnimationCurve: reverseAnimationCurve, animationDuration: animationDuration, overlayBlur: overlayBlur, overlayColor: overlayColor, userInputForm: userInputForm, ); final controller = SnackbarController(getSnackBar); if (instantInit) { controller.show(); } else { ambiguate(SchedulerBinding.instance)?.addPostFrameCallback((_) { controller.show(); }); } return controller; } SnackbarController showSnackbar(GetSnackBar snackbar) { final controller = SnackbarController(snackbar); controller.show(); return controller; } SnackbarController snackbar( String title, String message, { Color? colorText, Duration? duration = const Duration(seconds: 3), /// 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? boxShadows, Gradient? backgroundGradient, TextButton? mainButton, OnTap? onTap, bool? isDismissible, bool? showProgressIndicator, DismissDirection? dismissDirection, AnimationController? progressIndicatorController, Color? progressIndicatorBackgroundColor, Animation? progressIndicatorValueColor, SnackStyle? snackStyle, Curve? forwardAnimationCurve, Curve? reverseAnimationCurve, Duration? animationDuration, double? barBlur, double? overlayBlur, SnackbarStatusCallback? snackbarStatus, Color? overlayColor, Form? userInputForm, }) { final getSnackBar = GetSnackBar( snackbarStatus: snackbarStatus, titleText: 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 ?? const EdgeInsets.symmetric(horizontal: 10), duration: duration, barBlur: barBlur ?? 7.0, backgroundColor: backgroundColor ?? Colors.grey.withOpacity(0.2), icon: icon, shouldIconPulse: shouldIconPulse ?? true, maxWidth: maxWidth, padding: padding ?? const EdgeInsets.all(16), borderColor: borderColor, borderWidth: borderWidth, leftBarIndicatorColor: leftBarIndicatorColor, boxShadows: boxShadows, backgroundGradient: backgroundGradient, mainButton: mainButton, onTap: onTap, isDismissible: isDismissible ?? true, dismissDirection: dismissDirection, showProgressIndicator: showProgressIndicator ?? false, progressIndicatorController: progressIndicatorController, progressIndicatorBackgroundColor: progressIndicatorBackgroundColor, progressIndicatorValueColor: progressIndicatorValueColor, snackStyle: snackStyle ?? SnackStyle.FLOATING, forwardAnimationCurve: forwardAnimationCurve ?? Curves.easeOutCirc, reverseAnimationCurve: reverseAnimationCurve ?? Curves.easeOutCirc, animationDuration: animationDuration ?? const Duration(seconds: 1), overlayBlur: overlayBlur ?? 0.0, overlayColor: overlayColor ?? Colors.transparent, userInputForm: userInputForm); final controller = SnackbarController(getSnackBar); if (instantInit) { controller.show(); } else { //routing.isSnackbar = true; ambiguate(SchedulerBinding.instance)?.addPostFrameCallback((_) { controller.show(); }); } return controller; } } extension GetNavigation on GetInterface { /// **Navigation.push()** shortcut.

/// /// 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? to( dynamic page, { bool? opaque, Transition? transition, Curve? curve, Duration? duration, int? id, String? routeName, bool fullscreenDialog = false, dynamic arguments, Bindings? binding, bool preventDuplicates = true, bool? popGesture, double Function(BuildContext context)? gestureWidth, }) { // var routeName = "/${page.runtimeType}"; routeName ??= "/${page.runtimeType}"; routeName = _cleanRouteName(routeName); if (preventDuplicates && routeName == currentRoute) { return null; } return global(id).currentState?.push( GetPageRoute( opaque: opaque ?? true, page: _resolvePage(page, 'to'), routeName: routeName, gestureWidth: gestureWidth, settings: RouteSettings( name: routeName, arguments: arguments, ), popGesture: popGesture ?? defaultPopGesture, transition: transition ?? defaultTransition, curve: curve ?? defaultTransitionCurve, fullscreenDialog: fullscreenDialog, binding: binding, transitionDuration: duration ?? defaultTransitionDuration, ), ); } GetPageBuilder _resolvePage(dynamic page, String method) { if (page is GetPageBuilder) { return page; } else if (page is Widget) { Get.log( '''WARNING, consider using: "Get.$method(() => Page())" instead of "Get.$method(Page())". Using a widget function instead of a widget fully guarantees that the widget and its controllers will be removed from memory when they are no longer used. '''); return () => page; } else if (page is String) { throw '''Unexpected String, use toNamed() instead'''; } else { throw '''Unexpected format, you can only use widgets and widget functions here'''; } } /// **Navigation.pushNamed()** shortcut.

/// /// 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? toNamed( String page, { dynamic arguments, int? id, bool preventDuplicates = true, Map? parameters, }) { if (preventDuplicates && page == currentRoute) { return null; } if (parameters != null) { final uri = Uri(path: page, queryParameters: parameters); page = uri.toString(); } return global(id).currentState?.pushNamed( page, arguments: arguments, ); } /// **Navigation.pushReplacementNamed()** shortcut.

/// /// 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? offNamed( String page, { dynamic arguments, int? id, bool preventDuplicates = true, Map? parameters, }) { if (preventDuplicates && page == currentRoute) { return null; } if (parameters != null) { final uri = Uri(path: page, queryParameters: parameters); page = uri.toString(); } return global(id).currentState?.pushReplacementNamed( page, arguments: arguments, ); } /// **Navigation.popUntil()** shortcut.

/// /// 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.

/// /// 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(GetPageRoute(page: () => HomePage()), predicate) /// /// [predicate] can be used like this: /// `Get.offUntil(page, (route) => (route as GetPageRoute).routeName == '/home')` /// to pop routes in stack until home, /// or also like this: /// `Get.until((route) => !Get.isDialogOpen())`, to make sure the dialog /// is closed Future? offUntil(Route 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.

/// /// 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.offNamedUntil(page, ModalRoute.withName('/home'))` /// to pop routes in stack until home, /// or like this: /// `Get.offNamedUntil((route) => !Get.isDialogOpen())`, /// to make sure the dialog is closed /// /// Note: Always put a slash on the route name ('/page1'), to avoid unexpected errors Future? offNamedUntil( String page, RoutePredicate predicate, { int? id, dynamic arguments, Map? parameters, }) { if (parameters != null) { final uri = Uri(path: page, queryParameters: parameters); page = uri.toString(); } return global(id).currentState?.pushNamedAndRemoveUntil( page, predicate, arguments: arguments, ); } /// **Navigation.popAndPushNamed()** shortcut.

/// /// 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? offAndToNamed( String page, { dynamic arguments, int? id, dynamic result, Map? parameters, }) { if (parameters != null) { final uri = Uri(path: page, queryParameters: parameters); page = uri.toString(); } return global(id).currentState?.popAndPushNamed( page, arguments: arguments, result: result, ); } /// **Navigation.removeRoute()** shortcut.

/// /// Remove a specific [route] from the stack /// /// [id] is for when you are using nested navigation, /// as explained in documentation void removeRoute(Route route, {int? id}) { return global(id).currentState?.removeRoute(route); } /// **Navigation.pushNamedAndRemoveUntil()** shortcut.

/// /// 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? offAllNamed( String newRouteName, { RoutePredicate? predicate, dynamic arguments, int? id, Map? parameters, }) { if (parameters != null) { final uri = Uri(path: newRouteName, queryParameters: parameters); newRouteName = uri.toString(); } 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.

/// /// 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({ T? result, bool closeOverlays = false, bool canPop = true, int? id, }) { //TODO: This code brings compatibility of the new snackbar with GetX 4, // remove this code in version 5 if (isSnackbarOpen && !closeOverlays) { closeCurrentSnackbar(); return; } if (closeOverlays && isOverlaysOpen) { //TODO: This code brings compatibility of the new snackbar with GetX 4, // remove this code in version 5 if (isSnackbarOpen) { closeAllSnackbars(); } navigator?.popUntil((route) { return (!isDialogOpen! && !isBottomSheetOpen!); }); } if (canPop) { if (global(id).currentState?.canPop() == true) { global(id).currentState?.pop(result); } } else { global(id).currentState?.pop(result); } } /// **Navigation.popUntil()** (with predicate) shortcut .

/// /// 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 < 1) { times = 1; } var count = 0; var back = global(id).currentState?.popUntil((route) => count++ == times); return back; } /// **Navigation.pushReplacement()** shortcut .

/// /// 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? off( dynamic page, { bool opaque = false, Transition? transition, Curve? curve, bool? popGesture, int? id, String? routeName, dynamic arguments, Bindings? binding, bool fullscreenDialog = false, bool preventDuplicates = true, Duration? duration, double Function(BuildContext context)? gestureWidth, }) { routeName ??= "/${page.runtimeType.toString()}"; routeName = _cleanRouteName(routeName); if (preventDuplicates && routeName == currentRoute) { return null; } return global(id).currentState?.pushReplacement(GetPageRoute( opaque: opaque, gestureWidth: gestureWidth, page: _resolvePage(page, 'off'), binding: binding, settings: RouteSettings( arguments: arguments, name: routeName, ), routeName: routeName, fullscreenDialog: fullscreenDialog, popGesture: popGesture ?? defaultPopGesture, transition: transition ?? defaultTransition, curve: curve ?? defaultTransitionCurve, transitionDuration: duration ?? defaultTransitionDuration)); } /// /// 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? offAll( dynamic page, { RoutePredicate? predicate, bool opaque = false, bool? popGesture, int? id, String? routeName, dynamic arguments, Bindings? binding, bool fullscreenDialog = false, Transition? transition, Curve? curve, Duration? duration, double Function(BuildContext context)? gestureWidth, }) { routeName ??= "/${page.runtimeType.toString()}"; routeName = _cleanRouteName(routeName); return global(id).currentState?.pushAndRemoveUntil( GetPageRoute( opaque: opaque, popGesture: popGesture ?? defaultPopGesture, page: _resolvePage(page, 'offAll'), binding: binding, gestureWidth: gestureWidth, settings: RouteSettings( name: routeName, arguments: arguments, ), fullscreenDialog: fullscreenDialog, routeName: routeName, transition: transition ?? defaultTransition, curve: curve ?? defaultTransitionCurve, transitionDuration: duration ?? defaultTransitionDuration, ), predicate ?? (route) => false); } /// Takes a route [name] String generated by [to], [off], [offAll] /// (and similar context navigation methods), cleans the extra chars and /// accommodates the format. /// TODO: check for a more "appealing" URL naming convention. /// `() => MyHomeScreenView` becomes `/my-home-screen-view`. String _cleanRouteName(String name) { name = name.replaceAll('() => ', ''); /// uncommonent for URL styling. // name = name.paramCase!; if (!name.startsWith('/')) { name = '/$name'; } return Uri.tryParse(name)?.toString() ?? name; } /// change default config of Get void config( {bool? enableLog, LogWriterCallback? logWriterCallback, bool? defaultPopGesture, bool? defaultOpaqueRoute, Duration? defaultDurationTransition, bool? defaultGlobalState, Transition? defaultTransition}) { if (enableLog != null) { Get.isLogEnable = enableLog; } if (logWriterCallback != null) { Get.log = logWriterCallback; } if (defaultPopGesture != null) { _getxController.defaultPopGesture = defaultPopGesture; } if (defaultOpaqueRoute != null) { _getxController.defaultOpaqueRoute = defaultOpaqueRoute; } if (defaultTransition != null) { _getxController.defaultTransition = defaultTransition; } if (defaultDurationTransition != null) { _getxController.defaultTransitionDuration = defaultDurationTransition; } } Future updateLocale(Locale l) async { Get.locale = l; await forceAppUpdate(); } /// As a rule, Flutter knows which widget to update, /// so this command is rarely needed. We can mention situations /// where you use const so that widgets are not updated with setState, /// but you want it to be forcefully updated when an event like /// language change happens. using context to make the widget dirty /// for performRebuild() is a viable solution. /// However, in situations where this is not possible, or at least, /// is not desired by the developer, the only solution for updating /// widgets that Flutter does not want to update is to use reassemble /// to forcibly rebuild all widgets. Attention: calling this function will /// reconstruct the application from the sketch, use this with caution. /// Your entire application will be rebuilt, and touch events will not /// work until the end of rendering. Future forceAppUpdate() async { await engine.performReassemble(); } void appUpdate() => _getxController.update(); void changeTheme(ThemeData theme) { _getxController.setTheme(theme); } void changeThemeMode(ThemeMode themeMode) { _getxController.setThemeMode(themeMode); } GlobalKey? addKey(GlobalKey newKey) { return _getxController.addKey(newKey); } GlobalKey? nestedKey(dynamic key) { keys.putIfAbsent( key, () => GlobalKey( debugLabel: 'Getx nested key: ${key.toString()}', ), ); return keys[key]; } GlobalKey global(int? k) { GlobalKey newKey; if (k == null) { newKey = key; } else { if (!keys.containsKey(k)) { throw 'Route id ($k) not found'; } newKey = keys[k]!; } if (newKey.currentContext == null && !testMode) { throw """You are trying to use contextless navigation without a GetMaterialApp or Get.key. If you are testing your app, you can use: [Get.testMode = true], or if you are running your app on a physical device or emulator, you must exchange your [MaterialApp] for a [GetMaterialApp]. """; } return newKey; } /// give current arguments dynamic get arguments => routing.args; /// give name from current route String get currentRoute => routing.current; /// give name from previous route String get previousRoute => routing.previous; /// check if snackbar is open bool get isSnackbarOpen => SnackbarController.isSnackbarBeingShown; //routing.isSnackbar; void closeAllSnackbars() { SnackbarController.cancelAllSnackbars(); } Future closeCurrentSnackbar() async { await SnackbarController.closeCurrentSnackbar(); } /// check if dialog is open bool? get isDialogOpen => routing.isDialog; /// check if bottomsheet is open bool? get isBottomSheetOpen => routing.isBottomSheet; /// check a raw current route Route? get rawRoute => routing.route; /// check if popGesture is enable bool get isPopGestureEnable => defaultPopGesture; /// check if default opaque route is enable bool get isOpaqueRouteDefault => defaultOpaqueRoute; /// give access to currentContext BuildContext? get context => key.currentContext; /// give access to current Overlay Context BuildContext? get overlayContext { BuildContext? overlay; key.currentState?.overlay?.context.visitChildElements((element) { overlay = element; }); return overlay; } /// give access to Theme.of(context) ThemeData get theme { var theme = ThemeData.fallback(); if (context != null) { theme = Theme.of(context!); } return theme; } ///The current [WidgetsBinding] WidgetsBinding get engine { return WidgetsFlutterBinding.ensureInitialized(); } /// The window to which this binding is bound. ui.SingletonFlutterWindow get window => ui.window; Locale? get deviceLocale => ui.window.locale; ///The number of device pixels for each logical pixel. double get pixelRatio => ui.window.devicePixelRatio; Size get size => ui.window.physicalSize / pixelRatio; ///The horizontal extent of this size. double get width => size.width; ///The vertical extent of this size double get height => size.height; ///The distance from the top edge to the first unpadded pixel, ///in physical pixels. double get statusBarHeight => ui.window.padding.top; ///The distance from the bottom edge to the first unpadded pixel, ///in physical pixels. double get bottomBarHeight => ui.window.padding.bottom; ///The system-reported text scale. double get textScaleFactor => ui.window.textScaleFactor; /// give access to TextTheme.of(context) TextTheme get textTheme => theme.textTheme; /// give access to Mediaquery.of(context) MediaQueryData get mediaQuery => MediaQuery.of(context!); /// Check if dark mode theme is enable bool get isDarkMode => (theme.brightness == Brightness.dark); /// Check if dark mode theme is enable on platform on android Q+ bool get isPlatformDarkMode => (ui.window.platformBrightness == Brightness.dark); /// give access to Theme.of(context).iconTheme.color Color? get iconColor => theme.iconTheme.color; /// 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.width // double get width => MediaQuery.of(context).size.width; GlobalKey get key => _getxController.key; Map> get keys => _getxController.keys; GetMaterialController get rootController => _getxController; bool get defaultPopGesture => _getxController.defaultPopGesture; bool get defaultOpaqueRoute => _getxController.defaultOpaqueRoute; Transition? get defaultTransition => _getxController.defaultTransition; Duration get defaultTransitionDuration { return _getxController.defaultTransitionDuration; } Curve get defaultTransitionCurve => _getxController.defaultTransitionCurve; Curve get defaultDialogTransitionCurve { return _getxController.defaultDialogTransitionCurve; } Duration get defaultDialogTransitionDuration { return _getxController.defaultDialogTransitionDuration; } Routing get routing => _getxController.routing; Map get parameters => _getxController.parameters; set parameters(Map newParameters) => _getxController.parameters = newParameters; CustomTransition? get customTransition => _getxController.customTransition; set customTransition(CustomTransition? newTransition) => _getxController.customTransition = newTransition; bool get testMode => _getxController.testMode; set testMode(bool isTest) => _getxController.testMode = isTest; void resetRootNavigator() { _getxController = GetMaterialController(); } static GetMaterialController _getxController = GetMaterialController(); } extension NavTwoExt on GetInterface { void addPages(List getPages) { routeTree.addRoutes(getPages); } void clearRouteTree() { _routeTree.routes.clear(); } static final _routeTree = ParseRouteTree(routes: []); ParseRouteTree get routeTree => _routeTree; void addPage(GetPage getPage) { routeTree.addRoute(getPage); } /// Casts the stored router delegate to a desired type TDelegate? delegate, TPage>() => routerDelegate as TDelegate?; // // ignore: use_setters_to_change_properties // void setDefaultDelegate(RouterDelegate? delegate) { // _routerDelegate = delegate; // } // GetDelegate? getDelegate() => delegate(); GetInformationParser createInformationParser({String initialRoute = '/'}) { if (routeInformationParser == null) { return routeInformationParser = GetInformationParser( initialRoute: initialRoute, ); } else { return routeInformationParser as GetInformationParser; } } // static GetDelegate? _delegate; GetDelegate get rootDelegate => createDelegate(); GetDelegate createDelegate({ GetPage? notFoundRoute, List? navigatorObservers, TransitionDelegate? transitionDelegate, PopMode backButtonPopMode = PopMode.History, PreventDuplicateHandlingMode preventDuplicateHandlingMode = PreventDuplicateHandlingMode.ReorderRoutes, }) { if (routerDelegate == null) { return routerDelegate = GetDelegate( notFoundRoute: notFoundRoute, navigatorObservers: navigatorObservers, transitionDelegate: transitionDelegate, backButtonPopMode: backButtonPopMode, preventDuplicateHandlingMode: preventDuplicateHandlingMode, ); } else { return routerDelegate as GetDelegate; } } } extension OverlayExt on GetInterface { Future showOverlay({ required Future Function() asyncFunction, Color opacityColor = Colors.black, Widget? loadingWidget, double opacity = .5, }) async { final navigatorState = Navigator.of(Get.overlayContext!, rootNavigator: false); final overlayState = navigatorState.overlay!; final overlayEntryOpacity = OverlayEntry(builder: (context) { return Opacity( opacity: opacity, child: Container( color: opacityColor, )); }); final overlayEntryLoader = OverlayEntry(builder: (context) { return loadingWidget ?? const Center( child: SizedBox( height: 90, width: 90, child: Text('Loading...'), )); }); overlayState.insert(overlayEntryOpacity); overlayState.insert(overlayEntryLoader); T data; try { data = await asyncFunction(); } on Exception catch (_) { overlayEntryLoader.remove(); overlayEntryOpacity.remove(); rethrow; } overlayEntryLoader.remove(); overlayEntryOpacity.remove(); return data; } }