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/
最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!
flutter系列之:构建Widget的上下文环境BuildContext详解的更多相关文章
- 转载 CSS3 经典教程系列:CSS3 盒阴影(box-shadow)详解
目标大纲 文章转载 CSS3 经典教程系列:CSS3 盒阴影(box-shadow)详解 IE中CSS-filter滤镜小知识大全 CSS实现跨浏览器兼容性的盒阴影效果
- Spark Streaming揭秘 Day28 在集成开发环境中详解Spark Streaming的运行日志内幕
Spark Streaming揭秘 Day28 在集成开发环境中详解Spark Streaming的运行日志内幕 今天会逐行解析一下SparkStreaming运行的日志,运行的是WordCountO ...
- Nginx+Tomcat的服务器端环境配置详解
这篇文章主要介绍了Nginx+Tomcat的服务器端环境配置详解,包括Nginx与Tomcat的监控开启方法,需要的朋友可以参考下 Nginx+tomcat是目前主流的Javaweb架构,如何让ngi ...
- Windows GTK+ 环境搭建(详解)
来源:http://blog.sina.com.cn/s/blog_a6fb6cc901017ygy.html Windows GTK+ 环境搭建 最近要做界面的一些东西,但是对微软提供的类库MFC不 ...
- 单元测试系列之十一:Jmockit之mock特性详解
本文是Jmockit学习过程中,根据官网所列的工具特性进行解读. 1.调用次数约束(Invocation count constraints) 可以通过调用计数约束来指定预期和/或允许匹配给定期望的调 ...
- MySQL高可用方案-PXC(Percona XtraDB Cluster)环境部署详解
MySQL高可用方案-PXC(Percona XtraDB Cluster)环境部署详解 Percona XtraDB Cluster简称PXC.Percona Xtradb Cluster的实现是在 ...
- net core体系-web应用程序-4net core2.0大白话带你入门-5asp.net core环境变量详解
asp.net core环境变量详解 环境变量详解 Windows操作系统的环境变量在哪设置应该都知道了. Linux(centos版本)的环境变量在/etc/profile里面进行设置.用户级的 ...
- Maya 常用环境变量详解
Maya 常用环境变量详解 前言: Maya 的环境变量让用户可以很方便的自定义 Maya 的功能. 在 Maya 的 Help 帮助文档中有专门的一个章节< Environment Varia ...
- Python环境搭建详解(Window平台)
前言 Python,是一种面向对象的解释型计算机程序设计语言,是纯粹的自由软件,Python语法简洁清晰,特色是强制用空白符作为语句缩进,具有丰富和强大的库,它常被称为胶水语言. Python是一种解 ...
随机推荐
- Wget命令解释
Wget主要用于下载文件,在安装软件时会经常用到,以下对wget做简单说明. 1.下载单个文件:wget http://www.baidu.com.命令会直接在当前目录下载一个index.html的文 ...
- 我的第一个springboot starter
在springboot中有很多starter,很多是官方开发的,也有是个人或开源组织开发的.这些starter是用来做什么的呐? 一.认识starter 所谓的starter,在springb ...
- Jmeter(五十四) - 从入门到精通高级篇 - 如何在linux系统下运行jmeter脚本 - 上篇(详解教程)
1.简介 上一篇宏哥已经介绍了如何在Linux系统中安装Jmeter,想必各位小伙伴都已经在Linux服务器或者虚拟机上已经实践并且都已经成功安装好了,那么今天宏哥就来介绍一下如何在Linux系统下运 ...
- dotnet 在 linux 上构建问题(RID 的问题)
个人理解 一方面 /etc/os-release 中定义的的 ID VERSION_ID 是会与源代码中定义 RID 的相对应,如果不对应,就会报错 The specified RuntimeIden ...
- jetbrains 系列产品无限试用
无限试用插件 在线安装 需要添加第三方插件仓库地址 设置 -- Manage Plugins Reposition... -- + https://plugins.zhile.io plugins 中 ...
- SAP APO - 简介
SAP高级计划和优化(SAP APO)是SAP SCM中的关键模块之一,在供应链流程中控制供应网络计划,备件计划,TP / VS和需求计划. 它可以帮助组织在动态环境中管理其供应链流程. SAP AP ...
- React技巧之检查元素是否可见
原文链接:https://bobbyhadz.com/blog/react-check-if-element-in-viewport 作者:Borislav Hadzhiev 正文从这开始~ 总览 在 ...
- Java中Double类型数据比较大小
方法一:转成字符串之后比较 如果要比较的两个double数据的字符串精度相等,可以将数据转换成string然后借助string的equals方法来间接实现比较两个double数据是否相等.注意这种方法 ...
- Nacos配置失败(java.lang.IllegalStateException: failed to req API:/nacos/v1/ns/instance after all server)
解决: nacos服务器过载,可以删掉nacos文件夹下的data文件夹,重新启动.
- 从UI Designer上面动态创建下拉列表
在UI Desigher上创建一个新的列表 并创建2个值,code 和value 添加一个EventHandler 在EventHandler上面添加一个Operation 类型为script$dat ...