InheritedWidget
是Flutter 中非常重要的一个功能性组件,他的主要作用是用来提供一种从上到下的一种传递数据的方式,
在flutter app中的theme 和 local 就是这样写的,
didChangeDependencies
这个方法在前面介绍StatefulWidget 的时候介绍过该方法,表示他所依赖的父布局发现变化时这个方法会被framework 调用,但是如何判断父控件是否发生变化,就是根据 子布局是否使用并注册了父布局的 InheritedWidget,这里为什么说是使用并注册,我们会在下面介绍一下,
先写一个widget , 他含有一个count 属性,
class TsmShareInteritedWidget extends InheritedWidget {
TsmShareInteritedWidget({@required this.data, Widget child}) : super(child: child);
int data;
@override
bool updateShouldNotify(TsmShareInteritedWidget oldWidget) {
return oldWidget.data != data;
}
static TsmShareInteritedWidget of(BuildContext context) {
return context
.dependOnInheritedWidgetOfExactType();
}
}
想要使用 TsmShareInteritedWidget 共享出来的count 这个数据的使用方法就是
TsmShareInteritedWidget.of(context).data;
写一个TsmInheritedWidget 使用共享数据
class TsmInheritedWidget extends StatefulWidget{
@override
State createState() =>_TsmInheritedWidgetState();
}
class _TsmInheritedWidgetState extends State{
@override
Widget build(BuildContext context) {
printString('build');
return Text(TsmShareInteritedWidget.ofDate(context).data.toString());
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
printString('didChangeDependencies');
}
}
按照上面我的对InheritedWidget 这个控件介绍,他的作用是从上至下传递,也就是 InheritedWidget所共享的这个count 只能由他的子widget使用,最后再来一个使用他的例子
class _TsmInheritedSendPageState extends State {
int count = 0;
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
title: Text('Inherited 学习'),
),
body: Container(
child: Center(
child: TsmShareInteritedWidget(
data: count,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TsmInheritedWidget(),
SizedBox(
height: 15,
),
RaisedButton(
child: Text('计数器'),
onPressed: () {
setState(() {
count++;
});
},
)
],
),
)),
),
);
}
点击按钮的时候发现didChangeDependencies 和build 方法同时打印
那么为什么非要多此一举监听didChangeDependencies,值机监听build方法不就可以了,其实如果数据发生变化是触发请求接口的条件,如果该条件放在build中,则请求接口会发发生的很频繁,而didChangeDependencies 则是可控制的变成,更为合理,(即didChangeDependencies 调用 build必定调用,但是build调用 didChangeDependencies 不一定被调用)
那么这里如果我只想引用数据不想要didChangeDependencies 回调被调用呢,
按照上面的说法是,如果想要didChangeDependencies 这个回调被调用必须是使用数据并注册,
也就是说我们不注册就可以了,
这里看了一下源码
@override
T dependOnInheritedWidgetOfExactType({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;
}
源码的逻辑就是先找到InheritedElement ,然后再做数据变化依赖,dependOnInheritedElement
通过搜索final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T]; 这句代码找到另一个只是获取数据widgit的方法,他只有获取数据的方法,并没有注册
@override
InheritedElement getElementForInheritedWidgetOfExactType() {
assert(_debugCheckStateIsActiveForAncestorLookup());
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
return ancestor;
}
下面我们在TsmShareInteritedWidget 这添加一个ofData方法,修改TsmInheritedWidget 中的 TsmShareInteritedWidget.of 改成TsmShareInteritedWidget.ofData ,
static TsmShareInteritedWidget ofDate(BuildContext context) {
return context
.getElementForInheritedWidgetOfExactType()
.widget;
}
修改后的TsmShareInteritedWidget 变化为
class TsmShareInteritedWidget extends InheritedWidget {
TsmShareInteritedWidget({@required this.data, Widget child}) : super(child: child);
int data;
@override
bool updateShouldNotify(TsmShareInteritedWidget oldWidget) {
return oldWidget.data != data;
}
/// 源码中方法
/// 获取数据同时绑定
///
/// @override
/// T dependOnInheritedWidgetOfExactType({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;
/// }
///
///
static TsmShareInteritedWidget of(BuildContext context) {
return context
.dependOnInheritedWidgetOfExactType();
}
/// 只获取数据
///
///
///
///源码中的方法
/// @override
/// InheritedElement getElementForInheritedWidgetOfExactType() {
/// assert(_debugCheckStateIsActiveForAncestorLookup());
/// final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
/// return ancestor;
/// }
static TsmShareInteritedWidget ofData(BuildContext context) {
return context
.getElementForInheritedWidgetOfExactType()
.widget;
}
}
/// 总结,通过查看源码发现,dependOnInheritedWidgetOfExactType 依赖关系真正建立的时机是 dependOnInheritedElement 这个方法,除了这个,其实这两个方法都是一样的
///
/// 这就实现了使用 dependOnInheritedWidgetOfExactType 该方法时,不仅可以使用数据,同时在数据发生变更的同时,didChangeDependencies 方法还是调用
/// 而 getElementForInheritedWidgetOfExactType 这个方法只能使用数据,
///
/// 其实在使用过程中还会发生 如果 didChangeDependencies 方法发生变化,其实build 方法也会被调用,费什么非要多此一举呢,其实如果数据发生变化是触发请求接口的条件
/// 如果该条件放在build中,则请求接口会发发生的很频繁,而didChangeDependencies 则是可控制的变成,更为合理
///
///
这里reload代码发现 didChangeDependencies 还是会打印日志,重新打包后就不打印,貌似就是在重新reload的时候,已经注册的方法,没办法取消注册导致的
其实现在这种用法还是有弊端的,那就是我们只想更新一个TsmInheritedWidget 中的数据,但是调用setState方法时,整个页面都刷新了,这样做不是很好,这个问题我会在下一篇Provider中解决
我学习flutter的整个过程都记录在里面了
https://www.jianshu.com/c/36554cb4c804
最后附上demo 地址
https://github.com/tsm19911014/tsm_flutter