1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/cupertino.dart';
import 'page_transitions_theme.dart';
import 'theme.dart';
/// A modal route that replaces the entire screen with a platform-adaptive
/// transition.
///
/// {@macro flutter.material.materialRouteTransitionMixin}
///
/// By default, when a modal route is replaced by another, the previous route
/// remains in memory. To free all the resources when this is not necessary, set
/// [maintainState] to false.
///
/// The `fullscreenDialog` property specifies whether the incoming route is a
/// fullscreen modal dialog. On iOS, those routes animate from the bottom to the
/// top rather than horizontally.
///
/// The type `T` specifies the return type of the route which can be supplied as
/// the route is popped from the stack via [Navigator.pop] by providing the
/// optional `result` argument.
///
/// See also:
///
/// * [MaterialRouteTransitionMixin], which provides the material transition
/// for this route.
/// * [MaterialPage], which is a [Page] of this class.
class MaterialPageRoute<T> extends PageRoute<T> with MaterialRouteTransitionMixin<T> {
/// Construct a MaterialPageRoute whose contents are defined by [builder].
///
/// The values of [builder], [maintainState], and [PageRoute.fullscreenDialog]
/// must not be null.
MaterialPageRoute({
required this.builder,
RouteSettings? settings,
this.maintainState = true,
bool fullscreenDialog = false,
}) : assert(builder != null),
assert(maintainState != null),
assert(fullscreenDialog != null),
super(settings: settings, fullscreenDialog: fullscreenDialog) {
assert(opaque);
}
/// Builds the primary contents of the route.
final WidgetBuilder builder;
@override
Widget buildContent(BuildContext context) => builder(context);
@override
final bool maintainState;
@override
String get debugLabel => '${super.debugLabel}(${settings.name})';
}
/// A mixin that provides platform-adaptive transitions for a [PageRoute].
///
/// {@template flutter.material.materialRouteTransitionMixin}
/// For Android, the entrance transition for the page slides the route upwards
/// and fades it in. The exit transition is the same, but in reverse.
///
/// The transition is adaptive to the platform and on iOS, the route slides in
/// from the right and exits in reverse. The route also shifts to the left in
/// parallax when another page enters to cover it. (These directions are flipped
/// in environments with a right-to-left reading direction.)
/// {@endtemplate}
///
/// See also:
///
/// * [PageTransitionsTheme], which defines the default page transitions used
/// by the [MaterialRouteTransitionMixin.buildTransitions].
mixin MaterialRouteTransitionMixin<T> on PageRoute<T> {
/// Builds the primary contents of the route.
@protected
Widget buildContent(BuildContext context);
@override
Duration get transitionDuration => const Duration(milliseconds: 300);
@override
Color? get barrierColor => null;
@override
String? get barrierLabel => null;
@override
bool canTransitionTo(TransitionRoute<dynamic> nextRoute) {
// Don't perform outgoing animation if the next route is a fullscreen dialog.
return (nextRoute is MaterialRouteTransitionMixin && !nextRoute.fullscreenDialog)
|| (nextRoute is CupertinoRouteTransitionMixin && !nextRoute.fullscreenDialog);
}
@override
Widget buildPage(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
final Widget result = buildContent(context);
assert(() {
if (result == null) {
throw FlutterError(
'The builder for route "${settings.name}" returned null.\n'
'Route builders must never return null.',
);
}
return true;
}());
return Semantics(
scopesRoute: true,
explicitChildNodes: true,
child: result,
);
}
@override
Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
final PageTransitionsTheme theme = Theme.of(context).pageTransitionsTheme;
return theme.buildTransitions<T>(this, context, animation, secondaryAnimation, child);
}
}
/// A page that creates a material style [PageRoute].
///
/// {@macro flutter.material.materialRouteTransitionMixin}
///
/// By default, when the created route is replaced by another, the previous
/// route remains in memory. To free all the resources when this is not
/// necessary, set [maintainState] to false.
///
/// The `fullscreenDialog` property specifies whether the created route is a
/// fullscreen modal dialog. On iOS, those routes animate from the bottom to the
/// top rather than horizontally.
///
/// The type `T` specifies the return type of the route which can be supplied as
/// the route is popped from the stack via [Navigator.transitionDelegate] by
/// providing the optional `result` argument to the
/// [RouteTransitionRecord.markForPop] in the [TransitionDelegate.resolve].
///
/// See also:
///
/// * [MaterialPageRoute], which is the [PageRoute] version of this class
class MaterialPage<T> extends Page<T> {
/// Creates a material page.
const MaterialPage({
required this.child,
this.maintainState = true,
this.fullscreenDialog = false,
LocalKey? key,
String? name,
Object? arguments,
String? restorationId,
}) : assert(child != null),
assert(maintainState != null),
assert(fullscreenDialog != null),
super(key: key, name: name, arguments: arguments, restorationId: restorationId);
/// The content to be shown in the [Route] created by this page.
final Widget child;
/// {@macro flutter.widgets.ModalRoute.maintainState}
final bool maintainState;
/// {@macro flutter.widgets.PageRoute.fullscreenDialog}
final bool fullscreenDialog;
@override
Route<T> createRoute(BuildContext context) {
return _PageBasedMaterialPageRoute<T>(page: this);
}
}
// A page-based version of MaterialPageRoute.
//
// This route uses the builder from the page to build its content. This ensures
// the content is up to date after page updates.
class _PageBasedMaterialPageRoute<T> extends PageRoute<T> with MaterialRouteTransitionMixin<T> {
_PageBasedMaterialPageRoute({
required MaterialPage<T> page,
}) : assert(page != null),
super(settings: page) {
assert(opaque);
}
MaterialPage<T> get _page => settings as MaterialPage<T>;
@override
Widget buildContent(BuildContext context) {
return _page.child;
}
@override
bool get maintainState => _page.maintainState;
@override
bool get fullscreenDialog => _page.fullscreenDialog;
@override
String get debugLabel => '${super.debugLabel}(${_page.name})';
}