Flutter核心类分析深入理解BuildContext
Posted 牧羊人.阿标
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter核心类分析深入理解BuildContext相关的知识,希望对你有一定的参考价值。
背景
不管是刚写Flutter开发还是Flutter开发的老将,相信对BuildContext一定不陌生。那么BuildContext是什么呢?为什么我们每次StatelessWidget.build
(context),State.build(context)
在调用build的时候都需要传入一个BuildContext
呢?
本文主要讲下BuildContext来龙去脉,已经它的使用场景。
常见的BuildContext场景
首先我们一起来看看BuildContext的使用场景,相信做过Flutter开发的同学也知道,BuildContext的使用场景最多的无非就是各种of方法:Object.of(context)查找特定的对象,事实功能也确实是如此,比如:
// flutter路由2.0里面通过of方法查找到最近的Router对象
Router.of(context).routerDelegate.setNewRoutePath(routerName);
// flutter路由1.0里面查找最近的NavigatorState对象
Navigator.of(context).push
// 向上查找最近的ThemeData对象
Theme.of(context).textTheme
// .... 各种各样的of(context)方法
还有我们在创建widget的时候,build里面传参:
Widget build(BuildContext context)
BuildContext从哪来
我们通过编写widget知道BuildContext是来自于widget或者state的build(BuildContext context)方法,所以我们需要从Widget的build出发。这里以StatelessWidget
为例一步步来看
- 先看
StatelessWidget
源码
abstract class StatelessWidget extends Widget {
@protected
Widget build(BuildContext context);
}
StatelessWidget
只是一个抽象方法,定义了一个build方法,再看看build方法在哪调用到:
- 定位到了’StatelessElement’方法调用了
class StatelessElement extends ComponentElement {
@override
Widget build() => widget.build(this);
}
我们看到Element的build调用到了widget.build方法并且传入了this作为参数,这个参数类型就是BuildContext。
StatefulWidget
也是如此,就是通过Element的直接调用了state.build(Context context)
并且将自己(this)作为参数传递进去:
class StatefulElement extends ComponentElement {
@override
Widget build() => state.build(this);
}
我们传入的明明是Element,怎么又会变成BuildContext(肯定是继承关系啦),我们再来看看BuildContext源码,看看它又是何方神圣:
abstract class BuildContext {}
这里我们只需要知道它是一个抽象类,所以:
通过上面分析不容易总结出,Element必定是BuildContext的子类(去看Element继承关系就知道)。BuildContext其实就是对应的Element对象,将BuildContext作为参数传递也就是为了阻止开发人员直接操作Element(Element一般是不能够直接操作的,如果直接将context as Element编译也是能通过的(最好别这么完)),讲到这里我们就知道了BuildContext是个什么玩意儿了吧
解决什么问题
通过上面的分析我们知道BuildContext的本尊其实就是Element,Element的来龙去买已经作用可以看之前的文章【Flutter原理】三棵树的诞生与核心流程,Element作为widget和renderObject的桥梁,这就意味着我们可以拿到三棵树中的widget,element,renderObject三个节点。所以可以做的事情有很多,我们主要来看看官方提供给了我们哪些功能,来解决哪些问题:
看看BuildContext源码
abstract class BuildContext {
/// 当前BuildContext的Element的配置信息Widget
Widget get widget;
// Widget管理器
BuildOwner? get owner;
// 当前widget是否正在更新
bool get debugDoingBuild;
// 获取当前对应widget的RenderObject
RenderObject? findRenderObject();
// findRenderObject 返回的 RenderBox 的大小
Size? get size;
// 注册依赖祖先InheritWigdet
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect });
// 注册并获取指定类型的祖先InheritWidget(内部使用的是Set集合,所以不存在重复注册问题)
T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({ Object? aspect });
// 获取指定类型祖先InteritWidget的Element
InheritedElement? getElementForInheritedWidgetOfExactType<T extends InheritedWidget>();
// 获取指定类型的最近祖先Widget
T? findAncestorWidgetOfExactType<T extends Widget>();
// 获取指定类型的祖先State
T? findAncestorStateOfType<T extends State>();
// 获取指定类型的最远祖先State
T? findRootAncestorStateOfType<T extends State>();
// 获取指定类型的RendObject
T? findAncestorRenderObjectOfType<T extends RenderObject>();
// 访问祖先Element
void visitAncestorElements(bool visitor(Element element));
// 访问孩子Element
void visitChildElements(ElementVisitor visitor);
}
通过上面源码部分的方法作用可以知道,BuildContext的主要作用就是访问三个树中间的指定节点,那么拿到这些节点有什么用呢?
首先我们知道widget是一个immutable
对象,element和renderObject一般情况下是需要对开发者屏蔽的。那么他的最大意义对于开发者来说也就是获取指定的widget。我们知道flutter开发中都是各种各样的widget层级嵌套,我们要使用跨级访问某些widget获取数据时候,这个时候BuildContext的作用就来了。也就是我们所说的Widget数据共享需要使用到BuildContext,比如,上面各种Object.of(BuildContext context)获取指定的类,拿到需要的对象,在进行对应的操作。
那么我们能不能在自己定义的widget方法中也利用BuildContext实现数据共享呢。肯定是可以的,下面我们简单来写个实例:
简单的例子
添加静态of方法
class CommonWidgets extends StatefulWidget {
final String routerName;
// 查找指定类型的最近祖先CommonWidgets
static CommonWidgets of(BuildContext context) {
CommonWidgets widget =
context.findAncestorWidgetOfExactType<CommonWidgets>();
return widget;
}
CommonWidgets({this.routerName, Key key}) : super(key: key);
@override
_CommonWidgetsState createState() => _CommonWidgetsState();
}
使用
//使用方法跟系统的诸多widget类似,通过of方法获取到CommonWidgets,读取routerName
CommonWidgets.of(context).routerName
总结
- BuildContext底层原理实现实际上就是Element
- of(context)原理,其实就是通过调用BuildContext各种实现方法遍历widget tree和Element tree 从而获取到指定的对象来达到数据共享的目的。
以上是关于Flutter核心类分析深入理解BuildContext的主要内容,如果未能解决你的问题,请参考以下文章