這是博客《Flutter路由 - Navigator 》的番外篇,若是你沒有看過主篇真的不建議你直接看這篇文章,由於它真的炒雞炒雞枯燥乏味……node
不講客套話啦,咱們分別從Navigator
的push
和pop
兩個方法去探索源碼以及一些重要的細節。bash
當咱們想要push一個Page在界面上時,咱們能夠調用以下代碼:ide
Navigator.push(
context,
PageRouteBuilder(pageBuilder: (context, animation, secondaryAnimation) {
return MyPage(args);
}));
複製代碼
咱們以Navigator.push(BuildContext context, Route<T> route)
方法爲起始進行追述:post
static Future<T> push<T extends Object>(BuildContext context, Route<T> route) {
return Navigator.of(context).push(route);
}
複製代碼
NavigatorState
對象檢索就像上篇文章有提到過的,Navigator.push
是一個靜態方法,使得你能夠在任何地方進行調用,其內部經過of
方法在Element
樹(BuildContext
是 Element
的抽象類)中進行向上搜索。咱們看下Navigator.of
方法:動畫
static NavigatorState of(
BuildContext context, {
bool rootNavigator = false,
bool nullOk = false,
}) {
final NavigatorState navigator = rootNavigator
? context.rootAncestorStateOfType(const TypeMatcher<NavigatorState>())
: context.ancestorStateOfType(const TypeMatcher<NavigatorState>());
return navigator;
}
複製代碼
主要經過rootNavigator
變量判斷是否要檢索根部Navigator,rootAncestorStateOfType
向上查找最根部匹配類型對象,ancestorStateOfType
向上查找最近的匹配類型對象。從這個方法咱們也能夠知道Navigator.of
方法查找的不是Navigator
而是NavigatorState
,這個也比較容易理解,Navigator
是一個StatefulWidget
,具體的邏輯都在它的State對象當中。ui
接下來看NavigatorState.push
實現:this
//0
final List<Route<dynamic>> _history = <Route<dynamic>>[];
Future<T> push<T extends Object>(Route<T> route) {
...
//1
final Route<dynamic> oldRoute = _history.isNotEmpty ? _history.last : null;
//2
route._navigator = this;
//3
route.install(_currentOverlayEntry);
//4
_history.add(route);
//5
route.didPush();
route.didChangeNext(null);
if (oldRoute != null) {
oldRoute.didChangeNext(route);
route.didChangePrevious(oldRoute);
}
//6
for (NavigatorObserver observer in widget.observers)
observer.didPush(route, oldRoute);
_afterNavigation();
return route.popped;
}
複製代碼
0._history
就是Navigator
所維護的界面棧,但它只是一個普通的List
。spa
_history
是一個普通的List
,因此棧頂就是最後一個元素。2.讓新加入的route
和Navigator
引用綁定。debug
3.install
是 route
轉換爲OverlayEntry
,並插入到List<OverlayEntry>
中的重要過程,_currentOverlayEntry
是oldRoute
對應的OverlayEntry
,傳入_currentOverlayEntry
的意思是插入到它的上面。具體細節咱們稍後細講。code
4.route
入棧。
5.完成新老界面的轉換,內部有一些事件和動畫處理。
6.通知全部的Navigator
觀察者。
咱們最關心的是第3步,route.install(_currentOverlayEntry);
Route
類裏這個方法是一個空實現,具體細節在它的子類中,咱們重點看OverlayRoute
的實現:
abstract class OverlayRoute<T> extends Route<T> {
...
@override
List<OverlayEntry> get overlayEntries => _overlayEntries;
final List<OverlayEntry> _overlayEntries = <OverlayEntry>[];
@override
void install(OverlayEntry insertionPoint) {
_overlayEntries.addAll(createOverlayEntries());
navigator.overlay?.insertAll(_overlayEntries, above: insertionPoint);
super.install(insertionPoint);
}
...
複製代碼
一個Route
的_overlayEntries
一般包含兩個OverlayEntry
,一個是遮罩,一個是界面自己,都在createOverlayEntries
中建立。
先對_overlayEntries
完成兩個OverlayEntry
的add
,而後調用navigator
所持有的overlay
對象,將遮罩和界面插入到overlay
所持有的List<OverlayEntry>
中,以備繪製到界面之上。
這是Route
到OverlayEntry
的關鍵,具體的實如今ModalRoute
中:
abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T> {
...
@override
Iterable<OverlayEntry> createOverlayEntries() sync* {
yield _modalBarrier = OverlayEntry(builder: _buildModalBarrier);
yield OverlayEntry(builder: _buildModalScope, maintainState: maintainState);
}
...
複製代碼
對yield
語法不熟悉不要緊,你只要知道這個方法最終會返回兩個OverlayEntry
對象就行了。_modalBarrier
就是遮罩層,就是Dialog那個背景遮罩。
咱們重點關心第二個page的建立。OverlayEntry
中傳入兩個參數:
builder:這是一個function, 咱們自定義的Page建立就在這個方法之中。
maintainState: 這個屬性咱們在上篇博客有講,表示當這個 Widget 不可見時,是否須要繼續保持它的狀態,是否須要讓它繼續活着。一般一個 Page maintainState
爲true, Dialog 爲 false。
看下_buildModalScope
實現:
Widget _buildModalScope(BuildContext context) {
return _modalScopeCache ??= _ModalScope<T>(
key: _scopeKey,
route: this,
// _ModalScope calls buildTransitions() and buildChild(), defined above
);
}
複製代碼
_buildModalScope
建立了一個 Widget _ModalScope
,並將Route
本身傳了進去。_ModalScope
是個啥呢?
_ModalScope
是一個StatefulWidget
,咱們直接看它的 State
的build
方法:
@override
Widget build(BuildContext context) {
return _ModalScopeStatus(
route: widget.route,
isCurrent: widget.route.isCurrent, // _routeSetState is called if this updates
canPop: widget.route.canPop, // _routeSetState is called if this updates
child: Offstage(
offstage: widget.route.offstage, // _routeSetState is called if this updates
child: PageStorage(
bucket: widget.route._storageBucket, // immutable
child: FocusScope(
node: widget.route.focusScopeNode, // immutable
child: RepaintBoundary(
child: AnimatedBuilder(
animation: _listenable, // immutable
builder: (BuildContext context, Widget child) {
return widget.route.buildTransitions(
context,
widget.route.animation,
widget.route.secondaryAnimation,
IgnorePointer(
ignoring: widget.route.animation?.status == AnimationStatus.reverse,
child: child,
),
);
},
child: _page ??= RepaintBoundary(
key: widget.route._subtreeKey, // immutable
child: Builder( // ======!!!重點關注這個代碼!!!!!!
builder: (BuildContext context) {
return widget.route.buildPage(
context,
widget.route.animation,
widget.route.secondaryAnimation,
);
...
複製代碼
能夠看到_ModalScope
內部又嵌套了不少Widget
,而且建立所用的數據都來自Route
,咱們重點關注最後一個Builder
,能夠看到它最後return
調用的是 route
的buildPage
方法,眼熟嗎?就是咱們最開始調用push
方法傳入的自定義PageRouteBuilder
對象:
Navigator.push(
context,
PageRouteBuilder(pageBuilder: (context, animation, secondaryAnimation) {
return MyPage(args);
}));
複製代碼
到這裏你知道你的傳入的 Route 是如何被使用的了。
咱們回頭看一下Route
轉化爲OverlayEntry
以後,Overlay
是如何處理的:
以前咱們是這樣插入到overlay
中的:navigator.overlay?.insertAll(_overlayEntries, above: insertionPoint);
navigator.overlay
實際是OverlayState
class OverlayState extends State<Overlay> with TickerProviderStateMixin {
...
void insertAll(Iterable<OverlayEntry> entries, { OverlayEntry above }) {
...
//1
for (OverlayEntry entry in entries) {
entry._overlay = this;
}
//2
setState(() {
final int index = above == null ? _entries.length : _entries.indexOf(above) + 1;
_entries.insertAll(index, entries);
});
}
...
複製代碼
OverlayEntry
綁定自身,綁定自身的緣由是咱們上篇文章所講的:元素自治。 插入是由Overlay
中進行的,但刪除倒是每一個元素本身調用的。setState((){}
方法,集合插入完成後,將觸發Overlay
的 rebuild。因此接下來咱們看一下 OverlayState
的 build
方法:
@override
Widget build(BuildContext context) {
//1
final List<Widget> onstageChildren = <Widget>[];
final List<Widget> offstageChildren = <Widget>[];
bool onstage = true;
//2
for (int i = _entries.length - 1; i >= 0; i -= 1) {
final OverlayEntry entry = _entries[i];
//3
if (onstage) {
//4
onstageChildren.add(_OverlayEntry(entry));
if (entry.opaque)
onstage = false;
//5
} else if (entry.maintainState) {
offstageChildren.add(TickerMode(enabled: false, child: _OverlayEntry(entry)));
}
}
//6
return _Theatre(
//7
onstage: Stack(
fit: StackFit.expand,
children: onstageChildren.reversed.toList(growable: false),
),
//8
offstage: offstageChildren,
);
}
複製代碼
1.建立兩個空列表,分別存儲「臺上」將要被繪製的,和「臺下」不須要繪製的 Widget。
2.開始遍歷全部的OverlayEntry,準備將他們分配到兩個集合當中。值得注意的是,這裏是倒序遍歷,後加入的元素繪製到最上層。
3.最開始每一個OverlayEntry
都是有機會被繪製的,直到某個OverlayEntry
的opaque=true
,其餘OverlayEntry
沒有機會「上臺「了
4.能夠看到OverlayEntry
被當作參數傳給了_OverlayEntry
,完成了一個純 Dart 類到 Widget 的轉換。_OverlayEntry
代碼很簡單,它將根據OverlayEntry中的屬性
進行 build。
5.沒有機會上臺的OverlayEntry
開始判斷maintainState
值,須要保存的狀態的進入offstageChildren
,不須要的保存狀態的,沒有機會參與這一次的 build ,他們將被銷燬。
6.分配結束以後,進入劇場: _Theatre
。
7.臺上須要被繪製的進入Stack
組件,準備繪製。
8.不須要被繪製的,只會進行build。
至此,新push的Page完成了建立和繪製。
看完了 push , 咱們在看一下 pop:
bool pop<T extends Object>([ T result ]) {
...
//1
final Route<dynamic> route = _history.last;
bool debugPredictedWouldPop;
//2
if (route.didPop(result ?? route.currentResult)) {
if (_history.length > 1) {
//3
_history.removeLast();
if (route._navigator != null)
_poppedRoutes.add(route);
//4
_history.last.didPopNext(route);
//5
for (NavigatorObserver observer in widget.observers)
observer.didPop(route, _history.last);
} else {
return false;
}
} else {
...
}
_afterNavigation();
return true;
}
複製代碼
1.獲取集合末尾,也就是棧頂的route
,它將被pop。
2.能夠看到didPop
是有返回值的,也就說若是返回了 false,是能夠不彈出的。若是返回了 true,didPop
內部有一些銷燬處理,咱們稍後看。
3.若是didPop
返回了 true, 會作出棧處理。
4.通知下一個route 你回到前臺了。
5.通知全部的觀察者。
咱們重點關注 Page 回收的處理,因此看一下OverlayRoute
中的 didPop
:
abstract class OverlayRoute<T> extends Route<T> {
...
@override
bool didPop(T result) {
final bool returnValue = super.didPop(result);
if (finishedWhenPopped)
navigator.finalizeRoute(this);
return returnValue;
}
複製代碼
看一下navigator.finalizeRoute
void finalizeRoute(Route<dynamic> route) {
...
route.dispose();
}
複製代碼
↓
abstract class OverlayRoute<T> extends Route<T> {
...
@override
void dispose() {
for (OverlayEntry entry in _overlayEntries)
entry.remove();
_overlayEntries.clear();
super.dispose();
}
複製代碼
能夠看到遍歷執行了全部OverlayEntry
的remove
方法:
void remove() {
//1
final OverlayState overlay = _overlay;
_overlay = null;
//2
if (SchedulerBinding.instance.schedulerPhase == SchedulerPhase.persistentCallbacks) {
SchedulerBinding.instance.addPostFrameCallback((Duration duration) {
//3
overlay._remove(this);
});
} else {
overlay._remove(this);
}
}
複製代碼
1.清楚引用,避免內存泄露
2.判斷一下當前調度機狀態,稍後或馬上執行OverlayState
的_remove
方法.
void _remove(OverlayEntry entry) {
if (mounted) {
_entries.remove(entry);
setState(() { /* entry was removed */ });
}
}
複製代碼
從集合中清楚當前的OverlayEntry
,並觸發一次 Overlay
的rebuild,由於_entries
已經沒有當前界面了,rebuild以後也就天然不會存在了。
好啦,push和pop的代碼流程都過完啦~