各位岖是,又到了我們原理篇的時(shí)間了帮毁,基于最近幾篇flutter原理篇的文章,這一期應(yīng)該是寫paint重繪篇的內(nèi)容的豺撑,但是最近在調(diào)試程序的時(shí)候遇到一個(gè)小的問題就是關(guān)于didChangeDependencies 方法什么時(shí)候被調(diào)用的有點(diǎn)搞不清楚聪轿,本著搞不清楚就上網(wǎng)查詢的習(xí)慣灯抛,結(jié)果搜了好多篇文章發(fā)現(xiàn)要么就是內(nèi)容非常簡單說了和沒說一樣夹抗,要么就是寫了一個(gè)Demo得出了一個(gè)沒有細(xì)節(jié)經(jīng)不住拷問的大概的結(jié)論(你也不能說他錯(cuò)漠烧,總覺得還是差了點(diǎn)什么),更有甚者就是千篇一律的拷貝內(nèi)容文章(??我想你應(yīng)該懂我的意思)摆舟,本著考究探索的精神發(fā)現(xiàn)了這些文章以后不驚要大呼一聲:
”恨诱!這樣下去是不對的〔蘧椋“
自己必須要搞清楚寫出來才算對得起大家剂碴,也對得起自己在flutter上面的探索忆矛,于是就有了今天這篇文章,好了廢話不多說了漫拭,讓我們進(jìn)入正題來討論下 《didChangeDependencies方法到底是什么時(shí)候被調(diào)用的》
其實(shí)在普通我們寫簡單例子的時(shí)候我可以一句話說清楚didChangeDependencies方法在什么時(shí)候被調(diào)用审胚,那就是在它的 State 對象對應(yīng)的 Element 被 createElement() 的時(shí)候并且被Mount的時(shí)候就會被調(diào)用菲盾,簡單就是說當(dāng)這個(gè)對應(yīng)的 Element 被重新創(chuàng)建的時(shí)候就會被調(diào)用
你看到這里就會說不對啊,你這個(gè)結(jié)論和我寫的Demo對不上啊临谱,和網(wǎng)上一些結(jié)論也大相徑庭啊,你先別著急先往下看抄课,看看到底是怎么一回事
我先上一個(gè)Demo跟磨,這個(gè)Demo也就是網(wǎng)上面?zhèn)鞯谋容^廣的,我們就以這個(gè)來舉例子說明網(wǎng)上的結(jié)論:“父級結(jié)構(gòu)中的層級發(fā)生變化時(shí)didChangeDependencies被調(diào)用“ 這個(gè)結(jié)論為什么是不完整
import 'package:flutter/material.dart';
class TestDidChangeDependencies2 extends StatefulWidget {
@override
State<StatefulWidget> createState() => TestDidChangeDependencies2State();
}
class TestDidChangeDependencies2State extends State<TestDidChangeDependencies2> {
bool bDependenciesShouldChange = true;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: bDependenciesShouldChange ?
Container(
height: 500,
alignment: Alignment.centerLeft,
child: C(child: B()),
)
: Container(
height: 500,
alignment: Alignment.centerLeft,
child: C(child: SizedBox(width: 20, height: 50, child: B())),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
)
);
}
void _incrementCounter(){
setState(() {
bDependenciesShouldChange = !bDependenciesShouldChange;
});
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
debugPrint("AdidChangeDependencies");
}
}
class B extends StatefulWidget {
@override
State<StatefulWidget> createState() => BState();
}
class BState extends State<B> {
@override
Widget build(BuildContext context) {
return Center(child: Text("Test"));
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
debugPrint("BdidChangeDependencies");
}
}
class C extends StatefulWidget {
final Widget child;
C({Key? key, required this.child}) : super(key: key);
@override
State<StatefulWidget> createState() => CState();
}
class CState extends State<C> {
@override
Widget build(BuildContext context) {
return widget.child;
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
debugPrint("CdidChangeDependencies");
}
}
class D extends StatelessWidget{
final Widget child_D;
D({required this.child_D});
@override
Widget build(BuildContext context) {
return child_D;
}
}
這個(gè)是網(wǎng)上流傳的Demo,我把它調(diào)試為比較好可運(yùn)行狀態(tài)充尉,大家可以來觀察下輸出,首先剛運(yùn)行的時(shí)候就會打印如下:
I/flutter (20121): AdidChangeDependencies
I/flutter (20121): CdidChangeDependencies
I/flutter (20121): BdidChangeDependencies
看吧這里第一次就會打印這些 Satet 對應(yīng)的 didChangeDependencies 函數(shù)呢,這是因?yàn)樵谝婚_始 Widget 對應(yīng)的 Element 為空的情況下會運(yùn)行如下代碼
Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
if (newWidget == null) {
if (child != null)
deactivateChild(child);
return null;
}
final Element newChild;
if (child != null) {
bool hasSameSuperclass = true;
// When the type of a widget is changed between Stateful and Stateless via
// hot reload, the element tree will end up in a partially invalid state.
// That is, if the widget was a StatefulWidget and is now a StatelessWidget,
// then the element tree currently contains a StatefulElement that is incorrectly
// referencing a StatelessWidget (and likewise with StatelessElement).
//
// To avoid crashing due to type errors, we need to gently guide the invalid
// element out of the tree. To do so, we ensure that the `hasSameSuperclass` condition
// returns false which prevents us from trying to update the existing element
// incorrectly.
//
// For the case where the widget becomes Stateful, we also need to avoid
// accessing `StatelessElement.widget` as the cast on the getter will
// cause a type error to be thrown. Here we avoid that by short-circuiting
// the `Widget.canUpdate` check once `hasSameSuperclass` is false.
assert(() {
final int oldElementClass = Element._debugConcreteSubtype(child);
final int newWidgetClass = Widget._debugConcreteSubtype(newWidget);
hasSameSuperclass = oldElementClass == newWidgetClass;
return true;
}());
if (hasSameSuperclass && child.widget == newWidget) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
newChild = child;
} else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
child.update(newWidget);
assert(child.widget == newWidget);
assert(() {
child.owner!._debugElementWasRebuilt(child);
return true;
}());
newChild = child;
} else {
deactivateChild(child);
assert(child._parent == null);
newChild = inflateWidget(newWidget, newSlot);
}
} else {
newChild = inflateWidget(newWidget, newSlot);
}
//省略以下代碼
}
在運(yùn)行到 updateChild 的時(shí)候 child 會為空(因?yàn)閺臎]運(yùn)行過)鲜锚,所以會運(yùn)行到 newChild = inflateWidget(newWidget, newSlot); 這一句代碼芜繁,如下:
Element inflateWidget(Widget newWidget, Object? newSlot) {
assert(newWidget != null);
final Key? key = newWidget.key;
if (key is GlobalKey) {
final Element? newChild = _retakeInactiveElement(key, newWidget);
if (newChild != null) {
assert(newChild._parent == null);
assert(() {
_debugCheckForCycles(newChild);
return true;
}());
newChild._activateWithParent(this, newSlot);
final Element? updatedChild = updateChild(newChild, newWidget, newSlot);
assert(newChild == updatedChild);
return updatedChild!;
}
}
final Element newChild = newWidget.createElement();
assert(() {
_debugCheckForCycles(newChild);
return true;
}());
newChild.mount(this, newSlot);
assert(newChild._lifecycleState == _ElementLifecycle.active);
return newChild;
}
其中會運(yùn)行到 final Element newChild = newWidget.createElement(); 這一句用對應(yīng)的Widget去創(chuàng)建對應(yīng)的Element蔬捷,隨后調(diào)用 newChild.mount(this, newSlot);
接下來到element.mount周拐,他主要有兩個(gè)運(yùn)行分支流程:
- 一個(gè)是ComponentElement的,
- 一個(gè)是RenderObjectElement的勾给,
我們最常見的就是 ComponentElement.mount 代碼如下:
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
assert(_child == null);
assert(_active);
_firstBuild();
assert(_child != null);
}
我可以給大家先說明一下 mount 這個(gè)函數(shù)其主要目的接著就把新建的子element掛載到了element樹上,這個(gè)主要不是我們今天的內(nèi)容所以不展開講旅择,想看細(xì)節(jié)的請大家關(guān)注我之前寫的文章 <Flutter運(yùn)行原理篇之Build構(gòu)建的過程>
RenderObjectElement.mount
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
_renderObject = widget.createRenderObject(this);
attachRenderObject(newSlot);
_dirty = false;
}
此方法會創(chuàng)建一個(gè)RenderObject,之后就會把此RenderObject添加到RenderObject樹上柱蟀,想更清楚的話請大家關(guān)注我之前寫的博客<Flutter運(yùn)行原理篇之Build構(gòu)建的過程>
ComponentElement.mount 里面會運(yùn)行一個(gè) _firstBuild 方法:
StatefulElement._firstBuild 如下:
@override
void _firstBuild() {
assert(state._debugLifecycleState == _StateLifecycle.created);
try {
_debugSetAllowIgnoredCallsToMarkNeedsBuild(true);
final Object? debugCheckForReturnedFuture = state.initState() as dynamic;
assert(() {
if (debugCheckForReturnedFuture is Future) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('${state.runtimeType}.initState() returned a Future.'),
ErrorDescription('State.initState() must be a void method without an `async` keyword.'),
ErrorHint(
'Rather than awaiting on asynchronous work directly inside of initState, '
'call a separate method to do this work without awaiting it.',
),
]);
}
return true;
}());
} finally {
_debugSetAllowIgnoredCallsToMarkNeedsBuild(false);
}
assert(() {
state._debugLifecycleState = _StateLifecycle.initialized;
return true;
}());
state.didChangeDependencies();
assert(() {
state._debugLifecycleState = _StateLifecycle.ready;
return true;
}());
super._firstBuild();
}
ComponentElement._firstBuild
void _firstBuild() {
rebuild();
}
這里面會調(diào)用 state.didChangeDependencies(); 這個(gè)方法术瓮,這也就是我們重寫的State的didChangeDependencies方法
好了胞四,我們搞清楚了第一次運(yùn)行的時(shí)候 didChangeDependencies 方法的被調(diào)用的過程氓侧,現(xiàn)在我們來點(diǎn)擊下一上面那個(gè)Demo的按鈕,看看他又做了什么呢独郎?
我們點(diǎn)擊了按鈕調(diào)用了 setState 方法,這個(gè)方法會使build方法運(yùn)行導(dǎo)致C的child被改變了顽铸,改為了SizedBox:
body: bDependenciesShouldChange ?
Container(
height: 500,
alignment: Alignment.centerLeft,
child: C(child: B()),
)
: Container(
height: 500,
alignment: Alignment.centerLeft,
child: C(child: SizedBox(width: 20, height: 50, child: B())),
)
熟悉Build運(yùn)行流程的同學(xué)應(yīng)該知道谓松,setState 這個(gè)方法會觸發(fā)到 updateChild 這個(gè)方法,上面有代碼我就不貼了优质,這個(gè)方法里面會進(jìn)行一個(gè)判斷,判斷 hasSameSuperclass 變量是否為false避乏,如果為false則會觸發(fā) inflateWidget 進(jìn)行Element的重構(gòu),也就是我們上面分析的步驟铆帽,我這里給大家貼一個(gè)圖會比較明顯:
大家看看,點(diǎn)擊了按鈕以后導(dǎo)致 hasSameSuperclass 變量判斷為false(意思是他們不是同一個(gè)父親),之后導(dǎo)致inflateWidget(SizeBox,newSlot) , 之后再會導(dǎo)致 SizeBox 的child為null冯键,再會觸發(fā)B進(jìn)行inflateWidget方法重構(gòu)其對應(yīng)的Element
邏輯部分都在 updateChild 函數(shù)里面:
if (child != null) {
bool hasSameSuperclass = true;
//省略部分代碼
}else{
newChild = inflateWidget(newWidget, newSlot); //這里面的newWidget是B
}
所以這樣就會觸發(fā)BState 對應(yīng)的 didChangeDependencies 方法
好了,到這里你應(yīng)該就明白了為什么網(wǎng)上流傳的結(jié)論 父級結(jié)構(gòu)中的層級發(fā)生變化時(shí)didChangeDependencies被調(diào)用 這個(gè)結(jié)論的到底是怎么一回事了庸汗,其實(shí)這個(gè)會觸發(fā)其對應(yīng)的 Element重構(gòu) 導(dǎo)致該方法被調(diào)用
好了惫确,既然我們已經(jīng)搞清楚了調(diào)用的規(guī)則,我們不妨再來看看還會不會有別的情況會觸發(fā)呢蚯舱,我們再來看看 updateChild 方法里面的邏輯判斷:
Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
if (newWidget == null) {
if (child != null)
deactivateChild(child);
return null;
}
final Element newChild;
if (child != null) {
bool hasSameSuperclass = true;
assert(() {
final int oldElementClass = Element._debugConcreteSubtype(child);
final int newWidgetClass = Widget._debugConcreteSubtype(newWidget);
hasSameSuperclass = oldElementClass == newWidgetClass;
return true;
}());
if (hasSameSuperclass && child.widget == newWidget) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
newChild = child;
} else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
child.update(newWidget);
assert(child.widget == newWidget);
assert(() {
child.owner!._debugElementWasRebuilt(child);
return true;
}());
newChild = child;
} else {
deactivateChild(child);
assert(child._parent == null);
newChild = inflateWidget(newWidget, newSlot);
}
} else {
newChild = inflateWidget(newWidget, newSlot);
}
//省略以下代碼
}
我們看到 (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) 這一行說明如果他們的父親沒有變化改化,但是 Widget.canUpdate 的返回發(fā)生了變化的話,那么他應(yīng)該也會觸發(fā)下面的 newChild = inflateWidget(newWidget, newSlot); 方法導(dǎo)致 didChangeDependencies 方法被調(diào)用枉昏,我們看看這個(gè)方法:
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
我們只需要改變他對應(yīng)的key就可以實(shí)現(xiàn)我們的猜想了不是嗎?
所以我列出了下面的例子說明改情況:
import 'package:flutter/material.dart';
class TestDidChangeDependencies3 extends StatefulWidget {
@override
State<StatefulWidget> createState() => TestDidChangeDependencies3State();
}
class TestDidChangeDependencies3State extends State<TestDidChangeDependencies3> {
bool bDependenciesShouldChange = false;
late Key k;
@override
void initState() {
super.initState();
k = Key('hello');
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
height: 500,
alignment: Alignment.centerLeft,
child: C(key:k,child: B()),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
)
);
}
void _incrementCounter(){
setState(() {
k = Key('world');
});
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
debugPrint("AdidChangeDependencies");
}
}
class B extends StatefulWidget {
@override
State<StatefulWidget> createState() => BState();
}
class BState extends State<B> {
@override
Widget build(BuildContext context) {
return Center(child: Text("Test"));
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
debugPrint("BdidChangeDependencies");
}
}
class C extends StatefulWidget {
final Widget child;
C({Key? key, required this.child}) : super(key: key);
@override
State<StatefulWidget> createState() => CState();
}
class CState extends State<C> {
@override
Widget build(BuildContext context) {
return widget.child;
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
debugPrint("CdidChangeDependencies");
}
}
例子很簡單不詳細(xì)講解了溯乒,我們點(diǎn)擊了按鈕導(dǎo)致了C的key發(fā)生了變化導(dǎo)致C重構(gòu)其Element也間接導(dǎo)致了B重構(gòu)了對應(yīng)的Element導(dǎo)致了如下的打印
I/flutter (22761): AdidChangeDependencies
I/flutter (22761): CdidChangeDependencies
I/flutter (22761): BdidChangeDependencies
I/flutter (22761): CdidChangeDependencies
I/flutter (22761): BdidChangeDependencies
好了逻住,這里我們又學(xué)到了在不改變父類結(jié)構(gòu)的情況下冀瓦,如果改變Key也會導(dǎo)致didChangeDependencies被調(diào)用,但是其本質(zhì)還是我一開始總結(jié)的:那就是在它的 State 對象對應(yīng)的 Element 被 createElement() 的時(shí)候并且被Mount的時(shí)候就會被調(diào)用狂巢,簡單就是說當(dāng)這個(gè)對應(yīng)的 Element 被重新創(chuàng)建的時(shí)候就會被調(diào)用
好了看到這里你可能會松了一口氣,總算明白了 didChangeDependencies 被調(diào)用背后的邏輯了腔呜,但是我要告訴你的是還沒有完哦跟束,??這只是最普通的情況之一哦,還有一種更常見的情況那就是使用 InheritedWidget 的時(shí)候也會觸發(fā)調(diào)用真友,這個(gè)是一個(gè)非常常用的控件站绪,我們作為要深入flutter的開發(fā)者來說坠非,不能只看簡單的情況,這個(gè)控件也要必須搞清楚赵辕,那么在該文章的下半結(jié)我們來聊一聊 《InheritedWidget 以及對應(yīng)的運(yùn)行函數(shù)極其背后的邏輯》
我們先來看看概念:InheritedWidget是 Flutter 中非常重要的一個(gè)功能型組件笆怠,它提供了一種在 widget 樹中從上到下共享數(shù)據(jù)的方式,比如我們在應(yīng)用的根 widget 中通過InheritedWidget共享了一個(gè)數(shù)據(jù),那么我們便可以在任意子widget 中來獲取該共享的數(shù)據(jù)混卵!
State對象有一個(gè)didChangeDependencies回調(diào)睦霎,它會在“依賴”發(fā)生變化時(shí)被Flutter 框架調(diào)用副女。而這個(gè)“依賴”指的就是子 widget 是否使用了父 widget 中InheritedWidget的數(shù)據(jù)诀浪!如果使用了,則代表子 widget 有依賴刑然;如果沒有使用則代表沒有依賴。
我們借用網(wǎng)上的Demo先舉一個(gè)例子:
import 'package:flutter/material.dart';
class ShareDataWidget extends InheritedWidget {
ShareDataWidget({
Key? key,
required this.data,
required Widget child,
}) : super(key: key, child: child);
final int data; //需要在子樹中共享的數(shù)據(jù)暇务,保存點(diǎn)擊次數(shù)
//定義一個(gè)便捷方法泼掠,方便子樹中的widget獲取共享數(shù)據(jù)
static ShareDataWidget? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();
}
//該回調(diào)決定當(dāng)data發(fā)生變化時(shí),是否通知子樹中依賴data的Widget
@override
bool updateShouldNotify(ShareDataWidget old) {
return old.data != data;
}
}
class _TestWidget extends StatefulWidget {
@override
__TestWidgetState createState() => __TestWidgetState();
}
class __TestWidgetState extends State<_TestWidget> {
@override
Widget build(BuildContext context) {
//使用InheritedWidget中的共享數(shù)據(jù)
return Text(ShareDataWidget.of(context)!.data.toString());
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
//父或祖先widget中的InheritedWidget改變(updateShouldNotify返回true)時(shí)會被調(diào)用垦细。
//如果build中沒有依賴InheritedWidget择镇,則此回調(diào)不會被調(diào)用。
print("Dependencies change");
}
}
class InheritedWidgetTestRoute extends StatefulWidget {
@override
_InheritedWidgetTestRouteState createState() => _InheritedWidgetTestRouteState();
}
class _InheritedWidgetTestRouteState extends State<InheritedWidgetTestRoute> {
int count = 0;
@override
Widget build(BuildContext context) {
return Center(
child: ShareDataWidget( //使用ShareDataWidget
data: count,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(bottom: 20.0),
child: _TestWidget(),//子widget中依賴ShareDataWidget
),
ElevatedButton(
child: Text("Increment"),
//每點(diǎn)擊一次括改,將count自增腻豌,然后重新build,ShareDataWidget的data將被更新
onPressed: () => setState(() => ++count),
)
],
),
),
);
}
}
我們點(diǎn)擊一次會加1,并且會打印一次 Dependencies change
這種情況不像上面我們分析的改變結(jié)構(gòu)的情況了,他是怎么回事呢饲梭?
我們來看看點(diǎn)擊了按鈕 _TestWidget 是怎么拿到數(shù)據(jù)的乘盖,如下:
Widget build(BuildContext context) {
//使用InheritedWidget中的共享數(shù)據(jù)
return Text(ShareDataWidget.of(context)!.data.toString());
}
//定義一個(gè)便捷方法,方便子樹中的widget獲取共享數(shù)據(jù)
static ShareDataWidget? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();
}
背后邏輯就在于 context.dependOnInheritedWidgetOfExactType<T>() 這句話:
Element.dependOnInheritedWidgetOfExactType
@override
T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object? aspect}) {
assert(_debugCheckStateIsActiveForAncestorLookup());
final InheritedElement? ancestor = _inheritedWidgets == null ? null : _inheritedWidgets![T];
if (ancestor != null) {
return dependOnInheritedElement(ancestor, aspect: aspect) as T;
}
_hadUnsatisfiedDependencies = true;
return null;
}
這里會從 _inheritedWidgets![T] 里面去取出對應(yīng)的Element憔涉,那這個(gè)Element什么時(shí)候被添加進(jìn)這個(gè)Map里面的呢订框,答案在于mount方法的調(diào)用邏輯:
Element.mount
@mustCallSuper
void mount(Element? parent, Object? newSlot) {
//省略部分代碼
_updateInheritance();
}
InheritedElement._updateInheritance
@override
void _updateInheritance() {
assert(_lifecycleState == _ElementLifecycle.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被mount的時(shí)候會把自己添加進(jìn)入這個(gè)Map _inheritedWidgets![widget.runtimeType] = this; ,所以在取的時(shí)候你可以拿到這個(gè)InheritedElement對象
接下來再看看 dependOnInheritedElement 這個(gè)函數(shù)做了什么呢兜叨?
Element.dependOnInheritedElement
@override
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object? aspect }) {
assert(ancestor != null);
_dependencies ??= HashSet<InheritedElement>();
_dependencies!.add(ancestor);
ancestor.updateDependencies(this, aspect);
return ancestor.widget;
}
@protected
void updateDependencies(Element dependent, Object? aspect) {
setDependencies(dependent, null);
}
@protected
void setDependencies(Element dependent, Object? value) {
_dependents[dependent] = value;
}
很簡單不細(xì)說穿扳,就是把_TestWidget對應(yīng)的Element添加進(jìn)了_dependents 這個(gè)Map里面,準(zhǔn)備作為觸發(fā)的時(shí)候使用
那什么時(shí)候觸發(fā)呢国旷,也就是在我們點(diǎn)擊按鈕以后會觸發(fā)setState間接的觸發(fā)InheritedElement的update方法更新的時(shí)候
InheritedElement.updated
@override
void updated(InheritedWidget oldWidget) {
if (widget.updateShouldNotify(oldWidget))
super.updated(oldWidget);
}
ProxyElement.updated
@protected
void updated(covariant ProxyWidget oldWidget) {
notifyClients(oldWidget);
}
InheritedElement.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); //注意這一句
}
}
會取出dependent矛物,也就是我們存的_TestWidget對應(yīng)的Element,然后調(diào)用notifyDependent
@protected
void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
dependent.didChangeDependencies();
}
StatefulElement.didChangeDependencies
@override
void didChangeDependencies() {
super.didChangeDependencies();
_didChangeDependencies = true;
}
這里這一句很重要_didChangeDependencies = true; 這里的意思是把_TestWidget對應(yīng)的Element的這個(gè)_didChangeDependencies屬性變?yōu)閠rue跪但,等待_TestWidget對應(yīng)的Element也就是StatefulElement對應(yīng)的performRebuild方法調(diào)用的時(shí)候觸發(fā)
StatefulElement.performRebuild
@override
void performRebuild() {
if (_didChangeDependencies) {
state.didChangeDependencies();
_didChangeDependencies = false;
}
super.performRebuild();
}
這里很明顯了state.didChangeDependencies();這一句被調(diào)用了履羞,說明我們重載的didChangeDependencies方法就會被調(diào)用了
好了,到這里我們把 didChangeDependencies 方法什么時(shí)候被調(diào)用說得差不多了屡久,但是大家還需要注意一個(gè)點(diǎn)就是 updateShouldNotify 這個(gè)函數(shù)的作用忆首,這個(gè)函數(shù)網(wǎng)上說的是:是否通知子樹中依賴的Widget調(diào)用其對應(yīng)的 didChangeDependencies 方法,返回true則為通知被环,其實(shí)這里還有更深一層的含義在里面糙及,就是這個(gè)返回值直接影響了依賴 InheritedWidget 對應(yīng)對象的build方法是否執(zhí)行
最后再仔細(xì)看看,確實(shí)如果InheritedWidget對應(yīng)的updateShouldNotify方法返回為false的話筛欢,那么super.updated(oldWidget);方法將無法調(diào)用
InheritedElement.updated
@override
void updated(InheritedWidget oldWidget) {
if (widget.updateShouldNotify(oldWidget))
super.updated(oldWidget);
}
InheritedElement繼承至ProxyElement
ProxyElement.updated
@protected
void updated(covariant ProxyWidget oldWidget) {
notifyClients(oldWidget);
}
InheritedElement.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);
}
}
}
這里說得很清楚了浸锨,如果updateShouldNotify為false的話notifyDependent將不會被調(diào)用,那么State對應(yīng)的didChangeDependencies也不會被調(diào)用
這里還沒有完版姑,接著往下看柱搜,notifyDependent會調(diào)用到 didChangeDependencies方法:
Element.didChangeDependencies
void didChangeDependencies() {
assert(_lifecycleState == _ElementLifecycle.active); // otherwise markNeedsBuild is a no-op
assert(_debugCheckOwnerBuildTargetExists('didChangeDependencies'));
markNeedsBuild();
}
最終就不會到這里,也不會調(diào)用到依賴 InheritedWidget 對應(yīng)對象的 markNeedsBuild 方法漠酿,所以他們的build方法無法被調(diào)用
ProxyElement.build
@override
Widget build() => widget.child;
究其原因在于ProxyElement對應(yīng)的build方法冯凹,他這個(gè)方法與其他的build方法不一樣,其他的build方法如下 Widget build() => state.build(this); 都會調(diào)用重寫的build方法炒嘲,他這個(gè)build只是返回widget.child宇姚,并沒有觸發(fā)build方法調(diào)用所以導(dǎo)致了他的build不具備構(gòu)建的能力,依賴 InheritedWidget 對應(yīng)對象要想更新就不能使updateShouldNotify返回false
這里是卡了我一個(gè)比較久的問題夫凸,當(dāng)時(shí)確實(shí)想了半天才發(fā)現(xiàn)這里
好了浑劳,這篇文章說到這里已經(jīng)差不多了,相信能讀玩的小伙伴一定是具有一定耐心的
我也不在往下深究了夭拌,那不然結(jié)束不了了魔熏,總之今天我們講了兩個(gè)大致的內(nèi)容:
- didChangeDependencies方法調(diào)用背后的邏輯
- InheritedWidget的基本使用
希望能給大家?guī)韼椭匝剩矚g我的文章的話歡迎給我點(diǎn)贊或者留言,我看到的話一定第一時(shí)間給你回復(fù)的謝謝大家蒜绽,你的點(diǎn)贊加留言是我持續(xù)更新的動(dòng)力镶骗,謝謝大家···