乐趣区

关于flutter: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/

最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!

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

退出移动版