flutter开发中我们经常要用到返回键,一般如果要对返回键进行一些操作需要加入WillPopScope,并写上自己得逻辑。
此处先分析一下我们常用得navigator.of(context)这里得of方法。
主要逻辑在这里
NavigatorState navigator;
if (context is StatefulElement && context.state is NavigatorState) {
navigator = context.state as NavigatorState;
}
if (rootNavigator) {
navigator = context.findRootAncestorStateOfType<NavigatorState>() ?? navigator;
} else {
navigator = navigator ?? context.findAncestorStateOfType<NavigatorState>();
}
其主要得作用就是通过findAncestorStateOfType找到其最近得NavigatorState,而navigator就是通过NavigatorState来进行操作得。
这里我们要先解析一下of传入的BuildContext
这个参数,其实这个参数和Android的context有些相似,因为flutter是通过elementTree来进行渲染的,不管是statelesswidget还是statefulwidget都是要通过createElement() 方法创建自己的element然后通过build方法将其加入elementTree,而element本身就是一个buildcontext,所以of获取的context实际上就是搭建当前widget的element,然后从这个element为起点在elementTree中向上寻找navigatorState,之后再进行比如push和pop 的操作,更详细可以看这篇文章。
了解了上面这些之后再来看我开发过程中的一个例子。
这是我的项目目录,我分别再main.dart和my_router.dart中维护了两个route栈,通过main可以进入books的页面,而books页面可以进入book_content页面,其中book_content和books的跳转是通过myRoute进行,main到books是通过main中的route进行。
此时就导致了一个问题,我在book_content页面中按下手机的返回键,并没有返回预期的books页面,而是直接回到了main页面,而如果我添加一个按钮,按钮调用了MyRouter.pop(),此时按这个按钮才可以回到Books页面。
这里是我的my_router的核心代码
MyRouter.push(BuildContext context, String url, dynamic params) {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return _getPage(url, params);
}));
MyRouter.pop(BuildContext context){
Navigator.pop(context);
}
此时就出现了按返回键和用 Navigator.pop()产生不一样结果的情况,我们来分析一下原因。
首先我们来看一下WillPopScope,其本身是一个SatefulWidget,这是他相关的代码。
class _WillPopScopeState extends State<WillPopScope> {
ModalRoute<dynamic> _route;
@override
void didChangeDependencies() {
super.didChangeDependencies();
if (widget.onWillPop != null)
_route?.removeScopedWillPopCallback(widget.onWillPop);
//可以看到这个调用了ModalRoute.of(context)获得route
//这里的context就是此时的widget,在这里就是bookContent这个widget
_route = ModalRoute.of(context);
if (widget.onWillPop != null)
_route?.addScopedWillPopCallback(widget.onWillPop);
}
其内部有一个_route变量,初始化为ModalRoute.of(context),所以关键我们就要知道此时的route是怎么得到的,得到的依据是什么。
@optionalTypeArgs
static ModalRoute<T> of<T extends Object>(BuildContext context) {
final _ModalScopeStatus widget = context.dependOnInheritedWidgetOfExactType<_ModalScopeStatus>();
return widget?.route as ModalRoute<T>;
}
此时情况就明了多了,之前说过navigator.of(context)是获得最近的NavigatorState,而这里的ModalRoute.of(context)是获得最近的route。
(avigator本身是一个StatefulWidget,其本身得route是在创建my_route时候传入的MaterialPageRoute,和我们用ModalRoute找到的最近的route是不相同的)。
所以按返回键的时候ModalRoute.of(context)判定最近的route是main.dart中MaterialApp为我们创建的route,而不是navigator的route,这个route的上一个页面自然就是main页面。
同样,如果调用navigator的pop(),会寻找navigatorState而不是route,此时就会利用找到的navigatorState返回navigator的上一个页面,也就是books页面,而不是main页面了。
其实我个人也是大概懂了一些,对于flutter的of方法判定的机制还没有研究清楚,希望有错误大家可以帮忙指正。