InheritedWidget 提供了數(shù)據(jù)在 widget 中從上到下傳遞共享的方式密浑;在根 widget 中共享一個數(shù)據(jù),便可以在任意子 widget 中獲取該共享數(shù)據(jù)县袱;對于需要在 widget 樹中共享數(shù)據(jù)的場景十分方便;Flutter SDK 中亦是通過 InheritedWidget 共享應(yīng)用的主題(theme)戳葵、語言環(huán)境(Locale) 信息瘩蚪;
InheritedWidget 和 React 中的 context 功能類似泉懦,和逐級傳遞數(shù)據(jù)相比,它們能實現(xiàn)組件跨級傳遞數(shù)據(jù)疹瘦。InheritedWidget 的在 widget 樹中數(shù)據(jù)傳遞方向是從上到下的崩哩,這和通知 Notification 的傳遞方向正好相反。
State 中的 didChangeDependencies 回調(diào)拱礁,會在父 widget 中 InheritedWidget 的數(shù)據(jù)發(fā)生變化時被 Flutter Framework 調(diào)用琢锋;如果有使用 didChangeDependencies 則表示子 widget 有依賴 InheritedWidget,沒有使用則表示沒有依賴呢灶;因此我們可以在 InheritedWidget 發(fā)生變化時對組件進行更新;當應(yīng)用的主題(theme)钉嘹、語言環(huán)境(Locale) 信息發(fā)生變化時, 子 widget 的 didChangeDependencies 方法將被調(diào)用鸯乃;
被調(diào)用當依賴發(fā)生變化后 Framework 都會調(diào)用 build 方法,因此一般情況下子 widget 很少會重寫 didChangeDependencies跋涣;但是如果需要執(zhí)行一些比較昂貴的操作(如網(wǎng)絡(luò)請求)缨睡,最好的方式就是在此方法中執(zhí)行,以避免每次 build 都觸發(fā)該操作陈辱;
??
通過繼承 InheritedWidget奖年,將當前計數(shù)器點擊次數(shù)保存在 ShareDataWidget的data 屬性中:
class ShareDataWidget extends InheritedWidget {
ShareDataWidget({
@required this.data,
Widget child
}) :super(child: child);
final int data; //需要在子樹中共享的數(shù)據(jù),保存點擊次數(shù)
//定義一個便捷方法沛贪,方便子樹中的widget獲取共享數(shù)據(jù)
static ShareDataWidget of(BuildContext context) {
return context.inheritFromWidgetOfExactType(ShareDataWidget);
}
//該回調(diào)決定當data發(fā)生變化時陋守,是否通知子樹中依賴data的Widget
@override
bool updateShouldNotify(ShareDataWidget old) {
//如果返回true,則子樹中依賴(build函數(shù)中有調(diào)用)本widget
//的子widget的`state.didChangeDependencies`會被調(diào)用
return old.data != data;
}
}
然后我們實現(xiàn)一個子組件_TestWidget利赋,在其build方法中引用ShareDataWidget中的數(shù)據(jù)水评。同時,在其didChangeDependencies() 回調(diào)中打印日志
class _TestWidget extends StatefulWidget {
@override
__TestWidgetState createState() => new __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)時會被調(diào)用媚送。
//如果build中沒有依賴InheritedWidget中燥,則此回調(diào)不會被調(diào)用。
print("Dependencies change");
}
}
最后塘偎,我們創(chuàng)建一個按鈕疗涉,每點擊一次,就將ShareDataWidget的值自增:
class InheritedWidgetTestRoute extends StatefulWidget {
@override
_InheritedWidgetTestRouteState createState() => new _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
),
RaisedButton(
child: Text("Increment"),
//每點擊一次吟秩,將count自增咱扣,然后重新build,ShareDataWidget的data將被更新
onPressed: () => setState(() => ++count),
)
],
),
),
);
}
}
如果_TestWidget的build方法中沒有使用ShareDataWidget的數(shù)據(jù),那么它的didChangeDependencies()將不會被調(diào)用峰尝,因為它并沒有依賴ShareDataWidget
class __TestWidgetState extends State<_TestWidget> {
@override
Widget build(BuildContext context) {
// 使用InheritedWidget中的共享數(shù)據(jù)
// return Text(ShareDataWidget
// .of(context)
// .data
// .toString());
return Text("text");
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
// build方法中沒有依賴InheritedWidget偏窝,此回調(diào)不會被調(diào)用。
print("Dependencies change");
}
}
如果只想在 __TestWidgetState 中引用 ShareDataWidget 數(shù)據(jù),但卻不希望在 ShareDataWidget 發(fā)生變化時調(diào)用 __TestWidgetState的didChangeDependencies() 方法
唯一的改動就是獲取 ShareDataWidget 對象的方式祭往,把 inheritFromWidgetOfExactType() 方法換成了
context.ancestorInheritedElementForWidgetOfExactType(ShareDataWidget).widget
@override
InheritedElement ancestorInheritedElementForWidgetOfExactType(Type targetType) {
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType];
return ancestor;
}
@override
InheritedWidget inheritFromWidgetOfExactType(Type targetType, { Object aspect }) {
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType];
//多出的部分
if (ancestor != null) {
assert(ancestor is InheritedElement);
return inheritFromElement(ancestor, aspect: aspect);
}
_hadUnsatisfiedDependencies = true;
return null;
}
- 注 inheritFromElement 主要是注冊依賴關(guān)系 因此調(diào)用 inheritFromWidgetOfExactType 后伦意,子孫組件便完成了注冊, InheritedWidget 發(fā)生變化后, 就會更新依賴它的子孫組件硼补,同時調(diào)用子孫組件的 didChangeDependencies 和 build 方法驮肉; 而使用 ancestorInheritedElementWidgetOfExactType 由于沒有注冊依賴關(guān)系,因此當 inheritedWidget 發(fā)生變化時就不會更新子孫組件
@override
InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect }) {
//注冊依賴關(guān)系
_dependencies ??= HashSet<InheritedElement>();
_dependencies.add(ancestor);
ancestor.updateDependencies(this, aspect);
return ancestor.widget;
}