通过以上的描述我们可以看到InheritedWidget的特点:
- 想要使用InheritedWidget,必须继承他
- InheritedWidget必须是祖先节点或者节点的层级比较高
- InheritedWidget的状态变化时,会构建引用自己的子节点
除了上面的类描述,文档还给出了使用的案例,以及常见的使用方法。
The convention is to provide a static method
of
on the [InheritedWidget] which does the call to [BuildContext.dependOnInheritedWidgetOfExactType]. This allows the class to define its own fallback logic in case there isn’t a widget in scope.
比较常见的做法是,在我们自定义的InheritedWidget类中,定义一个静态的of方法,这个方法去调用BuildContext.dependOnInheritedWidgetOfExactType方法。这样的话,就比较容易在of方法中做一些业务逻辑,比如刷新不刷新子节点,处理null情况等等。
通过上面的描述,我们可以知道其用法:
- 定义一个类继承自InheritedWidget
- 定义一个静态of方法,来向上查找
我们以实际的案例来看是怎么使用的。
2 使用案例
我们以计数器工程为例。
2.1 自定义InheritedWidget
class MyInheritedWidget extends InheritedWidget {
//InheritedWidget是ProxyWidget
//child用于显示
MyInheritedWidget(Widget child) : super(child: child);
//该方法的布尔值,用于决定是否子节点刷新
//用于避免数据相同时的无效刷新
@override
bool updateShouldNotify(InheritedWidget oldWidget) {
return true;
}
}
2.2 包裹数据
计数器案例管理的就是简单的数字,因此,我们可以通过构造方法传进来。d当时更多的时候,包裹的数据往往比较复杂,比如MediaQuery的屏幕数据,Theme的主题数据等等。
class MyInheritedWidget extends InheritedWidget {
//子孙节点 使用count数据
final int count;
MyInheritedWidget(Widget child, this.count) : super(child: child);
//这里就可以更新的时机了
//数据不一致的时候
@override
bool updateShouldNotify(MyInheritedWidget oldWidget) {
return oldWidget.count!=this.count;
}
}
2.3 添加of方法
class MyInheritedWidget extends InheritedWidget {
final int count;
MyInheritedWidget(Widget child, this.count) : super(child: child);
static MyInheritedWidget of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType();
}
@override
bool updateShouldNotify(MyInheritedWidget oldWidget) {
return oldWidget.count!=this.count;
}
}
添加of方法的目的是封装,封装业务逻辑,封装统一入口。 这里我们返回了自定义的MyInheritedWidget,也可以返回数据。
,
2.4 项目改造
具体代码👉这里查看。以下是部分代码。
class _MyHomePageState extends State {
int _counter = 0;
CenterWidget centerWidget;
@override
void initState() {
super.initState();
//构造字树
centerWidget = new CenterWidget();
}
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
//这里构造一个高层级 InheritedWidget节点
body: MyInheritedWidget(centerWidget, _counter),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: ‘Increment’,
child: Icon(Icons.add),
),
);
}
}
class MyInheritedWidget extends InheritedWidget {
final int count;
MyInheritedWidget(Widget child, this.count) : super(child: child);
//封装快速查阅方法
static MyInheritedWidget of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType();
}
@override
bool updateShouldNotify(MyInheritedWidget oldWidget) {
return oldWidget.count!=this.count;
}
}
class CenterWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
print(‘CenterWidget’);
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextDescWidget(),
TextCounterWidget(),
],
),
);
}
}
class TextDescWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
print(‘TextDescWidget’);
return Text(
‘You have pushed the button this many times:’,
);
}
}
class TextCounterWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
print(‘TextCounterWidget’);
return Text(
‘${MyInheritedWidget.of(context).count}’,
style: Theme.of(context).textTheme.display1,
);
}
}
我们把原来的一个文件,拆成几个组件,然后组合起来。如果不使用InheritedWidget,counter值就需要从Scaffold—>CenterWidget—>TextCounterWidget层层传递。现在只需要通过of方法就可以实现数据的向下传递。
2.6 定向刷新
上面的写法,当我们点击按钮的时候,数字就会变化,并且只有TextCounterWidget的build方法会执行。
2.7 用法小结
- 自定义Widget继承自InheritedWidget
- 自定义Widget中包裹数据参数
- 编写静态的of方法,获取数据或者自定义Widget
- dependOnInheritedWidgetOfExactType查找的方式会实现数据监听
3 原理
我们通过InheritedWidget实现了两个功能:
- 向上查找
子孙节点通过dependOnInheritedWidgetOfExactType方法向上找到了最近的指定类型的InheritedWidget
- 定向刷新
InheritedWidget只会刷新关注数据的子孙Widget
下面我们分别来看它的实现原理。
3.1 向上查找
dependOnInheritedWidgetOfExactType 我们先看方法简介。
Obtains the nearest widget of the given type [T], which must be the type of a concrete [InheritedWidget] subclass, and registers this build context with that widget such that when that widget changes (or a new widget of that type is introduced, or the widget goes away), this build context is rebuilt so that it can obtain new values from that widget.
Calling this method is O(1) with a small constant factor, but will lead to the widget being rebuilt more often.
这个方法是BuildContext的实例方法,该方法可以获取最近的指定类型的InheritedWidget,并且还会将调用的context实例,注册起来,当指定的类型的InheritedWidget数据变化时,就会调用context的build方法。
并且该方法的向上查找的时间复杂度是O(1)的。
通过上面的描述我们可以知道:
- dependOnInheritedWidgetOfExactType的作用有两个:查找、注册
- 查找的时间复杂度是O(1)
我们可以很自然的想到查找的过程,递归的看自己的parent是不是指定类型的Widget,就像下图: [外链图片转存中…(img-kOgQw6zl-1712050418336)]
我们看到这种方式,极端情况下的时间复杂度是O(n)的,那么Flutter是如何做到O(1)的呢?查找的效率比较快的是连续数组、哈希map。
Flutter就是通过map来实现的。Flutter的Element类中维护着一个Map成员变量[_inheritedWidgets],Map的key是Widget的类型,value是具体的Element。那么这个变量是怎么维护的呢
Element生命周期我们知道mount方法是挂载方法,该方法之后Element才可以显示到屏幕上。
void mount(Element parent, dynamic newSlot) {
…
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
最后
现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水!
为什么某些人会一直比你优秀,是因为他本身就很优秀还一直在持续努力变得更优秀,而你是不是还在满足于现状内心在窃喜!
Android架构师之路很漫长,一起共勉吧!
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
d而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水!
为什么某些人会一直比你优秀,是因为他本身就很优秀还一直在持续努力变得更优秀,而你是不是还在满足于现状内心在窃喜!
Android架构师之路很漫长,一起共勉吧!