InheritedWidget
是Flutter 中非常重要的一個(gè)功能性組件,他的主要作用是用來(lái)提供一種從上到下的一種傳遞數(shù)據(jù)的方式,
在flutter app中的theme 和 local 就是這樣寫的,
didChangeDependencies
這個(gè)方法在前面介紹StatefulWidget 的時(shí)候介紹過(guò)該方法,表示他所依賴的父布局發(fā)現(xiàn)變化時(shí)這個(gè)方法會(huì)被framework 調(diào)用,但是如何判斷父控件是否發(fā)生變化,就是根據(jù) 子布局是否使用并注冊(cè)了父布局的 InheritedWidget,這里為什么說(shuō)是使用并注冊(cè),我們會(huì)在下面介紹一下,
先寫一個(gè)widget , 他含有一個(gè)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>();
}
}
想要使用 TsmShareInteritedWidget 共享出來(lái)的count 這個(gè)數(shù)據(jù)的使用方法就是
TsmShareInteritedWidget.of(context).data;
寫一個(gè)TsmInheritedWidget 使用共享數(shù)據(jù)
class TsmInheritedWidget extends StatefulWidget{
@override
State<StatefulWidget> createState() =>_TsmInheritedWidgetState();
}
class _TsmInheritedWidgetState extends State<TsmInheritedWidget>{
@override
Widget build(BuildContext context) {
printString('build');
return Text(TsmShareInteritedWidget.ofDate(context).data.toString());
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
printString('didChangeDependencies');
}
}
按照上面我的對(duì)InheritedWidget 這個(gè)控件介紹,他的作用是從上至下傳遞,也就是 InheritedWidget所共享的這個(gè)count 只能由他的子widget使用,最后再來(lái)一個(gè)使用他的例子
class _TsmInheritedSendPageState extends State<TsmInheritedSendPage> {
int count = 0;
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
title: Text('Inherited 學(xué)習(xí)'),
),
body: Container(
child: Center(
child: TsmShareInteritedWidget(
data: count,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TsmInheritedWidget(),
SizedBox(
height: 15,
),
RaisedButton(
child: Text('計(jì)數(shù)器'),
onPressed: () {
setState(() {
count++;
});
},
)
],
),
)),
),
);
}
點(diǎn)擊按鈕的時(shí)候發(fā)現(xiàn)didChangeDependencies 和build 方法同時(shí)打印
那么為什么非要多此一舉監(jiān)聽didChangeDependencies,值機(jī)監(jiān)聽build方法不就可以了,其實(shí)如果數(shù)據(jù)發(fā)生變化是觸發(fā)請(qǐng)求接口的條件,如果該條件放在build中,則請(qǐng)求接口會(huì)發(fā)發(fā)生的很頻繁,而didChangeDependencies 則是可控制的變成,更為合理,(即didChangeDependencies 調(diào)用 build必定調(diào)用,但是build調(diào)用 didChangeDependencies 不一定被調(diào)用)
那么這里如果我只想引用數(shù)據(jù)不想要didChangeDependencies 回調(diào)被調(diào)用呢,
按照上面的說(shuō)法是,如果想要didChangeDependencies 這個(gè)回調(diào)被調(diào)用必須是使用數(shù)據(jù)并注冊(cè),
也就是說(shuō)我們不注冊(cè)就可以了,
這里看了一下源碼
@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;
}
源碼的邏輯就是先找到InheritedElement ,然后再做數(shù)據(jù)變化依賴,dependOnInheritedElement
通過(guò)搜索final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T]; 這句代碼找到另一個(gè)只是獲取數(shù)據(jù)widgit的方法,他只有獲取數(shù)據(jù)的方法,并沒(méi)有注冊(cè)
@override
InheritedElement getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() {
assert(_debugCheckStateIsActiveForAncestorLookup());
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
return ancestor;
}
下面我們?cè)赥smShareInteritedWidget 這添加一個(gè)ofData方法,修改TsmInheritedWidget 中的 TsmShareInteritedWidget.of 改成TsmShareInteritedWidget.ofData ,
static TsmShareInteritedWidget ofDate(BuildContext context) {
return context
.getElementForInheritedWidgetOfExactType<TsmShareInteritedWidget>()
.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;
}
/// 源碼中方法
/// 獲取數(shù)據(jù)同時(shí)綁定
///
/// @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;
/// }
///
///
static TsmShareInteritedWidget of(BuildContext context) {
return context
.dependOnInheritedWidgetOfExactType<TsmShareInteritedWidget>();
}
/// 只獲取數(shù)據(jù)
///
///
///
///源碼中的方法
/// @override
/// InheritedElement getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() {
/// assert(_debugCheckStateIsActiveForAncestorLookup());
/// final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
/// return ancestor;
/// }
static TsmShareInteritedWidget ofData(BuildContext context) {
return context
.getElementForInheritedWidgetOfExactType<TsmShareInteritedWidget>()
.widget;
}
}
/// 總結(jié),通過(guò)查看源碼發(fā)現(xiàn),dependOnInheritedWidgetOfExactType 依賴關(guān)系真正建立的時(shí)機(jī)是 dependOnInheritedElement 這個(gè)方法,除了這個(gè),其實(shí)這兩個(gè)方法都是一樣的
///
/// 這就實(shí)現(xiàn)了使用 dependOnInheritedWidgetOfExactType 該方法時(shí),不僅可以使用數(shù)據(jù),同時(shí)在數(shù)據(jù)發(fā)生變更的同時(shí),didChangeDependencies 方法還是調(diào)用
/// 而 getElementForInheritedWidgetOfExactType 這個(gè)方法只能使用數(shù)據(jù),
///
/// 其實(shí)在使用過(guò)程中還會(huì)發(fā)生 如果 didChangeDependencies 方法發(fā)生變化,其實(shí)build 方法也會(huì)被調(diào)用,費(fèi)什么非要多此一舉呢,其實(shí)如果數(shù)據(jù)發(fā)生變化是觸發(fā)請(qǐng)求接口的條件
/// 如果該條件放在build中,則請(qǐng)求接口會(huì)發(fā)發(fā)生的很頻繁,而didChangeDependencies 則是可控制的變成,更為合理
///
///
這里reload代碼發(fā)現(xiàn) didChangeDependencies 還是會(huì)打印日志,重新打包后就不打印,貌似就是在重新reload的時(shí)候,已經(jīng)注冊(cè)的方法,沒(méi)辦法取消注冊(cè)導(dǎo)致的
其實(shí)現(xiàn)在這種用法還是有弊端的,那就是我們只想更新一個(gè)TsmInheritedWidget 中的數(shù)據(jù),但是調(diào)用setState方法時(shí),整個(gè)頁(yè)面都刷新了,這樣做不是很好,這個(gè)問(wèn)題我會(huì)在下一篇Provider中解決
我學(xué)習(xí)flutter的整個(gè)過(guò)程都記錄在里面了
http://www.itdecent.cn/c/36554cb4c804
最后附上demo 地址