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

Generated by: LCOV version 1.14