Showing
16 changed files
with
240 additions
and
180 deletions
| 1 | library get; | 1 | library get; |
| 2 | 2 | ||
| 3 | -export 'src/routes/default_route.dart'; | ||
| 4 | -export 'src/get_main.dart'; | ||
| 5 | -export 'src/snackbar/snack.dart'; | ||
| 6 | -export 'src/bottomsheet/bottomsheet.dart'; | ||
| 7 | -export 'src/snackbar/snack_route.dart'; | ||
| 8 | -export 'src/state/get_state.dart'; | ||
| 9 | -export 'src/state/get_view.dart'; | ||
| 10 | -export 'src/regex/get_utils.dart'; | ||
| 11 | -export 'src/queue/get_queue.dart'; | ||
| 12 | -export 'src/state/mixin_state.dart'; | ||
| 13 | -export 'src/rx/rx_interface.dart'; | ||
| 14 | -export 'src/rx/rx_impl.dart'; | ||
| 15 | -export 'src/rx/rx_event.dart'; | ||
| 16 | -export 'src/rx/rx_obx.dart'; | ||
| 17 | -export 'src/rx/rx_getbuilder.dart'; | ||
| 18 | -export 'src/root/root_widget.dart'; | ||
| 19 | -export 'src/root/smart_management.dart'; | ||
| 20 | -export 'src/routes/default_route.dart'; | ||
| 21 | -export 'src/routes/get_route.dart'; | ||
| 22 | -export 'src/routes/bindings_interface.dart'; | ||
| 23 | -export 'src/routes/observers/route_observer.dart'; | ||
| 24 | -export 'src/routes/transitions_type.dart'; | ||
| 25 | -export 'src/platform/platform.dart'; | ||
| 26 | -export 'src/instance/extension_instance.dart'; | ||
| 27 | -export 'src/instance/get_instance.dart'; | ||
| 28 | -export 'src/typedefs/typedefs.dart'; | ||
| 29 | -export 'src/routes/custom_transition.dart'; | ||
| 30 | -export 'src/context_extensions/extensions.dart'; | 3 | +export 'instance_manager.dart'; |
| 4 | +export 'route_manager.dart'; | ||
| 5 | +export 'state_manager.dart'; | ||
| 6 | +export 'utils.dart'; |
lib/instance_manager.dart
0 → 100644
lib/route_manager.dart
0 → 100644
| 1 | +export 'src/routes/custom_transition.dart'; | ||
| 2 | +export 'src/routes/transitions_type.dart'; | ||
| 3 | +export 'src/routes/get_route.dart'; | ||
| 4 | +export 'src/routes/default_route.dart'; | ||
| 5 | +export 'src/routes/observers/route_observer.dart'; | ||
| 6 | +export 'src/root/root_widget.dart'; | ||
| 7 | +export 'src/snackbar/snack_route.dart'; | ||
| 8 | +export 'src/bottomsheet/bottomsheet.dart'; | ||
| 9 | +export 'src/snackbar/snack.dart'; | ||
| 10 | +export 'src/get_main.dart'; | ||
| 11 | +export 'src/routes/default_route.dart'; | ||
| 12 | +export 'src/root/smart_management.dart'; |
| @@ -27,6 +27,7 @@ class GetImpl implements GetService { | @@ -27,6 +27,7 @@ class GetImpl implements GetService { | ||
| 27 | Duration defaultDurationTransition = Duration(milliseconds: 400); | 27 | Duration defaultDurationTransition = Duration(milliseconds: 400); |
| 28 | bool defaultGlobalState = true; | 28 | bool defaultGlobalState = true; |
| 29 | RouteSettings settings; | 29 | RouteSettings settings; |
| 30 | + String defaultSeparator = "_"; | ||
| 30 | 31 | ||
| 31 | ///Use to instead of Navigator.push, off instead of Navigator.pushReplacement, | 32 | ///Use to instead of Navigator.push, off instead of Navigator.pushReplacement, |
| 32 | ///offAll instead of Navigator.pushAndRemoveUntil. For named routes just add "named" | 33 | ///offAll instead of Navigator.pushAndRemoveUntil. For named routes just add "named" |
| 1 | -import 'dart:collection'; | ||
| 2 | - | ||
| 3 | import 'package:flutter/widgets.dart'; | 1 | import 'package:flutter/widgets.dart'; |
| 4 | import 'package:get/src/routes/get_route.dart'; | 2 | import 'package:get/src/routes/get_route.dart'; |
| 5 | 3 | ||
| @@ -22,7 +20,7 @@ class ParseRouteTree { | @@ -22,7 +20,7 @@ class ParseRouteTree { | ||
| 22 | // throw ("Default route was already defined"); | 20 | // throw ("Default route was already defined"); |
| 23 | // } | 21 | // } |
| 24 | var node = ParseRouteTreeNode(path, ParseRouteTreeNodeType.component); | 22 | var node = ParseRouteTreeNode(path, ParseRouteTreeNodeType.component); |
| 25 | - node.routes = HashSet<GetPage>()..add(route); | 23 | + node.routes = [route]; |
| 26 | _nodes.add(node); | 24 | _nodes.add(node); |
| 27 | // _hasDefaultRoute = true; | 25 | // _hasDefaultRoute = true; |
| 28 | return; | 26 | return; |
| @@ -47,7 +45,7 @@ class ParseRouteTree { | @@ -47,7 +45,7 @@ class ParseRouteTree { | ||
| 47 | } | 45 | } |
| 48 | if (i == pathComponents.length - 1) { | 46 | if (i == pathComponents.length - 1) { |
| 49 | if (node.routes == null) { | 47 | if (node.routes == null) { |
| 50 | - node.routes = HashSet<GetPage>()..add(route); | 48 | + node.routes = [route]; |
| 51 | } else { | 49 | } else { |
| 52 | node.routes.add(route); | 50 | node.routes.add(route); |
| 53 | } | 51 | } |
| @@ -133,8 +131,8 @@ class ParseRouteTree { | @@ -133,8 +131,8 @@ class ParseRouteTree { | ||
| 133 | if (nodeToUse != null && | 131 | if (nodeToUse != null && |
| 134 | nodeToUse.routes != null && | 132 | nodeToUse.routes != null && |
| 135 | nodeToUse.routes.length > 0) { | 133 | nodeToUse.routes.length > 0) { |
| 136 | - HashSet<GetPage> routes = nodeToUse.routes; | ||
| 137 | - GetPageMatch routeMatch = GetPageMatch(routes.first); | 134 | + List<GetPage> routes = nodeToUse.routes; |
| 135 | + GetPageMatch routeMatch = GetPageMatch(routes[0]); | ||
| 138 | 136 | ||
| 139 | routeMatch.parameters = match.parameters; | 137 | routeMatch.parameters = match.parameters; |
| 140 | 138 | ||
| @@ -203,7 +201,7 @@ class ParseRouteTreeNode { | @@ -203,7 +201,7 @@ class ParseRouteTreeNode { | ||
| 203 | 201 | ||
| 204 | String part; | 202 | String part; |
| 205 | ParseRouteTreeNodeType type; | 203 | ParseRouteTreeNodeType type; |
| 206 | - HashSet<GetPage> routes = HashSet<GetPage>(); | 204 | + List<GetPage> routes = <GetPage>[]; |
| 207 | List<ParseRouteTreeNode> nodes = <ParseRouteTreeNode>[]; | 205 | List<ParseRouteTreeNode> nodes = <ParseRouteTreeNode>[]; |
| 208 | ParseRouteTreeNode parent; | 206 | ParseRouteTreeNode parent; |
| 209 | 207 |
| 1 | import 'dart:math'; | 1 | import 'dart:math'; |
| 2 | import 'dart:ui' show lerpDouble; | 2 | import 'dart:ui' show lerpDouble; |
| 3 | +import 'package:flutter/cupertino.dart'; | ||
| 3 | import 'package:flutter/gestures.dart'; | 4 | import 'package:flutter/gestures.dart'; |
| 4 | import 'package:flutter/material.dart'; | 5 | import 'package:flutter/material.dart'; |
| 5 | import 'package:get/src/get_main.dart'; | 6 | import 'package:get/src/get_main.dart'; |
| @@ -27,7 +28,7 @@ class GetPageRoute<T> extends PageRouteBuilder<T> { | @@ -27,7 +28,7 @@ class GetPageRoute<T> extends PageRouteBuilder<T> { | ||
| 27 | // this.transitionComponent, | 28 | // this.transitionComponent, |
| 28 | RouteSettings settings, | 29 | RouteSettings settings, |
| 29 | this.duration, | 30 | this.duration, |
| 30 | - this.transition = Transition.native, | 31 | + this.transition, |
| 31 | this.binding, | 32 | this.binding, |
| 32 | @required this.page, | 33 | @required this.page, |
| 33 | this.bindings, | 34 | this.bindings, |
| @@ -60,9 +61,33 @@ class GetPageRoute<T> extends PageRouteBuilder<T> { | @@ -60,9 +61,33 @@ class GetPageRoute<T> extends PageRouteBuilder<T> { | ||
| 60 | @override | 61 | @override |
| 61 | final bool fullscreenDialog; | 62 | final bool fullscreenDialog; |
| 62 | 63 | ||
| 64 | + static bool isPopGestureInProgress(PageRoute<dynamic> route) { | ||
| 65 | + return route.navigator.userGestureInProgress; | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + bool get popGestureInProgress => isPopGestureInProgress(this); | ||
| 69 | + | ||
| 70 | + @override | ||
| 71 | + bool canTransitionTo(TransitionRoute<dynamic> nextRoute) { | ||
| 72 | + // Don't perform outgoing animation if the next route is a fullscreen dialog. | ||
| 73 | + return nextRoute is GetPageRoute && !nextRoute.fullscreenDialog; | ||
| 74 | + } | ||
| 75 | + | ||
| 63 | @override | 76 | @override |
| 64 | Widget buildTransitions(BuildContext context, Animation<double> animation, | 77 | Widget buildTransitions(BuildContext context, Animation<double> animation, |
| 65 | Animation<double> secondaryAnimation, Widget child) { | 78 | Animation<double> secondaryAnimation, Widget child) { |
| 79 | + if (fullscreenDialog != null && | ||
| 80 | + transition == null && | ||
| 81 | + customTransition == null) { | ||
| 82 | + final bool linearTransition = isPopGestureInProgress(this); | ||
| 83 | + return CupertinoFullscreenDialogTransition( | ||
| 84 | + primaryRouteAnimation: animation, | ||
| 85 | + secondaryRouteAnimation: secondaryAnimation, | ||
| 86 | + child: child, | ||
| 87 | + linearTransition: linearTransition, | ||
| 88 | + ); | ||
| 89 | + } | ||
| 90 | + | ||
| 66 | if (this.customTransition != null) { | 91 | if (this.customTransition != null) { |
| 67 | return this.customTransition.buildTransition( | 92 | return this.customTransition.buildTransition( |
| 68 | context, | 93 | context, |
| @@ -174,12 +199,6 @@ class GetPageRoute<T> extends PageRouteBuilder<T> { | @@ -174,12 +199,6 @@ class GetPageRoute<T> extends PageRouteBuilder<T> { | ||
| 174 | controller: route.controller, | 199 | controller: route.controller, |
| 175 | ); | 200 | ); |
| 176 | } | 201 | } |
| 177 | - | ||
| 178 | - static bool isPopGestureInProgress(PageRoute<dynamic> route) { | ||
| 179 | - return route.navigator.userGestureInProgress; | ||
| 180 | - } | ||
| 181 | - | ||
| 182 | - bool get popGestureInProgress => isPopGestureInProgress(this); | ||
| 183 | } | 202 | } |
| 184 | 203 | ||
| 185 | const double _kBackGestureWidth = 20.0; | 204 | const double _kBackGestureWidth = 20.0; |
| @@ -2,11 +2,7 @@ import 'package:flutter/cupertino.dart'; | @@ -2,11 +2,7 @@ import 'package:flutter/cupertino.dart'; | ||
| 2 | import 'package:flutter/material.dart'; | 2 | import 'package:flutter/material.dart'; |
| 3 | import 'transitions_component.dart'; | 3 | import 'transitions_component.dart'; |
| 4 | 4 | ||
| 5 | -class LeftToRightFadeTransition extends TransitionInterface { | ||
| 6 | - LeftToRightFadeTransition({ | ||
| 7 | - TransitionComponent transitionComponent, | ||
| 8 | - }) : super(transitionComponent: transitionComponent); | ||
| 9 | - | 5 | +class LeftToRightFadeTransition extends TransitionComponent { |
| 10 | @override | 6 | @override |
| 11 | Widget buildChildWithTransition( | 7 | Widget buildChildWithTransition( |
| 12 | BuildContext context, | 8 | BuildContext context, |
| @@ -33,11 +29,7 @@ class LeftToRightFadeTransition extends TransitionInterface { | @@ -33,11 +29,7 @@ class LeftToRightFadeTransition extends TransitionInterface { | ||
| 33 | } | 29 | } |
| 34 | } | 30 | } |
| 35 | 31 | ||
| 36 | -class RightToLeftFadeTransition extends TransitionInterface { | ||
| 37 | - RightToLeftFadeTransition({ | ||
| 38 | - TransitionComponent transitionComponent, | ||
| 39 | - }) : super(transitionComponent: transitionComponent); | ||
| 40 | - | 32 | +class RightToLeftFadeTransition extends TransitionComponent { |
| 41 | @override | 33 | @override |
| 42 | Widget buildChildWithTransition( | 34 | Widget buildChildWithTransition( |
| 43 | BuildContext context, | 35 | BuildContext context, |
| @@ -64,11 +56,7 @@ class RightToLeftFadeTransition extends TransitionInterface { | @@ -64,11 +56,7 @@ class RightToLeftFadeTransition extends TransitionInterface { | ||
| 64 | } | 56 | } |
| 65 | } | 57 | } |
| 66 | 58 | ||
| 67 | -class NoTransition extends TransitionInterface { | ||
| 68 | - NoTransition({ | ||
| 69 | - TransitionComponent transitionComponent, | ||
| 70 | - }) : super(transitionComponent: transitionComponent); | ||
| 71 | - | 59 | +class NoTransition extends TransitionComponent { |
| 72 | @override | 60 | @override |
| 73 | Widget buildChildWithTransition( | 61 | Widget buildChildWithTransition( |
| 74 | BuildContext context, | 62 | BuildContext context, |
| @@ -77,16 +65,11 @@ class NoTransition extends TransitionInterface { | @@ -77,16 +65,11 @@ class NoTransition extends TransitionInterface { | ||
| 77 | Animation<double> animation, | 65 | Animation<double> animation, |
| 78 | Animation<double> secondaryAnimation, | 66 | Animation<double> secondaryAnimation, |
| 79 | Widget child) { | 67 | Widget child) { |
| 80 | - return transitionComponent.buildChildWithTransition( | ||
| 81 | - context, curve, alignment, animation, secondaryAnimation, child); | 68 | + return child; |
| 82 | } | 69 | } |
| 83 | } | 70 | } |
| 84 | 71 | ||
| 85 | -class FadeInTransition extends TransitionInterface { | ||
| 86 | - FadeInTransition({ | ||
| 87 | - TransitionComponent transitionComponent, | ||
| 88 | - }) : super(transitionComponent: transitionComponent); | ||
| 89 | - | 72 | +class FadeInTransition extends TransitionComponent { |
| 90 | @override | 73 | @override |
| 91 | Widget buildChildWithTransition( | 74 | Widget buildChildWithTransition( |
| 92 | BuildContext context, | 75 | BuildContext context, |
| @@ -95,19 +78,11 @@ class FadeInTransition extends TransitionInterface { | @@ -95,19 +78,11 @@ class FadeInTransition extends TransitionInterface { | ||
| 95 | Animation<double> animation, | 78 | Animation<double> animation, |
| 96 | Animation<double> secondaryAnimation, | 79 | Animation<double> secondaryAnimation, |
| 97 | Widget child) { | 80 | Widget child) { |
| 98 | - return FadeTransition( | ||
| 99 | - opacity: animation, | ||
| 100 | - child: transitionComponent.buildChildWithTransition( | ||
| 101 | - context, curve, alignment, animation, secondaryAnimation, child), | ||
| 102 | - ); | 81 | + return FadeTransition(opacity: animation, child: child); |
| 103 | } | 82 | } |
| 104 | } | 83 | } |
| 105 | 84 | ||
| 106 | -class SlideDownTransition extends TransitionInterface { | ||
| 107 | - SlideDownTransition({ | ||
| 108 | - TransitionComponent transitionComponent, | ||
| 109 | - }) : super(transitionComponent: transitionComponent); | ||
| 110 | - | 85 | +class SlideDownTransition extends TransitionComponent { |
| 111 | @override | 86 | @override |
| 112 | Widget buildChildWithTransition( | 87 | Widget buildChildWithTransition( |
| 113 | BuildContext context, | 88 | BuildContext context, |
| @@ -121,17 +96,12 @@ class SlideDownTransition extends TransitionInterface { | @@ -121,17 +96,12 @@ class SlideDownTransition extends TransitionInterface { | ||
| 121 | begin: Offset(0.0, 1.0), | 96 | begin: Offset(0.0, 1.0), |
| 122 | end: Offset.zero, | 97 | end: Offset.zero, |
| 123 | ).animate(animation), | 98 | ).animate(animation), |
| 124 | - child: transitionComponent.buildChildWithTransition( | ||
| 125 | - context, curve, alignment, animation, secondaryAnimation, child), | 99 | + child: child, |
| 126 | ); | 100 | ); |
| 127 | } | 101 | } |
| 128 | } | 102 | } |
| 129 | 103 | ||
| 130 | -class SlideLeftTransition extends TransitionInterface { | ||
| 131 | - SlideLeftTransition({ | ||
| 132 | - TransitionComponent transitionComponent, | ||
| 133 | - }) : super(transitionComponent: transitionComponent); | ||
| 134 | - | 104 | +class SlideLeftTransition extends TransitionComponent { |
| 135 | @override | 105 | @override |
| 136 | Widget buildChildWithTransition( | 106 | Widget buildChildWithTransition( |
| 137 | BuildContext context, | 107 | BuildContext context, |
| @@ -145,17 +115,12 @@ class SlideLeftTransition extends TransitionInterface { | @@ -145,17 +115,12 @@ class SlideLeftTransition extends TransitionInterface { | ||
| 145 | begin: Offset(-1.0, 0.0), | 115 | begin: Offset(-1.0, 0.0), |
| 146 | end: Offset.zero, | 116 | end: Offset.zero, |
| 147 | ).animate(animation), | 117 | ).animate(animation), |
| 148 | - child: transitionComponent.buildChildWithTransition( | ||
| 149 | - context, curve, alignment, animation, secondaryAnimation, child), | 118 | + child: child, |
| 150 | ); | 119 | ); |
| 151 | } | 120 | } |
| 152 | } | 121 | } |
| 153 | 122 | ||
| 154 | -class SlideRightTransition extends TransitionInterface { | ||
| 155 | - SlideRightTransition({ | ||
| 156 | - TransitionComponent transitionComponent, | ||
| 157 | - }) : super(transitionComponent: transitionComponent); | ||
| 158 | - | 123 | +class SlideRightTransition extends TransitionComponent { |
| 159 | @override | 124 | @override |
| 160 | Widget buildChildWithTransition( | 125 | Widget buildChildWithTransition( |
| 161 | BuildContext context, | 126 | BuildContext context, |
| @@ -169,17 +134,12 @@ class SlideRightTransition extends TransitionInterface { | @@ -169,17 +134,12 @@ class SlideRightTransition extends TransitionInterface { | ||
| 169 | begin: Offset(1.0, 0.0), | 134 | begin: Offset(1.0, 0.0), |
| 170 | end: Offset.zero, | 135 | end: Offset.zero, |
| 171 | ).animate(animation), | 136 | ).animate(animation), |
| 172 | - child: transitionComponent.buildChildWithTransition( | ||
| 173 | - context, curve, alignment, animation, secondaryAnimation, child), | 137 | + child: child, |
| 174 | ); | 138 | ); |
| 175 | } | 139 | } |
| 176 | } | 140 | } |
| 177 | 141 | ||
| 178 | -class SlideTopTransition extends TransitionInterface { | ||
| 179 | - SlideTopTransition({ | ||
| 180 | - TransitionComponent transitionComponent, | ||
| 181 | - }) : super(transitionComponent: transitionComponent); | ||
| 182 | - | 142 | +class SlideTopTransition extends TransitionComponent { |
| 183 | @override | 143 | @override |
| 184 | Widget buildChildWithTransition( | 144 | Widget buildChildWithTransition( |
| 185 | BuildContext context, | 145 | BuildContext context, |
| @@ -193,17 +153,12 @@ class SlideTopTransition extends TransitionInterface { | @@ -193,17 +153,12 @@ class SlideTopTransition extends TransitionInterface { | ||
| 193 | begin: Offset(0.0, -1.0), | 153 | begin: Offset(0.0, -1.0), |
| 194 | end: Offset.zero, | 154 | end: Offset.zero, |
| 195 | ).animate(animation), | 155 | ).animate(animation), |
| 196 | - child: transitionComponent.buildChildWithTransition( | ||
| 197 | - context, curve, alignment, animation, secondaryAnimation, child), | 156 | + child: child, |
| 198 | ); | 157 | ); |
| 199 | } | 158 | } |
| 200 | } | 159 | } |
| 201 | 160 | ||
| 202 | -class ZoomInTransition extends TransitionInterface { | ||
| 203 | - ZoomInTransition({ | ||
| 204 | - TransitionComponent transitionComponent, | ||
| 205 | - }) : super(transitionComponent: transitionComponent); | ||
| 206 | - | 161 | +class ZoomInTransition extends TransitionComponent { |
| 207 | @override | 162 | @override |
| 208 | Widget buildChildWithTransition( | 163 | Widget buildChildWithTransition( |
| 209 | BuildContext context, | 164 | BuildContext context, |
| @@ -214,17 +169,12 @@ class ZoomInTransition extends TransitionInterface { | @@ -214,17 +169,12 @@ class ZoomInTransition extends TransitionInterface { | ||
| 214 | Widget child) { | 169 | Widget child) { |
| 215 | return ScaleTransition( | 170 | return ScaleTransition( |
| 216 | scale: animation, | 171 | scale: animation, |
| 217 | - child: transitionComponent.buildChildWithTransition( | ||
| 218 | - context, curve, alignment, animation, secondaryAnimation, child), | 172 | + child: child, |
| 219 | ); | 173 | ); |
| 220 | } | 174 | } |
| 221 | } | 175 | } |
| 222 | 176 | ||
| 223 | -class SizeTransitions extends TransitionInterface { | ||
| 224 | - SizeTransitions({ | ||
| 225 | - TransitionComponent transitionComponent, | ||
| 226 | - }) : super(transitionComponent: transitionComponent); | ||
| 227 | - | 177 | +class SizeTransitions extends TransitionComponent { |
| 228 | @override | 178 | @override |
| 229 | Widget buildChildWithTransition( | 179 | Widget buildChildWithTransition( |
| 230 | BuildContext context, | 180 | BuildContext context, |
| @@ -246,11 +196,7 @@ class SizeTransitions extends TransitionInterface { | @@ -246,11 +196,7 @@ class SizeTransitions extends TransitionInterface { | ||
| 246 | } | 196 | } |
| 247 | } | 197 | } |
| 248 | 198 | ||
| 249 | -class CupertinoTransitions extends TransitionInterface { | ||
| 250 | - CupertinoTransitions({ | ||
| 251 | - TransitionComponent transitionComponent, | ||
| 252 | - }) : super(transitionComponent: transitionComponent); | ||
| 253 | - | 199 | +class CupertinoTransitions extends TransitionComponent { |
| 254 | @override | 200 | @override |
| 255 | Widget buildChildWithTransition( | 201 | Widget buildChildWithTransition( |
| 256 | BuildContext context, | 202 | BuildContext context, |
| @@ -267,8 +213,3 @@ class CupertinoTransitions extends TransitionInterface { | @@ -267,8 +213,3 @@ class CupertinoTransitions extends TransitionInterface { | ||
| 267 | ); | 213 | ); |
| 268 | } | 214 | } |
| 269 | } | 215 | } |
| 270 | - | ||
| 271 | -abstract class TransitionInterface implements TransitionComponent { | ||
| 272 | - TransitionComponent transitionComponent; | ||
| 273 | - TransitionInterface({this.transitionComponent}); | ||
| 274 | -} |
| @@ -6,42 +6,39 @@ class TransitionFilter { | @@ -6,42 +6,39 @@ class TransitionFilter { | ||
| 6 | static TransitionComponent newTransitionComponent( | 6 | static TransitionComponent newTransitionComponent( |
| 7 | Transition transition, | 7 | Transition transition, |
| 8 | ) { | 8 | ) { |
| 9 | - TransitionComponent transitionComponent = TransitionComponent(); | ||
| 10 | switch (transition) { | 9 | switch (transition) { |
| 11 | case Transition.leftToRight: | 10 | case Transition.leftToRight: |
| 12 | - return SlideLeftTransition(transitionComponent: transitionComponent); | 11 | + return SlideLeftTransition(); |
| 13 | 12 | ||
| 14 | case Transition.downToUp: | 13 | case Transition.downToUp: |
| 15 | - return SlideDownTransition(transitionComponent: transitionComponent); | 14 | + return SlideDownTransition(); |
| 16 | 15 | ||
| 17 | case Transition.upToDown: | 16 | case Transition.upToDown: |
| 18 | - return SlideTopTransition(transitionComponent: transitionComponent); | 17 | + return SlideTopTransition(); |
| 19 | 18 | ||
| 20 | case Transition.rightToLeft: | 19 | case Transition.rightToLeft: |
| 21 | - return SlideRightTransition(transitionComponent: transitionComponent); | 20 | + return SlideRightTransition(); |
| 22 | 21 | ||
| 23 | case Transition.zoom: | 22 | case Transition.zoom: |
| 24 | - return ZoomInTransition(transitionComponent: transitionComponent); | 23 | + return ZoomInTransition(); |
| 25 | 24 | ||
| 26 | case Transition.fadeIn: | 25 | case Transition.fadeIn: |
| 27 | - return FadeInTransition(transitionComponent: transitionComponent); | 26 | + return FadeInTransition(); |
| 28 | 27 | ||
| 29 | case Transition.rightToLeftWithFade: | 28 | case Transition.rightToLeftWithFade: |
| 30 | - return RightToLeftFadeTransition( | ||
| 31 | - transitionComponent: transitionComponent); | 29 | + return RightToLeftFadeTransition(); |
| 32 | 30 | ||
| 33 | case Transition.leftToRightWithFade: | 31 | case Transition.leftToRightWithFade: |
| 34 | - return LeftToRightFadeTransition( | ||
| 35 | - transitionComponent: transitionComponent); | 32 | + return LeftToRightFadeTransition(); |
| 36 | 33 | ||
| 37 | case Transition.cupertino: | 34 | case Transition.cupertino: |
| 38 | - return CupertinoTransitions(transitionComponent: transitionComponent); | 35 | + return CupertinoTransitions(); |
| 39 | 36 | ||
| 40 | case Transition.size: | 37 | case Transition.size: |
| 41 | - return SizeTransitions(transitionComponent: transitionComponent); | 38 | + return SizeTransitions(); |
| 42 | 39 | ||
| 43 | default: | 40 | default: |
| 44 | - return FadeInTransition(transitionComponent: transitionComponent); | 41 | + return FadeInTransition(); |
| 45 | } | 42 | } |
| 46 | } | 43 | } |
| 47 | } | 44 | } |
| @@ -3,7 +3,7 @@ import 'dart:ui'; | @@ -3,7 +3,7 @@ import 'dart:ui'; | ||
| 3 | import 'package:flutter/material.dart'; | 3 | import 'package:flutter/material.dart'; |
| 4 | import 'package:flutter/scheduler.dart'; | 4 | import 'package:flutter/scheduler.dart'; |
| 5 | import 'package:get/get.dart'; | 5 | import 'package:get/get.dart'; |
| 6 | -import 'snack_route.dart' as snackroute; | 6 | +import 'snack_route.dart' as route; |
| 7 | 7 | ||
| 8 | typedef void SnackStatusCallback(SnackStatus status); | 8 | typedef void SnackStatusCallback(SnackStatus status); |
| 9 | typedef void OnTap(GetBar snack); | 9 | typedef void OnTap(GetBar snack); |
| @@ -209,11 +209,11 @@ class GetBar<T extends Object> extends StatefulWidget { | @@ -209,11 +209,11 @@ class GetBar<T extends Object> extends StatefulWidget { | ||
| 209 | /// A [TextFormField] in case you want a simple user input. Every other widget is ignored if this is not null. | 209 | /// A [TextFormField] in case you want a simple user input. Every other widget is ignored if this is not null. |
| 210 | final Form userInputForm; | 210 | final Form userInputForm; |
| 211 | 211 | ||
| 212 | - snackroute.SnackRoute<T> _snackRoute; | 212 | + route.SnackRoute<T> _snackRoute; |
| 213 | 213 | ||
| 214 | /// Show the snack. Kicks in [SnackStatus.IS_APPEARING] state followed by [SnackStatus.SHOWING] | 214 | /// Show the snack. Kicks in [SnackStatus.IS_APPEARING] state followed by [SnackStatus.SHOWING] |
| 215 | Future<T> show() async { | 215 | Future<T> show() async { |
| 216 | - _snackRoute = snackroute.showSnack<T>( | 216 | + _snackRoute = route.showSnack<T>( |
| 217 | snack: this, | 217 | snack: this, |
| 218 | ) as SnackRoute<T>; | 218 | ) as SnackRoute<T>; |
| 219 | return await Get.key.currentState.push(_snackRoute); | 219 | return await Get.key.currentState.push(_snackRoute); |
| @@ -374,19 +374,18 @@ class _GetBarState<K extends Object> extends State<GetBar> | @@ -374,19 +374,18 @@ class _GetBarState<K extends Object> extends State<GetBar> | ||
| 374 | : widget.backgroundColor, | 374 | : widget.backgroundColor, |
| 375 | child: SafeArea( | 375 | child: SafeArea( |
| 376 | minimum: widget.snackPosition == SnackPosition.BOTTOM | 376 | minimum: widget.snackPosition == SnackPosition.BOTTOM |
| 377 | - ? EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom) | 377 | + ? EdgeInsets.only( |
| 378 | + bottom: (GetUtils.isGreaterThan( | ||
| 379 | + MediaQuery.of(context).viewInsets.bottom, | ||
| 380 | + MediaQuery.of(context).padding.bottom) | ||
| 381 | + ? MediaQuery.of(context).viewInsets.bottom | ||
| 382 | + : MediaQuery.of(context).padding.bottom)) | ||
| 378 | : EdgeInsets.only(top: MediaQuery.of(context).padding.top), | 383 | : EdgeInsets.only(top: MediaQuery.of(context).padding.top), |
| 379 | - child: SafeArea( | ||
| 380 | - minimum: widget.snackPosition == SnackPosition.BOTTOM | ||
| 381 | - ? EdgeInsets.only( | ||
| 382 | - bottom: MediaQuery.of(context).viewInsets.bottom) | ||
| 383 | - : EdgeInsets.only(top: MediaQuery.of(context).viewInsets.top), | ||
| 384 | - bottom: widget.snackPosition == SnackPosition.BOTTOM, | ||
| 385 | - top: widget.snackPosition == SnackPosition.TOP, | ||
| 386 | - left: false, | ||
| 387 | - right: false, | ||
| 388 | - child: _getSnack(), | ||
| 389 | - ), | 384 | + bottom: widget.snackPosition == SnackPosition.BOTTOM, |
| 385 | + top: widget.snackPosition == SnackPosition.TOP, | ||
| 386 | + left: false, | ||
| 387 | + right: false, | ||
| 388 | + child: _getSnack(), | ||
| 390 | ), | 389 | ), |
| 391 | ), | 390 | ), |
| 392 | ); | 391 | ); |
| 1 | import 'dart:async'; | 1 | import 'dart:async'; |
| 2 | import 'dart:ui'; | 2 | import 'dart:ui'; |
| 3 | -import 'package:flutter/material.dart'; | ||
| 4 | -import 'package:get/src/snackbar/snack.dart'; | 3 | +import 'package:flutter/widgets.dart'; |
| 4 | +import 'snack.dart'; | ||
| 5 | 5 | ||
| 6 | class SnackRoute<T> extends OverlayRoute<T> { | 6 | class SnackRoute<T> extends OverlayRoute<T> { |
| 7 | - final GetBar snack; | ||
| 8 | - final Builder _builder; | ||
| 9 | - final Completer<T> _transitionCompleter = Completer<T>(); | ||
| 10 | - final SnackStatusCallback _onStatusChanged; | ||
| 11 | - Alignment _initialAlignment; | ||
| 12 | - Alignment _endAlignment; | ||
| 13 | - bool _wasDismissedBySwipe = false; | ||
| 14 | - Timer _timer; | ||
| 15 | - T _result; | ||
| 16 | - SnackStatus currentStatus; | 7 | + Animation<double> _filterBlurAnimation; |
| 8 | + Animation<Color> _filterColorAnimation; | ||
| 17 | 9 | ||
| 18 | SnackRoute({ | 10 | SnackRoute({ |
| 19 | @required this.snack, | 11 | @required this.snack, |
| 20 | RouteSettings settings, | 12 | RouteSettings settings, |
| 21 | - }) : _builder = Builder(builder: (BuildContext innerContext) { | ||
| 22 | - return GestureDetector( | ||
| 23 | - child: snack, | ||
| 24 | - onTap: snack.onTap != null ? () => snack.onTap(snack) : null, | ||
| 25 | - ); | ||
| 26 | - }), | ||
| 27 | - _onStatusChanged = snack.onStatusChanged, | ||
| 28 | - super(settings: settings) { | 13 | + }) : super(settings: settings) { |
| 14 | + this._builder = Builder(builder: (BuildContext innerContext) { | ||
| 15 | + return GestureDetector( | ||
| 16 | + child: snack, | ||
| 17 | + onTap: snack.onTap != null ? () => snack.onTap(snack) : null, | ||
| 18 | + ); | ||
| 19 | + }); | ||
| 20 | + | ||
| 29 | _configureAlignment(this.snack.snackPosition); | 21 | _configureAlignment(this.snack.snackPosition); |
| 22 | + _onStatusChanged = snack.onStatusChanged; | ||
| 30 | } | 23 | } |
| 31 | 24 | ||
| 32 | - void _configureAlignment(SnackPosition snackPosition) { | 25 | + _configureAlignment(SnackPosition snackPosition) { |
| 33 | switch (snack.snackPosition) { | 26 | switch (snack.snackPosition) { |
| 34 | case SnackPosition.TOP: | 27 | case SnackPosition.TOP: |
| 35 | { | 28 | { |
| @@ -46,12 +39,51 @@ class SnackRoute<T> extends OverlayRoute<T> { | @@ -46,12 +39,51 @@ class SnackRoute<T> extends OverlayRoute<T> { | ||
| 46 | } | 39 | } |
| 47 | } | 40 | } |
| 48 | 41 | ||
| 42 | + GetBar snack; | ||
| 43 | + Builder _builder; | ||
| 44 | + | ||
| 49 | Future<T> get completed => _transitionCompleter.future; | 45 | Future<T> get completed => _transitionCompleter.future; |
| 46 | + final Completer<T> _transitionCompleter = Completer<T>(); | ||
| 47 | + | ||
| 48 | + SnackStatusCallback _onStatusChanged; | ||
| 49 | + Alignment _initialAlignment; | ||
| 50 | + Alignment _endAlignment; | ||
| 51 | + bool _wasDismissedBySwipe = false; | ||
| 52 | + | ||
| 53 | + Timer _timer; | ||
| 54 | + | ||
| 50 | bool get opaque => false; | 55 | bool get opaque => false; |
| 51 | 56 | ||
| 52 | @override | 57 | @override |
| 53 | Iterable<OverlayEntry> createOverlayEntries() { | 58 | Iterable<OverlayEntry> createOverlayEntries() { |
| 54 | - final List<OverlayEntry> overlays = []; | 59 | + List<OverlayEntry> overlays = []; |
| 60 | + | ||
| 61 | + if (snack.overlayBlur > 0.0) { | ||
| 62 | + overlays.add( | ||
| 63 | + OverlayEntry( | ||
| 64 | + builder: (BuildContext context) { | ||
| 65 | + return GestureDetector( | ||
| 66 | + onTap: snack.isDismissible ? () => snack.dismiss() : null, | ||
| 67 | + child: AnimatedBuilder( | ||
| 68 | + animation: _filterBlurAnimation, | ||
| 69 | + builder: (context, child) { | ||
| 70 | + return BackdropFilter( | ||
| 71 | + filter: ImageFilter.blur( | ||
| 72 | + sigmaX: _filterBlurAnimation.value, | ||
| 73 | + sigmaY: _filterBlurAnimation.value), | ||
| 74 | + child: Container( | ||
| 75 | + constraints: BoxConstraints.expand(), | ||
| 76 | + color: _filterColorAnimation.value, | ||
| 77 | + ), | ||
| 78 | + ); | ||
| 79 | + }, | ||
| 80 | + ), | ||
| 81 | + ); | ||
| 82 | + }, | ||
| 83 | + maintainState: false, | ||
| 84 | + opaque: opaque), | ||
| 85 | + ); | ||
| 86 | + } | ||
| 55 | 87 | ||
| 56 | overlays.add( | 88 | overlays.add( |
| 57 | OverlayEntry( | 89 | OverlayEntry( |
| @@ -59,7 +91,9 @@ class SnackRoute<T> extends OverlayRoute<T> { | @@ -59,7 +91,9 @@ class SnackRoute<T> extends OverlayRoute<T> { | ||
| 59 | final Widget annotatedChild = Semantics( | 91 | final Widget annotatedChild = Semantics( |
| 60 | child: AlignTransition( | 92 | child: AlignTransition( |
| 61 | alignment: _animation, | 93 | alignment: _animation, |
| 62 | - child: _getSnack(), | 94 | + child: snack.isDismissible |
| 95 | + ? _getDismissibleSnack(_builder) | ||
| 96 | + : _getSnack(), | ||
| 63 | ), | 97 | ), |
| 64 | focused: false, | 98 | focused: false, |
| 65 | container: true, | 99 | container: true, |
| @@ -74,9 +108,35 @@ class SnackRoute<T> extends OverlayRoute<T> { | @@ -74,9 +108,35 @@ class SnackRoute<T> extends OverlayRoute<T> { | ||
| 74 | return overlays; | 108 | return overlays; |
| 75 | } | 109 | } |
| 76 | 110 | ||
| 77 | - /// This string is a workaround until Dismissible supports a returning item | ||
| 78 | String dismissibleKeyGen = ""; | 111 | String dismissibleKeyGen = ""; |
| 79 | 112 | ||
| 113 | + Widget _getDismissibleSnack(Widget child) { | ||
| 114 | + return Dismissible( | ||
| 115 | + direction: _getDismissDirection(), | ||
| 116 | + resizeDuration: null, | ||
| 117 | + confirmDismiss: (_) { | ||
| 118 | + if (currentStatus == SnackStatus.IS_APPEARING || | ||
| 119 | + currentStatus == SnackStatus.IS_HIDING) { | ||
| 120 | + return Future.value(false); | ||
| 121 | + } | ||
| 122 | + return Future.value(true); | ||
| 123 | + }, | ||
| 124 | + key: Key(dismissibleKeyGen), | ||
| 125 | + onDismissed: (_) { | ||
| 126 | + dismissibleKeyGen += "1"; | ||
| 127 | + _cancelTimer(); | ||
| 128 | + _wasDismissedBySwipe = true; | ||
| 129 | + | ||
| 130 | + if (isCurrent) { | ||
| 131 | + navigator.pop(); | ||
| 132 | + } else { | ||
| 133 | + navigator.removeRoute(this); | ||
| 134 | + } | ||
| 135 | + }, | ||
| 136 | + child: _getSnack(), | ||
| 137 | + ); | ||
| 138 | + } | ||
| 139 | + | ||
| 80 | Widget _getSnack() { | 140 | Widget _getSnack() { |
| 81 | return Container( | 141 | return Container( |
| 82 | margin: snack.margin, | 142 | margin: snack.margin, |
| @@ -84,6 +144,18 @@ class SnackRoute<T> extends OverlayRoute<T> { | @@ -84,6 +144,18 @@ class SnackRoute<T> extends OverlayRoute<T> { | ||
| 84 | ); | 144 | ); |
| 85 | } | 145 | } |
| 86 | 146 | ||
| 147 | + DismissDirection _getDismissDirection() { | ||
| 148 | + if (snack.dismissDirection == SnackDismissDirection.HORIZONTAL) { | ||
| 149 | + return DismissDirection.horizontal; | ||
| 150 | + } else { | ||
| 151 | + if (snack.snackPosition == SnackPosition.TOP) { | ||
| 152 | + return DismissDirection.up; | ||
| 153 | + } else { | ||
| 154 | + return DismissDirection.down; | ||
| 155 | + } | ||
| 156 | + } | ||
| 157 | + } | ||
| 158 | + | ||
| 87 | @override | 159 | @override |
| 88 | bool get finishedWhenPopped => | 160 | bool get finishedWhenPopped => |
| 89 | _controller.status == AnimationStatus.dismissed; | 161 | _controller.status == AnimationStatus.dismissed; |
| @@ -132,8 +204,6 @@ class SnackRoute<T> extends OverlayRoute<T> { | @@ -132,8 +204,6 @@ class SnackRoute<T> extends OverlayRoute<T> { | ||
| 132 | } | 204 | } |
| 133 | 205 | ||
| 134 | Animation<double> createBlurFilterAnimation() { | 206 | Animation<double> createBlurFilterAnimation() { |
| 135 | - if (snack.overlayBlur == null) return null; | ||
| 136 | - | ||
| 137 | return Tween(begin: 0.0, end: snack.overlayBlur).animate( | 207 | return Tween(begin: 0.0, end: snack.overlayBlur).animate( |
| 138 | CurvedAnimation( | 208 | CurvedAnimation( |
| 139 | parent: _controller, | 209 | parent: _controller, |
| @@ -147,9 +217,7 @@ class SnackRoute<T> extends OverlayRoute<T> { | @@ -147,9 +217,7 @@ class SnackRoute<T> extends OverlayRoute<T> { | ||
| 147 | } | 217 | } |
| 148 | 218 | ||
| 149 | Animation<Color> createColorFilterAnimation() { | 219 | Animation<Color> createColorFilterAnimation() { |
| 150 | - if (snack.overlayColor == null) return null; | ||
| 151 | - | ||
| 152 | - return ColorTween(begin: Colors.transparent, end: snack.overlayColor) | 220 | + return ColorTween(begin: Color(0x00000000), end: snack.overlayColor) |
| 153 | .animate( | 221 | .animate( |
| 154 | CurvedAnimation( | 222 | CurvedAnimation( |
| 155 | parent: _controller, | 223 | parent: _controller, |
| @@ -162,6 +230,9 @@ class SnackRoute<T> extends OverlayRoute<T> { | @@ -162,6 +230,9 @@ class SnackRoute<T> extends OverlayRoute<T> { | ||
| 162 | ); | 230 | ); |
| 163 | } | 231 | } |
| 164 | 232 | ||
| 233 | + T _result; | ||
| 234 | + SnackStatus currentStatus; | ||
| 235 | + | ||
| 165 | //copy of `routes.dart` | 236 | //copy of `routes.dart` |
| 166 | void _handleStatusChanged(AnimationStatus status) { | 237 | void _handleStatusChanged(AnimationStatus status) { |
| 167 | switch (status) { | 238 | switch (status) { |
| @@ -205,6 +276,8 @@ class SnackRoute<T> extends OverlayRoute<T> { | @@ -205,6 +276,8 @@ class SnackRoute<T> extends OverlayRoute<T> { | ||
| 205 | _controller = createAnimationController(); | 276 | _controller = createAnimationController(); |
| 206 | assert(_controller != null, | 277 | assert(_controller != null, |
| 207 | '$runtimeType.createAnimationController() returned null.'); | 278 | '$runtimeType.createAnimationController() returned null.'); |
| 279 | + _filterBlurAnimation = createBlurFilterAnimation(); | ||
| 280 | + _filterColorAnimation = createColorFilterAnimation(); | ||
| 208 | _animation = createAnimation(); | 281 | _animation = createAnimation(); |
| 209 | assert(_animation != null, '$runtimeType.createAnimation() returned null.'); | 282 | assert(_animation != null, '$runtimeType.createAnimation() returned null.'); |
| 210 | super.install(); | 283 | super.install(); |
| @@ -212,17 +285,28 @@ class SnackRoute<T> extends OverlayRoute<T> { | @@ -212,17 +285,28 @@ class SnackRoute<T> extends OverlayRoute<T> { | ||
| 212 | 285 | ||
| 213 | @override | 286 | @override |
| 214 | TickerFuture didPush() { | 287 | TickerFuture didPush() { |
| 288 | + super.didPush(); | ||
| 215 | assert(_controller != null, | 289 | assert(_controller != null, |
| 216 | '$runtimeType.didPush called before calling install() or after calling dispose().'); | 290 | '$runtimeType.didPush called before calling install() or after calling dispose().'); |
| 217 | assert(!_transitionCompleter.isCompleted, | 291 | assert(!_transitionCompleter.isCompleted, |
| 218 | 'Cannot reuse a $runtimeType after disposing it.'); | 292 | 'Cannot reuse a $runtimeType after disposing it.'); |
| 219 | _animation.addStatusListener(_handleStatusChanged); | 293 | _animation.addStatusListener(_handleStatusChanged); |
| 220 | _configureTimer(); | 294 | _configureTimer(); |
| 221 | - super.didPush(); | ||
| 222 | return _controller.forward(); | 295 | return _controller.forward(); |
| 223 | } | 296 | } |
| 224 | 297 | ||
| 225 | @override | 298 | @override |
| 299 | + void didReplace(Route<dynamic> oldRoute) { | ||
| 300 | + assert(_controller != null, | ||
| 301 | + '$runtimeType.didReplace called before calling install() or after calling dispose().'); | ||
| 302 | + assert(!_transitionCompleter.isCompleted, | ||
| 303 | + 'Cannot reuse a $runtimeType after disposing it.'); | ||
| 304 | + if (oldRoute is SnackRoute) _controller.value = oldRoute._controller.value; | ||
| 305 | + _animation.addStatusListener(_handleStatusChanged); | ||
| 306 | + super.didReplace(oldRoute); | ||
| 307 | + } | ||
| 308 | + | ||
| 309 | + @override | ||
| 226 | bool didPop(T result) { | 310 | bool didPop(T result) { |
| 227 | assert(_controller != null, | 311 | assert(_controller != null, |
| 228 | '$runtimeType.didPop called before calling install() or after calling dispose().'); | 312 | '$runtimeType.didPop called before calling install() or after calling dispose().'); |
| @@ -270,6 +354,17 @@ class SnackRoute<T> extends OverlayRoute<T> { | @@ -270,6 +354,17 @@ class SnackRoute<T> extends OverlayRoute<T> { | ||
| 270 | } | 354 | } |
| 271 | } | 355 | } |
| 272 | 356 | ||
| 357 | + /// Whether this route can perform a transition to the given route. | ||
| 358 | + /// Subclasses can override this method to restrict the set of routes they | ||
| 359 | + /// need to coordinate transitions with. | ||
| 360 | + bool canTransitionTo(SnackRoute<dynamic> nextRoute) => true; | ||
| 361 | + | ||
| 362 | + /// Whether this route can perform a transition from the given route. | ||
| 363 | + /// | ||
| 364 | + /// Subclasses can override this method to restrict the set of routes they | ||
| 365 | + /// need to coordinate transitions with. | ||
| 366 | + bool canTransitionFrom(SnackRoute<dynamic> previousRoute) => true; | ||
| 367 | + | ||
| 273 | @override | 368 | @override |
| 274 | void dispose() { | 369 | void dispose() { |
| 275 | assert(!_transitionCompleter.isCompleted, | 370 | assert(!_transitionCompleter.isCompleted, |
| @@ -287,8 +382,10 @@ class SnackRoute<T> extends OverlayRoute<T> { | @@ -287,8 +382,10 @@ class SnackRoute<T> extends OverlayRoute<T> { | ||
| 287 | } | 382 | } |
| 288 | 383 | ||
| 289 | SnackRoute showSnack<T>({@required GetBar snack}) { | 384 | SnackRoute showSnack<T>({@required GetBar snack}) { |
| 385 | + assert(snack != null); | ||
| 386 | + | ||
| 290 | return SnackRoute<T>( | 387 | return SnackRoute<T>( |
| 291 | snack: snack, | 388 | snack: snack, |
| 292 | - settings: RouteSettings(name: 'snackbar'), | 389 | + settings: RouteSettings(name: "snackbar"), |
| 293 | ); | 390 | ); |
| 294 | } | 391 | } |
lib/state_manager.dart
0 → 100644
lib/utils.dart
0 → 100644
| 1 | name: get | 1 | name: get |
| 2 | description: Open screens/snackbars/dialogs/bottomSheets without context, manage states and inject dependencies easily with Get. | 2 | description: Open screens/snackbars/dialogs/bottomSheets without context, manage states and inject dependencies easily with Get. |
| 3 | -version: 3.2.0 | 3 | +version: 3.2.1 |
| 4 | homepage: https://github.com/jonataslaw/get | 4 | homepage: https://github.com/jonataslaw/get |
| 5 | 5 | ||
| 6 | environment: | 6 | environment: |
-
Please register or login to post a comment