LCOV - code coverage report
Current view: top level - src - beam_page.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 72 72 100.0 %
Date: 2021-09-11 22:34:03 Functions: 0 0 -

          Line data    Source code
       1             : import 'package:flutter/cupertino.dart';
       2             : import 'package:flutter/material.dart';
       3             : 
       4             : import '../beamer.dart';
       5             : 
       6             : /// Types for how to route should be built.
       7          12 : enum BeamPageType {
       8             :   material,
       9             :   cupertino,
      10             :   fadeTransition,
      11             :   slideTransition,
      12             :   scaleTransition,
      13             :   noTransition,
      14             : }
      15             : 
      16             : /// A wrapper for screens in a navigation stack.
      17             : class BeamPage extends Page {
      18          19 :   const BeamPage({
      19             :     LocalKey? key,
      20             :     String? name,
      21             :     required this.child,
      22             :     this.title,
      23             :     this.onPopPage = pathSegmentPop,
      24             :     this.popToNamed,
      25             :     this.type = BeamPageType.material,
      26             :     this.routeBuilder,
      27             :     this.fullScreenDialog = false,
      28             :     this.keepQueryOnPop = false,
      29           6 :   }) : super(key: key, name: name);
      30             : 
      31             :   /// The default pop behavior for [BeamPage].
      32             :   ///
      33             :   /// Pops the last path segment from URI and calls [BeamerDelegate.update].
      34           3 :   static bool pathSegmentPop(
      35             :     BuildContext context,
      36             :     BeamerDelegate delegate,
      37             :     RouteInformationSerializable state,
      38             :     BeamPage poppedPage,
      39             :   ) {
      40           6 :     if (!delegate.navigator.canPop() ||
      41           6 :         delegate.beamingHistoryCompleteLength < 2) {
      42             :       return false;
      43             :     }
      44             : 
      45             :     // take the data in case we remove the BeamLocation from history
      46             :     // and generate a new one (but the same).
      47           6 :     final data = delegate.currentBeamLocation.data;
      48             : 
      49             :     // Take the history element that is being popped and the one before
      50             :     // as they will be compared later on to fine-tune the pop experience.
      51           3 :     final poppedHistoryElement = delegate.removeLastHistoryElement()!;
      52          12 :     final previousHistoryElement = delegate.beamingHistory.last.history.last;
      53             : 
      54             :     // Convert both to Uri as their path and query will be compared.
      55           3 :     final poppedUri = Uri.parse(
      56           9 :       poppedHistoryElement.state.routeInformation.location ?? '/',
      57             :     );
      58           3 :     final previousUri = Uri.parse(
      59           9 :       previousHistoryElement.state.routeInformation.location ?? '/',
      60             :     );
      61             : 
      62           3 :     final poppedPathSegments = poppedUri.pathSegments;
      63           3 :     final poppedQueryParameters = poppedUri.queryParameters;
      64             : 
      65             :     // Pop path is obtained via removing the last path segment from path
      66             :     // that is beaing popped.
      67           6 :     final popPathSegments = List.from(poppedPathSegments)..removeLast();
      68           6 :     final popPath = '/' + popPathSegments.join('/');
      69           3 :     var popUri = Uri(
      70             :       path: popPath,
      71           3 :       queryParameters: poppedPage.keepQueryOnPop
      72             :           ? poppedQueryParameters
      73           6 :           : (popPath == previousUri.path)
      74           2 :               ? previousUri.queryParameters
      75             :               : null,
      76             :     );
      77             : 
      78             :     // We need the routeState from the route we are trying to pop to.
      79             :     //
      80             :     // Remove the last history element if it's the same as the path
      81             :     // we're trying to pop to, because `update` will add it to history.
      82             :     // This is `false` in case we deep-linked.
      83             :     //
      84             :     // Otherwise, find the state with popPath in history.
      85             :     RouteInformationSerializable? lastState;
      86           6 :     if (popPath == previousUri.path) {
      87           4 :       lastState = delegate.removeLastHistoryElement()?.state;
      88             :     } else {
      89             :       // find the last
      90             :       bool found = false;
      91           6 :       for (var beamLocation in delegate.beamingHistory.reversed) {
      92             :         if (found) {
      93             :           break;
      94             :         }
      95           6 :         for (var historyElement in beamLocation.history.reversed) {
      96             :           final uri =
      97           8 :               Uri.parse(historyElement.state.routeInformation.location ?? '/');
      98           4 :           if (uri.path == popPath) {
      99           1 :             lastState = historyElement.state;
     100             :             found = true;
     101             :             break;
     102             :           }
     103             :         }
     104             :       }
     105             :     }
     106             : 
     107           3 :     delegate.update(
     108           6 :       configuration: delegate.configuration.copyWith(
     109           3 :         location: popUri.toString(),
     110           4 :         state: lastState?.routeInformation.state,
     111             :       ),
     112             :       data: data,
     113             :     );
     114             : 
     115             :     return true;
     116             :   }
     117             : 
     118             :   /// Pops the last route from history and calls [BeamerDelegate.update].
     119           1 :   static bool routePop(
     120             :     BuildContext context,
     121             :     BeamerDelegate delegate,
     122             :     RouteInformationSerializable state,
     123             :     BeamPage poppedPage,
     124             :   ) {
     125           2 :     if (delegate.beamingHistoryCompleteLength < 2) {
     126             :       return false;
     127             :     }
     128             : 
     129           1 :     delegate.removeLastHistoryElement();
     130           1 :     final previousHistoryElement = delegate.removeLastHistoryElement()!;
     131             : 
     132           1 :     delegate.update(
     133           3 :       configuration: previousHistoryElement.state.routeInformation.copyWith(),
     134             :     );
     135             : 
     136             :     return true;
     137             :   }
     138             : 
     139             :   /// The concrete Widget representing app's screen.
     140             :   final Widget child;
     141             : 
     142             :   /// The BeamPage's title. On the web, this is used for the browser tab title.
     143             :   final String? title;
     144             : 
     145             :   /// Overrides the default pop by executing an arbitrary closure.
     146             :   /// Mainly used to manually update the [delegate.currentBeamLocation] state.
     147             :   ///
     148             :   /// [poppedPage] is this [BeamPage].
     149             :   ///
     150             :   /// Return `false` (rarely used) to prevent **any** navigation from happening,
     151             :   /// otherwise return `true`.
     152             :   ///
     153             :   /// More powerful than [popToNamed].
     154             :   final bool Function(
     155             :     BuildContext context,
     156             :     BeamerDelegate delegate,
     157             :     RouteInformationSerializable state,
     158             :     BeamPage poppedPage,
     159             :   ) onPopPage;
     160             : 
     161             :   /// Overrides the default pop by beaming to specified URI string.
     162             :   ///
     163             :   /// Less powerful than [onPopPage].
     164             :   final String? popToNamed;
     165             : 
     166             :   /// The type to determine how a route should be built.
     167             :   ///
     168             :   /// See [BeamPageType] for available types.
     169             :   final BeamPageType type;
     170             : 
     171             :   /// A builder for custom [Route] to use in [createRoute].
     172             :   ///
     173             :   /// [settings] must be passed to [PageRoute.settings].
     174             :   /// [child] is the child of this [BeamPage]
     175             :   final Route Function(RouteSettings settings, Widget child)? routeBuilder;
     176             : 
     177             :   /// Whether to present current [BeamPage] as a fullscreen dialog
     178             :   ///
     179             :   /// On iOS, dialog transitions animate differently and are also not closeable with the back swipe gesture
     180             :   final bool fullScreenDialog;
     181             : 
     182             :   /// When this [BeamPage] pops from [Navigator] stack, whether to keep the
     183             :   /// query parameters within current [BeamLocation].
     184             :   ///
     185             :   /// Defaults to `false`.
     186             :   final bool keepQueryOnPop;
     187             : 
     188           6 :   @override
     189             :   Route createRoute(BuildContext context) {
     190           6 :     if (routeBuilder != null) {
     191           2 :       return routeBuilder!(this, child);
     192             :     }
     193           6 :     switch (type) {
     194           6 :       case BeamPageType.cupertino:
     195           1 :         return CupertinoPageRoute(
     196           1 :           title: title,
     197           1 :           fullscreenDialog: fullScreenDialog,
     198             :           settings: this,
     199           2 :           builder: (context) => child,
     200             :         );
     201           6 :       case BeamPageType.fadeTransition:
     202           1 :         return PageRouteBuilder(
     203           1 :           fullscreenDialog: fullScreenDialog,
     204             :           settings: this,
     205           2 :           pageBuilder: (_, __, ___) => child,
     206           2 :           transitionsBuilder: (_, animation, __, child) => FadeTransition(
     207             :             opacity: animation,
     208             :             child: child,
     209             :           ),
     210             :         );
     211           6 :       case BeamPageType.slideTransition:
     212           1 :         return PageRouteBuilder(
     213           1 :           fullscreenDialog: fullScreenDialog,
     214             :           settings: this,
     215           2 :           pageBuilder: (_, __, ___) => child,
     216           2 :           transitionsBuilder: (_, animation, __, child) => SlideTransition(
     217           1 :             position: animation.drive(
     218           1 :                 Tween(begin: const Offset(0, 1), end: const Offset(0, 0))
     219           2 :                     .chain(CurveTween(curve: Curves.ease))),
     220             :             child: child,
     221             :           ),
     222             :         );
     223           6 :       case BeamPageType.scaleTransition:
     224           1 :         return PageRouteBuilder(
     225           1 :           fullscreenDialog: fullScreenDialog,
     226             :           settings: this,
     227           2 :           pageBuilder: (_, __, ___) => child,
     228           2 :           transitionsBuilder: (_, animation, __, child) => ScaleTransition(
     229             :             scale: animation,
     230             :             child: child,
     231             :           ),
     232             :         );
     233           6 :       case BeamPageType.noTransition:
     234           1 :         return PageRouteBuilder(
     235           1 :           fullscreenDialog: fullScreenDialog,
     236             :           settings: this,
     237           2 :           pageBuilder: (context, animation, secondaryAnimation) => child,
     238             :         );
     239             :       default:
     240           6 :         return MaterialPageRoute(
     241           6 :           fullscreenDialog: fullScreenDialog,
     242             :           settings: this,
     243          12 :           builder: (context) => child,
     244             :         );
     245             :     }
     246             :   }
     247             : }

Generated by: LCOV version 1.14