Jonny Borges
Committed by GitHub

Add files via upload

... ... @@ -117,5 +117,32 @@
## [1.8.1]
-Fix new snackbar features
## [1.9.0]
-Added: Navigator observer
-Added: Get.args to named routes
-Improve snackbar performance
## [1.9.1]
-Fix typo on snackbar route
## [1.9.2]
-Added docs to GetObserver
## [1.10.0]
-Added backdrop
## [1.10.1]
-Backdrop improvement
## [1.10.2]
-Improve snackbar text color
## [1.10.3]
-Improve default color from dialogs
## [1.10.4]
-Improve Get.offAll() - predicate now is optional
... ...
... ... @@ -9,13 +9,15 @@ I worked on a pull to fix it in the framework, and seeing how things work I real
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,
increasing your productivity, and eliminating all the bugs present in Flutter's default navigation altogether.
##### If you use MODULAR, you can to use [GET MODULAR](https://pub.dev/packages/get_modular)
## How to use?
Add this to your package's pubspec.yaml file:
```
dependencies:
get: ^1.9.2
get: ^1.10.4
```
And import it:
... ... @@ -51,7 +53,7 @@ Get.off(NextScreen());
To go to the next screen and cancel all previous routes (useful in shopping carts, polls, and tests)
```dart
Get.offAll(NextScreen(), (route) => false);
Get.offAll(NextScreen());
```
To navigate to the next route, and receive or update data as soon as you return from it:
... ... @@ -195,7 +197,7 @@ Get.offNamed("/NextScreen");
```
To navigate and remove all previous screens from the tree.
```dart
Get.offAllNamed("/NextScreen", (route) => false);
Get.offAllNamed("/NextScreen");
```
## Using with Named Routes and And offering full flutter_web support (REQUIRED FOR NAMED ROUTES):
... ... @@ -213,7 +215,7 @@ void main() {
}
```
#### Middleware
If you want to hear Get events to trigger actions, you can add a GetObserver to your materialApp. This is extremely useful for triggering events whenever a specific Screen is displayed on the screen. Currently on Flutter you would have to put the event on initState and wait for a possible response in a navigator.pop (context); to get that. But with Get, this is extremely simple!
If you want listen Get events to trigger actions, you can add a GetObserver to your materialApp. This is extremely useful for triggering events whenever a specific Screen is displayed on the screen. Currently on Flutter you would have to put the event on initState and wait for a possible response in a navigator.pop (context); to get that. But with Get, this is extremely simple!
##### add GetObserver();
```dart
... ...
... ... @@ -5,3 +5,4 @@ export 'src/routes.dart';
export 'src/snack.dart';
export 'src/bottomsheet.dart';
export 'src/snack_route.dart';
export 'src/route_observer.dart';
... ...
import 'dart:math' as math;
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
class RippleBackdropAnimatePage extends StatefulWidget {
const RippleBackdropAnimatePage({
Key key,
this.child,
this.childFade = false,
this.duration = 300,
this.blurRadius = 15.0,
this.bottomButton,
this.bottomHeight = kBottomNavigationBarHeight,
this.bottomButtonRotate = true,
this.bottomButtonRotateDegree = 45.0,
}) : super(key: key);
/// Child for page.
final Widget child;
/// When enabled, [child] will fade in when animation is going and fade out when popping.
/// [false] is by default.
final bool childFade;
/// Animation's duration,
/// including [Navigator.push], [Navigator.pop].
final int duration;
/// Blur radius for [BackdropFilter].
final double blurRadius;
/// [Widget] for bottom of the page.
final Widget bottomButton;
/// The height which [bottomButton] will occupy.
/// [kBottomNavigationBarHeight] is by default.
final double bottomHeight;
/// When enabled, [bottomButton] will rotate when to animation is going.
/// [true] is by default.
final bool bottomButtonRotate;
/// The degree which [bottomButton] will rotate.
/// 45.0 is by default.
final double bottomButtonRotateDegree;
@override
_RippleBackdropAnimatePageState createState() =>
_RippleBackdropAnimatePageState();
}
class _RippleBackdropAnimatePageState extends State<RippleBackdropAnimatePage>
with TickerProviderStateMixin {
/// Boolean to prevent duplicate pop.
bool _popping = false;
/// Animation.
int _animateDuration;
double _backdropFilterSize = 0.0;
double _popButtonOpacity = 0.0;
double _popButtonRotateAngle = 0.0;
Animation<double> _backDropFilterAnimation;
AnimationController _backDropFilterController;
Animation<double> _popButtonAnimation;
AnimationController _popButtonController;
Animation<double> _popButtonOpacityAnimation;
AnimationController _popButtonOpacityController;
@override
void initState() {
_animateDuration = widget.duration;
SchedulerBinding.instance
.addPostFrameCallback((_) => backDropFilterAnimate(context, true));
super.initState();
}
@override
void dispose() {
_backDropFilterController?.dispose();
_popButtonController?.dispose();
super.dispose();
}
double pythagoreanTheorem(double short, double long) {
return math.sqrt(math.pow(short, 2) + math.pow(long, 2));
}
void popButtonAnimate(context, bool forward) {
if (!forward) {
_popButtonController?.stop();
_popButtonOpacityController?.stop();
}
final double rotateDegree =
widget.bottomButtonRotateDegree * (math.pi / 180) * 3;
_popButtonOpacityController = _popButtonController = AnimationController(
duration: Duration(milliseconds: _animateDuration),
vsync: this,
);
Animation _popButtonCurve = CurvedAnimation(
parent: _popButtonController,
curve: Curves.easeInOut,
);
_popButtonAnimation = Tween(
begin: forward ? 0.0 : _popButtonRotateAngle,
end: forward ? rotateDegree : 0.0,
).animate(_popButtonCurve)
..addListener(() {
setState(() {
_popButtonRotateAngle = _popButtonAnimation.value;
});
});
_popButtonOpacityAnimation = Tween(
begin: forward ? 0.0 : _popButtonOpacity,
end: forward ? 1.0 : 0.0,
).animate(_popButtonCurve)
..addListener(() {
setState(() {
_popButtonOpacity = _popButtonOpacityAnimation.value;
});
});
_popButtonController.forward();
_popButtonOpacityController.forward();
}
Future backDropFilterAnimate(BuildContext context, bool forward) async {
final MediaQueryData m = MediaQuery.of(context);
final Size s = m.size;
final double r =
pythagoreanTheorem(s.width, s.height * 2 + m.padding.top) / 2;
if (!forward) _backDropFilterController?.stop();
popButtonAnimate(context, forward);
_backDropFilterController = AnimationController(
duration: Duration(milliseconds: _animateDuration),
vsync: this,
);
Animation _backDropFilterCurve = CurvedAnimation(
parent: _backDropFilterController,
curve: forward ? Curves.easeInOut : Curves.easeIn,
);
_backDropFilterAnimation = Tween(
begin: forward ? 0.0 : _backdropFilterSize,
end: forward ? r * 2 : 0.0,
).animate(_backDropFilterCurve)
..addListener(() {
setState(() {
_backdropFilterSize = _backDropFilterAnimation.value;
});
});
await _backDropFilterController.forward();
}
Widget popButton() {
Widget button = widget.bottomButton ?? Icon(Icons.add, color: Colors.grey);
if (widget.bottomButtonRotate) {
button = Transform.rotate(
angle: _popButtonRotateAngle,
child: button,
);
}
button = Opacity(
opacity: _popButtonOpacity,
child: SizedBox(
width: widget.bottomHeight,
height: widget.bottomHeight,
child: Center(
child: GestureDetector(
behavior: HitTestBehavior.opaque,
child: button,
onTap: willPop,
),
),
),
);
return button;
}
Widget wrapper(context, {Widget child}) {
final MediaQueryData m = MediaQuery.of(context);
final Size s = m.size;
final double r =
pythagoreanTheorem(s.width, s.height * 2 + m.padding.top) / 2;
final double topOverflow = r - s.height;
final double horizontalOverflow = r - s.width;
return Stack(
overflow: Overflow.visible,
children: <Widget>[
Positioned(
left: -horizontalOverflow,
right: -horizontalOverflow,
top: -topOverflow,
bottom: -r,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: willPop,
child: Center(
child: SizedBox(
width: _backdropFilterSize,
height: _backdropFilterSize,
child: ClipRRect(
borderRadius: BorderRadius.circular(r * 2),
child: BackdropFilter(
filter: ui.ImageFilter.blur(
sigmaX: widget.blurRadius,
sigmaY: widget.blurRadius,
),
child: Text(" "),
),
),
),
),
),
),
Align(
alignment: Alignment.topCenter,
child: Container(
margin: EdgeInsets.only(top: topOverflow + 10),
width: s.width,
height: s.height,
constraints: BoxConstraints(
maxWidth: s.width,
maxHeight: s.height,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Expanded(
child: Opacity(
opacity: widget.childFade ? _popButtonOpacity : 1.0,
child: child,
),
),
popButton(),
],
),
),
),
],
);
}
Future<bool> willPop() async {
await backDropFilterAnimate(context, false);
if (!_popping) {
_popping = true;
await Future.delayed(Duration(milliseconds: _animateDuration), () {
Navigator.of(context).pop();
});
}
return null;
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.transparent,
body: WillPopScope(
onWillPop: willPop,
child: wrapper(
context,
child: widget.child,
),
),
);
}
}
... ...
... ... @@ -43,6 +43,7 @@ Future<T> getShowGeneralDialog<T>({
assert(!barrierDismissible || barrierLabel != null);
return Get.key.currentState.push<T>(_DialogRoute<T>(
pageBuilder: pageBuilder,
settings: RouteSettings(name: "dialog"), // REMOVE THIS IF ERROR
barrierDismissible: barrierDismissible,
barrierLabel: barrierLabel,
barrierColor: barrierColor,
... ...
import 'package:flutter/widgets.dart';
class Routing {
final current;
final previous;
final args;
final previousArgs;
final removed;
final bool isBack;
final bool isSnackbar;
final bool isBottomSheet;
final bool isDialog;
Routing({
this.current,
this.previous,
this.args,
this.previousArgs,
this.removed,
this.isBack,
this.isSnackbar,
this.isBottomSheet,
this.isDialog,
});
}
class GetObserver extends NavigatorObserver {
final Function(Routing) routing;
GetObserver(this.routing);
@override
void didPush(Route<dynamic> route, Route<dynamic> previousRoute) {
if ('${route?.settings?.name}' == 'snackbar') {
print("[OPEN SNACKBAR] ${route?.settings?.name}");
} else if ('${route?.settings?.name}' == 'bottomsheet') {
print("[OPEN BOTTOMSHEET] ${route?.settings?.name}");
} else if ('${route?.settings?.name}' == 'dialog') {
print("[OPEN DIALOG] ${route?.settings?.name}");
} else {
print("[GOING TO ROUTE] ${route?.settings?.name}");
}
routing(Routing(
removed: null,
isBack: false,
current: '${route?.settings?.name}',
previous: '${previousRoute?.settings?.name}',
args: route?.settings?.arguments,
previousArgs: previousRoute?.settings?.arguments,
isSnackbar: '${route?.settings?.name}' == 'snackbar'));
}
@override
void didPop(Route route, Route previousRoute) {
super.didPop(route, previousRoute);
if ('${route?.settings?.name}' == 'snackbar') {
print("[CLOSE SNACKBAR] ${route?.settings?.name}");
} else if ('${route?.settings?.name}' == 'bottomsheet') {
print("[CLOSE BOTTOMSHEET] ${route?.settings?.name}");
} else if ('${route?.settings?.name}' == 'dialog') {
print("[CLOSE DIALOG] ${route?.settings?.name}");
} else if ('${route?.settings?.name}' == 'snackbar') {
print("[CLOSE SNACKBAR] ${route?.settings?.name}");
} else {
print("[BACK ROUTE] ${route?.settings?.name}");
}
routing(Routing(
removed: null,
isBack: true,
current: '${previousRoute?.settings?.name}',
previous: '${route?.settings?.name}',
args: previousRoute?.settings?.arguments,
previousArgs: route?.settings?.arguments,
isSnackbar: '${route?.settings?.name}' == 'snackbar'));
}
@override
void didReplace({Route newRoute, Route oldRoute}) {
super.didReplace(newRoute: newRoute, oldRoute: oldRoute);
print("[REPLACE ROUTE] ${oldRoute?.settings?.name}");
routing(Routing(
removed: null, // add '${oldRoute?.settings?.name}' or remain null ???
isBack: false,
current: '${newRoute?.settings?.name}',
previous: '${oldRoute?.settings?.name}',
args: newRoute?.settings?.arguments,
isSnackbar: null,
previousArgs: null));
}
@override
void didRemove(Route route, Route previousRoute) {
super.didRemove(route, previousRoute);
print("[REMOVING ROUTE] ${route?.settings?.name}");
routing(Routing(
isBack: false,
current: '${previousRoute?.settings?.name}',
removed: '${route?.settings?.name}',
previous: null,
isSnackbar: null,
args: previousRoute?.settings?.arguments,
previousArgs: route?.settings?.arguments));
}
}
... ...
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'backdrop_blur.dart';
import 'bottomsheet.dart';
import 'dialog.dart';
import 'snack.dart';
import 'getroute.dart';
import 'transparent_route.dart';
class Get {
static Get _get;
... ... @@ -31,11 +34,16 @@ class Get {
/// routes rebuild bug present in Flutter. If for some strange reason you want the default behavior
/// of rebuilding every app after a route, use rebuildRoutes = true as the parameter.
static to(Widget page,
{bool rebuildRoutes = false, Transition transition = Transition.fade}) {
{bool rebuildRoutes = false,
Transition transition = Transition.fade,
Duration duration = const Duration(milliseconds: 400)}) {
// if (key.currentState.mounted) // add this if appear problems on future with route navigate
// when widget don't mounted
return key.currentState.push(
GetRoute(opaque: rebuildRoutes, page: page, transition: transition));
return key.currentState.push(GetRoute(
opaque: rebuildRoutes,
page: page,
transition: transition,
duration: duration));
}
/// It replaces Navigator.pushNamed, but needs no context, and it doesn't have the Navigator.pushNamed
... ... @@ -82,16 +90,18 @@ class Get {
/// It replaces Navigator.pushNamedAndRemoveUntil, but needs no context.
static offAllNamed(
String newRouteName,
RoutePredicate predicate, {
String newRouteName, {
RoutePredicate predicate,
arguments,
}) {
return key.currentState
.pushNamedAndRemoveUntil(newRouteName, predicate, arguments: arguments);
var route = (Route<dynamic> rota) => false;
return key.currentState.pushNamedAndRemoveUntil(
newRouteName, predicate ?? route,
arguments: arguments);
}
/// It replaces Navigator.pop, but needs no context.
static back({result}) {
static back({dynamic result}) {
return key.currentState.pop(result);
}
... ... @@ -112,18 +122,24 @@ class Get {
/// of rebuilding every app after a route, use rebuildRoutes = true as the parameter.
static off(Widget page,
{bool rebuildRoutes = false,
Transition transition = Transition.rightToLeft}) {
return key.currentState.pushReplacement(
GetRoute(opaque: rebuildRoutes, page: page, transition: transition));
Transition transition = Transition.rightToLeft,
Duration duration = const Duration(milliseconds: 400)}) {
return key.currentState.pushReplacement(GetRoute(
opaque: rebuildRoutes,
page: page,
transition: transition,
duration: duration));
}
/// It replaces Navigator.pushAndRemoveUntil, but needs no context
static offAll(Widget page, RoutePredicate predicate,
{bool rebuildRoutes = false,
static offAll(Widget page,
{RoutePredicate predicate,
bool rebuildRoutes = false,
Transition transition = Transition.rightToLeft}) {
var route = (Route<dynamic> rota) => false;
return key.currentState.pushAndRemoveUntil(
GetRoute(opaque: rebuildRoutes, page: page, transition: transition),
predicate);
predicate ?? route);
}
/// Show a dialog. You can choose color and opacity of background
... ... @@ -137,6 +153,7 @@ class Get {
assert(useRootNavigator != null);
final ThemeData theme =
Theme.of(Get.key.currentContext, shadowThemeOnly: true);
return getShowGeneralDialog(
pageBuilder: (BuildContext buildContext, Animation<double> animation,
Animation<double> secondaryAnimation) {
... ... @@ -208,10 +225,40 @@ class Get {
clipBehavior: clipBehavior,
isDismissible: isDismissible,
modalBarrierColor: barrierColor,
settings: RouteSettings(name: "bottomsheet"),
enableDrag: enableDrag,
));
}
/// get arguments from current screen. You need of context
static args(context) {
return ModalRoute.of(context).settings.arguments;
}
static backdrop(Widget child,
{double radius = 20.0,
double blurRadius: 20.0,
int duration = 300,
Transition transition = Transition.fade,
Widget bottomButton = const Icon(Icons.visibility),
double bottomHeight = 60.0,
bool bottomButtonRotate = false}) {
final page = RippleBackdropAnimatePage(
child: child,
childFade: true,
duration: duration,
blurRadius: blurRadius,
bottomButton: bottomButton,
bottomHeight: bottomHeight,
bottomButtonRotate: bottomButtonRotate,
);
return key.currentState
.push(TransparentRoute(builder: (BuildContext context) {
return page;
}));
}
static snackbar(title, message,
{Color colorText,
Duration duration,
... ... @@ -246,56 +293,56 @@ class Get {
double overlayBlur,
Color overlayColor,
Form userInputForm}) {
// if (key.currentState.mounted)
return GetBar(
titleText: titleText ??
Text(
title,
style: TextStyle(
color:
colorText ?? Theme.of(Get.key.currentContext).accentColor,
fontWeight: FontWeight.w800,
fontSize: 16),
),
messageText: messageText ??
Text(
message,
style: TextStyle(
color:
colorText ?? Theme.of(Get.key.currentContext).accentColor,
fontWeight: FontWeight.w300,
fontSize: 14),
),
snackPosition: snackPosition ?? SnackPosition.TOP,
borderRadius: borderRadius ?? 15,
margin: margin ?? EdgeInsets.symmetric(horizontal: 10),
duration: duration ?? Duration(seconds: 3),
barBlur: barBlur ?? 7.0,
backgroundColor: backgroundColor ?? Colors.grey.withOpacity(0.2),
icon: icon,
shouldIconPulse: shouldIconPulse ?? true,
maxWidth: maxWidth,
padding: padding ?? EdgeInsets.all(16),
borderColor: borderColor,
borderWidth: borderWidth,
leftBarIndicatorColor: leftBarIndicatorColor,
boxShadows: boxShadows,
backgroundGradient: backgroundGradient,
mainButton: mainButton,
onTap: onTap,
isDismissible: isDismissible ?? true,
dismissDirection: dismissDirection ?? SnackDismissDirection.VERTICAL,
showProgressIndicator: showProgressIndicator ?? false,
progressIndicatorController: progressIndicatorController,
progressIndicatorBackgroundColor: progressIndicatorBackgroundColor,
progressIndicatorValueColor: progressIndicatorValueColor,
snackStyle: snackStyle ?? SnackStyle.FLOATING,
forwardAnimationCurve: forwardAnimationCurve ?? Curves.easeOutCirc,
reverseAnimationCurve: reverseAnimationCurve ?? Curves.easeOutCirc,
animationDuration: animationDuration ?? Duration(seconds: 1),
overlayBlur: overlayBlur ?? 0.0,
overlayColor: overlayColor ?? Colors.transparent,
userInputForm: userInputForm)
..show();
SchedulerBinding.instance.addPostFrameCallback((_) {
final Color defaultColor = IconTheme.of(Get.key.currentContext).color;
return GetBar(
titleText: titleText ??
Text(
title,
style: TextStyle(
color: colorText ?? defaultColor,
fontWeight: FontWeight.w800,
fontSize: 16),
),
messageText: messageText ??
Text(
message,
style: TextStyle(
color: colorText ?? defaultColor,
fontWeight: FontWeight.w300,
fontSize: 14),
),
snackPosition: snackPosition ?? SnackPosition.TOP,
borderRadius: borderRadius ?? 15,
margin: margin ?? EdgeInsets.symmetric(horizontal: 10),
duration: duration ?? Duration(seconds: 3),
barBlur: barBlur ?? 7.0,
backgroundColor: backgroundColor ?? Colors.grey.withOpacity(0.2),
icon: icon,
shouldIconPulse: shouldIconPulse ?? true,
maxWidth: maxWidth,
padding: padding ?? EdgeInsets.all(16),
borderColor: borderColor,
borderWidth: borderWidth,
leftBarIndicatorColor: leftBarIndicatorColor,
boxShadows: boxShadows,
backgroundGradient: backgroundGradient,
mainButton: mainButton,
onTap: onTap,
isDismissible: isDismissible ?? true,
dismissDirection: dismissDirection ?? SnackDismissDirection.VERTICAL,
showProgressIndicator: showProgressIndicator ?? false,
progressIndicatorController: progressIndicatorController,
progressIndicatorBackgroundColor: progressIndicatorBackgroundColor,
progressIndicatorValueColor: progressIndicatorValueColor,
snackStyle: snackStyle ?? SnackStyle.FLOATING,
forwardAnimationCurve: forwardAnimationCurve ?? Curves.easeOutCirc,
reverseAnimationCurve: reverseAnimationCurve ?? Curves.easeOutCirc,
animationDuration: animationDuration ?? Duration(seconds: 1),
overlayBlur: overlayBlur ?? 0.0,
overlayColor: overlayColor ?? Colors.transparent,
userInputForm: userInputForm)
..show();
});
}
}
... ...
import 'package:flutter/material.dart';
class TransparentRoute extends PageRoute<void> {
TransparentRoute({
@required this.builder,
RouteSettings settings,
}) : assert(builder != null),
super(settings: settings, fullscreenDialog: false);
final WidgetBuilder builder;
@override
bool get opaque => false;
@override
Color get barrierColor => null;
@override
String get barrierLabel => null;
@override
bool get maintainState => true;
@override
Duration get transitionDuration => Duration.zero;
@override
Widget buildPage(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation) {
final result = builder(context);
return Semantics(
scopesRoute: true,
explicitChildNodes: true,
child: result,
);
}
}
... ...
name: get
description: A consistent navigation library that lets you navigate between screens, open dialogs, and display snackbars easily with no context.
version: 1.8.1
version: 1.10.4
homepage: https://github.com/jonataslaw/get
environment:
... ...