LCOV - code coverage report
Current view: top level - src/routes - default_route.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 36 240 15.0 %
Date: 2020-06-12 23:08:29 Functions: 0 0 -

          Line data    Source code
       1             : import 'dart:math';
       2             : import 'dart:ui' show lerpDouble;
       3             : 
       4             : import 'package:flutter/cupertino.dart';
       5             : import 'package:flutter/foundation.dart';
       6             : import 'package:flutter/gestures.dart';
       7             : import 'package:flutter/material.dart';
       8             : import 'package:get_core/src/get_main.dart';
       9             : import 'package:get_core/src/routes/bindings_interface.dart';
      10             : 
      11             : import '../platform/platform.dart';
      12             : import 'transitions_type.dart';
      13             : 
      14             : const double _kBackGestureWidth = 20.0;
      15             : const double _kMinFlingVelocity = 1.0;
      16             : const int _kMaxDroppedSwipePageForwardAnimationTime = 800; // Milliseconds.
      17             : 
      18             : // The maximum time for a page to get reset to it's original position if the
      19             : // user releases a page mid swipe.
      20             : const int _kMaxPageBackAnimationTime = 300;
      21             : 
      22             : class GetRouteBase<T> extends PageRoute<T> {
      23             :   /// The [builder], [maintainState], and [fullscreenDialog] arguments must not
      24             :   /// be null.
      25           1 :   GetRouteBase({
      26             :     @required this.page,
      27             :     this.title,
      28             :     RouteSettings settings,
      29             :     this.maintainState = true,
      30             :     this.curve = Curves.linear,
      31             :     this.alignment,
      32             :     this.parameter,
      33             :     this.binding,
      34             :     this.bindings,
      35             :     this.customBuildPageTransitions,
      36             :     this.opaque = true,
      37             :     this.transitionDuration = const Duration(milliseconds: 400),
      38             :     this.popGesture,
      39             :     this.transition,
      40             :     // this.duration = const Duration(milliseconds: 400),
      41             :     bool fullscreenDialog = false,
      42           0 :   })  : assert(page != null),
      43           0 :         assert(maintainState != null),
      44           0 :         assert(fullscreenDialog != null),
      45             :         //  assert(opaque),
      46           1 :         super(settings: settings, fullscreenDialog: fullscreenDialog) {
      47             :     /// prebuild dependencies
      48           1 :     if (binding != null) {
      49           0 :       binding.dependencies();
      50             :     }
      51           1 :     if (bindings != null) {
      52           0 :       bindings.forEach((element) => element.dependencies());
      53             :     }
      54             :   }
      55             : 
      56             :   /// Builds the primary contents of the route.
      57             :   final Widget page;
      58             : 
      59             :   final Widget customBuildPageTransitions;
      60             : 
      61             :   final bool popGesture;
      62             : 
      63             :   final Bindings binding;
      64             : 
      65             :   final List<Bindings> bindings;
      66             : 
      67             :   // final Duration duration;
      68             : 
      69             :   final Map<String, String> parameter;
      70             : 
      71             :   final String title;
      72             : 
      73             :   final Transition transition;
      74             : 
      75             :   final Curve curve;
      76             : 
      77             :   final Alignment alignment;
      78             : 
      79             :   ValueNotifier<String> _previousTitle;
      80             : 
      81             :   /// The title string of the previous [GetRoute].
      82             :   ///
      83             :   /// The [ValueListenable]'s value is readable after the route is installed
      84             :   /// onto a [Navigator]. The [ValueListenable] will also notify its listeners
      85             :   /// if the value changes (such as by replacing the previous route).
      86             :   ///
      87             :   /// The [ValueListenable] itself will be null before the route is installed.
      88             :   /// Its content value will be null if the previous route has no title or
      89             :   /// is not a [GetRoute].
      90             :   ///
      91             :   /// See also:
      92             :   ///
      93             :   ///  * [ValueListenableBuilder], which can be used to listen and rebuild
      94             :   ///    widgets based on a ValueListenable.
      95           0 :   ValueListenable<String> get previousTitle {
      96             :     assert(
      97           0 :       _previousTitle != null,
      98             :       'Cannot read the previousTitle for a route that has not yet been installed',
      99             :     );
     100           0 :     return _previousTitle;
     101             :   }
     102             : 
     103           1 :   @override
     104             :   void didChangePrevious(Route<dynamic> previousRoute) {
     105             :     final String previousTitleString =
     106           2 :         previousRoute is GetRouteBase ? previousRoute.title : null;
     107           1 :     if (_previousTitle == null) {
     108           2 :       _previousTitle = ValueNotifier<String>(previousTitleString);
     109             :     } else {
     110           2 :       _previousTitle.value = previousTitleString;
     111             :     }
     112           1 :     super.didChangePrevious(previousRoute);
     113             :   }
     114             : 
     115             :   @override
     116             :   final bool maintainState;
     117             : 
     118             :   /// Allows you to set opaque to false to prevent route reconstruction.
     119             :   @override
     120             :   final bool opaque;
     121             : 
     122             :   @override
     123             :   final Duration transitionDuration;
     124             : 
     125           1 :   @override
     126             :   Color get barrierColor => null; //Color(0x00FFFFFF);
     127             : 
     128           1 :   @override
     129             :   String get barrierLabel => null;
     130             : 
     131           1 :   @override
     132             :   bool canTransitionTo(TransitionRoute<dynamic> nextRoute) {
     133             :     // Don't perform outgoing animation if the next route is a fullscreen dialog.
     134           2 :     return nextRoute is GetRouteBase && !nextRoute.fullscreenDialog;
     135             :   }
     136             : 
     137             :   /// True if an iOS-style back swipe pop gesture is currently underway for [route].
     138             :   ///
     139             :   /// This just check the route's [NavigatorState.userGestureInProgress].
     140             :   ///
     141             :   /// See also:
     142             :   ///
     143             :   ///  * [popGestureEnabled], which returns true if a user-triggered pop gesture
     144             :   ///    would be allowed.
     145           0 :   static bool isPopGestureInProgress(PageRoute<dynamic> route) {
     146           0 :     return route.navigator.userGestureInProgress;
     147             :   }
     148             : 
     149             :   /// True if an iOS-style back swipe pop gesture is currently underway for this route.
     150             :   ///
     151             :   /// See also:
     152             :   ///
     153             :   ///  * [isPopGestureInProgress], which returns true if a Cupertino pop gesture
     154             :   ///    is currently underway for specific route.
     155             :   ///  * [popGestureEnabled], which returns true if a user-triggered pop gesture
     156             :   ///    would be allowed.
     157           0 :   bool get popGestureInProgress => isPopGestureInProgress(this);
     158             : 
     159             :   /// Whether a pop gesture can be started by the user.
     160             :   ///
     161             :   /// Returns true if the user can edge-swipe to a previous route.
     162             :   ///
     163             :   /// Returns false once [isPopGestureInProgress] is true, but
     164             :   /// [isPopGestureInProgress] can only become true if [popGestureEnabled] was
     165             :   /// true first.
     166             :   ///
     167             :   /// This should only be used between frames, not during build.
     168           0 :   bool get popGestureEnabled => _isPopGestureEnabled(this);
     169             : 
     170           0 :   static bool _isPopGestureEnabled<T>(PageRoute<T> route) {
     171             :     // If there's nothing to go back to, then obviously we don't support
     172             :     // the back gesture.
     173           0 :     if (route.isFirst) return false;
     174             :     // If the route wouldn't actually pop if we popped it, then the gesture
     175             :     // would be really confusing (or would skip internal routes), so disallow it.
     176           0 :     if (route.willHandlePopInternally) return false;
     177             :     // If attempts to dismiss this route might be vetoed such as in a page
     178             :     // with forms, then do not allow the user to dismiss the route with a swipe.
     179           0 :     if (route.hasScopedWillPopCallback) return false;
     180             :     // Fullscreen dialogs aren't dismissible by back swipe.
     181           0 :     if (route.fullscreenDialog) return false;
     182             :     // If we're in an animation already, we cannot be manually swiped.
     183           0 :     if (route.animation.status != AnimationStatus.completed) return false;
     184             :     // If we're being popped into, we also cannot be swiped until the pop above
     185             :     // it completes. This translates to our secondary animation being
     186             :     // dismissed.
     187           0 :     if (route.secondaryAnimation.status != AnimationStatus.dismissed)
     188             :       return false;
     189             :     // If we're in a gesture already, we cannot start another.
     190           0 :     if (isPopGestureInProgress(route)) return false;
     191             : 
     192             :     // Looks like a back gesture would be welcome!
     193             :     return true;
     194             :   }
     195             : 
     196           1 :   @override
     197             :   Widget buildPage(BuildContext context, Animation<double> animation,
     198             :       Animation<double> secondaryAnimation) {
     199           1 :     final Widget child = page;
     200           1 :     final Widget result = Semantics(
     201             :       scopesRoute: true,
     202             :       explicitChildNodes: true,
     203             :       child: child,
     204             :     );
     205           1 :     assert(() {
     206             :       if (child == null) {
     207           0 :         throw FlutterError.fromParts(<DiagnosticsNode>[
     208           0 :           ErrorSummary(
     209           0 :               'The builder for route "${settings.name}" returned null.'),
     210           0 :           ErrorDescription('Route builders must never return null.'),
     211             :         ]);
     212             :       }
     213             :       return true;
     214           1 :     }());
     215             :     return result;
     216             :   }
     217             : 
     218             :   // Called by _CupertinoBackGestureDetector when a pop ("back") drag start
     219             :   // gesture is detected. The returned controller handles all of the subsequent
     220             :   // drag events.
     221           0 :   static _CupertinoBackGestureController<T> _startPopGesture<T>(
     222             :       PageRoute<T> route) {
     223           0 :     assert(_isPopGestureEnabled(route));
     224             : 
     225           0 :     return _CupertinoBackGestureController<T>(
     226           0 :       navigator: route.navigator,
     227           0 :       controller: route.controller, // protected access
     228             :     );
     229             :   }
     230             : 
     231             :   /// Returns a [CupertinoFullscreenDialogTransition] if [route] is a full
     232             :   /// screen dialog, otherwise a [CupertinoPageTransition] is returned.
     233             :   ///
     234             :   /// Used by [GetRoute.buildTransitions].
     235             :   ///
     236             :   /// This method can be applied to any [PageRoute], not just
     237             :   /// [GetRoute]. It's typically used to provide a Cupertino style
     238             :   /// horizontal transition for material widgets when the target platform
     239             :   /// is [TargetPlatform.iOS].
     240             :   ///
     241             :   /// See also:
     242             :   ///
     243             :   ///  * [CupertinoPageTransitionsBuilder], which uses this method to define a
     244             :   ///    [PageTransitionsBuilder] for the [PageTransitionsTheme].
     245           1 :   Widget buildPageTransitions<T>(
     246             :     PageRoute<T> route,
     247             :     BuildContext context,
     248             :     bool popGesture,
     249             :     Animation<double> animation,
     250             :     Animation<double> secondaryAnimation,
     251             :     Widget child,
     252             :     Transition tr,
     253             :     Curve curve,
     254             :     Alignment alignment,
     255             :   ) {
     256           2 :     Transition transition = (tr ?? Get.defaultTransition);
     257             : 
     258           1 :     if (route.fullscreenDialog) {
     259           0 :       final bool linearTransition = isPopGestureInProgress(route);
     260           0 :       return CupertinoFullscreenDialogTransition(
     261             :         primaryRouteAnimation: animation,
     262             :         secondaryRouteAnimation: secondaryAnimation,
     263             :         child: child,
     264             :         linearTransition: linearTransition,
     265             :       );
     266             :     } else {
     267             :       switch (transition) {
     268           1 :         case Transition.fade:
     269             :           final PageTransitionsBuilder matchingBuilder =
     270           1 :               FadeUpwardsPageTransitionsBuilder();
     271           1 :           return matchingBuilder.buildTransitions<T>(
     272             :               route,
     273             :               context,
     274             :               animation,
     275             :               secondaryAnimation,
     276             :               popGesture
     277           0 :                   ? _CupertinoBackGestureDetector<T>(
     278           0 :                       enabledCallback: () => _isPopGestureEnabled<T>(route),
     279           0 :                       onStartPopGesture: () => _startPopGesture<T>(route),
     280             :                       child: child)
     281             :                   : child);
     282             :           break;
     283           0 :         case Transition.rightToLeft:
     284           0 :           return SlideTransition(
     285             :             transformHitTests: false,
     286           0 :             position: new Tween<Offset>(
     287             :               begin: const Offset(1.0, 0.0),
     288             :               end: Offset.zero,
     289           0 :             ).animate(animation),
     290           0 :             child: new SlideTransition(
     291           0 :                 position: new Tween<Offset>(
     292             :                   begin: Offset.zero,
     293             :                   end: const Offset(-1.0, 0.0),
     294           0 :                 ).animate(secondaryAnimation),
     295             :                 child: popGesture
     296           0 :                     ? _CupertinoBackGestureDetector<T>(
     297           0 :                         enabledCallback: () => _isPopGestureEnabled<T>(route),
     298           0 :                         onStartPopGesture: () => _startPopGesture<T>(route),
     299             :                         child: child)
     300             :                     : child),
     301             :           );
     302             :           break;
     303           0 :         case Transition.leftToRight:
     304           0 :           return SlideTransition(
     305             :             transformHitTests: false,
     306           0 :             position: Tween<Offset>(
     307             :               begin: const Offset(-1.0, 0.0),
     308             :               end: Offset.zero,
     309           0 :             ).animate(animation),
     310           0 :             child: new SlideTransition(
     311           0 :                 position: new Tween<Offset>(
     312             :                   begin: Offset.zero,
     313             :                   end: const Offset(1.0, 0.0),
     314           0 :                 ).animate(secondaryAnimation),
     315             :                 child: popGesture
     316           0 :                     ? _CupertinoBackGestureDetector<T>(
     317           0 :                         enabledCallback: () => _isPopGestureEnabled<T>(route),
     318           0 :                         onStartPopGesture: () => _startPopGesture<T>(route),
     319             :                         child: child)
     320             :                     : child),
     321             :           );
     322             :           break;
     323           0 :         case Transition.upToDown:
     324           0 :           return SlideTransition(
     325             :             transformHitTests: false,
     326           0 :             position: Tween<Offset>(
     327             :               begin: const Offset(0.0, -1.0),
     328             :               end: Offset.zero,
     329           0 :             ).animate(animation),
     330           0 :             child: new SlideTransition(
     331           0 :                 position: new Tween<Offset>(
     332             :                   begin: Offset.zero,
     333             :                   end: const Offset(0.0, 1.0),
     334           0 :                 ).animate(secondaryAnimation),
     335             :                 child: popGesture
     336           0 :                     ? _CupertinoBackGestureDetector<T>(
     337           0 :                         enabledCallback: () => _isPopGestureEnabled<T>(route),
     338           0 :                         onStartPopGesture: () => _startPopGesture<T>(route),
     339             :                         child: child)
     340             :                     : child),
     341             :           );
     342             :           break;
     343           0 :         case Transition.downToUp:
     344           0 :           return SlideTransition(
     345             :             transformHitTests: false,
     346           0 :             position: Tween<Offset>(
     347             :               begin: const Offset(0.0, 1.0),
     348             :               end: Offset.zero,
     349           0 :             ).animate(animation),
     350           0 :             child: new SlideTransition(
     351           0 :                 position: new Tween<Offset>(
     352             :                   begin: Offset.zero,
     353             :                   end: const Offset(0.0, -1.0),
     354           0 :                 ).animate(secondaryAnimation),
     355             :                 child: popGesture
     356           0 :                     ? _CupertinoBackGestureDetector<T>(
     357           0 :                         enabledCallback: () => _isPopGestureEnabled<T>(route),
     358           0 :                         onStartPopGesture: () => _startPopGesture<T>(route),
     359             :                         child: child)
     360             :                     : child),
     361             :           );
     362             :           break;
     363           0 :         case Transition.scale:
     364           0 :           return ScaleTransition(
     365             :               alignment: alignment,
     366           0 :               scale: CurvedAnimation(
     367             :                 parent: animation,
     368           0 :                 curve: Interval(
     369             :                   0.00,
     370             :                   0.50,
     371             :                   curve: curve,
     372             :                 ),
     373             :               ),
     374             :               child: popGesture
     375           0 :                   ? _CupertinoBackGestureDetector<T>(
     376           0 :                       enabledCallback: () => _isPopGestureEnabled<T>(route),
     377           0 :                       onStartPopGesture: () => _startPopGesture<T>(route),
     378             :                       child: child)
     379             :                   : child);
     380             :           break;
     381           0 :         case Transition.rotate:
     382           0 :           return RotationTransition(
     383             :             alignment: alignment,
     384             :             turns: animation,
     385           0 :             child: ScaleTransition(
     386             :               alignment: alignment,
     387             :               scale: animation,
     388           0 :               child: FadeTransition(
     389             :                   opacity: animation,
     390             :                   child: popGesture
     391           0 :                       ? _CupertinoBackGestureDetector<T>(
     392           0 :                           enabledCallback: () => _isPopGestureEnabled<T>(route),
     393           0 :                           onStartPopGesture: () => _startPopGesture<T>(route),
     394             :                           child: child)
     395             :                       : child),
     396             :             ),
     397             :           );
     398             :           break;
     399             : 
     400           0 :         case Transition.rightToLeftWithFade:
     401           0 :           return SlideTransition(
     402           0 :             position: Tween<Offset>(
     403             :               begin: const Offset(1.0, 0.0),
     404             :               end: Offset.zero,
     405           0 :             ).animate(animation),
     406           0 :             child: FadeTransition(
     407             :               opacity: animation,
     408           0 :               child: SlideTransition(
     409           0 :                   position: Tween<Offset>(
     410             :                     begin: Offset.zero,
     411             :                     end: const Offset(-1.0, 0.0),
     412           0 :                   ).animate(secondaryAnimation),
     413             :                   child: popGesture
     414           0 :                       ? _CupertinoBackGestureDetector<T>(
     415           0 :                           enabledCallback: () => _isPopGestureEnabled<T>(route),
     416           0 :                           onStartPopGesture: () => _startPopGesture<T>(route),
     417             :                           child: child)
     418             :                       : child),
     419             :             ),
     420             :           );
     421             :           break;
     422           0 :         case Transition.leftToRightWithFade:
     423           0 :           return SlideTransition(
     424           0 :             position: Tween<Offset>(
     425             :               begin: const Offset(-1.0, 0.0),
     426             :               end: Offset.zero,
     427           0 :             ).animate(animation),
     428           0 :             child: FadeTransition(
     429             :               opacity: animation,
     430           0 :               child: SlideTransition(
     431           0 :                   position: Tween<Offset>(
     432             :                     begin: Offset.zero,
     433             :                     end: const Offset(1.0, 0.0),
     434           0 :                   ).animate(secondaryAnimation),
     435             :                   child: popGesture
     436           0 :                       ? _CupertinoBackGestureDetector<T>(
     437           0 :                           enabledCallback: () => _isPopGestureEnabled<T>(route),
     438           0 :                           onStartPopGesture: () => _startPopGesture<T>(route),
     439             :                           child: child)
     440             :                       : child),
     441             :             ),
     442             :           );
     443             :           break;
     444           0 :         case Transition.cupertino:
     445           0 :           return CupertinoPageTransition(
     446             :             primaryRouteAnimation: animation,
     447             :             secondaryRouteAnimation: secondaryAnimation,
     448             :             // Check if the route has an animation that's currently participating
     449             :             // in a back swipe gesture.
     450             :             //
     451             :             // In the middle of a back gesture drag, let the transition be linear to
     452             :             // match finger motions.
     453           0 :             linearTransition: isPopGestureInProgress(route),
     454           0 :             child: _CupertinoBackGestureDetector<T>(
     455           0 :               enabledCallback: () => _isPopGestureEnabled<T>(route),
     456           0 :               onStartPopGesture: () => _startPopGesture<T>(route),
     457             :               child: child,
     458             :             ),
     459             :           );
     460             :           break;
     461             :         default:
     462           0 :           return CupertinoPageTransition(
     463             :             primaryRouteAnimation: animation,
     464             :             secondaryRouteAnimation: secondaryAnimation,
     465             :             // Check if the route has an animation that's currently participating
     466             :             // in a back swipe gesture.
     467             :             //
     468             :             // In the middle of a back gesture drag, let the transition be linear to
     469             :             // match finger motions.
     470           0 :             linearTransition: isPopGestureInProgress(route),
     471             :             child: popGesture
     472           0 :                 ? _CupertinoBackGestureDetector<T>(
     473           0 :                     enabledCallback: () => _isPopGestureEnabled<T>(route),
     474           0 :                     onStartPopGesture: () => _startPopGesture<T>(route),
     475             :                     child: child)
     476             :                 : child,
     477             :           );
     478             :       }
     479             :     }
     480             :   }
     481             : 
     482           1 :   @override
     483             :   Widget buildTransitions(BuildContext context, Animation<double> animation,
     484             :       Animation<double> secondaryAnimation, Widget child) {
     485           1 :     if (customBuildPageTransitions != null) {
     486           0 :       return customBuildPageTransitions;
     487             :     } else {
     488           1 :       return buildPageTransitions<T>(
     489             :           this,
     490             :           context,
     491           2 :           popGesture ?? GetPlatform.isIOS,
     492             :           animation,
     493             :           secondaryAnimation,
     494             :           child,
     495           1 :           transition,
     496           1 :           curve,
     497           1 :           alignment);
     498             :     }
     499             :   }
     500             : 
     501           1 :   @override
     502           4 :   String get debugLabel => '${super.debugLabel}(${settings.name})';
     503             : }
     504             : 
     505             : class _CupertinoBackGestureDetector<T> extends StatefulWidget {
     506           0 :   const _CupertinoBackGestureDetector({
     507             :     Key key,
     508             :     @required this.enabledCallback,
     509             :     @required this.onStartPopGesture,
     510             :     @required this.child,
     511           0 :   })  : assert(enabledCallback != null),
     512           0 :         assert(onStartPopGesture != null),
     513           0 :         assert(child != null),
     514           0 :         super(key: key);
     515             : 
     516             :   final Widget child;
     517             : 
     518             :   final ValueGetter<bool> enabledCallback;
     519             : 
     520             :   final ValueGetter<_CupertinoBackGestureController<T>> onStartPopGesture;
     521             : 
     522           0 :   @override
     523             :   _CupertinoBackGestureDetectorState<T> createState() =>
     524           0 :       _CupertinoBackGestureDetectorState<T>();
     525             : }
     526             : 
     527             : class _CupertinoBackGestureDetectorState<T>
     528             :     extends State<_CupertinoBackGestureDetector<T>> {
     529             :   _CupertinoBackGestureController<T> _backGestureController;
     530             : 
     531             :   HorizontalDragGestureRecognizer _recognizer;
     532             : 
     533           0 :   @override
     534             :   void initState() {
     535           0 :     super.initState();
     536           0 :     _recognizer = HorizontalDragGestureRecognizer(debugOwner: this)
     537           0 :       ..onStart = _handleDragStart
     538           0 :       ..onUpdate = _handleDragUpdate
     539           0 :       ..onEnd = _handleDragEnd
     540           0 :       ..onCancel = _handleDragCancel;
     541             :   }
     542             : 
     543           0 :   @override
     544             :   void dispose() {
     545           0 :     _recognizer.dispose();
     546           0 :     super.dispose();
     547             :   }
     548             : 
     549           0 :   void _handleDragStart(DragStartDetails details) {
     550           0 :     assert(mounted);
     551           0 :     assert(_backGestureController == null);
     552           0 :     _backGestureController = widget.onStartPopGesture();
     553             :   }
     554             : 
     555           0 :   void _handleDragUpdate(DragUpdateDetails details) {
     556           0 :     assert(mounted);
     557           0 :     assert(_backGestureController != null);
     558           0 :     _backGestureController.dragUpdate(
     559           0 :         _convertToLogical(details.primaryDelta / context.size.width));
     560             :   }
     561             : 
     562           0 :   void _handleDragEnd(DragEndDetails details) {
     563           0 :     assert(mounted);
     564           0 :     assert(_backGestureController != null);
     565           0 :     _backGestureController.dragEnd(_convertToLogical(
     566           0 :         details.velocity.pixelsPerSecond.dx / context.size.width));
     567           0 :     _backGestureController = null;
     568             :   }
     569             : 
     570           0 :   void _handleDragCancel() {
     571           0 :     assert(mounted);
     572             :     // This can be called even if start is not called, paired with the "down" event
     573             :     // that we don't consider here.
     574           0 :     _backGestureController?.dragEnd(0.0);
     575           0 :     _backGestureController = null;
     576             :   }
     577             : 
     578           0 :   void _handlePointerDown(PointerDownEvent event) {
     579           0 :     if (widget.enabledCallback()) _recognizer.addPointer(event);
     580             :   }
     581             : 
     582           0 :   double _convertToLogical(double value) {
     583           0 :     switch (Directionality.of(context)) {
     584           0 :       case TextDirection.rtl:
     585           0 :         return -value;
     586           0 :       case TextDirection.ltr:
     587             :         return value;
     588             :     }
     589             :     return null;
     590             :   }
     591             : 
     592           0 :   @override
     593             :   Widget build(BuildContext context) {
     594           0 :     assert(debugCheckHasDirectionality(context));
     595             :     // For devices with notches, the drag area needs to be larger on the side
     596             :     // that has the notch.
     597           0 :     double dragAreaWidth = Directionality.of(context) == TextDirection.ltr
     598           0 :         ? MediaQuery.of(context).padding.left
     599           0 :         : MediaQuery.of(context).padding.right;
     600          12 :     dragAreaWidth = max(dragAreaWidth, _kBackGestureWidth);
     601           0 :     return Stack(
     602             :       fit: StackFit.passthrough,
     603           0 :       children: <Widget>[
     604           0 :         widget.child,
     605           0 :         PositionedDirectional(
     606             :           start: 0.0,
     607             :           width: dragAreaWidth,
     608             :           top: 0.0,
     609             :           bottom: 0.0,
     610           0 :           child: Listener(
     611           0 :             onPointerDown: _handlePointerDown,
     612             :             behavior: HitTestBehavior.translucent,
     613             :           ),
     614             :         ),
     615             :       ],
     616             :     );
     617             :   }
     618             : }
     619             : 
     620             : class _CupertinoBackGestureController<T> {
     621             :   /// Creates a controller for an iOS-style back gesture.
     622             :   ///
     623             :   /// The [navigator] and [controller] arguments must not be null.
     624           0 :   _CupertinoBackGestureController({
     625             :     @required this.navigator,
     626             :     @required this.controller,
     627           0 :   })  : assert(navigator != null),
     628           0 :         assert(controller != null) {
     629           0 :     navigator.didStartUserGesture();
     630             :   }
     631             : 
     632             :   final AnimationController controller;
     633             :   final NavigatorState navigator;
     634             : 
     635             :   /// The drag gesture has changed by [fractionalDelta]. The total range of the
     636             :   /// drag should be 0.0 to 1.0.
     637           0 :   void dragUpdate(double delta) {
     638           0 :     controller.value -= delta;
     639             :   }
     640             : 
     641             :   /// The drag gesture has ended with a horizontal motion of
     642             :   /// [fractionalVelocity] as a fraction of screen width per second.
     643           0 :   void dragEnd(double velocity) {
     644             :     // Fling in the appropriate direction.
     645             :     // AnimationController.fling is guaranteed to
     646             :     // take at least one frame.
     647             :     //
     648             :     // This curve has been determined through rigorously eyeballing native iOS
     649             :     // animations.
     650             :     const Curve animationCurve = Curves.fastLinearToSlowEaseIn;
     651             :     bool animateForward;
     652             : 
     653             :     // If the user releases the page before mid screen with sufficient velocity,
     654             :     // or after mid screen, we should animate the page out. Otherwise, the page
     655             :     // should be animated back in.
     656           0 :     if (velocity.abs() >= _kMinFlingVelocity)
     657           0 :       animateForward = velocity <= 0;
     658             :     else
     659           0 :       animateForward = controller.value > 0.5;
     660             : 
     661             :     if (animateForward) {
     662             :       // The closer the panel is to dismissing, the shorter the animation is.
     663             :       // We want to cap the animation time, but we want to use a linear curve
     664             :       // to determine it.
     665          12 :       final int droppedPageForwardAnimationTime = min(
     666           0 :         lerpDouble(
     667           0 :                 _kMaxDroppedSwipePageForwardAnimationTime, 0, controller.value)
     668           0 :             .floor(),
     669             :         _kMaxPageBackAnimationTime,
     670             :       );
     671           0 :       controller.animateTo(1.0,
     672           0 :           duration: Duration(milliseconds: droppedPageForwardAnimationTime),
     673             :           curve: animationCurve);
     674             :     } else {
     675             :       // This route is destined to pop at this point. Reuse navigator's pop.
     676           0 :       navigator.pop();
     677             : 
     678             :       // The popping may have finished inline if already at the target destination.
     679           0 :       if (controller.isAnimating) {
     680             :         // Otherwise, use a custom popping animation duration and curve.
     681           0 :         final int droppedPageBackAnimationTime = lerpDouble(
     682           0 :                 0, _kMaxDroppedSwipePageForwardAnimationTime, controller.value)
     683           0 :             .floor();
     684           0 :         controller.animateBack(0.0,
     685           0 :             duration: Duration(milliseconds: droppedPageBackAnimationTime),
     686             :             curve: animationCurve);
     687             :       }
     688             :     }
     689             : 
     690           0 :     if (controller.isAnimating) {
     691             :       // Keep the userGestureInProgress in true state so we don't change the
     692             :       // curve of the page transition mid-flight since CupertinoPageTransition
     693             :       // depends on userGestureInProgress.
     694             :       AnimationStatusListener animationStatusCallback;
     695           0 :       animationStatusCallback = (AnimationStatus status) {
     696           0 :         navigator.didStopUserGesture();
     697           0 :         controller.removeStatusListener(animationStatusCallback);
     698             :       };
     699           0 :       controller.addStatusListener(animationStatusCallback);
     700             :     } else {
     701           0 :       navigator.didStopUserGesture();
     702             :     }
     703             :   }
     704             : }

Generated by: LCOV version 1.14