Jonny Borges
Committed by GitHub

Compatibility with dev/master branch

... ... @@ -153,8 +153,6 @@
## [1.11.1]
-Improve swipe to back on iOS devices
## [1.12.0-dev]
-Compatibility with Dev branch
... ...
... ... @@ -11,13 +11,15 @@ increasing your productivity, and eliminating all the bugs present in Flutter's
##### If you use MODULAR, add on your MaterialApp this: navigatorKey: Get.addKey(Modular.navigatorKey)
##### If you use master/dev branch of Flutter, use the version 1.12.0-dev.
## How to use?
Add this to your package's pubspec.yaml file:
```
dependencies:
get: ^1.11.1
get: ^1.11.1 // get: ^1.12.0-dev on dev/master
```
And import it:
... ...
... ... @@ -49,7 +49,7 @@ class Get {
/// It replaces Navigator.push, but needs no context, and it doesn't have the Navigator.push
/// 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,
static Future<T> to<T>(Widget page,
{bool rebuildRoutes,
Transition transition,
Duration duration = const Duration(milliseconds: 400)}) {
... ... @@ -72,47 +72,47 @@ class Get {
/// It replaces Navigator.pushNamed, but needs no context, and it doesn't have the Navigator.pushNamed
/// 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 toNamed(String page, {arguments}) {
static Future<T> toNamed<T>(String page, {arguments}) {
// if (key.currentState.mounted) // add this if appear problems on future with route navigate
// when widget don't mounted
return key.currentState.pushNamed(page, arguments: arguments);
}
/// It replaces Navigator.pushReplacementNamed, but needs no context.
static offNamed(String page, {arguments}) {
static Future<T> offNamed<T>(String page, {arguments}) {
// if (key.currentState.mounted) // add this if appear problems on future with route navigate
// when widget don't mounted
return key.currentState.pushReplacementNamed(page, arguments: arguments);
}
/// It replaces Navigator.popUntil, but needs no context.
static until(String page, predicate) {
static void until(String page, predicate) {
// if (key.currentState.mounted) // add this if appear problems on future with route navigate
// when widget don't mounted
return key.currentState.popUntil(predicate);
}
/// It replaces Navigator.pushAndRemoveUntil, but needs no context.
static offUntil(page, predicate) {
static Future<T> offUntil<T>(page, predicate) {
// if (key.currentState.mounted) // add this if appear problems on future with route navigate
// when widget don't mounted
return key.currentState.pushAndRemoveUntil(page, predicate);
}
/// It replaces Navigator.pushNamedAndRemoveUntil, but needs no context.
static offNamedUntil(page, predicate) {
static Future<T> offNamedUntil<T>(page, predicate) {
// if (key.currentState.mounted) // add this if appear problems on future with route navigate
// when widget don't mounted
return key.currentState.pushNamedAndRemoveUntil(page, predicate);
}
/// It replaces Navigator.removeRoute, but needs no context.
static removeRoute(route) {
static void removeRoute(route) {
return key.currentState.removeRoute(route);
}
/// It replaces Navigator.pushNamedAndRemoveUntil, but needs no context.
static offAllNamed(
static Future<T> offAllNamed<T>(
String newRouteName, {
RoutePredicate predicate,
arguments,
... ... @@ -124,12 +124,12 @@ class Get {
}
/// It replaces Navigator.pop, but needs no context.
static back({dynamic result}) {
static void back({dynamic result}) {
return key.currentState.pop(result);
}
/// It will close as many screens as you define. Times must be> 0;
static close(int times) {
static void close(int times) {
if ((times == null) || (times < 1)) {
times = 1;
}
... ... @@ -143,25 +143,33 @@ class Get {
/// It replaces Navigator.pushReplacement, but needs no context, and it doesn't have the Navigator.pushReplacement
/// 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 off(Widget page,
static Future<T> off<T>(Widget page,
{bool rebuildRoutes = false,
Transition transition = Transition.rightToLeft,
Transition transition,
Duration duration = const Duration(milliseconds: 400)}) {
return key.currentState.pushReplacement(GetRoute(
opaque: rebuildRoutes,
page: page,
transition: transition,
transition: transition ?? Platform.isIOS
? Transition.cupertino
: Transition.fade,
duration: duration));
}
/// It replaces Navigator.pushAndRemoveUntil, but needs no context
static offAll(Widget page,
static Future<T> offAll<T>(Widget page,
{RoutePredicate predicate,
bool rebuildRoutes = false,
Transition transition = Transition.rightToLeft}) {
Transition transition}) {
var route = (Route<dynamic> rota) => false;
return key.currentState.pushAndRemoveUntil(
GetRoute(opaque: rebuildRoutes, page: page, transition: transition),
GetRoute(
opaque: rebuildRoutes,
page: page,
transition: transition ?? Platform.isIOS
? Transition.cupertino
: Transition.fade,
),
predicate ?? route);
}
... ... @@ -258,7 +266,7 @@ class Get {
return ModalRoute.of(context).settings.arguments;
}
static backdrop(Widget child,
static Future backdrop(Widget child,
{double radius = 20.0,
double blurRadius: 20.0,
int duration = 300,
... ... @@ -282,7 +290,7 @@ class Get {
}));
}
static snackbar(title, message,
static void snackbar(title, message,
{Color colorText,
Duration duration,
SnackPosition snackPosition,
... ... @@ -369,15 +377,23 @@ class Get {
});
}
static iconColor() {
return Theme.of(key.currentContext).iconTheme.color;
static BuildContext context() {
return key.currentContext;
}
static ThemeData theme() {
return Theme.of(context());
}
static Color iconColor() {
return Theme.of(context()).iconTheme.color;
}
static height() {
return MediaQuery.of(key.currentContext).size.height;
static double height() {
return MediaQuery.of(context()).size.height;
}
static width() {
return MediaQuery.of(key.currentContext).size.width;
static double width() {
return MediaQuery.of(context()).size.width;
}
}
... ...
import 'dart:math';
import 'dart:ui' show lerpDouble;
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
const double _kBackGestureWidth = 20.0;
const double _kMinFlingVelocity = 1.0;
const int _kMaxDroppedSwipePageForwardAnimationTime = 800; // Milliseconds.
// The maximum time for a page to get reset to it's original position if the
// user releases a page mid swipe.
const int _kMaxPageBackAnimationTime = 300;
class GetCupertino<T> extends PageRoute<T> {
/// Creates a page route for use in an iOS designed app.
///
/// The [builder], [maintainState], and [fullscreenDialog] arguments must not
/// be null.
GetCupertino({
@required this.builder,
this.title,
RouteSettings settings,
this.maintainState = true,
bool fullscreenDialog = false,
}) : assert(builder != null),
assert(maintainState != null),
assert(fullscreenDialog != null),
assert(opaque),
super(settings: settings, fullscreenDialog: fullscreenDialog);
/// Builds the primary contents of the route.
final WidgetBuilder builder;
/// A title string for this route.
///
/// Used to auto-populate [CupertinoNavigationBar] and
/// [CupertinoSliverNavigationBar]'s `middle`/`largeTitle` widgets when
/// one is not manually supplied.
final String title;
ValueNotifier<String> _previousTitle;
/// The title string of the previous [GetCupertino].
///
/// The [ValueListenable]'s value is readable after the route is installed
/// onto a [Navigator]. The [ValueListenable] will also notify its listeners
/// if the value changes (such as by replacing the previous route).
///
/// The [ValueListenable] itself will be null before the route is installed.
/// Its content value will be null if the previous route has no title or
/// is not a [GetCupertino].
///
/// See also:
///
/// * [ValueListenableBuilder], which can be used to listen and rebuild
/// widgets based on a ValueListenable.
ValueListenable<String> get previousTitle {
assert(
_previousTitle != null,
'Cannot read the previousTitle for a route that has not yet been installed',
);
return _previousTitle;
}
@override
void didChangePrevious(Route<dynamic> previousRoute) {
final String previousTitleString =
previousRoute is GetCupertino ? previousRoute.title : null;
if (_previousTitle == null) {
_previousTitle = ValueNotifier<String>(previousTitleString);
} else {
_previousTitle.value = previousTitleString;
}
super.didChangePrevious(previousRoute);
}
@override
final bool maintainState;
@override
// A relatively rigorous eyeball estimation.
Duration get transitionDuration => const Duration(milliseconds: 400);
@override
Color get barrierColor => null;
@override
String get barrierLabel => null;
@override
bool canTransitionTo(TransitionRoute<dynamic> nextRoute) {
// Don't perform outgoing animation if the next route is a fullscreen dialog.
return nextRoute is GetCupertino && !nextRoute.fullscreenDialog;
}
/// True if an iOS-style back swipe pop gesture is currently underway for [route].
///
/// This just check the route's [NavigatorState.userGestureInProgress].
///
/// See also:
///
/// * [popGestureEnabled], which returns true if a user-triggered pop gesture
/// would be allowed.
static bool isPopGestureInProgress(PageRoute<dynamic> route) {
return route.navigator.userGestureInProgress;
}
/// True if an iOS-style back swipe pop gesture is currently underway for this route.
///
/// See also:
///
/// * [isPopGestureInProgress], which returns true if a Cupertino pop gesture
/// is currently underway for specific route.
/// * [popGestureEnabled], which returns true if a user-triggered pop gesture
/// would be allowed.
bool get popGestureInProgress => isPopGestureInProgress(this);
/// Whether a pop gesture can be started by the user.
///
/// Returns true if the user can edge-swipe to a previous route.
///
/// Returns false once [isPopGestureInProgress] is true, but
/// [isPopGestureInProgress] can only become true if [popGestureEnabled] was
/// true first.
///
/// This should only be used between frames, not during build.
bool get popGestureEnabled => _isPopGestureEnabled(this);
static bool _isPopGestureEnabled<T>(PageRoute<T> route) {
// If there's nothing to go back to, then obviously we don't support
// the back gesture.
if (route.isFirst) return false;
// If the route wouldn't actually pop if we popped it, then the gesture
// would be really confusing (or would skip internal routes), so disallow it.
if (route.willHandlePopInternally) return false;
// If attempts to dismiss this route might be vetoed such as in a page
// with forms, then do not allow the user to dismiss the route with a swipe.
if (route.hasScopedWillPopCallback) return false;
// Fullscreen dialogs aren't dismissible by back swipe.
if (route.fullscreenDialog) return false;
// If we're in an animation already, we cannot be manually swiped.
if (route.animation.status != AnimationStatus.completed) return false;
// If we're being popped into, we also cannot be swiped until the pop above
// it completes. This translates to our secondary animation being
// dismissed.
if (route.secondaryAnimation.status != AnimationStatus.dismissed)
return false;
// If we're in a gesture already, we cannot start another.
if (isPopGestureInProgress(route)) return false;
// Looks like a back gesture would be welcome!
return true;
}
@override
Widget buildPage(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation) {
final Widget child = builder(context);
final Widget result = Semantics(
scopesRoute: true,
explicitChildNodes: true,
child: child,
);
assert(() {
if (child == null) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary(
'The builder for route "${settings.name}" returned null.'),
ErrorDescription('Route builders must never return null.'),
]);
}
return true;
}());
return result;
}
// Called by _CupertinoBackGestureDetector when a pop ("back") drag start
// gesture is detected. The returned controller handles all of the subsequent
// drag events.
static _CupertinoBackGestureController<T> _startPopGesture<T>(
PageRoute<T> route) {
assert(_isPopGestureEnabled(route));
return _CupertinoBackGestureController<T>(
navigator: route.navigator,
controller: route.controller, // protected access
);
}
/// Returns a [CupertinoFullscreenDialogTransition] if [route] is a full
/// screen dialog, otherwise a [CupertinoPageTransition] is returned.
///
/// Used by [GetCupertino.buildTransitions].
///
/// This method can be applied to any [PageRoute], not just
/// [GetCupertino]. It's typically used to provide a Cupertino style
/// horizontal transition for material widgets when the target platform
/// is [TargetPlatform.iOS].
///
/// See also:
///
/// * [CupertinoPageTransitionsBuilder], which uses this method to define a
/// [PageTransitionsBuilder] for the [PageTransitionsTheme].
static Widget buildPageTransitions<T>(
PageRoute<T> route,
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
if (route.fullscreenDialog) {
return CupertinoFullscreenDialogTransition(
animation: animation,
child: child,
);
} else {
return CupertinoPageTransition(
primaryRouteAnimation: animation,
secondaryRouteAnimation: secondaryAnimation,
// Check if the route has an animation that's currently participating
// in a back swipe gesture.
//
// In the middle of a back gesture drag, let the transition be linear to
// match finger motions.
linearTransition: isPopGestureInProgress(route),
child: _CupertinoBackGestureDetector<T>(
enabledCallback: () => _isPopGestureEnabled<T>(route),
onStartPopGesture: () => _startPopGesture<T>(route),
child: child,
),
);
}
}
@override
Widget buildTransitions(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation, Widget child) {
return buildPageTransitions<T>(
this, context, animation, secondaryAnimation, child);
}
@override
String get debugLabel => '${super.debugLabel}(${settings.name})';
}
class _CupertinoBackGestureDetector<T> extends StatefulWidget {
const _CupertinoBackGestureDetector({
Key key,
@required this.enabledCallback,
@required this.onStartPopGesture,
@required this.child,
}) : assert(enabledCallback != null),
assert(onStartPopGesture != null),
assert(child != null),
super(key: key);
final Widget child;
final ValueGetter<bool> enabledCallback;
final ValueGetter<_CupertinoBackGestureController<T>> onStartPopGesture;
@override
_CupertinoBackGestureDetectorState<T> createState() =>
_CupertinoBackGestureDetectorState<T>();
}
class _CupertinoBackGestureDetectorState<T>
extends State<_CupertinoBackGestureDetector<T>> {
_CupertinoBackGestureController<T> _backGestureController;
HorizontalDragGestureRecognizer _recognizer;
@override
void initState() {
super.initState();
_recognizer = HorizontalDragGestureRecognizer(debugOwner: this)
..onStart = _handleDragStart
..onUpdate = _handleDragUpdate
..onEnd = _handleDragEnd
..onCancel = _handleDragCancel;
}
@override
void dispose() {
_recognizer.dispose();
super.dispose();
}
void _handleDragStart(DragStartDetails details) {
assert(mounted);
assert(_backGestureController == null);
_backGestureController = widget.onStartPopGesture();
}
void _handleDragUpdate(DragUpdateDetails details) {
assert(mounted);
assert(_backGestureController != null);
_backGestureController.dragUpdate(
_convertToLogical(details.primaryDelta / context.size.width));
}
void _handleDragEnd(DragEndDetails details) {
assert(mounted);
assert(_backGestureController != null);
_backGestureController.dragEnd(_convertToLogical(
details.velocity.pixelsPerSecond.dx / context.size.width));
_backGestureController = null;
}
void _handleDragCancel() {
assert(mounted);
// This can be called even if start is not called, paired with the "down" event
// that we don't consider here.
_backGestureController?.dragEnd(0.0);
_backGestureController = null;
}
void _handlePointerDown(PointerDownEvent event) {
if (widget.enabledCallback()) _recognizer.addPointer(event);
}
double _convertToLogical(double value) {
switch (Directionality.of(context)) {
case TextDirection.rtl:
return -value;
case TextDirection.ltr:
return value;
}
return null;
}
@override
Widget build(BuildContext context) {
assert(debugCheckHasDirectionality(context));
// For devices with notches, the drag area needs to be larger on the side
// that has the notch.
double dragAreaWidth = Directionality.of(context) == TextDirection.ltr
? MediaQuery.of(context).padding.left
: MediaQuery.of(context).padding.right;
dragAreaWidth = max(dragAreaWidth, _kBackGestureWidth);
return Stack(
fit: StackFit.passthrough,
children: <Widget>[
widget.child,
PositionedDirectional(
start: 0.0,
width: dragAreaWidth,
top: 0.0,
bottom: 0.0,
child: Listener(
onPointerDown: _handlePointerDown,
behavior: HitTestBehavior.translucent,
),
),
],
);
}
}
class _CupertinoBackGestureController<T> {
/// Creates a controller for an iOS-style back gesture.
///
/// The [navigator] and [controller] arguments must not be null.
_CupertinoBackGestureController({
@required this.navigator,
@required this.controller,
}) : assert(navigator != null),
assert(controller != null) {
navigator.didStartUserGesture();
}
final AnimationController controller;
final NavigatorState navigator;
/// The drag gesture has changed by [fractionalDelta]. The total range of the
/// drag should be 0.0 to 1.0.
void dragUpdate(double delta) {
controller.value -= delta;
}
/// The drag gesture has ended with a horizontal motion of
/// [fractionalVelocity] as a fraction of screen width per second.
void dragEnd(double velocity) {
// Fling in the appropriate direction.
// AnimationController.fling is guaranteed to
// take at least one frame.
//
// This curve has been determined through rigorously eyeballing native iOS
// animations.
const Curve animationCurve = Curves.fastLinearToSlowEaseIn;
bool animateForward;
// If the user releases the page before mid screen with sufficient velocity,
// or after mid screen, we should animate the page out. Otherwise, the page
// should be animated back in.
if (velocity.abs() >= _kMinFlingVelocity)
animateForward = velocity <= 0;
else
animateForward = controller.value > 0.5;
if (animateForward) {
// The closer the panel is to dismissing, the shorter the animation is.
// We want to cap the animation time, but we want to use a linear curve
// to determine it.
final int droppedPageForwardAnimationTime = min(
lerpDouble(
_kMaxDroppedSwipePageForwardAnimationTime, 0, controller.value)
.floor(),
_kMaxPageBackAnimationTime,
);
controller.animateTo(1.0,
duration: Duration(milliseconds: droppedPageForwardAnimationTime),
curve: animationCurve);
} else {
// This route is destined to pop at this point. Reuse navigator's pop.
navigator.pop();
// The popping may have finished inline if already at the target destination.
if (controller.isAnimating) {
// Otherwise, use a custom popping animation duration and curve.
final int droppedPageBackAnimationTime = lerpDouble(
0, _kMaxDroppedSwipePageForwardAnimationTime, controller.value)
.floor();
controller.animateBack(0.0,
duration: Duration(milliseconds: droppedPageBackAnimationTime),
curve: animationCurve);
}
}
if (controller.isAnimating) {
// Keep the userGestureInProgress in true state so we don't change the
// curve of the page transition mid-flight since CupertinoPageTransition
// depends on userGestureInProgress.
AnimationStatusListener animationStatusCallback;
animationStatusCallback = (AnimationStatus status) {
navigator.didStopUserGesture();
controller.removeStatusListener(animationStatusCallback);
};
controller.addStatusListener(animationStatusCallback);
} else {
navigator.didStopUserGesture();
}
}
}
... ...
... ... @@ -276,7 +276,7 @@ class SnackRoute<T> extends OverlayRoute<T> {
}
@override
void install(OverlayEntry insertionPoint) {
void install() {
assert(!_transitionCompleter.isCompleted,
'Cannot install a $runtimeType after disposing it.');
_controller = createAnimationController();
... ... @@ -286,7 +286,7 @@ class SnackRoute<T> extends OverlayRoute<T> {
_filterColorAnimation = createColorFilterAnimation();
_animation = createAnimation();
assert(_animation != null, '$runtimeType.createAnimation() returned null.');
super.install(insertionPoint);
super.install();
}
@override
... ...
... ... @@ -95,13 +95,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.6.4"
pedantic:
dependency: transitive
description:
name: pedantic
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0+1"
petitparser:
dependency: transitive
description:
... ... @@ -162,7 +155,7 @@ packages:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.11"
version: "0.2.15"
typed_data:
dependency: transitive
description:
... ...
name: get
description: A consistent navigation library that lets you navigate between screens, open dialogs, and display snackbars easily with no context.
version: 1.11.1
version: 1.12.0-dev
homepage: https://github.com/jonataslaw/get
environment:
... ...