## InheritedWidget
InheritedWidget是Flutter中非常重要的一個功能型Widget,它可以高效的將數據在Widget樹中向下傳遞、共享,這在一些需要在Widget樹中共享數據的場景中非常方便,如Flutter中,正是通過InheritedWidget來共享應用主題(Theme)和Locale(當前語言環境)信息的。
> InheritedWidget和React中的context功能類似,和逐級傳遞數據相比,它們能實現組件跨級傳遞數據。InheritedWidget的在Widget樹中數據傳遞方向是從上到下的,這和Notification的傳遞方向正好相反。
### didChangeDependencies
在介紹StatefulWidget時,我們提到State對象有一個回調`didChangeDependencies`,它會在“依賴”發生變化時被Flutter Framework調用。而這個“依賴”指的就是是否使用了父widget中InheritedWidget的數據,如果使用了,則代表有依賴,如果沒有使用則代表沒有依賴。這種機制可以使子組件在所依賴的主題、locale等發生變化時有機會來做一些事情。
我們看一下之前“計數器”示例應用程序的InheritedWidget版本,需要說明的是,本例主要是為了演示InheritedWidget的功能特性,并不是計數器的推薦實現方式。
首先,我們通過繼承InheritedWidget,將當前計數器點擊次數保存在ShareDataWidget的`data`屬性中:
```
class ShareDataWidget extends InheritedWidget {
ShareDataWidget({
@required this.data,
Widget child
}) :super(child: child);
int data; //需要在子樹中共享的數據,保存點擊次數
//定義一個便捷方法,方便子樹中的widget獲取共享數據
static ShareDataWidget of(BuildContext context) {
return context.inheritFromWidgetOfExactType(ShareDataWidget);
}
//該回調決定當data發生變化時,是否通知子樹中依賴data的Widget
@override
bool updateShouldNotify(ShareDataWidget old) {
//如果返回false,則子樹中依賴(build函數中有調用)本widget
//的子widget的`state.didChangeDependencies`會被調用
return old.data != data;
}
}
```
然后我們實現一個子widget \_TestWidget,在其build方法中引用ShareDataWidget中的數據;同時,在其`didChangeDependencies()` 回調中打印日志:
```
class _TestWidget extends StatefulWidget {
@override
__TestWidgetState createState() => new __TestWidgetState();
}
class __TestWidgetState extends State<_TestWidget> {
@override
Widget build(BuildContext context) {
//使用InheritedWidget中的共享數據
return Text(ShareDataWidget
.of(context)
.data
.toString());
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
//父或祖先widget中的InheritedWidget改變(updateShouldNotify返回true)時會被調用。
//如果build中沒有依賴InheritedWidget,則此回調不會被調用。
print("Dependencies change");
}
}
```
最后,我們創建一個按鈕,每點擊一次,就將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),
)
],
),
),
);
}
}
```
運行后界面如下:

每點擊一次按鈕,計數器就會自增,控制臺就會打印一句日志:
```
I/flutter ( 8513): Dependencies change
```
可見依賴發生變化后,其`didChangeDependencies()`會被調用。但是讀者要注意,如果\_TestWidget的build方法中沒有使用ShareDataWidget的數據,那么它的`didChangeDependencies()`將不會被調用,因為它并沒有依賴ShareDataWidget。
> 思考題:Flutter framework是怎么知道子widget有沒有依賴InheritedWidget的?
#### 應該在didChangeDependencies()中做什么?
一般來說,子widget很少會重寫此方法,因為在依賴改變后framework也都會調用`build()`方法。但是,如果你需要在依賴改變后執行一些昂貴的操作,比如網絡請求,這時最好的方式就是在此方法中執行,這樣可以避免每次`build()`都執行這些昂貴操作。
- 緣起
- 起步
- 移動開發技術簡介
- Flutter簡介
- 搭建Flutter開發環境
- 常見配置問題
- Dart語言簡介
- 第一個Flutter應用
- 計數器示例
- 路由管理
- 包管理
- 資源管理
- 調試Flutter APP
- Dart線程模型及異常捕獲
- 基礎Widgets
- Widget簡介
- 文本、字體樣式
- 按鈕
- 圖片和Icon
- 單選框和復選框
- 輸入框和表單
- 布局類Widgets
- 布局類Widgets簡介
- 線性布局Row、Column
- 彈性布局Flex
- 流式布局Wrap、Flow
- 層疊布局Stack、Positioned
- 容器類Widgets
- Padding
- 布局限制類容器ConstrainedBox、SizeBox
- 裝飾容器DecoratedBox
- 變換Transform
- Container容器
- Scaffold、TabBar、底部導航
- 可滾動Widgets
- 可滾動Widgets簡介
- SingleChildScrollView
- ListView
- GridView
- CustomScrollView
- 滾動監聽及控制ScrollController
- 功能型Widgets
- 導航返回攔截-WillPopScope
- 數據共享-InheritedWidget
- 主題-Theme
- 事件處理與通知
- 原始指針事件處理
- 手勢識別
- 全局事件總線
- 通知Notification
- 動畫
- Flutter動畫簡介
- 動畫結構
- 自定義路由過渡動畫
- Hero動畫
- 交錯動畫
- 自定義Widget
- 自定義Widget方法簡介
- 通過組合現有Widget實現
- 實例:TurnBox
- CustomPaint與Canvas
- 實例:圓形漸變進度條(自繪)
- 文件操作與網絡請求
- 文件操作
- Http請求-HttpClient
- Http請求-Dio package
- 實例:Http分塊下載
- WebSocket
- 使用Socket API
- Json轉Model
- 包與插件
- 開發package
- 插件開發:平臺通道簡介
- 插件開發:實現Android端API
- 插件開發:實現IOS端API
- 系統能力調用
- 國際化
- 讓App支持多語言
- 實現Localizations
- 使用Intl包
- Flutter核心原理
- Flutter UI系統
- Element和BuildContext
- RenderObject與RenderBox
- Flutter從啟動到顯示