Jonny Borges
Committed by GitHub

Add files via upload

@@ -25,3 +25,63 @@ @@ -25,3 +25,63 @@
25 ## [1.2.1] 25 ## [1.2.1]
26 26
27 - Fix bug currentState = null 27 - Fix bug currentState = null
  28 +
  29 +## [1.3.0]
  30 +
  31 +- Update docs, readme, and add full support to flutter_web
  32 +
  33 +## [1.3.1]
  34 +
  35 +- Update docs
  36 +
  37 +## [1.3.2]
  38 +
  39 +- Improve performance
  40 +
  41 +## [1.3.3]
  42 +
  43 +- Fix Get.back arguments
  44 +
  45 +## [1.3.4]
  46 +
  47 +- Improve performance
  48 +
  49 +## [1.4.0]
  50 +
  51 +- Added Get.removeRoute // remove one route.
  52 + Get.until // back repeatedly until the predicate returns true.
  53 + Get.offUntil // go to next route and remove all the previous routes until the predicate returns true.
  54 + Get.offNamedUntil // go to next named route and remove all the previous routes until the predicate returns true.
  55 +
  56 +## [1.4.0+6]
  57 +
  58 +- Improve performance and bug fix
  59 +
  60 +
  61 +## [1.4.0+7]
  62 +
  63 + - Add more documentation
  64 +
  65 +## [1.5.0]
  66 +
  67 + - Add support to dialogs
  68 +
  69 +## [1.5.0+1]
  70 +
  71 + - Add color and opacity to dialogs
  72 +
  73 +## [1.6.0]
  74 +
  75 + - Add support to snackbars
  76 +
  77 +## [1.6.1]
  78 +
  79 + - Add docs and improve performance
  80 +
  81 +## [1.6.2]
  82 +
  83 + - Fix bugs on blurred Snackbars
  84 +
  85 +## [1.6.3]
  86 +
  87 + - Clean code.
1 # Get 1 # Get
2 2
3 -A consistent Flutter route navigation library that navigate with no context and not rebuild materialApp with each navigation.  
4 - 3 +A consistent navigation library that lets you navigate between screens, open dialogs, and display snackbars from anywhere in your code without context.
5 ## Getting Started 4 ## Getting Started
6 5
7 -Flutter's conventional navigation method has a route reconstruction bug that makes it inconsistent  
8 -for large applications with undefined routes.  
9 -Get came to solve this problem.  
10 -In addition, Get needs no context, also solving Flutter's biggest problem with patterns like BLoC.  
11 -Get also makes navigation much clearer and more concise for beginners and friendly to those who came from Web programming. 6 +Flutter's conventional navigation has a lot of unnecessary boilerplate, requires context to navigate between screens, open dialogs, and snacking is really painful.
  7 +In addition, with each route navigation, all of your screens below MaterialApp are rebuilt, often causing RAM and CPU bottlenecks.
  8 +I worked on a pull to fix it in the framework, and seeing how things work I realized that a lot of cliche code could be avoided to get clean and concise code.
  9 +With that in mind, I created this library that will change the way you work with the Framework and save your life from cliche code,
  10 +increasing your productivity, and eliminating all the bugs present in Flutter's default navigation altogether.
12 11
13 ## How to use? 12 ## How to use?
14 13
@@ -16,7 +15,7 @@ Add this to your package's pubspec.yaml file: @@ -16,7 +15,7 @@ Add this to your package's pubspec.yaml file:
16 15
17 ``` 16 ```
18 dependencies: 17 dependencies:
19 - get: ^1.4.0 18 + get: ^1.6.3
20 ``` 19 ```
21 20
22 And import it: 21 And import it:
@@ -69,6 +68,41 @@ ex: @@ -69,6 +68,41 @@ ex:
69 ```dart 68 ```dart
70 if(data == 'sucess') madeAnything(); 69 if(data == 'sucess') madeAnything();
71 ``` 70 ```
  71 +To open dialog:
  72 +
  73 +```dart
  74 +Get.dialog(YourDialogWidget());
  75 +```
  76 +
  77 +To open default dialog:
  78 +
  79 +```dart
  80 + Get.defaultDialog(
  81 + title: "My Title",
  82 + content: Text("Hi, it's my dialog"),
  83 + confirm: FlatButton(
  84 + child: Text("Ok"),
  85 + onPressed: () => print("OK pressed"),
  86 + ),
  87 + cancel: FlatButton(
  88 + child: Text("Cancel"),
  89 + onPressed: () => Get.back(),
  90 + ));
  91 +```
  92 +
  93 +To have a simple SnackBar with Flutter, you must get the context of Scaffold, or you must use a GlobalKey attached to your Scaffold,
  94 +but with Get, all you have to do is call your SnackBar from anywhere in your code! No context, no cliche code!
  95 +
  96 +```dart
  97 + GetBar(
  98 + title: "Hey i'm a Get SnackBar!",
  99 + message:
  100 + "It's unbelievable! I'm using SnackBar without context, without boilerplate, without Scaffold, it is something truly amazing!",
  101 + duration: Duration(seconds: 3),
  102 + )..show();
  103 +```
  104 +Plus, the default SnackBar is completely inflexible, while GetBar lets you change the color, shape, opacity, and anything else you want!
  105 +
72 106
73 Others methods: 107 Others methods:
74 Get.removeRoute // remove one route. 108 Get.removeRoute // remove one route.
@@ -154,6 +188,17 @@ class FirstRoute extends StatelessWidget { @@ -154,6 +188,17 @@ class FirstRoute extends StatelessWidget {
154 Widget build(BuildContext context) { 188 Widget build(BuildContext context) {
155 return Scaffold( 189 return Scaffold(
156 appBar: AppBar( 190 appBar: AppBar(
  191 + leading: IconButton(
  192 + icon: Icon(Icons.add),
  193 + onPressed: () {
  194 + GetBar(
  195 + title: "Hey i'm a Get SnackBar!",
  196 + message:
  197 + "It's unbelievable! I'm using SnackBar without context, without boilerplate, without Scaffold, it is something truly amazing!",
  198 + duration: Duration(seconds: 3),
  199 + )..show();
  200 + },
  201 + ),
157 title: Text('First Route'), 202 title: Text('First Route'),
158 ), 203 ),
159 body: Center( 204 body: Center(
@@ -7,7 +7,7 @@ packages: @@ -7,7 +7,7 @@ packages:
7 name: archive 7 name: archive
8 url: "https://pub.dartlang.org" 8 url: "https://pub.dartlang.org"
9 source: hosted 9 source: hosted
10 - version: "2.0.10" 10 + version: "2.0.11"
11 args: 11 args:
12 dependency: transitive 12 dependency: transitive
13 description: 13 description:
@@ -21,7 +21,7 @@ packages: @@ -21,7 +21,7 @@ packages:
21 name: async 21 name: async
22 url: "https://pub.dartlang.org" 22 url: "https://pub.dartlang.org"
23 source: hosted 23 source: hosted
24 - version: "2.3.0" 24 + version: "2.4.0"
25 boolean_selector: 25 boolean_selector:
26 dependency: transitive 26 dependency: transitive
27 description: 27 description:
@@ -94,14 +94,14 @@ packages: @@ -94,14 +94,14 @@ packages:
94 name: matcher 94 name: matcher
95 url: "https://pub.dartlang.org" 95 url: "https://pub.dartlang.org"
96 source: hosted 96 source: hosted
97 - version: "0.12.5" 97 + version: "0.12.6"
98 meta: 98 meta:
99 dependency: transitive 99 dependency: transitive
100 description: 100 description:
101 name: meta 101 name: meta
102 url: "https://pub.dartlang.org" 102 url: "https://pub.dartlang.org"
103 source: hosted 103 source: hosted
104 - version: "1.1.7" 104 + version: "1.1.8"
105 path: 105 path:
106 dependency: transitive 106 dependency: transitive
107 description: 107 description:
@@ -176,7 +176,7 @@ packages: @@ -176,7 +176,7 @@ packages:
176 name: test_api 176 name: test_api
177 url: "https://pub.dartlang.org" 177 url: "https://pub.dartlang.org"
178 source: hosted 178 source: hosted
179 - version: "0.2.5" 179 + version: "0.2.11"
180 typed_data: 180 typed_data:
181 dependency: transitive 181 dependency: transitive
182 description: 182 description:
@@ -2,4 +2,5 @@ library get; @@ -2,4 +2,5 @@ library get;
2 2
3 export 'src/getroute.dart'; 3 export 'src/getroute.dart';
4 export 'src/routes.dart'; 4 export 'src/routes.dart';
5 - 5 +export 'src/snack.dart';
  6 +export 'src/snack_route.dart';
  1 +import 'package:flutter/material.dart';
  2 +import 'package:get/src/routes.dart';
  3 +
  4 +class DialogGet extends StatelessWidget {
  5 + final Widget child;
  6 + final color;
  7 + final double opacity;
  8 +
  9 + const DialogGet({Key key, this.child, this.color, this.opacity = 0.5})
  10 + : super(key: key);
  11 +
  12 + @override
  13 + Widget build(BuildContext context) {
  14 + return GestureDetector(
  15 + onTap: () => Get.back(),
  16 + child: Container(
  17 + color: (color == null)
  18 + ? Theme.of(context).accentColor.withOpacity(opacity)
  19 + : color,
  20 + child: GestureDetector(onTap: () => null, child: child),
  21 + ),
  22 + );
  23 + }
  24 +}
  25 +
  26 +class DefaultDialogGet extends StatelessWidget {
  27 + final color;
  28 + final double opacity;
  29 + final String title;
  30 + final Widget content;
  31 + final Widget cancel;
  32 + final Widget confirm;
  33 +
  34 + const DefaultDialogGet(
  35 + {Key key,
  36 + this.color,
  37 + this.opacity = 0.5,
  38 + this.title,
  39 + this.content,
  40 + this.cancel,
  41 + this.confirm})
  42 + : super(key: key);
  43 +
  44 + @override
  45 + Widget build(BuildContext context) {
  46 + return GestureDetector(
  47 + onTap: () => Get.back(),
  48 + child: Container(
  49 + color: (color == null)
  50 + ? Theme.of(context).accentColor.withOpacity(opacity)
  51 + : color,
  52 + child: GestureDetector(
  53 + onTap: () => null,
  54 + child: AlertDialog(
  55 + title: Text(title, textAlign: TextAlign.center),
  56 + content: content,
  57 + actions: <Widget>[cancel, confirm],
  58 + ),
  59 + ),
  60 + ),
  61 + );
  62 + }
  63 +}
@@ -2,6 +2,8 @@ import 'package:flutter/cupertino.dart'; @@ -2,6 +2,8 @@ import 'package:flutter/cupertino.dart';
2 import 'package:flutter/material.dart'; 2 import 'package:flutter/material.dart';
3 3
4 class GetRoute<T> extends PageRoute<T> { 4 class GetRoute<T> extends PageRoute<T> {
  5 + /// Construct a MaterialPageRoute whose contents are defined by [builder].
  6 + ///
5 /// The values of [builder], [maintainState], and [fullScreenDialog] must not 7 /// The values of [builder], [maintainState], and [fullScreenDialog] must not
6 /// be null. 8 /// be null.
7 GetRoute({ 9 GetRoute({
@@ -43,8 +45,8 @@ class GetRoute<T> extends PageRoute<T> { @@ -43,8 +45,8 @@ class GetRoute<T> extends PageRoute<T> {
43 @override 45 @override
44 bool canTransitionTo(TransitionRoute<dynamic> nextRoute) { 46 bool canTransitionTo(TransitionRoute<dynamic> nextRoute) {
45 // Don't perform outgoing animation if the next route is a fullscreen dialog. 47 // Don't perform outgoing animation if the next route is a fullscreen dialog.
46 - return (nextRoute is GetRoute && !nextRoute.fullscreenDialog)  
47 - || (nextRoute is CupertinoPageRoute && !nextRoute.fullscreenDialog); 48 + return (nextRoute is GetRoute && !nextRoute.fullscreenDialog) ||
  49 + (nextRoute is CupertinoPageRoute && !nextRoute.fullscreenDialog);
48 } 50 }
49 51
50 @override 52 @override
@@ -57,7 +59,8 @@ class GetRoute<T> extends PageRoute<T> { @@ -57,7 +59,8 @@ class GetRoute<T> extends PageRoute<T> {
57 assert(() { 59 assert(() {
58 if (result == null) { 60 if (result == null) {
59 throw FlutterError.fromParts(<DiagnosticsNode>[ 61 throw FlutterError.fromParts(<DiagnosticsNode>[
60 - ErrorSummary('The builder for route "${settings.name}" returned null.'), 62 + ErrorSummary(
  63 + 'The builder for route "${settings.name}" returned null.'),
61 ErrorDescription('Route builders must never return null.') 64 ErrorDescription('Route builders must never return null.')
62 ]); 65 ]);
63 } 66 }
@@ -71,9 +74,11 @@ class GetRoute<T> extends PageRoute<T> { @@ -71,9 +74,11 @@ class GetRoute<T> extends PageRoute<T> {
71 } 74 }
72 75
73 @override 76 @override
74 - Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) { 77 + Widget buildTransitions(BuildContext context, Animation<double> animation,
  78 + Animation<double> secondaryAnimation, Widget child) {
75 final PageTransitionsTheme theme = Theme.of(context).pageTransitionsTheme; 79 final PageTransitionsTheme theme = Theme.of(context).pageTransitionsTheme;
76 - return theme.buildTransitions<T>(this, context, animation, secondaryAnimation, child); 80 + return theme.buildTransitions<T>(
  81 + this, context, animation, secondaryAnimation, child);
77 } 82 }
78 83
79 @override 84 @override
  1 +import 'package:flutter/material.dart';
1 import 'package:flutter/widgets.dart'; 2 import 'package:flutter/widgets.dart';
  3 +import 'dialog.dart';
2 import 'getroute.dart'; 4 import 'getroute.dart';
3 5
4 class Get { 6 class Get {
@@ -87,6 +89,28 @@ class Get { @@ -87,6 +89,28 @@ class Get {
87 .pushReplacement(GetRoute(opaque: rebuildRoutes, builder: (_) => page)); 89 .pushReplacement(GetRoute(opaque: rebuildRoutes, builder: (_) => page));
88 } 90 }
89 91
  92 + /// Show a dialog. You can choose color and opacity of background
  93 + static dialog(Widget page, {Color color, double opacity = 0.5}) {
  94 + Get.to(DialogGet(child: page, color: color, opacity: opacity));
  95 + }
  96 +
  97 + static defaultDialog(
  98 + {Color color,
  99 + double opacity = 0.5,
  100 + String title = "Alert dialog",
  101 + Widget content,
  102 + Widget cancel,
  103 + Widget confirm}) {
  104 + Get.to(DefaultDialogGet(
  105 + color: color,
  106 + opacity: opacity,
  107 + title: title,
  108 + content: content,
  109 + cancel: cancel,
  110 + confirm: confirm,
  111 + ));
  112 + }
  113 +
90 /// It replaces Navigator.pushAndRemoveUntil, but needs no context 114 /// It replaces Navigator.pushAndRemoveUntil, but needs no context
91 static offAll(Widget page, RoutePredicate predicate, 115 static offAll(Widget page, RoutePredicate predicate,
92 {bool rebuildRoutes = false}) { 116 {bool rebuildRoutes = false}) {
  1 +import 'dart:async';
  2 +import 'dart:ui';
  3 +import 'package:flutter/material.dart';
  4 +import 'package:flutter/scheduler.dart';
  5 +import 'package:get/get.dart';
  6 +import 'snack_route.dart' as route;
  7 +
  8 +typedef void SnackStatusCallback(SnackStatus status);
  9 +typedef void OnTap(GetBar snack);
  10 +
  11 +// ignore: must_be_immutable
  12 +class GetBar<T extends Object> extends StatefulWidget {
  13 + GetBar(
  14 + {Key key,
  15 + String title,
  16 + String message,
  17 + Widget titleText,
  18 + Widget messageText,
  19 + Widget icon,
  20 + bool shouldIconPulse = true,
  21 + double maxWidth,
  22 + EdgeInsets margin = const EdgeInsets.all(0.0),
  23 + EdgeInsets padding = const EdgeInsets.all(16),
  24 + double borderRadius = 0.0,
  25 + Color borderColor,
  26 + double borderWidth = 1.0,
  27 + Color backgroundColor = const Color(0xFF303030),
  28 + Color leftBarIndicatorColor,
  29 + List<BoxShadow> boxShadows,
  30 + Gradient backgroundGradient,
  31 + FlatButton mainButton,
  32 + OnTap onTap,
  33 + Duration duration,
  34 + bool isDismissible = true,
  35 + SnackDismissDirection dismissDirection = SnackDismissDirection.VERTICAL,
  36 + bool showProgressIndicator = false,
  37 + AnimationController progressIndicatorController,
  38 + Color progressIndicatorBackgroundColor,
  39 + Animation<Color> progressIndicatorValueColor,
  40 + SnackPosition snackPosition = SnackPosition.BOTTOM,
  41 + SnackStyle snackStyle = SnackStyle.FLOATING,
  42 + Curve forwardAnimationCurve = Curves.easeOutCirc,
  43 + Curve reverseAnimationCurve = Curves.easeOutCirc,
  44 + Duration animationDuration = const Duration(seconds: 1),
  45 + SnackStatusCallback onStatusChanged,
  46 + double barBlur = 0.0,
  47 + double overlayBlur = 0.0,
  48 + Color overlayColor = Colors.transparent,
  49 + Form userInputForm})
  50 + : this.title = title,
  51 + this.message = message,
  52 + this.titleText = titleText,
  53 + this.messageText = messageText,
  54 + this.icon = icon,
  55 + this.shouldIconPulse = shouldIconPulse,
  56 + this.maxWidth = maxWidth,
  57 + this.margin = margin,
  58 + this.padding = padding,
  59 + this.borderRadius = borderRadius,
  60 + this.borderColor = borderColor,
  61 + this.borderWidth = borderWidth,
  62 + this.backgroundColor = backgroundColor,
  63 + this.leftBarIndicatorColor = leftBarIndicatorColor,
  64 + this.boxShadows = boxShadows,
  65 + this.backgroundGradient = backgroundGradient,
  66 + this.mainButton = mainButton,
  67 + this.onTap = onTap,
  68 + this.duration = duration,
  69 + this.isDismissible = isDismissible,
  70 + this.dismissDirection = dismissDirection,
  71 + this.showProgressIndicator = showProgressIndicator,
  72 + this.progressIndicatorController = progressIndicatorController,
  73 + this.progressIndicatorBackgroundColor =
  74 + progressIndicatorBackgroundColor,
  75 + this.progressIndicatorValueColor = progressIndicatorValueColor,
  76 + this.snackPosition = snackPosition,
  77 + this.snackStyle = snackStyle,
  78 + this.forwardAnimationCurve = forwardAnimationCurve,
  79 + this.reverseAnimationCurve = reverseAnimationCurve,
  80 + this.animationDuration = animationDuration,
  81 + this.barBlur = barBlur,
  82 + this.overlayBlur = overlayBlur,
  83 + this.overlayColor = overlayColor,
  84 + this.userInputForm = userInputForm,
  85 + super(key: key) {
  86 + this.onStatusChanged = onStatusChanged ?? (status) {};
  87 + }
  88 +
  89 + /// A callback for you to listen to the different Snack status
  90 + SnackStatusCallback onStatusChanged;
  91 +
  92 + /// The title displayed to the user
  93 + final String title;
  94 +
  95 + /// The message displayed to the user.
  96 + final String message;
  97 +
  98 + /// Replaces [title]. Although this accepts a [Widget], it is meant to receive [Text] or [RichText]
  99 + final Widget titleText;
  100 +
  101 + /// Replaces [message]. Although this accepts a [Widget], it is meant to receive [Text] or [RichText]
  102 + final Widget messageText;
  103 +
  104 + /// Will be ignored if [backgroundGradient] is not null
  105 + final Color backgroundColor;
  106 +
  107 + /// If not null, shows a left vertical colored bar on notification.
  108 + /// It is not possible to use it with a [Form] and I do not recommend using it with [LinearProgressIndicator]
  109 + final Color leftBarIndicatorColor;
  110 +
  111 + /// [boxShadows] The shadows generated by Snack. Leave it null if you don't want a shadow.
  112 + /// You can use more than one if you feel the need.
  113 + /// Check (this example)[https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/material/shadows.dart]
  114 + final List<BoxShadow> boxShadows;
  115 +
  116 + /// Makes [backgroundColor] be ignored.
  117 + final Gradient backgroundGradient;
  118 +
  119 + /// You can use any widget here, but I recommend [Icon] or [Image] as indication of what kind
  120 + /// of message you are displaying. Other widgets may break the layout
  121 + final Widget icon;
  122 +
  123 + /// An option to animate the icon (if present). Defaults to true.
  124 + final bool shouldIconPulse;
  125 +
  126 + /// A [FlatButton] widget if you need an action from the user.
  127 + final FlatButton mainButton;
  128 +
  129 + /// A callback that registers the user's click anywhere. An alternative to [mainButton]
  130 + final OnTap onTap;
  131 +
  132 + /// How long until Snack will hide itself (be dismissed). To make it indefinite, leave it null.
  133 + final Duration duration;
  134 +
  135 + /// True if you want to show a [LinearProgressIndicator].
  136 + final bool showProgressIndicator;
  137 +
  138 + /// An optional [AnimationController] when you want to control the progress of your [LinearProgressIndicator].
  139 + final AnimationController progressIndicatorController;
  140 +
  141 + /// A [LinearProgressIndicator] configuration parameter.
  142 + final Color progressIndicatorBackgroundColor;
  143 +
  144 + /// A [LinearProgressIndicator] configuration parameter.
  145 + final Animation<Color> progressIndicatorValueColor;
  146 +
  147 + /// Determines if the user can swipe or click the overlay (if [overlayBlur] > 0) to dismiss.
  148 + /// It is recommended that you set [duration] != null if this is false.
  149 + /// If the user swipes to dismiss or clicks the overlay, no value will be returned.
  150 + final bool isDismissible;
  151 +
  152 + /// Used to limit Snack width (usually on large screens)
  153 + final double maxWidth;
  154 +
  155 + /// Adds a custom margin to Snack
  156 + final EdgeInsets margin;
  157 +
  158 + /// Adds a custom padding to Snack
  159 + /// The default follows material design guide line
  160 + final EdgeInsets padding;
  161 +
  162 + /// Adds a radius to all corners of Snack. Best combined with [margin].
  163 + /// I do not recommend using it with [showProgressIndicator] or [leftBarIndicatorColor].
  164 + final double borderRadius;
  165 +
  166 + /// Adds a border to every side of Snack
  167 + /// I do not recommend using it with [showProgressIndicator] or [leftBarIndicatorColor].
  168 + final Color borderColor;
  169 +
  170 + /// Changes the width of the border if [borderColor] is specified
  171 + final double borderWidth;
  172 +
  173 + /// Snack can be based on [SnackPosition.TOP] or on [SnackPosition.BOTTOM] of your screen.
  174 + /// [SnackPosition.BOTTOM] is the default.
  175 + final SnackPosition snackPosition;
  176 +
  177 + /// [SnackDismissDirection.VERTICAL] by default.
  178 + /// Can also be [SnackDismissDirection.HORIZONTAL] in which case both left and right dismiss are allowed.
  179 + final SnackDismissDirection dismissDirection;
  180 +
  181 + /// Snack can be floating or be grounded to the edge of the screen.
  182 + /// If grounded, I do not recommend using [margin] or [borderRadius]. [SnackStyle.FLOATING] is the default
  183 + /// If grounded, I do not recommend using a [backgroundColor] with transparency or [barBlur]
  184 + final SnackStyle snackStyle;
  185 +
  186 + /// The [Curve] animation used when show() is called. [Curves.easeOut] is default
  187 + final Curve forwardAnimationCurve;
  188 +
  189 + /// The [Curve] animation used when dismiss() is called. [Curves.fastOutSlowIn] is default
  190 + final Curve reverseAnimationCurve;
  191 +
  192 + /// Use it to speed up or slow down the animation duration
  193 + final Duration animationDuration;
  194 +
  195 + /// Default is 0.0. If different than 0.0, blurs only Snack's background.
  196 + /// To take effect, make sure your [backgroundColor] has some opacity.
  197 + /// The greater the value, the greater the blur.
  198 + final double barBlur;
  199 +
  200 + /// Default is 0.0. If different than 0.0, creates a blurred
  201 + /// overlay that prevents the user from interacting with the screen.
  202 + /// The greater the value, the greater the blur.
  203 + final double overlayBlur;
  204 +
  205 + /// Default is [Colors.transparent]. Only takes effect if [overlayBlur] > 0.0.
  206 + /// Make sure you use a color with transparency here e.g. Colors.grey[600].withOpacity(0.2).
  207 + final Color overlayColor;
  208 +
  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;
  211 +
  212 + route.SnackRoute<T> _snackRoute;
  213 +
  214 + /// Show the snack. Kicks in [SnackStatus.IS_APPEARING] state followed by [SnackStatus.SHOWING]
  215 + Future<T> show() async {
  216 + _snackRoute = route.showSnack<T>(
  217 + snack: this,
  218 + );
  219 +
  220 + return await Get.key.currentState.push(_snackRoute);
  221 + }
  222 +
  223 + /// Dismisses the snack causing is to return a future containing [result].
  224 + /// When this future finishes, it is guaranteed that Snack was dismissed.
  225 + Future<T> dismiss([T result]) async {
  226 + // If route was never initialized, do nothing
  227 + if (_snackRoute == null) {
  228 + return null;
  229 + }
  230 +
  231 + if (_snackRoute.isCurrent) {
  232 + _snackRoute.navigator.pop(result);
  233 + return _snackRoute.completed;
  234 + } else if (_snackRoute.isActive) {
  235 + // removeRoute is called every time you dismiss a Snack that is not the top route.
  236 + // It will not animate back and listeners will not detect SnackStatus.IS_HIDING or SnackStatus.DISMISSED
  237 + // To avoid this, always make sure that Snack is the top route when it is being dismissed
  238 + _snackRoute.navigator.removeRoute(_snackRoute);
  239 + }
  240 +
  241 + return null;
  242 + }
  243 +
  244 + /// Checks if the snack is visible
  245 + bool isShowing() {
  246 + return _snackRoute?.currentStatus == SnackStatus.SHOWING;
  247 + }
  248 +
  249 + /// Checks if the snack is dismissed
  250 + bool isDismissed() {
  251 + return _snackRoute?.currentStatus == SnackStatus.DISMISSED;
  252 + }
  253 +
  254 + @override
  255 + State createState() {
  256 + return _GetBarState<T>();
  257 + }
  258 +}
  259 +
  260 +class _GetBarState<K extends Object> extends State<GetBar>
  261 + with TickerProviderStateMixin {
  262 + SnackStatus currentStatus;
  263 +
  264 + AnimationController _fadeController;
  265 + Animation<double> _fadeAnimation;
  266 +
  267 + final Widget _emptyWidget = SizedBox(width: 0.0, height: 0.0);
  268 + final double _initialOpacity = 1.0;
  269 + final double _finalOpacity = 0.4;
  270 +
  271 + final Duration _pulseAnimationDuration = Duration(seconds: 1);
  272 +
  273 + bool _isTitlePresent;
  274 + double _messageTopMargin;
  275 +
  276 + FocusScopeNode _focusNode;
  277 + FocusAttachment _focusAttachment;
  278 +
  279 + @override
  280 + void initState() {
  281 + super.initState();
  282 +
  283 + assert(
  284 + ((widget.userInputForm != null ||
  285 + ((widget.message != null && widget.message.isNotEmpty) ||
  286 + widget.messageText != null))),
  287 + "A message is mandatory if you are not using userInputForm. Set either a message or messageText");
  288 +
  289 + _isTitlePresent = (widget.title != null || widget.titleText != null);
  290 + _messageTopMargin = _isTitlePresent ? 6.0 : widget.padding.top;
  291 +
  292 + _configureLeftBarFuture();
  293 + _configureProgressIndicatorAnimation();
  294 +
  295 + if (widget.icon != null && widget.shouldIconPulse) {
  296 + _configurePulseAnimation();
  297 + _fadeController?.forward();
  298 + }
  299 +
  300 + _focusNode = FocusScopeNode();
  301 + _focusAttachment = _focusNode.attach(context);
  302 + }
  303 +
  304 + @override
  305 + void dispose() {
  306 + _fadeController?.dispose();
  307 +
  308 + widget.progressIndicatorController?.removeListener(_progressListener);
  309 + widget.progressIndicatorController?.dispose();
  310 +
  311 + _focusAttachment.detach();
  312 + _focusNode.dispose();
  313 + super.dispose();
  314 + }
  315 +
  316 + final Completer<Size> _boxHeightCompleter = Completer<Size>();
  317 +
  318 + void _configureLeftBarFuture() {
  319 + SchedulerBinding.instance.addPostFrameCallback(
  320 + (_) {
  321 + final keyContext = backgroundBoxKey.currentContext;
  322 +
  323 + if (keyContext != null) {
  324 + final RenderBox box = keyContext.findRenderObject();
  325 + _boxHeightCompleter.complete(box.size);
  326 + }
  327 + },
  328 + );
  329 + }
  330 +
  331 + void _configurePulseAnimation() {
  332 + _fadeController =
  333 + AnimationController(vsync: this, duration: _pulseAnimationDuration);
  334 + _fadeAnimation = Tween(begin: _initialOpacity, end: _finalOpacity).animate(
  335 + CurvedAnimation(
  336 + parent: _fadeController,
  337 + curve: Curves.linear,
  338 + ),
  339 + );
  340 +
  341 + _fadeController.addStatusListener((status) {
  342 + if (status == AnimationStatus.completed) {
  343 + _fadeController.reverse();
  344 + }
  345 + if (status == AnimationStatus.dismissed) {
  346 + _fadeController.forward();
  347 + }
  348 + });
  349 +
  350 + _fadeController.forward();
  351 + }
  352 +
  353 + Function _progressListener;
  354 +
  355 + void _configureProgressIndicatorAnimation() {
  356 + if (widget.showProgressIndicator &&
  357 + widget.progressIndicatorController != null) {
  358 + _progressListener = () {
  359 + setState(() {});
  360 + };
  361 + widget.progressIndicatorController.addListener(_progressListener);
  362 +
  363 + _progressAnimation = CurvedAnimation(
  364 + curve: Curves.linear, parent: widget.progressIndicatorController);
  365 + }
  366 + }
  367 +
  368 + @override
  369 + Widget build(BuildContext context) {
  370 + return Align(
  371 + heightFactor: 1.0,
  372 + child: Material(
  373 + color: widget.snackStyle == SnackStyle.FLOATING
  374 + ? Colors.transparent
  375 + : widget.backgroundColor,
  376 + child: SafeArea(
  377 + minimum: widget.snackPosition == SnackPosition.BOTTOM
  378 + ? EdgeInsets.only(
  379 + bottom: MediaQuery.of(context).viewInsets.bottom)
  380 + : EdgeInsets.only(top: MediaQuery.of(context).viewInsets.top),
  381 + bottom: widget.snackPosition == SnackPosition.BOTTOM,
  382 + top: widget.snackPosition == SnackPosition.TOP,
  383 + left: false,
  384 + right: false,
  385 + child: _getSnack(),
  386 + ),
  387 + ),
  388 + );
  389 + }
  390 +
  391 + Widget _getSnack() {
  392 + Widget snack;
  393 +
  394 + if (widget.userInputForm != null) {
  395 + snack = _generateInputSnack();
  396 + } else {
  397 + snack = _generateSnack();
  398 + }
  399 +
  400 + return Stack(
  401 + children: [
  402 + FutureBuilder(
  403 + future: _boxHeightCompleter.future,
  404 + builder: (context, AsyncSnapshot<Size> snapshot) {
  405 + if (snapshot.hasData) {
  406 + return ClipRRect(
  407 + borderRadius: BorderRadius.circular(widget.borderRadius),
  408 + child: BackdropFilter(
  409 + filter: ImageFilter.blur(
  410 + sigmaX: widget.barBlur, sigmaY: widget.barBlur),
  411 + child: Container(
  412 + height: snapshot.data.height,
  413 + width: snapshot.data.width,
  414 + decoration: BoxDecoration(
  415 + color: Colors.transparent,
  416 + borderRadius: BorderRadius.circular(widget.borderRadius),
  417 + ),
  418 + ),
  419 + ),
  420 + );
  421 + } else {
  422 + return _emptyWidget;
  423 + }
  424 + },
  425 + ),
  426 + snack,
  427 + ],
  428 + );
  429 + }
  430 +
  431 + Widget _generateInputSnack() {
  432 + return Container(
  433 + key: backgroundBoxKey,
  434 + constraints: widget.maxWidth != null
  435 + ? BoxConstraints(maxWidth: widget.maxWidth)
  436 + : null,
  437 + decoration: BoxDecoration(
  438 + color: widget.backgroundColor,
  439 + gradient: widget.backgroundGradient,
  440 + boxShadow: widget.boxShadows,
  441 + borderRadius: BorderRadius.circular(widget.borderRadius),
  442 + border: widget.borderColor != null
  443 + ? Border.all(color: widget.borderColor, width: widget.borderWidth)
  444 + : null,
  445 + ),
  446 + child: Padding(
  447 + padding: const EdgeInsets.only(
  448 + left: 8.0, right: 8.0, bottom: 8.0, top: 16.0),
  449 + child: FocusScope(
  450 + child: widget.userInputForm,
  451 + node: _focusNode,
  452 + autofocus: true,
  453 + ),
  454 + ),
  455 + );
  456 + }
  457 +
  458 + CurvedAnimation _progressAnimation;
  459 + GlobalKey backgroundBoxKey = GlobalKey();
  460 +
  461 + Widget _generateSnack() {
  462 + return Container(
  463 + key: backgroundBoxKey,
  464 + constraints: widget.maxWidth != null
  465 + ? BoxConstraints(maxWidth: widget.maxWidth)
  466 + : null,
  467 + decoration: BoxDecoration(
  468 + color: widget.backgroundColor,
  469 + gradient: widget.backgroundGradient,
  470 + boxShadow: widget.boxShadows,
  471 + borderRadius: BorderRadius.circular(widget.borderRadius),
  472 + border: widget.borderColor != null
  473 + ? Border.all(color: widget.borderColor, width: widget.borderWidth)
  474 + : null,
  475 + ),
  476 + child: Column(
  477 + mainAxisSize: MainAxisSize.min,
  478 + children: [
  479 + widget.showProgressIndicator
  480 + ? LinearProgressIndicator(
  481 + value: widget.progressIndicatorController != null
  482 + ? _progressAnimation.value
  483 + : null,
  484 + backgroundColor: widget.progressIndicatorBackgroundColor,
  485 + valueColor: widget.progressIndicatorValueColor,
  486 + )
  487 + : _emptyWidget,
  488 + Row(
  489 + mainAxisSize: MainAxisSize.max,
  490 + children: _getAppropriateRowLayout(),
  491 + ),
  492 + ],
  493 + ),
  494 + );
  495 + }
  496 +
  497 + List<Widget> _getAppropriateRowLayout() {
  498 + double buttonRightPadding;
  499 + double iconPadding = 0;
  500 + if (widget.padding.right - 12 < 0) {
  501 + buttonRightPadding = 4;
  502 + } else {
  503 + buttonRightPadding = widget.padding.right - 12;
  504 + }
  505 +
  506 + if (widget.padding.left > 16.0) {
  507 + iconPadding = widget.padding.left;
  508 + }
  509 +
  510 + if (widget.icon == null && widget.mainButton == null) {
  511 + return [
  512 + _buildLeftBarIndicator(),
  513 + Expanded(
  514 + flex: 1,
  515 + child: Column(
  516 + crossAxisAlignment: CrossAxisAlignment.stretch,
  517 + mainAxisSize: MainAxisSize.min,
  518 + children: <Widget>[
  519 + (_isTitlePresent)
  520 + ? Padding(
  521 + padding: EdgeInsets.only(
  522 + top: widget.padding.top,
  523 + left: widget.padding.left,
  524 + right: widget.padding.right,
  525 + ),
  526 + child: _getTitleText(),
  527 + )
  528 + : _emptyWidget,
  529 + Padding(
  530 + padding: EdgeInsets.only(
  531 + top: _messageTopMargin,
  532 + left: widget.padding.left,
  533 + right: widget.padding.right,
  534 + bottom: widget.padding.bottom,
  535 + ),
  536 + child: widget.messageText ?? _getDefaultNotificationText(),
  537 + ),
  538 + ],
  539 + ),
  540 + ),
  541 + ];
  542 + } else if (widget.icon != null && widget.mainButton == null) {
  543 + return <Widget>[
  544 + _buildLeftBarIndicator(),
  545 + ConstrainedBox(
  546 + constraints: BoxConstraints.tightFor(width: 42.0 + iconPadding),
  547 + child: _getIcon(),
  548 + ),
  549 + Expanded(
  550 + flex: 1,
  551 + child: Column(
  552 + crossAxisAlignment: CrossAxisAlignment.stretch,
  553 + mainAxisSize: MainAxisSize.min,
  554 + children: <Widget>[
  555 + (_isTitlePresent)
  556 + ? Padding(
  557 + padding: EdgeInsets.only(
  558 + top: widget.padding.top,
  559 + left: 4.0,
  560 + right: widget.padding.left,
  561 + ),
  562 + child: _getTitleText(),
  563 + )
  564 + : _emptyWidget,
  565 + Padding(
  566 + padding: EdgeInsets.only(
  567 + top: _messageTopMargin,
  568 + left: 4.0,
  569 + right: widget.padding.right,
  570 + bottom: widget.padding.bottom,
  571 + ),
  572 + child: widget.messageText ?? _getDefaultNotificationText(),
  573 + ),
  574 + ],
  575 + ),
  576 + ),
  577 + ];
  578 + } else if (widget.icon == null && widget.mainButton != null) {
  579 + return <Widget>[
  580 + _buildLeftBarIndicator(),
  581 + Expanded(
  582 + flex: 1,
  583 + child: Column(
  584 + crossAxisAlignment: CrossAxisAlignment.stretch,
  585 + mainAxisSize: MainAxisSize.min,
  586 + children: <Widget>[
  587 + (_isTitlePresent)
  588 + ? Padding(
  589 + padding: EdgeInsets.only(
  590 + top: widget.padding.top,
  591 + left: widget.padding.left,
  592 + right: widget.padding.right,
  593 + ),
  594 + child: _getTitleText(),
  595 + )
  596 + : _emptyWidget,
  597 + Padding(
  598 + padding: EdgeInsets.only(
  599 + top: _messageTopMargin,
  600 + left: widget.padding.left,
  601 + right: 8.0,
  602 + bottom: widget.padding.bottom,
  603 + ),
  604 + child: widget.messageText ?? _getDefaultNotificationText(),
  605 + ),
  606 + ],
  607 + ),
  608 + ),
  609 + Padding(
  610 + padding: EdgeInsets.only(right: buttonRightPadding),
  611 + child: _getMainActionButton(),
  612 + ),
  613 + ];
  614 + } else {
  615 + return <Widget>[
  616 + _buildLeftBarIndicator(),
  617 + ConstrainedBox(
  618 + constraints: BoxConstraints.tightFor(width: 42.0 + iconPadding),
  619 + child: _getIcon(),
  620 + ),
  621 + Expanded(
  622 + flex: 1,
  623 + child: Column(
  624 + crossAxisAlignment: CrossAxisAlignment.stretch,
  625 + mainAxisSize: MainAxisSize.min,
  626 + children: <Widget>[
  627 + (_isTitlePresent)
  628 + ? Padding(
  629 + padding: EdgeInsets.only(
  630 + top: widget.padding.top,
  631 + left: 4.0,
  632 + right: 8.0,
  633 + ),
  634 + child: _getTitleText(),
  635 + )
  636 + : _emptyWidget,
  637 + Padding(
  638 + padding: EdgeInsets.only(
  639 + top: _messageTopMargin,
  640 + left: 4.0,
  641 + right: 8.0,
  642 + bottom: widget.padding.bottom,
  643 + ),
  644 + child: widget.messageText ?? _getDefaultNotificationText(),
  645 + ),
  646 + ],
  647 + ),
  648 + ),
  649 + Padding(
  650 + padding: EdgeInsets.only(right: buttonRightPadding),
  651 + child: _getMainActionButton(),
  652 + ) ??
  653 + _emptyWidget,
  654 + ];
  655 + }
  656 + }
  657 +
  658 + Widget _buildLeftBarIndicator() {
  659 + if (widget.leftBarIndicatorColor != null) {
  660 + return FutureBuilder(
  661 + future: _boxHeightCompleter.future,
  662 + builder: (BuildContext buildContext, AsyncSnapshot<Size> snapshot) {
  663 + if (snapshot.hasData) {
  664 + return Container(
  665 + color: widget.leftBarIndicatorColor,
  666 + width: 5.0,
  667 + height: snapshot.data.height,
  668 + );
  669 + } else {
  670 + return _emptyWidget;
  671 + }
  672 + },
  673 + );
  674 + } else {
  675 + return _emptyWidget;
  676 + }
  677 + }
  678 +
  679 + Widget _getIcon() {
  680 + if (widget.icon != null && widget.icon is Icon && widget.shouldIconPulse) {
  681 + return FadeTransition(
  682 + opacity: _fadeAnimation,
  683 + child: widget.icon,
  684 + );
  685 + } else if (widget.icon != null) {
  686 + return widget.icon;
  687 + } else {
  688 + return _emptyWidget;
  689 + }
  690 + }
  691 +
  692 + Widget _getTitleText() {
  693 + return widget.titleText != null
  694 + ? widget.titleText
  695 + : Text(
  696 + widget.title ?? "",
  697 + style: TextStyle(
  698 + fontSize: 16.0,
  699 + color: Colors.white,
  700 + fontWeight: FontWeight.bold),
  701 + );
  702 + }
  703 +
  704 + Text _getDefaultNotificationText() {
  705 + return Text(
  706 + widget.message ?? "",
  707 + style: TextStyle(fontSize: 14.0, color: Colors.white),
  708 + );
  709 + }
  710 +
  711 + FlatButton _getMainActionButton() {
  712 + if (widget.mainButton != null) {
  713 + return widget.mainButton;
  714 + } else {
  715 + return null;
  716 + }
  717 + }
  718 +}
  719 +
  720 +/// Indicates if snack is going to start at the [TOP] or at the [BOTTOM]
  721 +enum SnackPosition { TOP, BOTTOM }
  722 +
  723 +/// Indicates if snack will be attached to the edge of the screen or not
  724 +enum SnackStyle { FLOATING, GROUNDED }
  725 +
  726 +/// Indicates the direction in which it is possible to dismiss
  727 +/// If vertical, dismiss up will be allowed if [SnackPosition.TOP]
  728 +/// If vertical, dismiss down will be allowed if [SnackPosition.BOTTOM]
  729 +enum SnackDismissDirection { HORIZONTAL, VERTICAL }
  730 +
  731 +/// Indicates the animation status
  732 +/// [SnackStatus.SHOWING] Snack has stopped and the user can see it
  733 +/// [SnackStatus.DISMISSED] Snack has finished its mission and returned any pending values
  734 +/// [SnackStatus.IS_APPEARING] Snack is moving towards [SnackStatus.SHOWING]
  735 +/// [SnackStatus.IS_HIDING] Snack is moving towards [] [SnackStatus.DISMISSED]
  736 +enum SnackStatus { SHOWING, DISMISSED, IS_APPEARING, IS_HIDING }
  1 +import 'dart:async';
  2 +import 'dart:ui';
  3 +import 'snack.dart';
  4 +import 'package:flutter/material.dart';
  5 +import 'package:flutter/scheduler.dart';
  6 +
  7 +class SnackRoute<T> extends OverlayRoute<T> {
  8 + Animation<double> _filterBlurAnimation;
  9 + Animation<Color> _filterColorAnimation;
  10 +
  11 + SnackRoute({
  12 + @required this.snack,
  13 + RouteSettings settings,
  14 + }) : super(settings: settings) {
  15 + this._builder = Builder(builder: (BuildContext innerContext) {
  16 + return GestureDetector(
  17 + child: snack,
  18 + onTap: snack.onTap != null
  19 + ? () {
  20 + snack.onTap(snack);
  21 + }
  22 + : null,
  23 + );
  24 + });
  25 +
  26 + _configureAlignment(this.snack.snackPosition);
  27 + _onStatusChanged = snack.onStatusChanged;
  28 + }
  29 +
  30 + _configureAlignment(SnackPosition snackPosition) {
  31 + switch (snack.snackPosition) {
  32 + case SnackPosition.TOP:
  33 + {
  34 + _initialAlignment = Alignment(-1.0, -2.0);
  35 + _endAlignment = Alignment(-1.0, -1.0);
  36 + break;
  37 + }
  38 + case SnackPosition.BOTTOM:
  39 + {
  40 + _initialAlignment = Alignment(-1.0, 2.0);
  41 + _endAlignment = Alignment(-1.0, 1.0);
  42 + break;
  43 + }
  44 + }
  45 + }
  46 +
  47 + GetBar snack;
  48 + Builder _builder;
  49 +
  50 + Future<T> get completed => _transitionCompleter.future;
  51 + final Completer<T> _transitionCompleter = Completer<T>();
  52 +
  53 + SnackStatusCallback _onStatusChanged;
  54 + Alignment _initialAlignment;
  55 + Alignment _endAlignment;
  56 + bool _wasDismissedBySwipe = false;
  57 +
  58 + Timer _timer;
  59 +
  60 + bool get opaque => false;
  61 +
  62 + @override
  63 + Iterable<OverlayEntry> createOverlayEntries() {
  64 + List<OverlayEntry> overlays = [];
  65 +
  66 + if (snack.overlayBlur > 0.0) {
  67 + overlays.add(
  68 + OverlayEntry(
  69 + builder: (BuildContext context) {
  70 + return GestureDetector(
  71 + onTap: snack.isDismissible ? () => snack.dismiss() : null,
  72 + child: AnimatedBuilder(
  73 + animation: _filterBlurAnimation,
  74 + builder: (context, child) {
  75 + return BackdropFilter(
  76 + filter: ImageFilter.blur(
  77 + sigmaX: _filterBlurAnimation.value,
  78 + sigmaY: _filterBlurAnimation.value),
  79 + child: Container(
  80 + constraints: BoxConstraints.expand(),
  81 + color: _filterColorAnimation.value,
  82 + ),
  83 + );
  84 + },
  85 + ),
  86 + );
  87 + },
  88 + maintainState: false,
  89 + opaque: opaque),
  90 + );
  91 + }
  92 +
  93 + overlays.add(
  94 + OverlayEntry(
  95 + builder: (BuildContext context) {
  96 + final Widget annotatedChild = Semantics(
  97 + child: AlignTransition(
  98 + alignment: _animation,
  99 + child: snack.isDismissible
  100 + ? _getDismissibleSnack(_builder)
  101 + : _getSnack(),
  102 + ),
  103 + focused: false,
  104 + container: true,
  105 + explicitChildNodes: true,
  106 + );
  107 + return annotatedChild;
  108 + },
  109 + maintainState: false,
  110 + opaque: opaque),
  111 + );
  112 +
  113 + return overlays;
  114 + }
  115 +
  116 + /// This string is a workaround until Dismissible supports a returning item
  117 + String dismissibleKeyGen = "";
  118 +
  119 + Widget _getDismissibleSnack(Widget child) {
  120 + return Dismissible(
  121 + direction: _getDismissDirection(),
  122 + resizeDuration: null,
  123 + confirmDismiss: (_) {
  124 + if (currentStatus == SnackStatus.IS_APPEARING ||
  125 + currentStatus == SnackStatus.IS_HIDING) {
  126 + return Future.value(false);
  127 + }
  128 + return Future.value(true);
  129 + },
  130 + key: Key(dismissibleKeyGen),
  131 + onDismissed: (_) {
  132 + dismissibleKeyGen += "1";
  133 + _cancelTimer();
  134 + _wasDismissedBySwipe = true;
  135 +
  136 + if (isCurrent) {
  137 + navigator.pop();
  138 + } else {
  139 + navigator.removeRoute(this);
  140 + }
  141 + },
  142 + child: _getSnack(),
  143 + );
  144 + }
  145 +
  146 + Widget _getSnack() {
  147 + return Container(
  148 + margin: snack.margin,
  149 + child: _builder,
  150 + );
  151 + }
  152 +
  153 + DismissDirection _getDismissDirection() {
  154 + if (snack.dismissDirection == SnackDismissDirection.HORIZONTAL) {
  155 + return DismissDirection.horizontal;
  156 + } else {
  157 + if (snack.snackPosition == SnackPosition.TOP) {
  158 + return DismissDirection.up;
  159 + } else {
  160 + return DismissDirection.down;
  161 + }
  162 + }
  163 + }
  164 +
  165 + @override
  166 + bool get finishedWhenPopped =>
  167 + _controller.status == AnimationStatus.dismissed;
  168 +
  169 + /// The animation that drives the route's transition and the previous route's
  170 + /// forward transition.
  171 + Animation<Alignment> get animation => _animation;
  172 + Animation<Alignment> _animation;
  173 +
  174 + /// The animation controller that the route uses to drive the transitions.
  175 + ///
  176 + /// The animation itself is exposed by the [animation] property.
  177 + @protected
  178 + AnimationController get controller => _controller;
  179 + AnimationController _controller;
  180 +
  181 + /// Called to create the animation controller that will drive the transitions to
  182 + /// this route from the previous one, and back to the previous route from this
  183 + /// one.
  184 + AnimationController createAnimationController() {
  185 + assert(!_transitionCompleter.isCompleted,
  186 + 'Cannot reuse a $runtimeType after disposing it.');
  187 + assert(snack.animationDuration != null &&
  188 + snack.animationDuration >= Duration.zero);
  189 + return AnimationController(
  190 + duration: snack.animationDuration,
  191 + debugLabel: debugLabel,
  192 + vsync: navigator,
  193 + );
  194 + }
  195 +
  196 + /// Called to create the animation that exposes the current progress of
  197 + /// the transition controlled by the animation controller created by
  198 + /// [createAnimationController()].
  199 + Animation<Alignment> createAnimation() {
  200 + assert(!_transitionCompleter.isCompleted,
  201 + 'Cannot reuse a $runtimeType after disposing it.');
  202 + assert(_controller != null);
  203 + return AlignmentTween(begin: _initialAlignment, end: _endAlignment).animate(
  204 + CurvedAnimation(
  205 + parent: _controller,
  206 + curve: snack.forwardAnimationCurve,
  207 + reverseCurve: snack.reverseAnimationCurve,
  208 + ),
  209 + );
  210 + }
  211 +
  212 + Animation<double> createBlurFilterAnimation() {
  213 + return Tween(begin: 0.0, end: snack.overlayBlur).animate(
  214 + CurvedAnimation(
  215 + parent: _controller,
  216 + curve: Interval(
  217 + 0.0,
  218 + 0.35,
  219 + curve: Curves.easeInOutCirc,
  220 + ),
  221 + ),
  222 + );
  223 + }
  224 +
  225 + Animation<Color> createColorFilterAnimation() {
  226 + return ColorTween(begin: Colors.transparent, end: snack.overlayColor)
  227 + .animate(
  228 + CurvedAnimation(
  229 + parent: _controller,
  230 + curve: Interval(
  231 + 0.0,
  232 + 0.35,
  233 + curve: Curves.easeInOutCirc,
  234 + ),
  235 + ),
  236 + );
  237 + }
  238 +
  239 + T _result;
  240 + SnackStatus currentStatus;
  241 +
  242 + //copy of `routes.dart`
  243 + void _handleStatusChanged(AnimationStatus status) {
  244 + switch (status) {
  245 + case AnimationStatus.completed:
  246 + currentStatus = SnackStatus.SHOWING;
  247 + _onStatusChanged(currentStatus);
  248 + if (overlayEntries.isNotEmpty) overlayEntries.first.opaque = opaque;
  249 +
  250 + break;
  251 + case AnimationStatus.forward:
  252 + currentStatus = SnackStatus.IS_APPEARING;
  253 + _onStatusChanged(currentStatus);
  254 + break;
  255 + case AnimationStatus.reverse:
  256 + currentStatus = SnackStatus.IS_HIDING;
  257 + _onStatusChanged(currentStatus);
  258 + if (overlayEntries.isNotEmpty) overlayEntries.first.opaque = false;
  259 + break;
  260 + case AnimationStatus.dismissed:
  261 + assert(!overlayEntries.first.opaque);
  262 + // We might still be the current route if a subclass is controlling the
  263 + // the transition and hits the dismissed status. For example, the iOS
  264 + // back gesture drives this animation to the dismissed status before
  265 + // popping the navigator.
  266 + currentStatus = SnackStatus.DISMISSED;
  267 + _onStatusChanged(currentStatus);
  268 +
  269 + if (!isCurrent) {
  270 + navigator.finalizeRoute(this);
  271 + assert(overlayEntries.isEmpty);
  272 + }
  273 + break;
  274 + }
  275 + changedInternalState();
  276 + }
  277 +
  278 + @override
  279 + void install(OverlayEntry insertionPoint) {
  280 + assert(!_transitionCompleter.isCompleted,
  281 + 'Cannot install a $runtimeType after disposing it.');
  282 + _controller = createAnimationController();
  283 + assert(_controller != null,
  284 + '$runtimeType.createAnimationController() returned null.');
  285 + _filterBlurAnimation = createBlurFilterAnimation();
  286 + _filterColorAnimation = createColorFilterAnimation();
  287 + _animation = createAnimation();
  288 + assert(_animation != null, '$runtimeType.createAnimation() returned null.');
  289 + super.install(insertionPoint);
  290 + }
  291 +
  292 + @override
  293 + TickerFuture didPush() {
  294 + super.didPush();
  295 + assert(_controller != null,
  296 + '$runtimeType.didPush called before calling install() or after calling dispose().');
  297 + assert(!_transitionCompleter.isCompleted,
  298 + 'Cannot reuse a $runtimeType after disposing it.');
  299 + _animation.addStatusListener(_handleStatusChanged);
  300 + _configureTimer();
  301 + return _controller.forward();
  302 + }
  303 +
  304 + @override
  305 + void didReplace(Route<dynamic> oldRoute) {
  306 + assert(_controller != null,
  307 + '$runtimeType.didReplace called before calling install() or after calling dispose().');
  308 + assert(!_transitionCompleter.isCompleted,
  309 + 'Cannot reuse a $runtimeType after disposing it.');
  310 + if (oldRoute is SnackRoute) _controller.value = oldRoute._controller.value;
  311 + _animation.addStatusListener(_handleStatusChanged);
  312 + super.didReplace(oldRoute);
  313 + }
  314 +
  315 + @override
  316 + bool didPop(T result) {
  317 + assert(_controller != null,
  318 + '$runtimeType.didPop called before calling install() or after calling dispose().');
  319 + assert(!_transitionCompleter.isCompleted,
  320 + 'Cannot reuse a $runtimeType after disposing it.');
  321 +
  322 + _result = result;
  323 + _cancelTimer();
  324 +
  325 + if (_wasDismissedBySwipe) {
  326 + Timer(Duration(milliseconds: 200), () {
  327 + _controller.reset();
  328 + });
  329 +
  330 + _wasDismissedBySwipe = false;
  331 + } else {
  332 + _controller.reverse();
  333 + }
  334 +
  335 + return super.didPop(result);
  336 + }
  337 +
  338 + void _configureTimer() {
  339 + if (snack.duration != null) {
  340 + if (_timer != null && _timer.isActive) {
  341 + _timer.cancel();
  342 + }
  343 + _timer = Timer(snack.duration, () {
  344 + if (this.isCurrent) {
  345 + navigator.pop();
  346 + } else if (this.isActive) {
  347 + navigator.removeRoute(this);
  348 + }
  349 + });
  350 + } else {
  351 + if (_timer != null) {
  352 + _timer.cancel();
  353 + }
  354 + }
  355 + }
  356 +
  357 + void _cancelTimer() {
  358 + if (_timer != null && _timer.isActive) {
  359 + _timer.cancel();
  360 + }
  361 + }
  362 +
  363 + /// Whether this route can perform a transition to the given route.
  364 + /// Subclasses can override this method to restrict the set of routes they
  365 + /// need to coordinate transitions with.
  366 + bool canTransitionTo(SnackRoute<dynamic> nextRoute) => true;
  367 +
  368 + /// Whether this route can perform a transition from the given route.
  369 + ///
  370 + /// Subclasses can override this method to restrict the set of routes they
  371 + /// need to coordinate transitions with.
  372 + bool canTransitionFrom(SnackRoute<dynamic> previousRoute) => true;
  373 +
  374 + @override
  375 + void dispose() {
  376 + assert(!_transitionCompleter.isCompleted,
  377 + 'Cannot dispose a $runtimeType twice.');
  378 + _controller?.dispose();
  379 + _transitionCompleter.complete(_result);
  380 + super.dispose();
  381 + }
  382 +
  383 + /// A short description of this route useful for debugging.
  384 + String get debugLabel => '$runtimeType';
  385 +
  386 + @override
  387 + String toString() => '$runtimeType(animation: $_controller)';
  388 +}
  389 +
  390 +SnackRoute showSnack<T>({@required GetBar snack}) {
  391 + assert(snack != null);
  392 +
  393 + return SnackRoute<T>(
  394 + snack: snack,
  395 + settings: RouteSettings(name: "getroute"),
  396 + );
  397 +}
  1 +# Generated by pub
  2 +# See https://dart.dev/tools/pub/glossary#lockfile
  3 +packages:
  4 + archive:
  5 + dependency: transitive
  6 + description:
  7 + name: archive
  8 + url: "https://pub.dartlang.org"
  9 + source: hosted
  10 + version: "2.0.11"
  11 + args:
  12 + dependency: transitive
  13 + description:
  14 + name: args
  15 + url: "https://pub.dartlang.org"
  16 + source: hosted
  17 + version: "1.5.2"
  18 + async:
  19 + dependency: transitive
  20 + description:
  21 + name: async
  22 + url: "https://pub.dartlang.org"
  23 + source: hosted
  24 + version: "2.4.0"
  25 + boolean_selector:
  26 + dependency: transitive
  27 + description:
  28 + name: boolean_selector
  29 + url: "https://pub.dartlang.org"
  30 + source: hosted
  31 + version: "1.0.5"
  32 + charcode:
  33 + dependency: transitive
  34 + description:
  35 + name: charcode
  36 + url: "https://pub.dartlang.org"
  37 + source: hosted
  38 + version: "1.1.2"
  39 + collection:
  40 + dependency: transitive
  41 + description:
  42 + name: collection
  43 + url: "https://pub.dartlang.org"
  44 + source: hosted
  45 + version: "1.14.11"
  46 + convert:
  47 + dependency: transitive
  48 + description:
  49 + name: convert
  50 + url: "https://pub.dartlang.org"
  51 + source: hosted
  52 + version: "2.1.1"
  53 + crypto:
  54 + dependency: transitive
  55 + description:
  56 + name: crypto
  57 + url: "https://pub.dartlang.org"
  58 + source: hosted
  59 + version: "2.1.3"
  60 + flutter:
  61 + dependency: "direct main"
  62 + description: flutter
  63 + source: sdk
  64 + version: "0.0.0"
  65 + flutter_test:
  66 + dependency: "direct dev"
  67 + description: flutter
  68 + source: sdk
  69 + version: "0.0.0"
  70 + image:
  71 + dependency: transitive
  72 + description:
  73 + name: image
  74 + url: "https://pub.dartlang.org"
  75 + source: hosted
  76 + version: "2.1.4"
  77 + matcher:
  78 + dependency: transitive
  79 + description:
  80 + name: matcher
  81 + url: "https://pub.dartlang.org"
  82 + source: hosted
  83 + version: "0.12.6"
  84 + meta:
  85 + dependency: transitive
  86 + description:
  87 + name: meta
  88 + url: "https://pub.dartlang.org"
  89 + source: hosted
  90 + version: "1.1.8"
  91 + path:
  92 + dependency: transitive
  93 + description:
  94 + name: path
  95 + url: "https://pub.dartlang.org"
  96 + source: hosted
  97 + version: "1.6.4"
  98 + pedantic:
  99 + dependency: transitive
  100 + description:
  101 + name: pedantic
  102 + url: "https://pub.dartlang.org"
  103 + source: hosted
  104 + version: "1.8.0+1"
  105 + petitparser:
  106 + dependency: transitive
  107 + description:
  108 + name: petitparser
  109 + url: "https://pub.dartlang.org"
  110 + source: hosted
  111 + version: "2.4.0"
  112 + quiver:
  113 + dependency: transitive
  114 + description:
  115 + name: quiver
  116 + url: "https://pub.dartlang.org"
  117 + source: hosted
  118 + version: "2.0.5"
  119 + sky_engine:
  120 + dependency: transitive
  121 + description: flutter
  122 + source: sdk
  123 + version: "0.0.99"
  124 + source_span:
  125 + dependency: transitive
  126 + description:
  127 + name: source_span
  128 + url: "https://pub.dartlang.org"
  129 + source: hosted
  130 + version: "1.5.5"
  131 + stack_trace:
  132 + dependency: transitive
  133 + description:
  134 + name: stack_trace
  135 + url: "https://pub.dartlang.org"
  136 + source: hosted
  137 + version: "1.9.3"
  138 + stream_channel:
  139 + dependency: transitive
  140 + description:
  141 + name: stream_channel
  142 + url: "https://pub.dartlang.org"
  143 + source: hosted
  144 + version: "2.0.0"
  145 + string_scanner:
  146 + dependency: transitive
  147 + description:
  148 + name: string_scanner
  149 + url: "https://pub.dartlang.org"
  150 + source: hosted
  151 + version: "1.0.5"
  152 + term_glyph:
  153 + dependency: transitive
  154 + description:
  155 + name: term_glyph
  156 + url: "https://pub.dartlang.org"
  157 + source: hosted
  158 + version: "1.1.0"
  159 + test_api:
  160 + dependency: transitive
  161 + description:
  162 + name: test_api
  163 + url: "https://pub.dartlang.org"
  164 + source: hosted
  165 + version: "0.2.11"
  166 + typed_data:
  167 + dependency: transitive
  168 + description:
  169 + name: typed_data
  170 + url: "https://pub.dartlang.org"
  171 + source: hosted
  172 + version: "1.1.6"
  173 + vector_math:
  174 + dependency: transitive
  175 + description:
  176 + name: vector_math
  177 + url: "https://pub.dartlang.org"
  178 + source: hosted
  179 + version: "2.0.8"
  180 + xml:
  181 + dependency: transitive
  182 + description:
  183 + name: xml
  184 + url: "https://pub.dartlang.org"
  185 + source: hosted
  186 + version: "3.5.0"
  187 +sdks:
  188 + dart: ">=2.4.0 <3.0.0"
1 name: get 1 name: get
2 -description: A consistent Flutter route navigation library that navigate with no context and not rebuild materialApp with each navigation.  
3 -version: 1.2.1 2 +description: A consistent navigation library that lets you navigate between screens, open dialogs, and display snackbars with no context.
  3 +version: 1.6.3
4 author: Jonny Borges <jonataborges01@gmail.com> 4 author: Jonny Borges <jonataborges01@gmail.com>
5 homepage: https://github.com/jonataslaw/get 5 homepage: https://github.com/jonataslaw/get
6 6