简介

咱们晓得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/

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

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