【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为例一步步来看

  1. 先看StatelessWidget源码
abstract class StatelessWidget extends Widget {
  @protected
  Widget build(BuildContext context);
}

StatelessWidget只是一个抽象方法,定义了一个build方法,再看看build方法在哪调用到:

  1. 定位到了’StatelessElement’方法调用了
class StatelessElement extends ComponentElement {
  @override
  Widget build() => widget.build(this);
}

我们看到Element的build调用到了widget.build方法并且传入了this作为参数,这个参数类型就是BuildContext。

  1. 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

总结

  1. BuildContext底层原理实现实际上就是Element
  2. of(context)原理,其实就是通过调用BuildContext各种实现方法遍历widget tree和Element tree 从而获取到指定的对象来达到数据共享的目的。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>