老孟导读:这是2021年源码系列的第一篇文章,其实源码系列的文章不是特别受欢迎,一个缘由是原理性的知识很是枯燥,我本身看源码的时候特别有感触,二是想把源码分析讲的通俗易懂很是困难,本身明白 和 让别人听懂彻底是两回事。不过我依然会坚持 Flutter 源码系列的文章,提升本身的技术水平的同时,也但愿你们收获一些知识。
为了使源码系列的文章不那么枯燥,文章中会有不少流程图,流程图比纯文字更直观,一图胜千言。html
我也是第一次写源码系列的文章,若是文章哪里有不对的地方请告诉我,虽然我也不必定听😄,开个玩笑。git
但愿你们来个 赞,您的 赞是我写下去的巨大动力😄。github
全部源码系列文章都会分享到我我的博客:http://laomengit.com/面试
注意:使用的 Flutter 版本 和 Dart 版本以下:Flutter 1.22.4 • channel stable • https://github.com/flutter/fl...
Framework • revision 1aafb3a8b9 (6 weeks ago) • 2020-11-13 09:59:28 -0800
Engine • revision 2c956a31c0
Tools • Dart 2.10.4服务器不一样的版本可能有所不一样,请注意版本之间的区别。微信
首先, InheritedWidget 是一个很是重要,很是重要,很是重要的组件,重要的事情说3遍😄,系统中不少功能都是功能型组件都是经过 InheritedWidget 实现的,著名的 Provider 状态管理框架也是基于 InheritedWidget 实现的,所以不论是工做中,仍是面试,InheritedWidget 组件的原理及使用场景都是考察的重点。app
此篇文章包括以下几个部分:框架
InheritedWidget 组件是功能型组件,提供了沿树向下,共享数据的功能,即子组件能够获取父组件(InheritedWidget 的子类)的数据,经过BuildContext.dependOnInheritedWidgetOfExactType 获取。less
InheritedWidget 组件的共享数据是沿着树从上到下,是否联想到 Notification,Notification 正好与 InheritedWidget 传递方向相反,Notification 是沿着树从下到上,二者功能的实现都是子节点主动发起,InheritedWidget 组件的子节点主动查找父节点上 InheritedWidget 共享的数据,Notification 也是子节点主动发起通知,沿着树向上通知。ide
Notification 也是 Flutter 中很是重要的,后面会有专门的文章详细介绍,此篇不作介绍。
那么什么样的场景适合使用 InheritedWidget 呢? 一般App会有登陆用户信息,登陆用户信息为全局共享信息,想象一下,子组件要如何获取登陆用户信息?将上面的场景抽象一下,有一颗组件树,A、H 组件依赖同一数据,以下:
A、H 组件要如何获取到数据呢?
有一种实现方式是 经过构造函数透传,数据经过A传递给B,B传递给C、E,C和E在传递给F、H,以下图虚线的传递:
反应到代码上就是:
return A( data:data child:B( data:data child:C( data:data child:F( data:data ) ) ) );
这样的实现缺点很是明显,B、C组件不须要 data 数据,若是组件树比较深的话,那将是噩梦。
为了处理此问题,Flutter Framework 提供了 InheritedWidget 组件,InheritedWidget 组件的子组件能够直接获取数据,以下图:
InheritedWidget 组件的全部子组件均可以直接经过 BuildContext.dependOnInheritedWidgetOfExactType 获取数据。
上面分析了 InheritedWidget 组件的使用场景,下面用一个最简单的 demo 展现如何使用 InheritedWidget 组件。
定一个用户信息共享数据的实体类,任何子组件均可以获取用户信息,用户信息实体类:
class UserInfo { String name; int age; UserInfo({this.name, this.age}); @override bool operator ==(Object other) { if (!(other is UserInfo)) { return false; } var old = other as UserInfo; return name == old.name && age == old.age; } }
UserInfo 类重写了 == 操做符,是为了后面数据是否发生变化作判断。
定义共享 UserInfo 数据的 InheritedWidget 组件。
class MyInheritedWidget extends InheritedWidget { final UserInfo userInfo; MyInheritedWidget({this.userInfo, Widget child}):super(child: child); static MyInheritedWidget of(BuildContext context) { return context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>(); } @override bool updateShouldNotify(covariant MyInheritedWidget oldWidget) { return oldWidget.userInfo != userInfo; } }
这里有两个地方须要注意:
静态(static) of 方法,这个方法不是必须的,但通常都会添加此方法,方便其子组件调用,固然也能够直接在子组件中使用 context.dependOnInheritedWidgetOfExactType 方法,不加 of 方法,子组件调用以下:
class F extends StatelessWidget { @override Widget build(BuildContext context) { var myInheritedWidget = context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>(); return Text('name:${myInheritedWidget.userInfo.name}'); } }
添加静态(static) of 方法,用法以下:
class F extends StatelessWidget { @override Widget build(BuildContext context) { return Text('name:${MyInheritedWidget.of(context).userInfo.name}'); } }
咱们常用的 MediaQuery.of(context) 和 Theme.of(context) 方法都是系统封装的此方法。
使用 MyInheritedWidget 组件:
class InheritedWidgetDemo extends StatefulWidget { @override _InheritedWidgetDemoState createState() => _InheritedWidgetDemoState(); } class _InheritedWidgetDemoState extends State<InheritedWidgetDemo> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('InheritedWidget Demo'), ), body: Center( child: MyInheritedWidget( userInfo: UserInfo(name: '老孟', age: 18), child: A( child: F(), ), ), ), ); } }
A 组件代码:
class A extends StatelessWidget { final Widget child; const A({Key key, this.child}) : super(key: key); @override Widget build(BuildContext context) { return Center( child: child, ); } }
F 组件代码:
class F extends StatelessWidget { @override Widget build(BuildContext context) { return Text('name:${MyInheritedWidget.of(context).userInfo.name}'); } }
上面代码构成的组件树为(无关的节点已忽略好比Scaffold、Center等):
注意: A 组件是为了表示树的深度,此 Demo 中将其简化了,仅仅设置了一层,也能够设多多层。
运行效果:
下面修改数据并刷新UI,下面的代码仅能用于Demo,是为了演示方便,千万不要用于实际项目,由于下面的写法有巨大的性能问题,由于重建了 InheritedWidget 组件下的全部子组件,重要的事情说3遍:下面演示Demo千万不要用于实际项目,千万不要用于实际项目,千万不要用于实际项目。文章最后我会给出正确用法。
修改 _InheritedWidgetDemoState :
class _InheritedWidgetDemoState extends State<InheritedWidgetDemo> { UserInfo _userInfo = UserInfo(name: '老孟', age: 18); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('InheritedWidget Demo'), ), body: Center( child: MyInheritedWidget( userInfo: _userInfo, child: A( child: F(), ), ), ), floatingActionButton: FloatingActionButton( onPressed: () { setState(() { _userInfo = UserInfo(name: '老孟1', age: 18); }); }, ), ); } }
点击按钮的时候,UI刷新了,但请重点看右侧 rebuild stats 部分,每点击一次按钮,MyInheritedWidget 组件及其子组件所有 从新构建(rebuild),但 A 组件并不依赖于 MyInheritedWidget 共享数据,理想状况下不该该 rebuild,实际项目中,树的结构会比这个复杂的多,所以所有 rebuild 会形成性能问题,这也是开头说千万不要将此方式用于实际项目,网上充斥着大量此种用法的文章,后面会给出正确用法,正确用法比较复杂,并且涉及其余相关知识,因此此处的Demo仅用于学习 InheritedWidget。
你们是否还记得 Stateful 组件的生命周期 文章中介绍的 didChangeDependencies 生命周期,对其的介绍以下:
didChangeDependencies 方法在 initState 以后由 Framework 当即调用。另外,当此 State 对象的依赖项更改时被调用,好比其所依赖的 InheritedWidget 发生变化时, Framework 会调用此方法通知组件发生变化。
下面将 A 和 F 组件改成 StatefulWidget 组件:
class F extends StatefulWidget { @override _FState createState() => _FState(); } class _FState extends State<F> { @override void initState() { super.initState(); print('F initState'); } @override Widget build(BuildContext context) { print('F build'); return Text('name:${MyInheritedWidget.of(context).userInfo.name}'); } @override void didChangeDependencies() { super.didChangeDependencies(); print('F didChangeDependencies'); } @override void dispose() { super.dispose(); print('F dispose'); } } class A extends StatefulWidget { final Widget child; const A({Key key, this.child}) : super(key: key); @override _AState createState() => _AState(); } class _AState extends State<A> { @override void initState() { super.initState(); print('A initState'); } @override Widget build(BuildContext context) { print('A build'); return Center( child: widget.child, ); } @override void didChangeDependencies() { super.didChangeDependencies(); print('A didChangeDependencies'); } @override void dispose() { super.dispose(); print('A dispose'); } }
给各个生命周期添加日志打印,从新运行,点击按钮,输出日志以下:
flutter: A build flutter: F didChangeDependencies flutter: F build
所以,依赖 MyInheritedWidget 组件的 F 组件调用 didChangeDependencies 方法,而 A 组件没有调用 didChangeDependencies 方法,由于 A 没有依赖 MyInheritedWidget 组件。
下面再说一个很是容易忽略的地方 MyInheritedWidget.updateShouldNotify方法,通常这样写:
@override bool updateShouldNotify(covariant MyInheritedWidget oldWidget) { return oldWidget.userInfo != userInfo; }
这样写有什么问题吗?若是数据(userInfo)是自定义的实体类且未在 UserInfo 中重写 ==,那么极大几率出现有问题,由于不重写 == 操做符方法,使用 != 判断是否相等的时候判断的是两个对象的内存地址,下面将 UserInfo 中 == 方法去掉,
class UserInfo { String name; int age; UserInfo({this.name, this.age}); }
修改 _InheritedWidgetDemoState 类:
class _InheritedWidgetDemoState extends State<InheritedWidgetDemo> { UserInfo _userInfo = UserInfo(name: '老孟', age: 18); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('InheritedWidget Demo'), ), body: Center( child: MyInheritedWidget( userInfo: _userInfo, child: A( child: F(), ), ), ), floatingActionButton: FloatingActionButton( onPressed: () { setState(() { _userInfo = UserInfo(name: '老孟', age: 18); }); }, ), ); } }
修改 updateShouldNotify 方法,添加日志打印:
@override bool updateShouldNotify(covariant MyInheritedWidget oldWidget) { bool flag = oldWidget.userInfo != userInfo; print('updateShouldNotify:$flag'); return flag; }
点击按钮,_userInfo 对象引用发生了变化,但其值( name 和 age)都没有发生变化,updateShouldNotify 应该返回false,但实际打印的结果:
flutter: updateShouldNotify:true flutter: A build flutter: F didChangeDependencies flutter: F build
实际返回了 true,由于先后 _userInfo 对象引用发生了变化,在 UserInfo 中重写 ==,比较具体的name 和 age 是否相等:
@override bool operator ==(Object other) { if (!(other is UserInfo)) { return false; } var old = other as UserInfo; return name == old.name && age == old.age; }
再次运行,日志以下:
flutter: updateShouldNotify:false flutter: A build flutter: F build
还有一种错误写法:
class _InheritedWidgetDemoState extends State<InheritedWidgetDemo> { UserInfo _userInfo = UserInfo(name: '老孟', age: 18); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('InheritedWidget Demo'), ), body: Center( child: MyInheritedWidget( userInfo: _userInfo, child: A( child: F(), ), ), ), floatingActionButton: FloatingActionButton( onPressed: () { setState(() { _userInfo.name = '老孟1'; // _userInfo = UserInfo(name: '老孟', age: 18); }); }, ), ); } }
重点看这部分修改:
floatingActionButton: FloatingActionButton( onPressed: () { setState(() { _userInfo.name = '老孟1'; // _userInfo = UserInfo(name: '老孟', age: 18); }); }, ),
将 _userInfo = UserInfo(name: '老孟', age: 18) 修改成 _userInfo.name = '老孟1',猜猜 updateShouldNotify 返回是 true or false?
运行日志:
flutter: updateShouldNotify:false flutter: A build flutter: F build
是否是感受很是难以想象,两次的 name 值不同啊?
那是由于 _userInfo.name = '老孟1' 也修改了 oldWidget 的_userInfo,先后两次都指向了同一个对象引用。
不少人应该会有这样一个疑问,假设设置 updateShouldNotify 返回false,点击的时候UI也会更改,由于整颗树都 rebuild 了,那么 updateShouldNotify 由什么意义呢?
确定是有意义的,看以下场景,F 组件使用 InheritedWidget 的共享数据访问服务器接口,获取服务器数据并展现,若是 updateShouldNotify 返回 false,那么 F 组件 rebuild 时只会执行 build 函数,而访问服务器接口是一个耗时工做,考虑性能因素,不能将访问服务器接口放在 build 函数中,那么 InheritedWidget 数据的更新就没法更新其依赖的组件,而 updateShouldNotify 返回 true时, F 组件 rebuild 时会执行 didChangeDependencies 和 build 函数,此时能够将访问服务器接口放在 didChangeDependencies 函数中,这也是 didChangeDependencies 生命周期存在的意义。
下面重点来了,那么如何正确使用 InheritedWidget 组件,答案是 InheritedWidget + ValueNotifier,关于 **的用法能够到个人我的博客中查看,地址:http://laomengit.com/flutter/widgets/ValueListenableBuilder.html ,这里不详细展开介绍。
修改 MyInheritedWidget 代码:
class MyInheritedWidget extends InheritedWidget { ValueNotifier<UserInfo> _valueNotifier; ValueNotifier<UserInfo> get valueNotifier => _valueNotifier; MyInheritedWidget(UserInfo userInfo, {Widget child}) : super(child: child) { _valueNotifier = ValueNotifier<UserInfo>(userInfo); } static MyInheritedWidget of(BuildContext context) { return context.getElementForInheritedWidgetOfExactType<MyInheritedWidget>().widget; } void updateData(UserInfo info) { _valueNotifier.value = info; } @override bool updateShouldNotify(covariant MyInheritedWidget oldWidget) { return false; } }
主要的变化是:
静态方法 of 由
static MyInheritedWidget of(BuildContext context) { return context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>(); }
改成
static MyInheritedWidget of(BuildContext context) { return context.getElementForInheritedWidgetOfExactType<MyInheritedWidget>().widget; }
区别是 dependOnInheritedWidgetOfExactType 注册了依赖关系,而 getElementForInheritedWidgetOfExactType 未注册,后面的源码部分会详细分析。
修改 F 组件的代码:
class F extends StatefulWidget { @override _FState createState() => _FState(); } class _FState extends State<F> { @override void initState() { super.initState(); print('F initState'); } @override void didChangeDependencies() { super.didChangeDependencies(); print('F didChangeDependencies'); } @override Widget build(BuildContext context) { print('F build'); return ValueListenableBuilder( builder: (context, UserInfo value, child) { return Text('${value.name}'); }, valueListenable: MyInheritedWidget.of(context).valueNotifier, ); } @override void dispose() { super.dispose(); print('F dispose'); } }
变化:
_InheritedWidgetDemoState 代码修改以下:
@override Widget build(BuildContext context) { return MyInheritedWidget(UserInfo(name: '老孟', age: 18), child: Builder( builder: (context) { return Scaffold( appBar: AppBar( title: Text('InheritedWidget Demo'), ), body: Center( child: A( child: F(), ), ), floatingActionButton: FloatingActionButton( onPressed: () { MyInheritedWidget.of(context) .updateData(UserInfo(name: '老孟${_clickCount++}', age: 18)); }, ), ); }, )); }
运行效果:
重点看 rebuild 的组件,无关的组件(好比 A)没有 rebuild。
固然也可使用 Provider 实现子组件更新,增长 UserInfoModel:
class UserInfoModel extends ChangeNotifier { UserInfoModel(this._userInfo); UserInfo _userInfo; UserInfo get userInfo => _userInfo; void update(UserInfo userInfo) { _userInfo = userInfo; notifyListeners(); } }
修改 _InheritedWidgetDemoState:
class _InheritedWidgetDemoState extends State<InheritedWidgetDemo> { int _clickCount =0; @override Widget build(BuildContext context) { return MultiProvider( providers: [ ChangeNotifierProvider( create: (_) => UserInfoModel(UserInfo(name: '老孟', age: 18))), ], builder: (context, child) { return Scaffold( appBar: AppBar( title: Text('InheritedWidget Demo'), ), body: Center( child: A( child: F(), ), ), floatingActionButton: Consumer<UserInfoModel>( builder: (ctx, userInfoModel, child) { return FloatingActionButton( child: child, onPressed: () { userInfoModel.update(UserInfo(name: '老孟${_clickCount++}', age: 18)); }, ); }, ), ); }, ); } }
分析源码的时候必定要先想一想,若是是我来实现这个组件,要如何实现? InheritedWidget 组件主要实现了两个功能:
依赖 InheritedWidget 的子组件如何获取 InheritedWidget 组件的共享数据?首先查看获取共享数据的方法:
context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>();
这段代码获取 MyInheritedWidget 实例,dependOnInheritedWidgetOfExactType 方法是 BuildContext 的方法,Element 实现了此方法:
@override T dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object aspect}) { assert(_debugCheckStateIsActiveForAncestorLookup()); final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T]; if (ancestor != null) { assert(ancestor is InheritedElement); return dependOnInheritedElement(ancestor, aspect: aspect) as T; } _hadUnsatisfiedDependencies = true; return null; }
从上面的源代码能够看出,首先到 _inheritedWidgets 中查找指定的 InheritedElement,_inheritedWidgets 这个 Map 是哪里来的?何时被初始化的?看下 _inheritedWidgets 属性的定义:
Map<Type, InheritedElement> _inheritedWidgets;
查找其引用和赋值的源代码:
@override void _updateInheritance() { assert(_active); final Map<Type, InheritedElement> incomingWidgets = _parent?._inheritedWidgets; if (incomingWidgets != null) _inheritedWidgets = HashMap<Type, InheritedElement>.from(incomingWidgets); else _inheritedWidgets = HashMap<Type, InheritedElement>(); _inheritedWidgets[widget.runtimeType] = this; }
上面的代码在 InheritedElement 中,但此方法在 Element 中也实现了:
void _updateInheritance() { assert(_active); _inheritedWidgets = _parent?._inheritedWidgets; }
上面的代码说明了非 InheritedElement 的 Element 中 _inheritedWidgets 等于父组件的 _inheritedWidgets,而 InheritedElement 会将自身添加到 _inheritedWidgets 中,系统经过此方式将组件和 InheritedWidgets 的依赖关系层层向下传递,每个 Element 中都含有 _inheritedWidgets 集合,此集合中包含了此组件的父组件且是InheritedWidgets 组件的引用关系。
那么是何时执行的 _updateInheritance 方法的呢?经过查找其引用,发如今 mount 和 activate 中调用了 _updateInheritance 方法。关于 mount 和 activate 阶段能够查看 Stateful 组件的生命周期 文章。
下面查看 dependOnInheritedElement 方法,在查找到依赖的 InheritedElement 后,执行 dependOnInheritedElement 方法,源代码以下:
@override InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) { assert(ancestor != null); _dependencies ??= HashSet<InheritedElement>(); _dependencies.add(ancestor); ancestor.updateDependencies(this, aspect); return ancestor.widget; }
updateDependencies 方法源代码以下:
@protected void updateDependencies(Element dependent, Object aspect) { setDependencies(dependent, null); } @protected void setDependencies(Element dependent, Object value) { _dependents[dependent] = value; }
上面的代码就是向 _dependents 中添加注册, InheritedWidget 组件更新时能够更具此列表通知子组件。
再来看下的代码:
@Deprecated( 'Use getElementForInheritedWidgetOfExactType instead. ' 'This feature was deprecated after v1.12.1.' ) @override InheritedElement ancestorInheritedElementForWidgetOfExactType(Type targetType) { assert(_debugCheckStateIsActiveForAncestorLookup()); final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType]; return ancestor; } @override InheritedElement getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() { assert(_debugCheckStateIsActiveForAncestorLookup()); final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T]; return ancestor; }
在 v1.12.1 版本之前使用 ancestorInheritedElementForWidgetOfExactType 方法,如今使用 getElementForInheritedWidgetOfExactType 方法,此方法和 dependOnInheritedWidgetOfExactType 方法的惟一却不就是 getElementForInheritedWidgetOfExactType 方法没有注册,也就是使用 getElementForInheritedWidgetOfExactType 方法获取共享数据的子组件,不会在 InheritedWidget 组件重建时调用 didChangeDependencies 方法。
下面看看为何 InheritedWidget 组件数据方式变化,重建时会调用其 didChangeDependencies 方法?
当组件发生变化时会调用 update方法:
@override void update(ProxyWidget newWidget) { final ProxyWidget oldWidget = widget; assert(widget != null); assert(widget != newWidget); super.update(newWidget); assert(widget == newWidget); updated(oldWidget); _dirty = true; rebuild(); }
InheritedElement 重写了 updated 方法:
@override void updated(InheritedWidget oldWidget) { if (widget.updateShouldNotify(oldWidget)) super.updated(oldWidget); }
当 updateShouldNotify 返回 true时,执行更新操做。而其父类的 updated 方法以下:
@protected void updated(covariant ProxyWidget oldWidget) { notifyClients(oldWidget); }
notifyClients 方法源代码:
@override void notifyClients(InheritedWidget oldWidget) { assert(_debugCheckOwnerBuildTargetExists('notifyClients')); for (final Element dependent in _dependents.keys) { assert(() { // check that it really is our descendant Element ancestor = dependent._parent; while (ancestor != this && ancestor != null) ancestor = ancestor._parent; return ancestor == this; }()); // check that it really depends on us assert(dependent._dependencies.contains(this)); notifyDependent(oldWidget, dependent); } }
遍历 _dependents,上面已经介绍,_dependents 是依赖它的子组件集合,遍历调用 notifyDependent 方法:
@protected void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) { dependent.didChangeDependencies(); }
这里调用了 didChangeDependencies 方法,这也是 InheritedWidget 组件发生变化,重建时执行生命周期 didChangeDependencies。
上面的代码都是在 InheritedElement 中的,在看下 InheritedWidget 的源代码:
abstract class InheritedWidget extends ProxyWidget { const InheritedWidget({ Key key, Widget child }) : super(key: key, child: child); @override InheritedElement createElement() => InheritedElement(this); @protected bool updateShouldNotify(covariant InheritedWidget oldWidget); }
这个类很是简单,建立了一个 InheritedElement,定义了一个 updateShouldNotify 方法(上面已经详细介绍此方法的做用),子类须要重写。
经过上面的源码解析,子组件获取共享数据时,实际是直接在 _inheritedWidgets 集合中匹配的,经过断点也能够查看其中的内容:
经过上面的分析,InheritedWidget 组件流程以下:
说明:
那么为何是在当前组件的中保存这样一个 Map 集合,而不是依次向上查找呢(我最开始的想法)?
下面是我我的的一点见解,若是你有不一样的见解,欢迎一块儿讨论:
当前组件的中保存这样一个 Map 集合,获取共享数据时直接定位依赖的 InheritedWidget,复杂度 O(1) 。
而依次向上查找的复杂度是 O(n),树的结构越深,消耗时间越长,复杂度线性增加。
老孟Flutter博客(330个控件用法+实战入门系列文章):http://laomengit.com
添加微信或者公众号领取 《330个控件大全》和 《Flutter 实战》PDF。
欢迎加入Flutter交流群(微信:laomengit)、关注公众号【老孟Flutter】:
![]() |
![]() |