注:本文代码基于Flutter SDK 3.13.5
一、什么是Navigator?
先来看下Navigator的注释。
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
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
|
/// 一个使用堆栈规则管理一组子widgets的widget
/// A c that manages a set of child widgets with a stack discipline.
///
/// 许多应用程序在其widget层次结构顶部附近都有一个navigator,以便使用 [Overlay] 显示其逻辑历史记录,并将最近访问的页面直观地显示在旧页面的顶部。
/// 使用此模式可以让navigator通过在overlay中移动widgets来直观地从一个页面过渡到另一页面。
/// 同样,navigator可用于通过将对话框widget放置在当前页面上方来显示对话框。
/// Many apps have a navigator near the top of their widget hierarchy in order
/// to display their logical history using an [Overlay] with the most recently
/// visited pages visually on top of the older pages. Using this pattern lets
/// the navigator visually transition from one page to another by moving the widgets
/// around in the overlay. Similarly, the navigator can be used to show a dialog
/// by positioning the dialog widget above the current page.
///
/// ## 使用Pages API
/// ## Using the Pages API
///
/// 如果提供的话,[Navigator] 会将其 [Navigator.pages] 转换为一堆 [Route]。
/// [Navigator.pages] 中的更改将触发 [Route] 堆栈的更新
/// [Navigator] 将更新其routes以匹配其 [Navigator.pages] 的新配置。
/// 要使用此 API,可以创建一个 [Page] 子类并为 [Navigator.pages] 定义一系列 [Page]。
/// 还需要 [Navigator.onPopPage] 回调来在弹出时正确清理输入页面。
/// The [Navigator] will convert its [Navigator.pages] into a stack of [Route]s
/// if it is provided. A change in [Navigator.pages] will trigger an update to
/// the stack of [Route]s. The [Navigator] will update its routes to match the
/// new configuration of its [Navigator.pages]. To use this API, one can create
/// a [Page] subclass and defines a list of [Page]s for [Navigator.pages]. A
/// [Navigator.onPopPage] callback is also required to properly clean up the
/// input pages in case of a pop.
///
/// 默认情况下,[Navigator] 将使用 [DefaultTransitionDelegate] 来决定routes如何转入或转出屏幕。
/// 要自定义它,请定义 [TransitionDelegate] 子类并将其提供给 [Navigator.transitionDelegate]。
/// By Default, the [Navigator] will use [DefaultTransitionDelegate] to decide
/// how routes transition in or out of the screen. To customize it, define a
/// [TransitionDelegate] subclass and provide it to the
/// [Navigator.transitionDelegate].
///
/// 有关使用pages API 的更多信息,请参阅 [Router] widget.
/// For more information on using the pages API, see the [Router] widget.
///
/// ## 使用Navigator API
/// ## Using the Navigator API
///
/// 移动应用程序通常通过称为“屏幕”或“页面”的全屏元素显示其内容。
/// 在 Flutter 中,这些元素称为routes,它们由 [Navigator] widget管理。
/// 导航器管理 [Route] 对象的堆栈,并提供两种管理堆栈的方式,声明式 API [Navigator.pages] 或命令式 API [Navigator.push] 和 [Navigator.pop]。
/// Mobile apps typically reveal their contents via full-screen elements
/// called "screens" or "pages". In Flutter these elements are called
/// routes and they're managed by a [Navigator] widget. The navigator
/// manages a stack of [Route] objects and provides two ways for managing
/// the stack, the declarative API [Navigator.pages] or imperative API
/// [Navigator.push] and [Navigator.pop].
///
/// 当您的用户界面适合这种堆栈范例时,用户应该能够_导航_回到堆栈中较早的元素,那么使用routes和Navigator是合适的。
/// 在某些平台(例如 Android)上,系统 UI 将提供后退按钮(在应用程序范围之外),允许用户导航回应用程序堆栈中较早的routes。
/// 在没有这种内置导航机制的平台上,使用 [AppBar](通常在 [Scaffold.appBar] 属性中使用)可以自动添加用于用户导航的后退按钮。
/// When your user interface fits this paradigm of a stack, where the user
/// should be able to _navigate_ back to an earlier element in the stack,
/// the use of routes and the Navigator is appropriate. On certain platforms,
/// such as Android, the system UI will provide a back button (outside the
/// bounds of your application) that will allow the user to navigate back
/// to earlier routes in your application's stack. On platforms that don't
/// have this build-in navigation mechanism, the use of an [AppBar] (typically
/// used in the [Scaffold.appBar] property) can automatically add a back
/// button for user navigation.
///
/// ### 全屏显示route
/// ### Displaying a full-screen route
///
/// 虽然您可以直接创建导航器,但最常见的是使用“Router”创建的导航器,它本身是由 [WidgetsApp] 或 [MaterialApp] widget创建和配置的。您可以使用 [Navigator.of] 引用该导航器。
/// Although you can create a navigator directly, it's most common to use the
/// navigator created by the `Router` which itself is created and configured by
/// a [WidgetsApp] or a [MaterialApp] widget. You can refer to that navigator
/// with [Navigator.of].
///
/// [MaterialApp] 是最简单的设置方法。 [MaterialApp] 的home成为 [Navigator] 堆栈底部的route。这是您在启动应用程序时看到的内容。
/// A [MaterialApp] is the simplest way to set things up. The [MaterialApp]'s
/// home becomes the route at the bottom of the [Navigator]'s stack. It is what
/// you see when the app is launched.
///
/// ```dart
/// void main() {
/// runApp(const MaterialApp(home: MyAppHome()));
/// }
/// ```
///
/// 要将新route推送到堆栈上,您可以使用builder函数创建 [MaterialPageRoute] 的实例,该builder函数可以创建您想要在屏幕上显示的任何内容。例如:
/// To push a new route on the stack you can create an instance of
/// [MaterialPageRoute] with a builder function that creates whatever you
/// want to appear on the screen. For example:
///
/// ```dart
/// Navigator.push(context, MaterialPageRoute<void>(
/// builder: (BuildContext context) {
/// return Scaffold(
/// appBar: AppBar(title: const Text('My Page')),
/// body: Center(
/// child: TextButton(
/// child: const Text('POP'),
/// onPressed: () {
/// Navigator.pop(context);
/// },
/// ),
/// ),
/// );
/// },
/// ));
/// ```
///
/// 该route使用builder函数而不是子widget定义其widget,因为它将根据推送和弹出的时间在不同的上下文中构建和重建。
/// The route defines its widget with a builder function instead of a
/// child widget because it will be built and rebuilt in different
/// contexts depending on when it's pushed and popped.
///
/// 如您所见,可以使用 Navigator 的 pop 方法弹出新路线,显示应用程序的主页:
/// As you can see, the new route can be popped, revealing the app's home
/// page, with the Navigator's pop method:
///
/// ```dart
/// Navigator.pop(context);
/// ```
///
/// 通常不需要提供一个使用 [Scaffold] 在路径中弹出Navigator的widget,因为 Scaffold 会自动向其 AppBar 添加“后退”按钮。
/// 按后退按钮会调用 [Navigator.pop]。在 Android 上,按系统后退按钮会执行相同的操作。
/// It usually isn't necessary to provide a widget that pops the Navigator
/// in a route with a [Scaffold] because the Scaffold automatically adds a
/// 'back' button to its AppBar. Pressing the back button causes
/// [Navigator.pop] to be called. On Android, pressing the system back
/// button does the same thing.
///
/// ### 使用命名路由
/// ### Using named navigator routes
///
/// 移动应用程序通常管理大量routes,并且通常最容易通过名称引用它们。
/// 按照惯例,route名称使用类似路径的结构(例如“/a/b/c”)。应用程序的主页route默认命名为“/”。
/// Mobile apps often manage a large number of routes and it's often
/// easiest to refer to them by name. Route names, by convention,
/// use a path-like structure (for example, '/a/b/c').
/// The app's home page route is named '/' by default.
///
/// [MaterialApp] 可以使用 [Map<String, WidgetBuilder>] 创建,它将route的名称map到将创建它的builder函数。
/// [MaterialApp] 使用此map为其导航器的 [onGenerateRoute] 回调创建一个值。
/// The [MaterialApp] can be created
/// with a [Map<String, WidgetBuilder>] which maps from a route's name to
/// a builder function that will create it. The [MaterialApp] uses this
/// map to create a value for its navigator's [onGenerateRoute] callback.
///
/// ```dart
/// void main() {
/// runApp(MaterialApp(
/// home: const MyAppHome(), // becomes the route named '/'
/// routes: <String, WidgetBuilder> {
/// '/a': (BuildContext context) => const MyPage(title: Text('page A')),
/// '/b': (BuildContext context) => const MyPage(title: Text('page B')),
/// '/c': (BuildContext context) => const MyPage(title: Text('page C')),
/// },
/// ));
/// }
/// ```
///
/// 要按名称显示route:
/// To show a route by name:
///
/// ```dart
/// Navigator.pushNamed(context, '/b');
/// ```
///
/// ### route可以返回一个值
/// ### Routes can return a value
///
/// 当推送route向用户询问值时,可以通过 [pop] 方法的结果参数返回该值。
/// When a route is pushed to ask the user for a value, the value can be
/// returned via the [pop] method's result parameter.
///
/// 推送route的方法返回 [Future]。当弹出路由时,Future会解析,并且 [Future] 的值是 [pop] 方法的“result”参数。
/// Methods that push a route return a [Future]. The Future resolves when the
/// route is popped and the [Future]'s value is the [pop] method's `result`
/// parameter.
///
/// 例如,如果我们想要求用户按“确定”来确认操作,我们可以“等待”[Navigator.push]的结果:
/// For example if we wanted to ask the user to press 'OK' to confirm an
/// operation we could `await` the result of [Navigator.push]:
///
/// ```dart
/// bool? value = await Navigator.push(context, MaterialPageRoute<bool>(
/// builder: (BuildContext context) {
/// return Center(
/// child: GestureDetector(
/// child: const Text('OK'),
/// onTap: () { Navigator.pop(context, true); }
/// ),
/// );
/// }
/// ));
/// ```
///
/// 如果用户按“确定”,则值将为 true。如果用户退出路线,例如按 Scaffold 的后退按钮,则该值将为 null。
/// If the user presses 'OK' then value will be true. If the user backs
/// out of the route, for example by pressing the Scaffold's back button,
/// the value will be null.
///
/// 当route用于返回值时,route的类型参数必须与[pop]结果的类型匹配。
/// 这就是为什么我们使用 `MaterialPageRoute<bool>` 而不是 `MaterialPageRoute<void>` 或只是 `MaterialPageRoute`。 (不过,如果您不想指定类型,也没关系。)
/// When a route is used to return a value, the route's type parameter must
/// match the type of [pop]'s result. That's why we've used
/// `MaterialPageRoute<bool>` instead of `MaterialPageRoute<void>` or just
/// `MaterialPageRoute`. (If you prefer to not specify the types, though, that's
/// fine too.)
///
/// ### Popup路由
/// ### Popup routes
///
/// Routes不必遮盖整个屏幕。 [PopupRoute] 使用 [ModalRoute.barrierColor] 覆盖屏幕,该颜色只能部分不透明,以允许当前屏幕显示出来。
/// 弹出路由是“模态的”,因为它们阻止对下面的小部件的输入。
/// Routes don't have to obscure the entire screen. [PopupRoute]s cover the
/// screen with a [ModalRoute.barrierColor] that can be only partially opaque to
/// allow the current screen to show through. Popup routes are "modal" because
/// they block input to the widgets below.
///
/// 有一些功能可以创建和显示popup路由。例如:[showDialog]、[showMenu] 和 [showModalBottomSheet]。
/// 这些函数返回其推送路线的 Future,如上所述。
/// 调用者可以等待返回值,以便在弹出路由时采取操作,或者发现路由的值。
/// There are functions which create and show popup routes. For
/// example: [showDialog], [showMenu], and [showModalBottomSheet]. These
/// functions return their pushed route's Future as described above.
/// Callers can await the returned value to take an action when the
/// route is popped, or to discover the route's value.
///
/// 还有一些创建popup路由的widgets,例如 [PopupMenuButton] 和 [DropdownButton]。
/// 这些widgets创建 PopupRoute 的内部子类,并使用导航器的推送和弹出方法来显示和关闭它们。
/// There are also widgets which create popup routes, like [PopupMenuButton] and
/// [DropdownButton]. These widgets create internal subclasses of PopupRoute
/// and use the Navigator's push and pop methods to show and dismiss them.
///
/// ### 自定义路由
/// ### Custom routes
///
/// 您可以创建自己的widget库路由类之一的子类,例如 [PopupRoute]、[ModalRoute] 或 [PageRoute],以控制用于显示路由的动画过渡、路由模态屏障的颜色和行为,以及route的其它方面。
/// You can create your own subclass of one of the widget library route classes
/// like [PopupRoute], [ModalRoute], or [PageRoute], to control the animated
/// transition employed to show the route, the color and behavior of the route's
/// modal barrier, and other aspects of the route.
///
/// [PageRouteBuilder] 类可以根据回调定义自定义路由。
/// 下面是一个示例,当route出现或消失时,其子级会旋转和淡出。
/// 该route不会遮挡整个屏幕,因为它指定了“opaque: false”,就像popup路由一样。
/// The [PageRouteBuilder] class makes it possible to define a custom route
/// in terms of callbacks. Here's an example that rotates and fades its child
/// when the route appears or disappears. This route does not obscure the entire
/// screen because it specifies `opaque: false`, just as a popup route does.
///
/// ```dart
/// Navigator.push(context, PageRouteBuilder<void>(
/// opaque: false,
/// pageBuilder: (BuildContext context, _, __) {
/// return const Center(child: Text('My PageRoute'));
/// },
/// transitionsBuilder: (___, Animation<double> animation, ____, Widget child) {
/// return FadeTransition(
/// opacity: animation,
/// child: RotationTransition(
/// turns: Tween<double>(begin: 0.5, end: 1.0).animate(animation),
/// child: child,
/// ),
/// );
/// }
/// ));
/// ```
///
/// 页面路由由两部分组成:“页面”和“转换”。
/// 该页面成为传递给“transitionsBuilder”函数的子级的后代。
/// 通常,页面仅构建一次,因为它不依赖于其动画参数(在本例中用“_”和“__”省略)。过渡是在其持续时间内的每一帧上构建的。
/// The page route is built in two parts, the "page" and the
/// "transitions". The page becomes a descendant of the child passed to
/// the `transitionsBuilder` function. Typically the page is only built once,
/// because it doesn't depend on its animation parameters (elided with `_`
/// and `__` in this example). The transition is built on every frame
/// for its duration.
///
/// 在此示例中,“void”用作路由的返回类型,因为它不返回值。
/// (In this example, `void` is used as the return type for the route, because
/// it does not return a value.)
///
/// ### 嵌套导航
/// ### Nesting Navigators
///
/// 一个应用程序可以使用多个[Navigator]。将一个 [Navigator] 嵌套在另一个 [Navigator] 之下可用于创建“内部旅程”,例如选项卡式导航、用户注册、商店结帐或代表整个应用程序的一部分的其他独立旅程。
/// An app can use more than one [Navigator]. Nesting one [Navigator] below
/// another [Navigator] can be used to create an "inner journey" such as tabbed
/// navigation, user registration, store checkout, or other independent journeys
/// that represent a subsection of your overall application.
///
/// #### 示例
/// #### Example
///
/// iOS应用程序的标准做法是使用选项卡式导航,其中每个选项卡都维护自己的导航历史记录。因此,每个选项卡都有自己的[Navigator],创建了一种“并行导航”。
/// It is standard practice for iOS apps to use tabbed navigation where each
/// tab maintains its own navigation history. Therefore, each tab has its own
/// [Navigator], creating a kind of "parallel navigation."
///
/// 除了选项卡的并行导航之外,仍然可以启动完全覆盖选项卡的全屏页面。例如:入门流程或警报对话框。
/// 因此,必须存在一个位于选项卡导航上方的“根”[Navigator]。因此,每个选项卡的 [Navigator] 实际上都是嵌套的 [Navigator],位于单个根 [Navigator] 的下方。
/// In addition to the parallel navigation of the tabs, it is still possible to
/// launch full-screen pages that completely cover the tabs. For example: an
/// on-boarding flow, or an alert dialog. Therefore, there must exist a "root"
/// [Navigator] that sits above the tab navigation. As a result, each of the
/// tab's [Navigator]s are actually nested [Navigator]s sitting below a single
/// root [Navigator].
///
/// 实际上,用于选项卡式导航的嵌套 [Navigator] 位于 [WidgetsApp] 和 [CupertinoTabView] widgets中,不需要显式创建或管理。
/// In practice, the nested [Navigator]s for tabbed navigation sit in the
/// [WidgetsApp] and [CupertinoTabView] widgets and do not need to be explicitly
/// created or managed.
///
/// {@tool sample}
/// 以下示例演示了如何使用嵌套的 [Navigator] 来呈现独立的用户注册过程。
/// The following example demonstrates how a nested [Navigator] can be used to
/// present a standalone user registration journey.
///
/// 尽管此示例使用两个 [Navigator] 来演示嵌套的 [Navigator],但仅使用单个 [Navigator] 也可能获得类似的结果。
/// Even though this example uses two [Navigator]s to demonstrate nested
/// [Navigator]s, a similar result is possible using only a single [Navigator].
///
/// 使用“flutter run --route=/signup”运行此示例,以通过注册流程而不是在主页上启动它。
/// Run this example with `flutter run --route=/signup` to start it with
/// the signup flow instead of on the home page.
///
/// ** See code in examples/api/lib/widgets/navigator/navigator.0.dart **
/// {@end-tool}
///
/// [Navigator.of] 对给定 [BuildContext] 最近的祖先 [Navigator] 进行操作。
/// 请务必在预期的 [Navigator] 下方提供 [BuildContext],尤其是在创建嵌套 [Navigator] 的大型“构建”方法中。
/// [Builder] widget可用于访问widget子树中所需位置的 [BuildContext]。
/// [Navigator.of] operates on the nearest ancestor [Navigator] from the given
/// [BuildContext]. Be sure to provide a [BuildContext] below the intended
/// [Navigator], especially in large `build` methods where nested [Navigator]s
/// are created. The [Builder] widget can be used to access a [BuildContext] at
/// a desired location in the widget subtree.
///
/// ### 寻找封闭route
/// ### Finding the enclosing route
///
/// 在模态路由的常见情况下,可以使用 [ModalRoute.of] 从构建方法内部获取封闭路由。
/// 要确定封闭路由是否是活动路由(例如,以便在路由不活动时控件可以变暗),可以在返回的路由上检查 [Route.isCurrent] 属性。
/// In the common case of a modal route, the enclosing route can be obtained
/// from inside a build method using [ModalRoute.of]. To determine if the
/// enclosing route is the active route (e.g. so that controls can be dimmed
/// when the route is not active), the [Route.isCurrent] property can be checked
/// on the returned route.
///
/// ## 状态恢复
/// ## State Restoration
///
/// 如果提供了 [restorationScopeId] 并且当被有效的 [RestorationScope] 包围时,[Navigator] 将通过在状态恢复期间重新创建 [Route] 的当前历史堆栈以及恢复这些 [Route] 的内部状态来恢复其状态。然而,并不是堆栈上的所有[Route]都可以恢复:
/// If provided with a [restorationScopeId] and when surrounded by a valid
/// [RestorationScope] the [Navigator] will restore its state by recreating
/// the current history stack of [Route]s during state restoration and by
/// restoring the internal state of those [Route]s. However, not all [Route]s
/// on the stack can be restored:
///
/// * 如果提供了 [Page.restorationId],基于 [Page] 的路由将恢复其状态。
/// * [Page]-based routes restore their state if [Page.restorationId] is
/// provided.
/// * 使用经典命令式 API([push]、[pushNamed] 等)添加的 [Route] 永远无法恢复其状态。
/// * [Route]s added with the classic imperative API ([push], [pushNamed], and
/// friends) can never restore their state.
/// * 添加了可恢复命令式 API([restorablePush]、[restorablePushNamed] 以及名称中带有“restorable”的所有其他命令式方法)的 [Route] 将恢复其状态,如果它下面的所有路由一直到并包括第一个 [Page]-其下方的基本路线已恢复。如果它下面没有基于 [Page] 的路由,则只有当它下面的所有路由都恢复其状态时,它才会恢复其状态。
/// * A [Route] added with the restorable imperative API ([restorablePush],
/// [restorablePushNamed], and all other imperative methods with "restorable"
/// in their name) restores its state if all routes below it up to and
/// including the first [Page]-based route below it are restored. If there
/// is no [Page]-based route below it, it only restores its state if all
/// routes below it restore theirs.
///
/// 如果 [Route] 被视为可恢复,[Navigator] 会将其 [Route.restorationScopeId] 设置为非空值。路由可以使用该 ID 来存储和恢复自己的状态。
/// 例如,[ModalRoute] 将使用此 ID 为其内容widgets创建 [RestorationScope]。
/// If a [Route] is deemed restorable, the [Navigator] will set its
/// [Route.restorationScopeId] to a non-null value. Routes can use that ID to
/// store and restore their own state. As an example, the [ModalRoute] will
/// use this ID to create a [RestorationScope] for its content widgets.
|
由上面注释可以总结一下Navigator的特性:
1、Navigator是一个使用堆栈规则管理一组子Widgets的Widget
2、Navigator通过在Overlay中移动Widgets来直观地从一个页面过渡到另一页面
3、移动App通常通过称为“屏幕”或“页面”的全屏元素显示其内容,而在Flutter中,这些元素称为Routes,它们由Navigator管理
4、Navigator管理Route对象的堆栈,并提供两种管理堆栈的方式,声明式APINavigator.pages
或命令式APINavigator.push
和Navigator.pop
,本文所讲的是命令式API
5、对于声明式API,Navigator会将其Navigator.pages转换为一堆Route。Navigator.pages中的更改将触发Route堆栈的更新;同理,Navigator将更新其Routes以匹配其Navigator.pages的新配置
6、WidgetsApp或MaterialApp底层已创建和配置Navigator,MaterialApp的home成为Navigator堆栈底部的Route,然后可以使用Navigator.of引用该Navigator
7、默认情况下,Navigator将使用DefaultTransitionDelegate来决定Routes如何转入或转出屏幕。要自定义它,请定义TransitionDelegate子类并将其提供给Navigator.transitionDelegate
8、移动App通常管理大量Routes,并且通常最容易通过名称引用它们。按照惯例,Route名称使用类似路径的结构(例如“/a/b/c”)。应用程序的主页Route默认命名为“/”。
这里给出了Navigator的部分特性,主要是想让大家对Navigator有一个初级认知。
二、Navigator的相关方法
2.1、路由跳转方法
对于路由跳转方法,它有两种方式:
- 组件路由跳转:想要跳转到哪个Page,那么直接将Page当作参数传递进去就可以了。
注意:不建议组件路由跳转与命名路由跳转进行混用,对于组件路由跳转来说,它没有路由名称,在使用pushAndRemoveUntil或pushNamedAndRemoveUntil方法时会因为无法找到ModalRoute.withName配置的路由名称而导致不能实现预期效果。
- 命名路由跳转:先将Page注册到路由表内并起一个名字,然后其它页面通过这个名字进行跳转。
官方建议:对于大多数应用程序,我们不建议使用命名路由。虽然命名路由可以处理深层链接,但其行为始终相同,无法定制。当平台收到新的深度链接时,Flutter将新的Route推到Navigator上,而不管用户当前在哪里。对于应用程序使用命名路由Flutter也是不支持浏览器的前进按钮,出于这些原因,我们不建议在大多数应用程序中使用命名路由。具有高级导航和路由要求的Flutter应用应该使用一个路由包,如go_router,它可以解析路由路径并在应用程序收到新的深度链接时配置Navigator。因为像go_router这样的包是声明性的,所以当接收到深度链接时,它们将始终显示相同的屏幕。
OK,接下来开始分析路由跳转方法。
2.1.1、push/pushNamed方法
方法描述:在栈顶入栈一个新的路由
push方法执行后,路由栈的变化情况:
1、当前路由栈中的路由顺序为A->B->C(由下往上)
2、从C push D
3、当前路由栈中的路由顺序变为A->B->C->D
2.1.2、pushReplacement/pushReplacementNamed方法
方法描述:将当前栈顶路由替换为一个新的路由
pushReplacement方法执行后,路由栈的变化情况:
1、当前路由栈中的路由顺序为A->B->C(由下往上)
2、从C pushReplacement D
3、当前路由栈中的路由顺序变为A->B->D
2.1.3、pushAndRemoveUntil/pushNamedAndRemoveUntil方法
方法描述:在栈顶入栈一个新的路由,然后删除所有先前的路由,直到predicate返回true。
这里分为两种情况:
如果要删除推送路由下面的所有路由,请使用始终返回false的RoutePredicate (例如(Route<dynamic> route) => false
),pushAndRemoveUntil方法执行后,路由栈的变化情况:
1、当前路由栈中的路由顺序为A->B->C(由下往上)
2、从C pushAndRemoveUntil D
3、当前路由栈中的路由顺序变为D,此时D成为根路由
如果要删除路由直到具有特定名称的路由,请使用从ModalRoute.withName返回的RoutePredicate(例如ModalRoute.withName('/')
),pushAndRemoveUntil方法执行后,路由栈的变化情况:
1、当前路由栈中的路由顺序为A->B->C(由下往上)
2、从C pushAndRemoveUntil D
3、当前路由栈中的路由顺序变为A->D
2.2、路由出栈方法
2.2.1、pop方法
方法描述:将栈顶路由出栈
pop方法执行后,路由栈的变化情况:
1、当前路由栈中的路由顺序为A->B->C(由下往上)
2、从C pop
3、当前路由栈中的路由顺序变为A->B
2.2.2、popUntil方法
方法描述:将栈顶路由逐个出栈,直到predicate返回true。
要弹出具有特定名称的路由,请使用从ModalRoute.withName返回的RoutePredicate(例如ModalRoute.withName('/')
),popUntil方法执行后,路由栈的变化情况:
1、当前路由栈中的路由顺序为A->B->C(由下往上)
2、从C popUntil
3、当前路由栈中的路由顺序变为A
2.2.3、popAndPushNamed方法
方法描述:将栈顶路由出栈,然后在栈顶入栈一个新的路由,弹出和推送的动画是同时执行的,即使旧Route和新Route都是不透明的,下面的Route也可能会短暂可见
popAndPushNamed方法执行后,路由栈的变化情况:
1、当前路由栈中的路由顺序为A->B->C(由下往上)
2、从C popAndPushNamed D
3、当前路由栈中的路由顺序变为A->B->D
2.2.4、canPop方法
方法描述:判断当前路由是否可以出栈,如果栈内只有当前路由,返回false,如果当前路由前面还有路由,返回true
2.2.5、maybePop方法
方法描述:当前路由如能出栈就出栈,如果不能就什么都不做。
2.3、路由删除方法
2.3.1、removeRoute方法
方法描述:删除Route,然后Route.dispose它。此方法调用不会运行任何动画。给定的Route必须在历史记录中;如果不是,此方法将抛出异常。使用场景例如:此方法用于立即关闭屏幕方向更改时出现的下拉菜单。
removeRoute方法执行后,路由栈的变化情况:
1、当前路由栈中的路由顺序为A->B->C(由下往上)
2、从C removeRoute B
3、当前路由栈中的路由顺序变为A->C
2.3.2、removeRouteBelow方法
方法描述:删除Route,然后Route.dispose它。要删除的Route是给定的anchorRoute下面的Route。此方法调用不会运行任何动画。给定的anchorRoute必须在历史记录中,并且下面必须有一条Route;如果不是或者没有,这个方法会抛出异常
removeRouteBelow方法执行后,路由栈的变化情况:
1、当前路由栈中的路由顺序为A->B->C(由下往上)
2、从C removeRouteBelow B
3、当前路由栈中的路由顺序变为B->C
2.4、路由替换方法
2.4.1、replace方法
方法描述:用一个新的Route,将路由表内的一个已存在的Route替换掉。旧Route当前不得可见,因为此方法会跳过动画,因此如果旧Route可见,则删除会很不和谐。要替换最顶层的路由,请考虑使用pushReplacement,它会为新路由设置动画,并延迟删除旧路由,直到新路由完成动画处理。
replace方法执行后,路由栈的变化情况:
1、当前路由栈中的路由顺序为A->B->C(由下往上)
2、从B replace newB
3、当前路由栈中的路由顺序变为A->newB->C
2.4.2、replaceRouteBelow方法
方法描述:用一个新的Route,将路由表内的一个已存在的anchorRoute的前面的Route给替换掉。旧Route当前不得可见,因为此方法会跳过动画,因此如果旧Route可见,则删除会很不和谐。要替换最顶层的路由,请考虑使用pushReplacement,它会为新路由设置动画,并延迟删除旧路由,直到新路由完成动画处理。
replaceRouteBelow方法执行后,路由栈的变化情况:
1、当前路由栈中的路由顺序为A->B->C(由下往上)
2、从B replaceRouteBelow newB
3、当前路由栈中的路由顺序变为newB->B->C
2.5、其它方法
2.5.1、of方法
方法描述:获取给定上下文的此类的最近实例的NavigatorState。如果rootNavigator设置为 true,则给出距此类最远实例的NavigatorState。如果给定context中没有Navigator,则该函数将在调试模式下抛出FlutterError,并在发布模式下抛出异常。这种方法可能很昂贵(它遍历Element树)。
2.5.2、maybeOf方法
方法描述:获取给定上下文(如果有)的此类的最近实例的NavigatorState。如果rootNavigator设置为true,则给出距此类最远实例的状态。如果context中没有祖先Navigator,则返回null。这种方法可能很昂贵(它遍历Element树)。
2.5.3、restorableXXX方法
这些方法与Route的恢复相关,一般情况下用不到这些方法,感兴趣可以自行学习。
三、源码分析
在Flutter中,万物皆Widget,Navigator自然也不例外,只是我们没有主动添加过Navigator,但是又可以通过Navigator来进行页面跳转,这是怎么一回事呢?
在进行Flutter开发时,一般情况下需要以MaterialApp或WidgetsApp(MaterialApp是对WidgetsApp的包装)来作为第一个Widget,否则可能会出现一些不可预估的错误。
例如,如果你没有以MaterialApp作为第一个Widget,后续在使用Navigator的方法时,可能就会因为找不到Navigator而报错。
这是因为Flutter默认在MaterialApp中添加了第一个Navigator,也就是根Navigator,如果您不主动添加过其它Navigator的话,一般情况下的跳转就是通过这个根Navigator来实现的。
看下面的例子,通过Navigator跳转传参以及将数据返回给上一个页面。
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
|
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Navigator示例'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
OutlinedButton(
onPressed: () async {
Object? value = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SecondPage(argument: 888),
),
);
if (!mounted) return;
ScaffoldMessenger.of(context)
..removeCurrentSnackBar()
..showSnackBar(SnackBar(content: Text('SecondPage返回的数据为:$value')));
},
child: const Text('跳转(从SecondPage构造方法传入参数)'),
),
OutlinedButton(
onPressed: () async {
Object? value = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SecondPage(),
settings: const RouteSettings(arguments: 999),
),
);
if (!mounted) return;
ScaffoldMessenger.of(context)
..removeCurrentSnackBar()
..showSnackBar(SnackBar(content: Text('SecondPage返回的数据为:$value')));
},
child: const Text('跳转(从settings传入参数)'),
),
],
),
),
);
}
}
class SecondPage extends StatelessWidget {
final Object? argument;
const SecondPage({super.key, this.argument});
@override
Widget build(BuildContext context) {
Object? value = argument ?? ModalRoute.of(context)?.settings.arguments;
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('SecondPage'),
),
body: Center(
child: OutlinedButton(
onPressed: () => Navigator.pop(context, value),
child: Text('获取的返回值为:$value,点击返回'),
),
),
);
}
}
|
程序运行起来后,UI效果如下所示。
然后通过Flutter Inspector查看Widget层次结构,可以发现MaterialApp中的确默认添加了第一个Navigator。
OK,下面按照示例来分析Navigator的源码(命令式部分)。
3.1、_MaterialAppState 的build方法
从MaterialApp入手,因为它是一个StatefulWidget,所以看下 _MaterialAppState 的build方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@override
Widget build(BuildContext context) {
// 执行_buildWidgetApp方法
Widget result = _buildWidgetApp(context);
...
return ScrollConfiguration(
behavior: widget.scrollBehavior ?? const MaterialScrollBehavior(),
child: HeroControllerScope(
controller: _heroController,
child: result,
),
);
}
|
看下 _MaterialAppState 的 _buildWidgetApp 方法,可以发现它根据 _usesRouter 来选择不同的WidgetsApp构造方法进行创建WidgetsApp。
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
|
// _usesRouter表示是否使用Router,它是声明式的范畴,本文所讲是命令式,因此_usesRouter可视为false
bool get _usesRouter => widget.routerDelegate != null || widget.routerConfig != null;
Widget _buildWidgetApp(BuildContext context) {
...
if (_usesRouter) {
// Router方式实现路由导航(声明式)
return WidgetsApp.router(
...
);
}
// 直接Navigator方式实现路由导航(命令式)
return WidgetsApp(
key: GlobalObjectKey(this),
// 此处是MaterialApp传入的navigatorKey
navigatorKey: widget.navigatorKey,
navigatorObservers: widget.navigatorObservers!,
// 返回MaterialPageRoute,这个和根布局Route相关,后面讲
pageRouteBuilder: <T>(RouteSettings settings, WidgetBuilder builder) {
return MaterialPageRoute<T>(settings: settings, builder: builder);
},
// 此处是MaterialApp传入的根布局
home: widget.home,
// 此处是MaterialApp传入的路由表
routes: widget.routes!,
// 此处是MaterialApp传入的初始路由名称
initialRoute: widget.initialRoute,
// 此处是MaterialApp传入的onGenerateRoute路由钩子函数
onGenerateRoute: widget.onGenerateRoute,
// 此处是MaterialApp传入的onGenerateInitialRoutes初始路由钩子函数
onGenerateInitialRoutes: widget.onGenerateInitialRoutes,
// 此处是MaterialApp传入的onUnknownRoute未知路由钩子函数
onUnknownRoute: widget.onUnknownRoute,
...
);
}
|
对于WidgetsApp构造方法传入的navigatorKey,如果没在MaterialApp中传入,那么它会创建一个默认的navigatorKey,先看下 _WidgetsAppState 的initState方法。
1
2
3
4
5
6
7
8
|
@override
void initState() {
super.initState();
// 执行_updateRouting方法
_updateRouting();
_locale = _resolveLocales(WidgetsBinding.instance.platformDispatcher.locales, widget.supportedLocales);
WidgetsBinding.instance.addObserver(this);
}
|
看下 _WidgetsAppState 的 _updateRouting 方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
bool get _usesNavigator => widget.home != null
|| (widget.routes?.isNotEmpty ?? false)
|| widget.onGenerateRoute != null
|| widget.onUnknownRoute != null;
GlobalKey<NavigatorState>? _navigator;
void _updateRouting({WidgetsApp? oldWidget}) {
if (_usesRouterWithDelegates) {
...
} else if (_usesNavigator) {
assert(!_usesRouterWithDelegates && !_usesRouterWithConfig);
_clearRouterResource();
// 创建一个默认的navigatorKey,也就是GlobalObjectKey<NavigatorState>,赋值给_navigator
if (_navigator == null || widget.navigatorKey != oldWidget!.navigatorKey) {
_navigator = widget.navigatorKey ?? GlobalObjectKey<NavigatorState>(this);
}
assert(_navigator != null);
} else {
...
}
// If we use a navigator, we have a navigator key.
assert(_usesNavigator == (_navigator != null));
}
|
再看下 _WidgetsAppState 的 build 方法。
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
|
bool get _usesRouterWithDelegates => widget.routerDelegate != null;
bool get _usesRouterWithConfig => widget.routerConfig != null;
bool get _usesNavigator => widget.home != null
|| (widget.routes?.isNotEmpty ?? false)
|| widget.onGenerateRoute != null
|| widget.onUnknownRoute != null;
@override
Widget build(BuildContext context) {
Widget? routing;
if (_usesRouterWithDelegates) {
// Router方式实现路由导航(声明式)
routing = Router<Object>(
restorationScopeId: 'router',
routeInformationProvider: _effectiveRouteInformationProvider,
routeInformationParser: widget.routeInformationParser,
routerDelegate: widget.routerDelegate!,
backButtonDispatcher: _effectiveBackButtonDispatcher,
);
} else if (_usesNavigator) {
// 直接Navigator方式实现路由导航(命令式)
assert(_navigator != null);
routing = FocusScope(
debugLabel: 'Navigator Scope',
autofocus: true,
child: Navigator(
clipBehavior: Clip.none,
restorationScopeId: 'nav',
// 因为我们没在MaterialApp中传入navigatorKey,所以这里使用了前面创建的默认_navigator
key: _navigator,
// 引用成员变量_initialRouteName,后面讲
initialRoute: _initialRouteName,
// 引用成员方法_onGenerateRoute,后面讲
onGenerateRoute: _onGenerateRoute,
// onGenerateInitialRoutes这里的判断后面再讲
onGenerateInitialRoutes: widget.onGenerateInitialRoutes == null
? Navigator.defaultGenerateInitialRoutes
: (NavigatorState navigator, String initialRouteName) {
return widget.onGenerateInitialRoutes!(initialRouteName);
},
// 引用成员方法_onUnknownRoute,后面讲
onUnknownRoute: _onUnknownRoute,
observers: widget.navigatorObservers!,
reportsRouteUpdateToEngine: true,
),
);
} else if (_usesRouterWithConfig) {
// 带config的Router方式实现路由导航(声明式)
routing = Router<Object>.withConfig(
restorationScopeId: 'router',
config: widget.routerConfig!,
);
}
...
return RootRestorationScope(
restorationId: widget.restorationScopeId,
child: SharedAppData(
child: Shortcuts(
debugLabel: '<Default WidgetsApp Shortcuts>',
shortcuts: widget.shortcuts ?? WidgetsApp.defaultShortcuts,
child: DefaultTextEditingShortcuts(
child: Actions(
...
child: FocusTraversalGroup(
policy: ReadingOrderTraversalPolicy(),
child: TapRegionSurface(
child: ShortcutRegistrar(
child: Localizations(
locale: appLocale,
delegates: _localizationsDelegates.toList(),
// 最后将包装多层的routing,放到这里
child: title,
),
),
),
),
),
),
),
),
);
}
|
上面已知道 _WidgetsAppState 的 build 方法中用到了Navigator,而Navigator是一个StatefulWidget,看下创建的NavigatorState的build方法。
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
|
// 注意:_overlayKey是延迟初始化
late GlobalKey<OverlayState> _overlayKey;
/// Navigator套了Overlay,Overlay是一个StatefulWidget,此处是Overlay的OverlayState
/// The overlay this navigator uses for its visual presentation.
OverlayState? get overlay => _overlayKey.currentState;
// 此处是Overlay的initialEntries,后面讲
Iterable<OverlayEntry> get _allRouteOverlayEntries {
return <OverlayEntry>[
for (final _RouteEntry entry in _history)
...entry.route.overlayEntries,
];
}
@override
Widget build(BuildContext context) {
...
return HeroControllerScope.none(
child: Listener(
onPointerDown: _handlePointerDown,
onPointerUp: _handlePointerUpOrCancel,
onPointerCancel: _handlePointerUpOrCancel,
child: AbsorbPointer(
absorbing: false, // it's mutated directly by _cancelActivePointers above
child: FocusTraversalGroup(
policy: FocusTraversalGroup.maybeOf(context),
child: Focus(
focusNode: focusNode,
autofocus: true,
skipTraversal: true,
includeSemantics: false,
child: UnmanagedRestorationScope(
bucket: bucket,
// 此处用到了Overlay,它是可以独立管理的条目堆栈
// 通过将独立的子widgets插入到overlay的堆栈中,overlay可以让独立的子widgets将视觉元素“浮动”在其它widgets的顶部。overlay允许每个widgets使用OverlayEntry对象管理它们在overlay中的参与。
child: Overlay(
key: _overlayKey,
clipBehavior: widget.clipBehavior,
// 因为_overlayKey是late延迟加载,所以一开始overlay会为null,此处传入的是_allRouteOverlayEntries
initialEntries: overlay == null ? _allRouteOverlayEntries.toList(growable: false) : const <OverlayEntry>[],
),
),
),
),
),
),
);
}
|
3.2、Navigator的push方法
1
2
3
4
|
@optionalTypeArgs
static Future<T?> push<T extends Object?>(BuildContext context, Route<T> route) {
return Navigator.of(context).push(route);
}
|
看下of方法,它的返回是NavigatorState对象,然后可以通过该对象实现路由跳转,例如调用上面的push方法。
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
|
static NavigatorState of(
BuildContext context, {
bool rootNavigator = false,
}) {
// Handles the case where the input context is a navigator element.
// 处理输入上下文是navigator element的情况。
NavigatorState? navigator;
if (context is StatefulElement && context.state is NavigatorState) {
navigator = context.state as NavigatorState;
}
// rootNavigator表示是否查找根Navigator
if (rootNavigator) {
// 向上查找根Navigator所创建的NavigatorState,如果找到就赋值给navigator
navigator = context.findRootAncestorStateOfType<NavigatorState>() ?? navigator;
} else {
// 向上查找最近的一个Navigator所创建的NavigatorState,,如果找到就赋值给navigator
navigator = navigator ?? context.findAncestorStateOfType<NavigatorState>();
}
// 如果上面context中没有找到navigator,就会报错
// 这也就是前面说的如果你没有以MaterialApp作为第一个Widget,后续在使用Navigator的方法时,可能就会因为找不到Navigator而报错。
assert(() {
if (navigator == null) {
throw FlutterError(
'Navigator operation requested with a context that does not include a Navigator.\n'
'The context used to push or pop routes from the Navigator must be that of a '
'widget that is a descendant of a Navigator widget.',
);
}
return true;
}());
// 最后把查找的NavigatorState返回
return navigator!;
}
|
看下context的findRootAncestorStateOfType方法,因为context是一个句柄,所以看它的实现类Element的findRootAncestorStateOfType方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
@override
T? findRootAncestorStateOfType<T extends State<StatefulWidget>>() {
assert(_debugCheckStateIsActiveForAncestorLookup());
Element? ancestor = _parent;
StatefulElement? statefulAncestor;
// 从当前Element的父Element开始遍历,一直找到最远的一个StatefulElement,然后将它的State转为T
while (ancestor != null) {
if (ancestor is StatefulElement && ancestor.state is T) {
statefulAncestor = ancestor;
}
ancestor = ancestor._parent;
}
return statefulAncestor?.state as T?;
}
|
看下实现类Element的findAncestorStateOfType方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
@override
T? findAncestorStateOfType<T extends State<StatefulWidget>>() {
assert(_debugCheckStateIsActiveForAncestorLookup());
Element? ancestor = _parent;
// 从当前Element的父Element开始遍历,一直找到最近的一个StatefulElement,然后将它的State转为T
while (ancestor != null) {
if (ancestor is StatefulElement && ancestor.state is T) {
break;
}
ancestor = ancestor._parent;
}
final StatefulElement? statefulAncestor = ancestor as StatefulElement?;
return statefulAncestor?.state as T?;
}
|
可以看到,of方法需要传入一个context来获取根Navigator或者最近一个Navigator所创建的NavigatorState。
但是,在某些需求(如Token失效跳转登录页)下,无法拿到context对象,但又需要跳转,那该如何处理?
之前讲过,MaterialApp可以传入一个navigatorKey,在不传的情况下底层也会创建一个默认的navigatorKey,现在的解决办法就是可以通过传入navigatorKey来获取根Navigator所创建的NavigatorState。
OK,再来看下NavigatorState的push方法。
1
2
3
4
5
6
7
8
|
@optionalTypeArgs
Future<T?> push<T extends Object?>(Route<T> route) {
// 将Route包装为_RouteEntry对象,然后交给_pushEntry方法处理
// 注意此时initialState为_RouteLifecycle.push,等下用到
_pushEntry(_RouteEntry(route, pageBased: false, initialState: _RouteLifecycle.push));
// 此处popped是一个Completer,其内部用到Future,等到_RouteLifecycle的状态变为complete,就会调用Route的didComplete方法,然后将结果值放入Completer中,这个后面讲。
return route.popped;
}
|
先看下 _RouteEntry 的部分源码,可以发现它继承自RouteTransitionRecord,而RouteTransitionRecord是为TransitionDelegate暂存的Route包装器接口,用来决定其底层Route应如何在屏幕上或屏幕外转换。
RouteTransitionRecord提供了几个markXXX抽象方法,例如markForPush、markForAdd等,用来标记Route,表示它会进行一些操作,子类 _RouteEntry 对markXXX方法的实现一般是修改当前所处的 _RouteLifecycle 状态。
对于 _RouteEntry,因为构造方法传入了Route,所以 _RouteEntry 实际上是对Route的方法操作与Route的生命周期管理,例如handleAdd方法等。
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
|
class _RouteEntry extends RouteTransitionRecord {
_RouteEntry(
// 传入Route对象
this.route, {
// Route生命周期的初始状态
required _RouteLifecycle initialState,
// 这个和声明式的page相关,不会讲
required this.pageBased,
// 恢复信息,这里用不到这个
this.restorationInformation,
}) : assert(!pageBased || route.settings is Page),
assert(
initialState == _RouteLifecycle.staging ||
initialState == _RouteLifecycle.add ||
initialState == _RouteLifecycle.push ||
initialState == _RouteLifecycle.pushReplace ||
initialState == _RouteLifecycle.replace,
),
// 将初始状态赋值给当前状态
currentState = initialState;
// 重写父类RouteTransitionRecord的成员变量
@override
final Route<dynamic> route;
final _RestorationInformation? restorationInformation;
final bool pageBased;
static Route<dynamic> notAnnounced = _NotAnnounced();
_RouteLifecycle currentState;
...
void handleAdd({ required NavigatorState navigator, required Route<dynamic>? previousPresent }) {
assert(currentState == _RouteLifecycle.add);
assert(navigator._debugLocked);
assert(route._navigator == null);
route._navigator = navigator;
// 管理Route的操作
route.install();
assert(route.overlayEntries.isNotEmpty);
// 管理Route的生命周期
currentState = _RouteLifecycle.adding;
navigator._observedRouteAdditions.add(
_NavigatorPushObservation(route, previousPresent),
);
}
...
@override
void markForPush() {
assert(
isWaitingForEnteringDecision && !isWaitingForExitingDecision,
'This route cannot be marked for push. Either a decision has already been '
'made or it does not require an explicit decision on how to transition in.',
);
// 管理Route的生命周期
currentState = _RouteLifecycle.push;
}
...
}
|
上面涉及一个Route生命周期类 _RouteLifecycle,看下它的源码。
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
|
// _RouteLifecycle 状态机(仅下降):
// The _RouteLifecycle state machine (only goes down):
//
// [creation of a _RouteEntry]
// |
// +
// |\
// | \
// | staging
// | /
// |/
// +-+----------+--+-------+
// / | | |
// / | | |
// / | | |
// / | | |
// / | | |
// pushReplace push* add* replace*
// \ | | |
// \ | | /
// +--pushing# adding /
// \ / /
// \ / /
// idle--+-----+
// / \
// / +------+
// / | |
// / | complete*
// | | /
// pop* remove*
// / \
// / removing#
// popping# |
// | |
// [finalizeRoute] |
// \ |
// dispose*
// |
// disposing
// |
// disposed
// |
// |
// [_RouteEntry garbage collected]
// (terminal state)
//
// *号表示这些状态是暂时的;一旦 _flushHistoryUpdates 运行,路由entry就会退出该状态。
// * These states are transient; as soon as _flushHistoryUpdates is run the
// route entry will exit that state.
// #号表示这些状态等待futures或其它事件,然后自动转换。
// # These states await futures or other events, then transition automatically.
enum _RouteLifecycle {
// 我们将等待过渡代理决定如何处理该路由。
staging, // we will wait for transition delegate to decide what to do with this route.
// 存在的路由:
// routes that are present:
//
// 我们需要运行install、didAdd等;由onGenerateInitialRoutes或初始widget.pages创建的路由
add, // we'll want to run install, didAdd, etc; a route created by onGenerateInitialRoutes or by the initial widget.pages
// 我们将等待来自最顶层路由的didPush完成的future
adding, // we'll waiting for the future from didPush of top-most route to complete
// 已准备好过渡的路由。
// routes that are ready for transition.
// 我们需要运行install、didPush等;通过push()和friends添加的路由
push, // we'll want to run install, didPush, etc; a route added via push() and friends
// 我们需要运行install、didPush等;通过pushReplace()和friends添加的路由
pushReplace, // we'll want to run install, didPush, etc; a route added via pushReplace() and friends
// 我们正在等待来自didPush完成的future
pushing, // we're waiting for the future from didPush to complete
// 我们需要运行install、didReplace等;通过replace()和friends添加的路由
replace, // we'll want to run install, didReplace, etc; a route added via replace() and friends
// 路由是无害的
idle, // route is being harmless
// 不存在的路由:
// routes that are not present:
//
// 应包含在路由公告中并且仍应侦听转换更改的路由。
// routes that should be included in route announcement and should still listen to transition changes.
// 我们要调用didPop
pop, // we'll want to call didPop
// 我们要调用didComplete
complete, // we'll want to call didComplete,
// 我们要运行didReplace/didRemove等
remove, // we'll want to run didReplace/didRemove etc
// 路由不应包含在路由公告中,但仍应监听转换更改。
// routes should not be included in route announcement but should still listen to transition changes.
// 我们正在等待路由调用finalizeRoute来切换到dispose
popping, // we're waiting for the route to call finalizeRoute to switch to dispose
// 我们正在等待后续路由完成动画,然后将切换到dispose
removing, // we are waiting for subsequent routes to be done animating, then will switch to dispose
// 从navigator和overlay中完全删除的路由。
// routes that are completely removed from the navigator and overlay.
// 我们将立即dispose路由
dispose, // we will dispose the route momentarily
// 该entry正在等待其widget子树首先被dispose。在等待时,它存储在 _entryWaitingForSubTreeDisposal中。
disposing, // The entry is waiting for its widget subtree to be disposed
// first. It is stored in _entryWaitingForSubTreeDisposal while
// awaiting that.
// 我们已经dispose了路由
disposed, // we have disposed the route
}
|
OK,关于 _RouteEntry 和 _RouteLifecycle 的表达&功能,现已大致了解清楚,继续看NavigatorState的 _pushEntry 方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
List<_RouteEntry> _history = <_RouteEntry>[];
void _pushEntry(_RouteEntry entry) {
assert(!_debugLocked);
assert(() {
_debugLocked = true;
return true;
}());
assert(entry.route._navigator == null);
assert(entry.currentState == _RouteLifecycle.push);
// 将_RouteEntry存入_history这个List中
_history.add(entry);
// 执行_flushHistoryUpdates方法
_flushHistoryUpdates();
assert(() {
_debugLocked = false;
return true;
}());
_afterNavigation(entry.route);
}
|
在 _pushEntry 方法中,执行了 _flushHistoryUpdates 方法,此时currentState为 _RouteLifecycle.push。
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
|
void _flushHistoryUpdates({bool rearrangeOverlay = true}) {
assert(_debugLocked && !_debugUpdatingPage);
_flushingHistory = true;
...
// _history中最后一个索引位置
int index = _history.length - 1;
// 下一个_RouteEntry
_RouteEntry? next;
// _history中最后一个索引位置的_RouteEntry
_RouteEntry? entry = _history[index];
// 前一个_RouteEntry
_RouteEntry? previous = index > 0 ? _history[index - 1] : null;
// 顶部是否有完全不透明的路由以静默删除或添加下面的路由。
bool canRemoveOrAdd = false; // Whether there is a fully opaque route on top to silently remove or add route underneath.
// 应在顶部活动路由上触发didPopNext的路由。
Route<dynamic>? poppedRoute; // The route that should trigger didPopNext on the top active route.
// 我们是否已经看到将获得didPopNext的路由。
bool seenTopActiveRoute = false; // Whether we've seen the route that would get didPopNext.
final List<_RouteEntry> toBeDisposed = <_RouteEntry>[];
// while循环倒序遍历
while (index >= 0) {
switch (entry!.currentState) {
case ...
case _RouteLifecycle.push: // 此时currentState为_RouteLifecycle.push,所以执行这里
case _RouteLifecycle.pushReplace:
case _RouteLifecycle.replace:
assert(rearrangeOverlay);
// 执行_RouteEntry的handlePush方法
entry.handlePush(
navigator: this,
previous: previous?.route,
previousPresent: _getRouteBefore(index - 1, _RouteEntry.isPresentPredicate)?.route,
isNewFirst: next == null,
);
assert(entry.currentState != _RouteLifecycle.push);
assert(entry.currentState != _RouteLifecycle.pushReplace);
assert(entry.currentState != _RouteLifecycle.replace);
if (entry.currentState == _RouteLifecycle.idle) {
continue;
}
case ...
}
// 更新循环条件
index -= 1;
next = entry;
entry = previous;
previous = index > 0 ? _history[index - 1] : null;
}
...
// 重点:因为_flushHistoryUpdates方法没有传入rearrangeOverlay,所以rearrangeOverlay默认为true,此时会执行OverlayState的rearrange方法。
// 有两个情况:
// 1、如果是根布局home,因为一开始OverlayKey是延迟加载,所以这里overlay为null,不会执行rearrange方法,后面讲。
// 2、如果是跳转新页面,这里的overlay已经有值了,所以会执行rearrange方法
if (rearrangeOverlay) {
overlay?.rearrange(_allRouteOverlayEntries);
}
...
_flushingHistory = false;
}
|
在while循环中,此时currentState为 _RouteLifecycle.push ,所以会执行 _RouteEntry 的 handlePush 方法。
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
|
void handlePush({ required NavigatorState navigator, required bool isNewFirst, required Route<dynamic>? previous, required Route<dynamic>? previousPresent }) {
...
// 将currentState赋值给前一个状态
final _RouteLifecycle previousState = currentState;
// Route持有navigator引用
route._navigator = navigator;
// 执行Route的install方法
route.install();
assert(route.overlayEntries.isNotEmpty);
if (currentState == _RouteLifecycle.push || currentState == _RouteLifecycle.pushReplace) {
// 执行Route的didPush方法
final TickerFuture routeFuture = route.didPush();
// 将currentState修改为_RouteLifecycle.pushing
currentState = _RouteLifecycle.pushing;
routeFuture.whenCompleteOrCancel(() {
if (currentState == _RouteLifecycle.pushing) {
// 等到didPush方法处理完成(这里是等待页面跳转动画完成),将currentState修改为_RouteLifecycle.idle
currentState = _RouteLifecycle.idle;
assert(!navigator._debugLocked);
assert(() { navigator._debugLocked = true; return true; }());
// 再次调用_flushHistoryUpdates方法
navigator._flushHistoryUpdates();
assert(() { navigator._debugLocked = false; return true; }());
}
});
} else {
assert(currentState == _RouteLifecycle.replace);
route.didReplace(previous);
currentState = _RouteLifecycle.idle;
}
// isNewFirst传入的next == null,那么倒序遍历第一个_RouteEntry时next肯定为null,所以这里为true
if (isNewFirst) {
// 执行Route的didChangeNext方法,该方法和动画操作相关
route.didChangeNext(null);
}
if (previousState == _RouteLifecycle.replace || previousState == _RouteLifecycle.pushReplace) {
navigator._observedRouteAdditions.add(
_NavigatorReplaceObservation(route, previousPresent),
);
} else {
assert(previousState == _RouteLifecycle.push);
// previousState当前为_RouteLifecycle.push,所以创建一个_NavigatorPushObservation对象添加进_observedRouteAdditions,采用观察者模式,
// 后续通知去执行Route的一些操作
navigator._observedRouteAdditions.add(
_NavigatorPushObservation(route, previousPresent),
);
}
}
|
在 _RouteEntry 的handlePush方法中,执行了Route的install方法,但是install是个抽象方法,需要Route的子类来实现。
一般情况下,当调用Navigator的push方法时,传入的Route为MaterialPageRoute,它的继承关系如下。
在上面的继承关系中,MaterialPageRoute与PageRoute均没有实现install方法,所以看下
ModalRoute的install方法。
1
2
3
4
5
6
7
|
@override
void install() {
// 执行父类的install方法
super.install();
_animationProxy = ProxyAnimation(super.animation);
_secondaryAnimationProxy = ProxyAnimation(super.secondaryAnimation);
}
|
可以看到,这里只是ProxyAnimation动画初始化,看下父类TransitionRoute的install方法。
1
2
3
4
5
6
7
8
9
|
@override
void install() {
...
// 执行父类的install方法
super.install();
if (_animation!.isCompleted && overlayEntries.isNotEmpty) {
overlayEntries.first.opaque = opaque;
}
}
|
继续看下父类OverlayRoute的install方法,OverlayRoute是在Navigator的Overlay中显示Widgets的路由。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
/// 子类应该重写此getter以返回overlay的构建器
/// Subclasses should override this getter to return the builders for the overlay.
@factory
Iterable<OverlayEntry> createOverlayEntries();
@override
List<OverlayEntry> get overlayEntries => _overlayEntries;
final List<OverlayEntry> _overlayEntries = <OverlayEntry>[];
@override
void install() {
assert(_overlayEntries.isEmpty);
// 通过createOverlayEntries方法创建OverlayEntry放入_overlayEntries,以待后续使用
_overlayEntries.addAll(createOverlayEntries());
super.install();
}
|
其中,createOverlayEntries方法由子类ModalRoute实现,
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
|
// one of the builders
late OverlayEntry _modalBarrier;
Widget _buildModalBarrier(BuildContext context) {
Widget barrier = buildModalBarrier();
...
return barrier;
}
/// 为此[ModalRoute]构建障碍,子类可以重写此方法以使用自定义功能(例如颜色或辅助功能焦点大小)创建自己的障碍。
/// Build the barrier for this [ModalRoute], subclasses can override
/// this method to create their own barrier with customized features such as
/// color or accessibility focus size.
Widget buildModalBarrier() {
Widget barrier;
if (barrierColor != null && barrierColor!.alpha != 0 && !offstage) {
...
} else {
barrier = ModalBarrier(
dismissible: barrierDismissible, // changedInternalState is called if barrierDismissible updates
semanticsLabel: barrierLabel, // changedInternalState is called if barrierLabel updates
barrierSemanticsDismissible: semanticsDismissible,
);
}
return barrier;
}
// 我们缓存模态范围中不随帧变化的部分,以便最大限度地减少发生的构建量。
// We cache the part of the modal scope that doesn't change from frame to
// frame so that we minimize the amount of building that happens.
Widget? _modalScopeCache;
// one of the builders
Widget _buildModalScope(BuildContext context) {
// To be sorted before the _modalBarrier.
return _modalScopeCache ??= Semantics(
sortKey: const OrdinalSortKey(0.0),
child: _ModalScope<T>(
key: _scopeKey,
// 此处this指MaterialPageRoute
route: this,
// _ModalScope calls buildTransitions() and buildChild(), defined above
),
);
}
late OverlayEntry _modalScope;
@override
Iterable<OverlayEntry> createOverlayEntries() {
return <OverlayEntry>[
// 创建了一个Barrier所对应的OverlayEntry(屏障)
_modalBarrier = OverlayEntry(builder: _buildModalBarrier),
// 创建了一个跳转新页面所对应的OverlayEntry
_modalScope = OverlayEntry(builder: _buildModalScope, maintainState: maintainState),
];
}
|
上面通过createOverlayEntries方法已创建了两个OverlayEntry,然后添加给 _overlayEntries,它的结构是这样的。
那么,OverlayRoute中的overlayEntries是在哪里被用到呢?它是在NavigatorState的成员变量 _allRouteOverlayEntries 中被用到。
1
2
3
4
5
6
7
8
|
// install方法后,此时_allRouteOverlayEntries的元素为[根布局屏障、根布局页面,新布局屏障、新布局页面]
Iterable<OverlayEntry> get _allRouteOverlayEntries {
return <OverlayEntry>[
// 遍历了_history中所有的_RouteEntry,每个_RouteEntry都包装了一个Route,每个Route都有一层Barrier和一层跳转新页面
for (final _RouteEntry entry in _history)
...entry.route.overlayEntries,
];
}
|
而成员变量 _allRouteOverlayEntries 会在两个地方被用到。
1、NavigatorState的build方法,作为Overlay的initialEntries传入,这个前面讲过了,因为 _overlayKey 是late延迟加载,所以一开始overlay会为null,传入的就是 _allRouteOverlayEntries,它所包含的是一层Barrier和一层根布局home页面,根布局home的初始化后面讲。
2、在 _flushHistoryUpdates 方法中执行,因为当执行完handlePush方法后,就会接着执行overlay的rearrange方法。
1
2
3
|
if (rearrangeOverlay) {
overlay?.rearrange(_allRouteOverlayEntries);
}
|
看下overlay的rearrange方法。
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
|
void rearrange(Iterable<OverlayEntry> newEntries, { OverlayEntry? below, OverlayEntry? above }) {
// 取出newEntries(也就是传入的_allRouteOverlayEntries),赋值给newEntriesList
// growable为false,表示列表是固定长度
final List<OverlayEntry> newEntriesList = newEntries is List<OverlayEntry> ? newEntries : newEntries.toList(growable: false);
...
// 此时newEntriesList不为null,因为已经包含了根布局的两层OverlayEntry以及新页面的两层OverlayEntry
if (newEntriesList.isEmpty) {
return;
}
// 判断OverlayState中的_entries是否与newEntriesList相同,此处肯定不相同,因为跳转了新页面,多出两层OverlayEntry
// 但是要注意:因为handlePush方法中还会执行_flushHistoryUpdates方法,造成第二次执行rearrange,此时两个List已经相等,所以此处会被拦截
if (listEquals(_entries, newEntriesList)) {
return;
}
// 将旧的OverlayEntry列表包装为LinkedHashSet
final LinkedHashSet<OverlayEntry> old = LinkedHashSet<OverlayEntry>.of(_entries);
// 让newEntriesList中所有的OverlayEntry持有OverlayState引用
for (final OverlayEntry entry in newEntriesList) {
entry._overlay ??= this;
}
// 刷新Overlay页面UI,触发OverlayState的build方法执行
setState(() {
// 更新OverlayState中的_entries为newEntriesList
_entries.clear();
_entries.addAll(newEntriesList);
// 移除与newEntriesList相交部分(根布局两层OverlayEntry),那么此时old为空了
old.removeAll(newEntriesList);
// 此处below为null,above为null,所以_insertionIndex的值为_entries的长度,也就是此时newEntriesList的长度
// 然后重新把旧_entries在新_entries末尾插入,old为空了相当于插入了个寂寞
_entries.insertAll(_insertionIndex(below, above), old);
});
}
|
OK,总结下OverlayState的build方法两次执行过程。
1、我们知道,跳转新页面会伴随着动画,这个动画控制类在TransitionRoute的install方法中初始化。
1
2
3
4
5
6
7
8
9
10
11
12
|
@override
void install() {
...
// 添加动画状态监听_handleStatusChanged
_animation = createAnimation()
..addStatusListener(_handleStatusChanged);
assert(_animation != null, '$runtimeType.createAnimation() returned null.');
super.install();
if (_animation!.isCompleted && overlayEntries.isNotEmpty) {
overlayEntries.first.opaque = opaque;
}
}
|
可以看到,在TransitionRoute的install方法中添加了动画状态监听,看下 _handleStatusChanged 方法。
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
|
void _handleStatusChanged(AnimationStatus status) {
switch (status) {
case AnimationStatus.completed:
// 动画完成
if (overlayEntries.isNotEmpty) {
overlayEntries.first.opaque = opaque;
}
_performanceModeRequestHandle?.dispose();
_performanceModeRequestHandle = null;
case AnimationStatus.forward:
case AnimationStatus.reverse:
// 动画正向/反向执行
if (overlayEntries.isNotEmpty) {
overlayEntries.first.opaque = false;
}
_performanceModeRequestHandle ??=
SchedulerBinding.instance
.requestPerformanceMode(ui.DartPerformanceMode.latency);
case AnimationStatus.dismissed:
// 动画未开始
// We might still be an active route if a subclass is controlling the
// transition and hits the dismissed status. For example, the iOS
// back gesture drives this animation to the dismissed status before
// removing the route and disposing it.
if (!isActive) {
navigator!.finalizeRoute(this);
_popFinalized = true;
_performanceModeRequestHandle?.dispose();
_performanceModeRequestHandle = null;
}
}
}
|
当 _RouteEntry 的handlePush方法执行时,会执行route.didPush(),看下Route子类TransitionRoute的didPush方法,可以看到,动画开始正向执行。
1
2
3
4
5
6
7
8
|
@override
TickerFuture didPush() {
assert(_controller != null, '$runtimeType.didPush called before calling install() or after calling dispose().');
assert(!_transitionCompleter.isCompleted, 'Cannot reuse a $runtimeType after disposing it.');
super.didPush();
// 动画开始正向执行
return _controller!.forward();
}
|
那么,_handleStatusChanged 方法就会被回调,一开始动画状态为AnimationStatus.forward时,会设置overlayEntries.first.opaque = false。此处overlayEntries正是createOverlayEntries方法创建,前面讲过有两层,一层是新布局屏障,一层是新布局页面,那么这里就是将新布局屏障OverlayEntry的opaque设置为false。
看下OverlayEntry的成员变量opaque,在OverlayEntry的构造方法中,如果不设置opaque,它默认为false。
之前讲ModalRoute的createOverlayEntries方法时,它内部创建了两个OverlayEntry,可以发现它们都没设置opaque,也就是opaque默认为false。
因而 _opaque 相同值会被拦截,不会执行下面的 _didChangeEntryOpacity 方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
class OverlayEntry implements Listenable {
OverlayEntry({
required this.builder,
// opaque默认为false
bool opaque = false,
bool maintainState = false,
}) : _opaque = opaque,
_maintainState = maintainState;
bool get opaque => _opaque;
bool _opaque;
set opaque(bool value) {
assert(!_disposedByOwner);
// _opaque相同值会被拦截
if (_opaque == value) {
return;
}
_opaque = value;
// _didChangeEntryOpacity方法中会执行setState方法
_overlay?._didChangeEntryOpacity();
}
}
|
2、当handlePush方法执行完后,就会执行到overlay的rearrange方法,此时会触发setState方法,然后第一次执行build方法。在build方法中会倒序遍历 _entries,新布局页面OverlayEntry是第一个被遍历到,根布局屏障OverlayEntry是最后一个被遍历到,那么children的最后一个是根布局屏障OverlayEntry。
之前讲了,新布局屏障OverlayEntry的opaque设置为false,所以当倒序遍历 _entries 时,遇到opaque为false时,onstage不会被修改为false,因此走的都是 if (onstage) 逻辑。
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
|
@override
Widget build(BuildContext context) {
// 该列表在添加到树之前先向后填充,然后在下面反转。
// This list is filled backwards and then reversed below before
// it is added to the tree.
final List<_OverlayEntryWidget> children = <_OverlayEntryWidget>[];
// onstage表示是否在舞台上,也就是我们自己的页面
bool onstage = true;
int onstageCount = 0;
// 倒序遍历_entries
// 注意:只有onstage或maintainState的情况,Route在内存中才会被保留下来
for (final OverlayEntry entry in _entries.reversed) {
if (onstage) {
onstageCount += 1;
children.add(_OverlayEntryWidget(
key: entry._key,
overlayState: this,
entry: entry,
));
// opaque表示该OverlayEntry是否遮挡整个Overlay
// 如果某个OverlayEntry声称是不透明的,那么为了提高效率,覆盖层将跳过该OverlayEntry下方的构建OverlayEntry,除非它们设置了maintainState
// 当一条不透明Route的入口过渡完成后,该不透明Route后面的Route将不再被构建,以节省资源。
if (entry.opaque) {
onstage = false;
}
} else if (entry.maintainState) {
// maintainState表示Route处于非活动状态时是否应保留在内存中。
// 如果MaterialPageRoute设置了maintainState为true,那么该Route将被维护,以便在下一个Route弹出时,它持有的下一个Route的任何future都将正确解析。
// 如果没有必要,可以将其设置为false,以允许框架在Route的widget层次结构不可见时完全丢弃它,当下次返回到上一个页面时会重新创建,也就是build方法会被触发。
children.add(_OverlayEntryWidget(
key: entry._key,
overlayState: this,
entry: entry,
tickerEnabled: false,
));
}
}
// 到这里第一次build方法时,children变为[新页面(有屏障),根布局(有屏障)],但是第二次build方法时,children变为[新页面(有屏障),根布局(无屏障)],根布局屏障因为maintainState为false,所以被干掉了
// _Theater是Stack的特殊版本,不会布局和渲染第一个skipCount children。
// 第一个skipCount children被认为是“offstage(不在舞台,即幕后)”
return _Theater(
skipCount: children.length - onstageCount,
clipBehavior: widget.clipBehavior,
// 再次反转List,传入的children变为[根布局(无屏障),新页面(有屏障)]
children: children.reversed.toList(growable: false),
);
}
|
3、等到动画状态变为AnimationStatus.completed时,会设置overlayEntries.first.opaque = opaque,将TransitionRoute的成员变量opaque赋值给新布局屏障OverlayEntry的opaque,因为子类PageRoute重写了opaque并赋值为true,那么,新布局屏障OverlayEntry的opaque为true。
此时 _opaque 的值不相同,就会执行 _overlay?._didChangeEntryOpacity(),再次触发OverlayState的setState方法,接着build方法执行。
4、此时build方法执行,因为新布局屏障OverlayEntry的opaque为true,当倒序遍历 _entries 时,遇到第一个opaque为true时,onstage会被修改为false,因此后续不再走 if (onstage) 逻辑,而是走 else if (entry.maintainState) 逻辑,根布局屏障因为maintainState为false,所以被干掉了。
此时整个页面的情况如下。
3.3、Navigator的pop方法
1
2
3
4
|
@optionalTypeArgs
static void pop<T extends Object?>(BuildContext context, [ T? result ]) {
Navigator.of(context).pop<T>(result);
}
|
Navigator的of方法前面讲过了,看下NavigatorState的pop方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
@optionalTypeArgs
void pop<T extends Object?>([ T? result ]) {
...
// 获取_history中满足isPresentPredicate条件的最后一个元素(此处条件为present,也就是Route是存在的)
final _RouteEntry entry = _history.lastWhere(_RouteEntry.isPresentPredicate);
// pageBased之前说过,和声明式相关,这里不会讲
if (entry.pageBased) {
...
} else {
// 所以执行这里
entry.pop<T>(result);
assert (entry.currentState == _RouteLifecycle.pop);
}
// 此时currentState为_RouteLifecycle.pop,所以执行_flushHistoryUpdates,注意rearrangeOverlay为false
if (entry.currentState == _RouteLifecycle.pop) {
_flushHistoryUpdates(rearrangeOverlay: false);
}
...
}
|
看下 _RouteEntry 的pop方法。
1
2
3
4
5
6
7
8
9
|
Object? pendingResult;
void pop<T>(T? result) {
assert(isPresent);
// 将result保存为成员变量pendingResult,以备后续使用
pendingResult = result;
// 修改currentState为_RouteLifecycle.pop
currentState = _RouteLifecycle.pop;
}
|
再看下NavigatorState的 _flushHistoryUpdates 方法。
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
|
void _flushHistoryUpdates({bool rearrangeOverlay = true}) {
assert(_debugLocked && !_debugUpdatingPage);
_flushingHistory = true;
int index = _history.length - 1;
_RouteEntry? next;
_RouteEntry? entry = _history[index];
_RouteEntry? previous = index > 0 ? _history[index - 1] : null;
bool canRemoveOrAdd = false; // Whether there is a fully opaque route on top to silently remove or add route underneath.
Route<dynamic>? poppedRoute; // The route that should trigger didPopNext on the top active route.
bool seenTopActiveRoute = false; // Whether we've seen the route that would get didPopNext.
final List<_RouteEntry> toBeDisposed = <_RouteEntry>[];
while (index >= 0) {
switch (entry!.currentState) {
case ...
case _RouteLifecycle.pop:
// 执行了_RouteEntry的handlePop方法
if (!entry.handlePop(
navigator: this,
previousPresent: _getRouteBefore(index, _RouteEntry.willBePresentPredicate)?.route)){
assert(entry.currentState == _RouteLifecycle.idle);
continue;
}
if (!seenTopActiveRoute) {
if (poppedRoute != null) {
entry.handleDidPopNext(poppedRoute);
}
poppedRoute = entry.route;
}
_observedRouteDeletions.add(
_NavigatorPopObservation(entry.route, _getRouteBefore(index, _RouteEntry.willBePresentPredicate)?.route),
);
if (entry.currentState == _RouteLifecycle.dispose) {
// The pop finished synchronously. This can happen if transition
// duration is zero.
continue;
}
assert(entry.currentState == _RouteLifecycle.popping);
canRemoveOrAdd = true;
case ...
}
index -= 1;
next = entry;
entry = previous;
previous = index > 0 ? _history[index - 1] : null;
}
...
// 因为传入的rearrangeOverlay为false,所以这里不会执行
if (rearrangeOverlay) {
overlay?.rearrange(_allRouteOverlayEntries);
}
...
}
|
看下 _RouteEntry的handlePop方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
bool handlePop({ required NavigatorState navigator, required Route<dynamic>? previousPresent }) {
assert(navigator._debugLocked);
assert(route._navigator == navigator);
currentState = _RouteLifecycle.popping;
// 此时状态不是isCompleted,这里不会执行
if (route._popCompleter.isCompleted) {
// This is a page-based route popped through the Navigator.pop. The
// didPop should have been called. No further action is needed.
assert(pageBased);
assert(pendingResult == null);
return true;
}
// 执行Route的didPop方法,返回true
if (!route.didPop(pendingResult)) {
currentState = _RouteLifecycle.idle;
return false;
}
// 将pendingResult置空,释放资源
pendingResult = null;
// 最后返回true
return true;
}
|
看下Route的didPop方法,此时会先执行子类TransitionRoute的didPop方法。
1
2
3
4
5
6
7
8
9
10
|
@override
bool didPop(T? result) {
assert(_controller != null, '$runtimeType.didPop called before calling install() or after calling dispose().');
assert(!_transitionCompleter.isCompleted, 'Cannot reuse a $runtimeType after disposing it.');
_result = result;
// 动画反向执行
_controller!.reverse();
// 执行父类的didPop方法
return super.didPop(result);
}
|
看下父类OverlayRoute的didPop方法。
1
2
3
4
5
6
7
8
9
10
11
12
|
@override
bool didPop(T? result) {
// 执行父类的didPop方法
final bool returnValue = super.didPop(result);
assert(returnValue);
// 此处finishedWhenPopped为false不会执行
if (finishedWhenPopped) {
navigator!.finalizeRoute(this);
}
// 这里是父类didPop方法的返回值
return returnValue;
}
|
看下父类Route的didPop方法。
1
2
3
4
5
6
|
@mustCallSuper
bool didPop(T? result) {
// 执行了didComplete方法
didComplete(result);
return true;
}
|
看下Route的didComplete方法。
1
2
3
4
5
6
7
8
9
|
// 当弹出此路由时(请参阅Navigator.pop ),如果未指定结果或结果为null,则将使用此值。
T? get currentResult => null;
@protected
@mustCallSuper
void didComplete(T? result) {
// 将result结果返回给await push()方法
_popCompleter.complete(result ?? currentResult);
}
|
前面TransitionRoute的didPop方法中执行了 _controller!.reverse() ,这里是启动了反向动画,那么TransitionRoute的 _handleStatusChanged 方法将会监听动画状态。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
void _handleStatusChanged(AnimationStatus status) {
switch (status) {
case AnimationStatus.completed:
...
case AnimationStatus.forward:
case AnimationStatus.reverse:
...
case AnimationStatus.dismissed:
// 动画未开始
// We might still be an active route if a subclass is controlling the
// transition and hits the dismissed status. For example, the iOS
// back gesture drives this animation to the dismissed status before
// removing the route and disposing it.
// navigator上是否有该路由,此处是栈顶,必须有
if (!isActive) {
navigator!.finalizeRoute(this);
_popFinalized = true;
_performanceModeRequestHandle?.dispose();
_performanceModeRequestHandle = null;
}
}
}
|
再看下Navigator的finalizeRoute方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
void finalizeRoute(Route<dynamic> route) {
...
final int index = _history.indexWhere(_RouteEntry.isRoutePredicate(route));
final _RouteEntry entry = _history[index];
...
// 此处将currentState修改为_RouteLifecycle.dispose
entry.finalize();
// 如果 pop 同步完成,则可以在 _flushHistoryUpdates 期间调用 FinalizeRoute。
// finalizeRoute can be called during _flushHistoryUpdates if a pop
// finishes synchronously.
if (!_flushingHistory) {
// 再次执行_flushHistoryUpdates方法,rearrangeOverlay为false
_flushHistoryUpdates(rearrangeOverlay: false);
}
assert(() { _debugLocked = wasDebugLocked!; return true; }());
}
|
看下NavigatorState的 _flushHistoryUpdates 方法。
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
|
void _flushHistoryUpdates({bool rearrangeOverlay = true}) {
assert(_debugLocked && !_debugUpdatingPage);
_flushingHistory = true;
int index = _history.length - 1;
_RouteEntry? next;
_RouteEntry? entry = _history[index];
_RouteEntry? previous = index > 0 ? _history[index - 1] : null;
bool canRemoveOrAdd = false; // Whether there is a fully opaque route on top to silently remove or add route underneath.
Route<dynamic>? poppedRoute; // The route that should trigger didPopNext on the top active route.
bool seenTopActiveRoute = false; // Whether we've seen the route that would get didPopNext.
final List<_RouteEntry> toBeDisposed = <_RouteEntry>[];
while (index >= 0) {
switch (entry!.currentState) {
case ...
case _RouteLifecycle.dispose:
// Delay disposal until didChangeNext/didChangePrevious have been sent.
// 将要删除的_RouteEntry添加进toBeDisposed
toBeDisposed.add(_history.removeAt(index));
entry = next;
case ...
}
index -= 1;
next = entry;
entry = previous;
previous = index > 0 ? _history[index - 1] : null;
}
...
// Lastly, removes the overlay entries of all marked entries and disposes
// them.
for (final _RouteEntry entry in toBeDisposed) {
// 执行_disposeRouteEntry方法,注意graceful传入了true
_disposeRouteEntry(entry, graceful: true);
}
// 传入的rearrangeOverlay为false,此处不会执行
if (rearrangeOverlay) {
overlay?.rearrange(_allRouteOverlayEntries);
}
...
}
|
看下NavigatorState的 _disposeRouteEntry 方法。
1
2
3
4
5
6
7
8
9
10
11
12
|
static void _disposeRouteEntry(_RouteEntry entry, {required bool graceful}) {
// 遍历删除当前_RouteEntry所关联Route的所有overlayEntry
for (final OverlayEntry overlayEntry in entry.route.overlayEntries) {
overlayEntry.remove();
}
if (graceful) {
// 主要是执行route.dispose(),主要工作是销毁动画资源,以及遍历Route中的_overlayEntries,进行OverlayEntry资源清除
entry.dispose();
} else {
entry.forcedDispose();
}
}
|
看下OverlayEntry的remove方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
void remove() {
assert(_overlay != null);
assert(!_disposedByOwner);
final OverlayState overlay = _overlay!;
_overlay = null;
if (!overlay.mounted) {
return;
}
// 在overlay的_entries中,删除当前的OverlayEntry
overlay._entries.remove(this);
if (SchedulerBinding.instance.schedulerPhase == SchedulerPhase.persistentCallbacks) {
SchedulerBinding.instance.addPostFrameCallback((Duration duration) {
overlay._markDirty();
});
} else {
// 执行overlay的_markDirty方法
overlay._markDirty();
}
}
|
看下OverlayState的 _markDirty 方法,可以看到,实际上还是执行了setState方法来更新Overlay中的Widget。
1
2
3
4
5
|
void _markDirty() {
if (mounted) {
setState(() {});
}
}
|
3.4、根布局MaterialApp的home之创建
先来回顾一下根Navigator的声明地方,它是在 _WidgetsAppState 的build方法中声明。
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
|
// 如果 window.defaultRouteName 不是 '/',我们应该假设它是通过 `setInitialRoute` 有意设置的,并且应该覆盖 [widget.initialRoute] 中的任何内容。
// If window.defaultRouteName isn't '/', we should assume it was set
// intentionally via `setInitialRoute`, and should override whatever is in
// [widget.initialRoute].
// 如果特定平台的初始路由不是'/'时,以特定平台的初始路由为准。
// 如果特定平台的初始路由是'/'时,以Flutter中的initialRoute为准,但是如果Flutter中的initialRoute为null,那么依然以特定平台的初始路由为准,也就是'/'。
String get _initialRouteName => WidgetsBinding.instance.platformDispatcher.defaultRouteName != Navigator.defaultRouteName
? WidgetsBinding.instance.platformDispatcher.defaultRouteName
: widget.initialRoute ?? WidgetsBinding.instance.platformDispatcher.defaultRouteName;
@override
Widget build(BuildContext context) {
Widget? routing;
if (_usesRouterWithDelegates) {
...
} else if (_usesNavigator) {
assert(_navigator != null);
routing = FocusScope(
debugLabel: 'Navigator Scope',
autofocus: true,
child: Navigator(
clipBehavior: Clip.none,
restorationScopeId: 'nav',
key: _navigator,
// 此处调用成员变量_initialRouteName
initialRoute: _initialRouteName,
// 此处会调用_onGenerateRoute方法,后面会讲
onGenerateRoute: _onGenerateRoute,
// 如果外部没传入onGenerateInitialRoutes,那么就会调用Navigator的defaultGenerateInitialRoutes方法,后面会讲
onGenerateInitialRoutes: widget.onGenerateInitialRoutes == null
? Navigator.defaultGenerateInitialRoutes
: (NavigatorState navigator, String initialRouteName) {
return widget.onGenerateInitialRoutes!(initialRouteName);
},
// 此处会调用_onUnknownRoute方法,后面会讲
onUnknownRoute: _onUnknownRoute,
observers: widget.navigatorObservers!,
reportsRouteUpdateToEngine: true,
),
);
} else if (_usesRouterWithConfig) {
...
}
return RootRestorationScope(
restorationId: widget.restorationScopeId,
child: ...,
);
}
|
首先会执行NavigatorState的didChangeDependencies方法。
1
2
3
4
5
6
7
8
9
|
@override
void didChangeDependencies() {
// 执行父类的didChangeDependencies方法
super.didChangeDependencies();
_updateHeroController(HeroControllerScope.maybeOf(context));
for (final _RouteEntry entry in _history) {
entry.route.changedExternalState();
}
}
|
因为NavigatorState混入了RestorationMixin,所以执行了RestorationMixin的didChangeDependencies方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
@override
void didChangeDependencies() {
super.didChangeDependencies();
final RestorationBucket? oldBucket = _bucket;
final bool needsRestore = restorePending;
_currentParent = RestorationScope.maybeOf(context);
final bool didReplaceBucket = _updateBucketIfNecessary(parent: _currentParent, restorePending: needsRestore);
if (needsRestore) {
// 执行这里
_doRestore(oldBucket);
}
if (didReplaceBucket) {
assert(oldBucket != _bucket);
oldBucket?.dispose();
}
}
|
在RestorationMixin的didChangeDependencies方法中,执行了 _doRestore 方法。
1
2
3
4
5
6
7
8
9
10
11
12
|
void _doRestore(RestorationBucket? oldBucket) {
assert(() {
_debugPropertiesWaitingForReregistration = _properties.keys.toList();
return true;
}());
// 执行这里
restoreState(oldBucket, _firstRestorePending);
_firstRestorePending = false;
...
}
|
在RestorationMixin的 _doRestore 方法中,执行了restoreState方法,这是一个空实现,由子类NavigatorState实现,看下NavigatorState的restoreState方法。
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
|
@override
void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
registerForRestoration(_rawNextPagelessRestorationScopeId, 'id');
registerForRestoration(_serializableHistory, 'history');
// 删除旧历史记录中的所有内容并清除overlay。
// Delete everything in the old history and clear the overlay.
_forcedDisposeAllRouteEntries();
assert(_history.isEmpty);
// _overlayKey延迟到这里才创建
_overlayKey = GlobalKey<OverlayState>();
// 从恢复数据填充新历史记录。
// 此处因为是根布局初始化,所以没有数据可恢复,_history添加了个寂寞
// Populate the new history from restoration data.
_history.addAll(_serializableHistory.restoreEntriesForPage(null, this));
// 这是声明式的范畴,不会讲
for (final Page<dynamic> page in widget.pages) {
final _RouteEntry entry = _RouteEntry(
page.createRoute(context),
pageBased: true,
initialState: _RouteLifecycle.add,
);
assert(
entry.route.settings == page,
'The settings getter of a page-based Route must return a Page object. '
'Please set the settings to the Page in the Page.createRoute method.',
);
_history.add(entry);
_history.addAll(_serializableHistory.restoreEntriesForPage(entry, this));
}
// 如果没有什么可恢复的,我们需要处理初始路由。
// 所以执行这里
// If there was nothing to restore, we need to process the initial route.
if (!_serializableHistory.hasData) {
// 获取传入的initialRoute
String? initialRoute = widget.initialRoute;
// widget.pages为空,说明采用命令式方案
if (widget.pages.isEmpty) {
// 如果传入的initialRoute为null,取默认值为'/',否则不变
// 本示例中是没有设置initialRoute的,所以取默认值为'/'
initialRoute = initialRoute ?? Navigator.defaultRouteName;
}
if (initialRoute != null) {
// 将onGenerateInitialRoutes返回的List<Route>转换为List<_RouteEntry>,然后添加进_history中
_history.addAll(
widget.onGenerateInitialRoutes(
this,
// 如果传入的initialRoute为null,取默认值为'/'
widget.initialRoute ?? Navigator.defaultRouteName,
).map((Route<dynamic> route) => _RouteEntry(
route,
// 表示非声明式
pageBased: false,
// 初始状态为_RouteLifecycle.add
initialState: _RouteLifecycle.add,
restorationInformation: route.settings.name != null
? _RestorationInformation.named(
name: route.settings.name!,
arguments: null,
restorationScopeId: _nextPagelessRestorationScopeId,
)
: null,
),
),
);
}
}
...
// 最后执行_flushHistoryUpdates方法,之前讲过该方法会调用setState来刷新UI
_flushHistoryUpdates();
assert(() { _debugLocked = false; return true; }());
}
|
之前讲过,如果外部没传入onGenerateInitialRoutes,那么就会调用Navigator的defaultGenerateInitialRoutes方法,本示例的确没传入。
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
|
// 此处传入的initialRouteName为:如果传入的initialRoute为null,取默认值为'/'
// 本示例没有设置initialRoute,所以取默认值为'/'
static List<Route<dynamic>> defaultGenerateInitialRoutes(NavigatorState navigator, String initialRouteName) {
// 创建一个List<Route>
final List<Route<dynamic>?> result = <Route<dynamic>?>[];
// 如果initialRouteName以'/'开始,并且字符串长度大于1时
if (initialRouteName.startsWith('/') && initialRouteName.length > 1) {
// 截取'/'之后的字符串作为initialRouteName,比如/A/B/C变为A/B/C
initialRouteName = initialRouteName.substring(1); // strip leading '/'
...
// 执行navigator的_routeNamed方法创建Route,添加进result,这里的第一个Route命名为'/'
// 此处添加进来的Route就是home了
result.add(navigator._routeNamed<dynamic>(Navigator.defaultRouteName, arguments: null, allowNull: true));
// 如果你的initialRouteName是这种形式:a/b/c,那么根据'/'进行分解[a, b, c]
final List<String> routeParts = initialRouteName.split('/');
if (initialRouteName.isNotEmpty) {
String routeName = '';
for (final String part in routeParts) {
// routeName变为这种形式:/a , /a/b,/a/b/c
routeName += '/$part';
...
// 执行navigator的_routeNamed方法创建Route,添加进result
result.add(navigator._routeNamed<dynamic>(routeName, arguments: null, allowNull: true));
}
}
// 如果List<Route>中最后一个元素为null,报错
// 如果你没有配置路由表,那么是找不到/a , /a/b,/a/b/c这些路由的,所以返回为null
if (result.last == null) {
...
// 清空List<Route>
result.clear();
}
} else if (initialRouteName != Navigator.defaultRouteName) {
// 如果initialRouteName不是'/',那么我们尝试使用allowNull:true来获取它,这样如果失败,我们就会回退到'/'(没有allowNull:true,见下文)。
// 如果initialRouteName不存在于路由表,那么Route会返回null
// If initialRouteName wasn't '/', then we try to get it with allowNull:true, so that if that fails,
// we fall back to '/' (without allowNull:true, see below).
result.add(navigator._routeNamed<dynamic>(initialRouteName, arguments: null, allowNull: true));
}
// 空路由可能是由于initialRouteName中的间隙造成的
// Null route might be a result of gap in initialRouteName
//
// 例如,routes = ['A', 'A/B/C'], 且initialRouteName = 'A/B/C' 这应导致 result = ['A', null,'A/B/C'],其中 'A/B' 生成null。在本例中,我们要过滤掉 null 并返回 result = ['A', 'A/B/C']。
// For example, routes = ['A', 'A/B/C'], and initialRouteName = 'A/B/C'
// This should result in result = ['A', null,'A/B/C'] where 'A/B' produces
// the null. In this case, we want to filter out the null and return
// result = ['A', 'A/B/C'].
// 当解析initialRouteName为A/B/C时,A/B在路由表中没有配置,就会造成Route为null,这里要移除List<Route>中null的情况
result.removeWhere((Route<dynamic>? route) => route == null);
// 如果List<Route>依然为空
if (result.isEmpty) {
// 执行navigator的_routeNamed方法创建Route,添加进result,name为'/'
// 本示例会执行这里,此处添加进来的Route就是home了
result.add(navigator._routeNamed<dynamic>(Navigator.defaultRouteName, arguments: null));
}
return result.cast<Route<dynamic>>();
}
|
看下NavigatorState的 _routeNamed 方法。
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
|
Route<T?>? _routeNamed<T>(String name, { required Object? arguments, bool allowNull = false }) {
assert(!_debugLocked);
// 如果allowNull为true,并且外部没设置onGenerateRoute时,Route直接返回null
// 当然在本示例中,根布局内部已有Navigator,会有一个默认的_onGenerateRoute方法,这里不会执行
// 如果你是自己添加的Navigator,就需要考虑onGenerateRoute为null的情况
if (allowNull && widget.onGenerateRoute == null) {
return null;
}
assert(() {
if (widget.onGenerateRoute == null) {
throw FlutterError(
'Navigator.onGenerateRoute was null, but the route named "$name" was referenced.\n'
'To use the Navigator API with named routes (pushNamed, pushReplacementNamed, or '
'pushNamedAndRemoveUntil), the Navigator must be provided with an '
'onGenerateRoute handler.\n'
'The Navigator was:\n'
' $this',
);
}
return true;
}());
final RouteSettings settings = RouteSettings(
name: name,
arguments: arguments,
);
// 执行外部传入的onGenerateRoute方法,这里调用了默认的_onGenerateRoute方法
Route<T?>? route = widget.onGenerateRoute!(settings) as Route<T?>?;
// 如果Route为null,并且allowNull为false
if (route == null && !allowNull) {
assert(() {
if (widget.onUnknownRoute == null) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('Navigator.onGenerateRoute returned null when requested to build route "$name".'),
ErrorDescription(
'The onGenerateRoute callback must never return null, unless an onUnknownRoute '
'callback is provided as well.',
),
DiagnosticsProperty<NavigatorState>('The Navigator was', this, style: DiagnosticsTreeStyle.errorProperty),
]);
}
return true;
}());
// 执行外部传入的onUnknownRoute方法,这里调用了默认的_onUnknownRoute方法
route = widget.onUnknownRoute!(settings) as Route<T?>?;
assert(() {
if (route == null) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('Navigator.onUnknownRoute returned null when requested to build route "$name".'),
ErrorDescription('The onUnknownRoute callback must never return null.'),
DiagnosticsProperty<NavigatorState>('The Navigator was', this, style: DiagnosticsTreeStyle.errorProperty),
]);
}
return true;
}());
}
assert(route != null || allowNull);
return route;
}
|
看下 _WidgetsAppState的 _onGenerateRoute 方法。
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
|
Route<dynamic>? _onGenerateRoute(RouteSettings settings) {
final String? name = settings.name;
// 如果你的Route的name是'/',那么包装widget.home为WidgetBuilder
// 如果你的Route的name不是'/'或者widget.home为空,那么从路由表中获取name对应的WidgetBuilder
final WidgetBuilder? pageContentBuilder = name == Navigator.defaultRouteName && widget.home != null
? (BuildContext context) => widget.home!
: widget.routes![name];
if (pageContentBuilder != null) {
assert(
widget.pageRouteBuilder != null,
'The default onGenerateRoute handler for WidgetsApp must have a '
'pageRouteBuilder set if the home or routes properties are set.',
);
// 在_MaterialAppState中,WidgetsApp传入的pageRouteBuilder实际上是创建了MaterialPageRoute
final Route<dynamic> route = widget.pageRouteBuilder!<dynamic>(
settings,
pageContentBuilder,
);
return route;
}
// 如果你的Route的name不是'/'并且路由表也没有配置该name,就会执行这里
if (widget.onGenerateRoute != null) {
return widget.onGenerateRoute!(settings);
}
// 如果没有设置onGenerateRoute兜底,最后Route返回null
return null;
}
|
看下 _WidgetsAppState的 _onUnknownRoute 方法。
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
|
Route<dynamic> _onUnknownRoute(RouteSettings settings) {
assert(() {
if (widget.onUnknownRoute == null) {
throw FlutterError(
'Could not find a generator for route $settings in the $runtimeType.\n'
'Make sure your root app widget has provided a way to generate \n'
'this route.\n'
'Generators for routes are searched for in the following order:\n'
' 1. For the "/" route, the "home" property, if non-null, is used.\n'
' 2. Otherwise, the "routes" table is used, if it has an entry for '
'the route.\n'
' 3. Otherwise, onGenerateRoute is called. It should return a '
'non-null value for any valid route not handled by "home" and "routes".\n'
' 4. Finally if all else fails onUnknownRoute is called.\n'
'Unfortunately, onUnknownRoute was not set.',
);
}
return true;
}());
// 执行外部传入的onUnknownRoute方法
final Route<dynamic>? result = widget.onUnknownRoute!(settings);
assert(() {
if (result == null) {
throw FlutterError(
'The onUnknownRoute callback returned null.\n'
'When the $runtimeType requested the route $settings from its '
'onUnknownRoute callback, the callback returned null. Such callbacks '
'must never return null.',
);
}
return true;
}());
return result!;
}
|
3.5、Navigator的pushNamed方法
1
2
3
4
5
6
7
|
@optionalTypeArgs
Future<T?> pushNamed<T extends Object?>(
String routeName, {
Object? arguments,
}) {
return push<T?>(_routeNamed<T>(routeName, arguments: arguments)!);
}
|
_routeNamed 方法返回一个Route对象,push方法和之前一样的逻辑,这里就不分析了,至于其它的Navigator的方法相信你也能举一反三。