Line data Source code
1 : import 'package:flutter/cupertino.dart';
2 : import 'package:flutter/material.dart';
3 :
4 : import 'beam_location.dart';
5 : import 'beam_state.dart';
6 : import 'beamer_delegate.dart';
7 :
8 : /// Types for how to route should be built.
9 9 : enum BeamPageType {
10 : material,
11 : cupertino,
12 : fadeTransition,
13 : slideTransition,
14 : scaleTransition,
15 : noTransition,
16 : }
17 :
18 : /// A wrapper for screens in a navigation stack.
19 : class BeamPage extends Page {
20 6 : BeamPage({
21 : LocalKey? key,
22 : String? name,
23 : required this.child,
24 : this.title,
25 : this.onPopPage = defaultOnPopPage,
26 : this.popToNamed,
27 : this.type = BeamPageType.material,
28 : this.pageRouteBuilder,
29 : this.keepQueryOnPop = false,
30 6 : }) : super(key: key, name: name);
31 :
32 : /// The default pop behavior for [BeamPage].
33 3 : static bool defaultOnPopPage(
34 : BuildContext context,
35 : BeamerDelegate delegate,
36 : BeamPage poppedPage,
37 : ) {
38 3 : final location = delegate.currentBeamLocation;
39 9 : final previousBeamState = delegate.beamStateHistory.length > 1
40 15 : ? delegate.beamStateHistory[delegate.beamStateHistory.length - 2]
41 : : null;
42 :
43 : final pathBlueprintSegments =
44 9 : List<String>.from(location.state.pathBlueprintSegments);
45 : final pathParameters =
46 9 : Map<String, String>.from(location.state.pathParameters);
47 3 : final pathSegment = pathBlueprintSegments.removeLast();
48 6 : if (pathSegment[0] == ':') {
49 2 : pathParameters.remove(pathSegment.substring(1));
50 : }
51 :
52 3 : var beamState = BeamState(
53 : pathBlueprintSegments: pathBlueprintSegments,
54 : pathParameters: pathParameters,
55 : queryParameters:
56 6 : poppedPage.keepQueryOnPop ? location.state.queryParameters : {},
57 6 : data: location.state.data,
58 : );
59 :
60 15 : if (beamState.uri.path == previousBeamState?.uri.path &&
61 2 : !poppedPage.keepQueryOnPop) {
62 2 : beamState = beamState.copyWith(
63 2 : queryParameters: previousBeamState?.queryParameters,
64 : );
65 : }
66 :
67 3 : delegate.removeLastBeamState();
68 :
69 6 : location.update((state) => beamState);
70 : return true;
71 : }
72 :
73 : /// The concrete Widget representing app's screen.
74 : final Widget child;
75 :
76 : /// The BeamPage's title. On the web, this is used for the browser tab title.
77 : final String? title;
78 :
79 : /// Overrides the default pop by executing an arbitrary closure.
80 : /// Mainly used to manually update the [delegate.currentBeamLocation] state.
81 : ///
82 : /// [poppedPage] is this [BeamPage].
83 : ///
84 : /// Return `false` (rarely used) to prevent **any** navigation from happening,
85 : /// otherwise return `true`.
86 : ///
87 : /// More powerful than [popToNamed].
88 : final bool Function(
89 : BuildContext context,
90 : BeamerDelegate delegate,
91 : BeamPage poppedPage,
92 : ) onPopPage;
93 :
94 : /// Overrides the default pop by beaming to specified URI string.
95 : ///
96 : /// Less powerful than [onPopPage].
97 : final String? popToNamed;
98 :
99 : /// The type to determine how a route should be built.
100 : ///
101 : /// See [BeamPageType] for available types.
102 : final BeamPageType type;
103 :
104 : /// A builder for custom [PageRoute] to use in [createRoute].
105 : ///
106 : /// [settings] must be passed to [PageRoute.settings].
107 : final PageRoute Function(RouteSettings settings, Widget child)?
108 : pageRouteBuilder;
109 :
110 : /// When this [BeamPage] pops from [Navigator] stack, whether to keep the
111 : /// query parameters within current [BeamLocation].
112 : ///
113 : /// Defaults to `false`.
114 : final bool keepQueryOnPop;
115 :
116 6 : @override
117 : Route createRoute(BuildContext context) {
118 6 : if (pageRouteBuilder != null) {
119 3 : return pageRouteBuilder!(this, child);
120 : }
121 6 : switch (type) {
122 6 : case BeamPageType.cupertino:
123 1 : return CupertinoPageRoute(
124 : settings: this,
125 2 : builder: (context) => child,
126 : );
127 6 : case BeamPageType.fadeTransition:
128 1 : return PageRouteBuilder(
129 : settings: this,
130 2 : pageBuilder: (_, __, ___) => child,
131 2 : transitionsBuilder: (_, animation, __, child) => FadeTransition(
132 : opacity: animation,
133 : child: child,
134 : ),
135 : );
136 6 : case BeamPageType.slideTransition:
137 1 : return PageRouteBuilder(
138 : settings: this,
139 2 : pageBuilder: (_, __, ___) => child,
140 2 : transitionsBuilder: (_, animation, __, child) => SlideTransition(
141 1 : position: animation.drive(
142 3 : Tween(begin: Offset(0, 1), end: Offset(0, 0))
143 2 : .chain(CurveTween(curve: Curves.ease))),
144 : child: child,
145 : ),
146 : );
147 6 : case BeamPageType.scaleTransition:
148 1 : return PageRouteBuilder(
149 : settings: this,
150 2 : pageBuilder: (_, __, ___) => child,
151 2 : transitionsBuilder: (_, animation, __, child) => ScaleTransition(
152 : scale: animation,
153 : child: child,
154 : ),
155 : );
156 6 : case BeamPageType.noTransition:
157 1 : return PageRouteBuilder(
158 : settings: this,
159 2 : pageBuilder: (context, animation, secondaryAnimation) => child,
160 : );
161 : default:
162 6 : return MaterialPageRoute(
163 : settings: this,
164 12 : builder: (context) => child,
165 : );
166 : }
167 : }
168 : }
|