写点什么

flutter 系列之: 构建 Widget 的上下文环境 BuildContext 详解

作者:程序那些事
  • 2022 年 6 月 07 日
  • 本文字数:2440 字

    阅读完需:约 8 分钟

flutter系列之:构建Widget的上下文环境BuildContext详解

简介

我们知道 Flutter 中有两种 Widget,分别是 StatelessWidget 和 StatefulWidget,StatelessWidget 中有一个 build 方法来创建对应的 Widget,虽然 StatefulWidget 中没有对应的 build 方法,但是和 StatefulWidget 对应的 State 中也有同样的 build 方法。


这个 build 方法就是用来创建 Widget 的核心方法。


我们来看下 build 方法的定义:


Widget build(BuildContext context);
复制代码


build 方法传入一个 BuildContext 对象,返回一个 Widget 对象,也就是说这个 BuildContext 中包含了要创建的 Widget 的所有信息。这个 BuildContext 被称为是 Widget 的上下文构建环境。


那么 BuildContext 有什么特性呢?我们又该如何使用 BuildContext 呢?一起来看看吧。

BuildContext 的本质

还记得 flutter 中的三颗树吗?



他们分别是 Widgets 树,Element 树和 Render 树。其中 Widgets 树和 Element 树是一一对应的。而 Render 树和 Element 中的 RenderObjectElement 是一一对应的。


事实上 BuildContext 就是一个 Element 对象。怎么说呢?


我们先看下 BuildContext 的定义:


abstract class BuildContext {
Widget get widget; ...}
复制代码


BuildContext 是一个抽象类,我们再看一下 Element 类的定义:


abstract class Element extends DiagnosticableTree implements BuildContext {
复制代码


可以看到,Element 对象实现了 BuildContext 接口,而每一个 BuildContext 都有一个和其绑定的 Widget 对象。


经过复杂的关系传递运算,我们可以知道 Element 对象和 Widget 对象从代码层面来说,确实是一一对应的。

BuildContext 和 InheritedWidget

InheritedWidget 是一种 widget 用来在 tree 中向下传递变动信息,在 tree 的子节点中,可以通过调用 BuildContext.dependOnInheritedWidgetOfExactType 在子节点中查找最近的父 InheritedWidget,从而将当前的 BuildContext 绑定的 widget 和 InheritedWidget 建立绑定关系,从而在下次 InheritedWidget 发生变动的时候,会自动触发 BuildContext 绑定的 widget 的 rebuild 方法。


听起来好像很复杂的样子,但是实际上很简单,我们举个例子,首先我们需要定义一个 Widget 用来继承 InheritedWidget:


class FrogColor extends InheritedWidget {   const FrogColor({     Key? key,     required this.color,     required Widget child,   }) : super(key: key, child: child);
final Color color;
static FrogColor of(BuildContext context) { final FrogColor? result = context.dependOnInheritedWidgetOfExactType<FrogColor>(); assert(result != null, 'No FrogColor found in context'); return result!; }
@override bool updateShouldNotify(FrogColor old) => color != old.color; }
复制代码


在这个方法中,我们需要定义一个 of 方法,这个该方法中,我们调用 context.dependOnInheritedWidgetOfExactType 方法,用来查找离 BuildContext 最近的 FrogColor。


然后可以这样使用:


class MyPage extends StatelessWidget {   const MyPage({Key? key}) : super(key: key);
@override Widget build(BuildContext context) { return Scaffold( body: FrogColor( color: Colors.green, child: Builder( builder: (BuildContext innerContext) { return Text( 'Hello Frog', style: TextStyle(color: FrogColor.of(innerContext).color), ); }, ), ), ); } }
复制代码


我们的本意是希望 child 中的 Text 组件的 style 根据父 widget 中的 FrogColor 的 color 来进行变化。所以在子组件的 style 中调用了 FrogColor.of(innerContext)方法,对 InheritedWidget 进行查找,同时建立绑定关系。


在 BuildContext 中,有两个查找并且进行绑定的方法,他们是:


InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect });
T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({ Object? aspect });
复制代码


两者的区别是,后者限定了查找的类型。


除了 dependOn 之外,BuildContext 还提供了两个查找的方法:


InheritedElement? getElementForInheritedWidgetOfExactType<T extends InheritedWidget>();T? findAncestorWidgetOfExactType<T extends Widget>();T? findAncestorStateOfType<T extends State>();T? findRootAncestorStateOfType<T extends State>();T? findAncestorRenderObjectOfType<T extends RenderObject>();
复制代码


他们和 depend 的区别是,他们不会建立依赖关系,只是单纯的进行查找。

BuildContext 的层级关系

因为每个 widget 都有一个 BuildContext,所以我们在使用的过程中一定要注意传入的 BuildContext 到底绑定的是哪个 widget。


如下面的代码所示:


class MyPage extends StatelessWidget {   const MyPage({Key? key}) : super(key: key);
@override Widget build(BuildContext context) { return Scaffold( body: FrogColor( color: Colors.green, child: Builder( builder: (BuildContext innerContext) { return Text( 'Hello Frog', style: TextStyle(color: FrogColor.of(innerContext).color), ); }, ), ), ); } }
复制代码


在 FrogColor 的 child 中,我们创建了一个新的 Builder,并提供了一个新的 innerContext。


为什么要这样做呢?因为如果我们不创建子 innnerContext 的话,使用的 context 就是 Scaffold 的,这样 FrogColor.of 将会找不到要找的对象,从而报错。


所以我们在使用 BuildContext 的时候,一定要注意。

总结

BuildContext 是构建 Widget 的基础,它也提供了一些非常有用的查找和绑定的功能,希望能对大家有所帮助。


更多内容请参考 http://www.flydean.com/04-flutter-buildcontext/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

发布于: 刚刚阅读数: 4
用户头像

关注公众号:程序那些事,更多精彩等着你! 2020.06.07 加入

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧,尽在公众号:程序那些事!

评论

发布
暂无评论
flutter系列之:构建Widget的上下文环境BuildContext详解_flutter_程序那些事_InfoQ写作社区