InheritedWidget 不繼承自StatefulWidget僻弹,而是 InheritedWidget -> ProxyWidget -> Widget 這樣的繼承關(guān)系腐芍。簡單來說拓颓,InheritedWidget 的作用是向它的子 Widget 有效地傳播和分享數(shù)據(jù)竞穷,當(dāng) InheritedWidget 作為一個(gè)Parent Widget時(shí)儡嘶,它下面的Widget tree的所有Widget都可以去和 InheritedWidget 發(fā)生數(shù)據(jù)傳遞和交互侧啼。當(dāng)數(shù)據(jù)發(fā)生改變時(shí),一部分控件需要 rebuild泪蔫,另外的控件不需要 rebuild 的時(shí)候棒旗,可以使用 InheritedWidget,具體的介紹結(jié)合代碼來看撩荣。
下面是 InheritedWidget 的一個(gè)實(shí)例:
class MyInheritedWidget extends InheritedWidget {
MyInheritedWidget({
Key key,
@required Widget child,
this.data,
}): super(key: key, child: child);
final data;
static MyInheritedWidget of(BuildContext context) {
return context.inheritFromWidgetOfExactType(MyInheritedWidget);
}
@override
bool updateShouldNotify(MyInheritedWidget oldWidget) => data != oldWidget.data;
}
updateShouldNotify 是必須重寫的一個(gè)方法铣揉,這個(gè)方法來決定什么時(shí)候需要去rebuild 控件
of 是一個(gè)類方法,返回了它自己餐曹,inheritFromWidgetOfExactType這個(gè)方法逛拱,我理解的是從父Widget中根據(jù)類型去找響應(yīng)的Widget,但這個(gè)方法還有其他的作用台猴,會在后面詳細(xì)介紹朽合。
初始化方法中俱两,會傳一個(gè)Child,并傳遞給super曹步,也就是它的子Widget宪彩。
使用這個(gè)類:
class MyParentWidget... {
...
@override
Widget build(BuildContext context){
return new MyInheritedWidget(
data: counter,
child: new Row(
children: <Widget>[
...
],
),
);
}
}
子Widget怎么獲取數(shù)據(jù)?
class MyChildWidget... {
...
@override
Widget build(BuildContext context){
final MyInheritedWidget inheritedWidget = MyInheritedWidget.of(context);
///
/// From this moment, the widget can use the data, exposed by the MyInheritedWidget
/// by calling: inheritedWidget.data
///
return new Container(
color: inheritedWidget.data.color,
);
}
}
應(yīng)用場景:
- Widget A是一個(gè)按鈕讲婚,點(diǎn)擊的時(shí)候購物車數(shù)量加1.
- Widget B是一個(gè)顯示購物車數(shù)量的文本.
- Widget C是顯示一個(gè)固定的文本
- 在點(diǎn)擊Widget A的時(shí)候尿孔,Widget B刷新數(shù)據(jù),而不需要rebuild Widget C
代碼如下:
class Item {
String reference;
Item(this.reference);
}
class _MyInherited extends InheritedWidget {
_MyInherited({
Key key,
@required Widget child,
@required this.data,
}) : super(key: key, child: child);
final MyInheritedWidgetState data;
@override
bool updateShouldNotify(_MyInherited oldWidget) {
return true;
}
}
class MyInheritedWidget extends StatefulWidget {
MyInheritedWidget({
Key key,
this.child,
}): super(key: key);
final Widget child;
@override
MyInheritedWidgetState createState() => new MyInheritedWidgetState();
static MyInheritedWidgetState of(BuildContext context){
return (context.inheritFromWidgetOfExactType(_MyInherited) as _MyInherited).data;
}
}
class MyInheritedWidgetState extends State<MyInheritedWidget>{
/// List of Items
List<Item> _items = <Item>[];
/// Getter (number of items)
int get itemsCount => _items.length;
/// Helper method to add an Item
void addItem(String reference){
setState((){
_items.add(new Item(reference));
});
}
@override
Widget build(BuildContext context){
return new _MyInherited(
data: this,
child: widget.child,
);
}
}
class MyTree extends StatefulWidget {
@override
_MyTreeState createState() => new _MyTreeState();
}
class _MyTreeState extends State<MyTree> {
@override
Widget build(BuildContext context) {
return new MyInheritedWidget(
child: new Scaffold(
appBar: new AppBar(
title: new Text('Title'),
),
body: new Column(
children: <Widget>[
new WidgetA(),
new Container(
child: new Row(
children: <Widget>[
new Icon(Icons.shopping_cart),
new WidgetB(),
new WidgetC(),
],
),
),
],
),
),
);
}
}
class WidgetA extends StatelessWidget {
@override
Widget build(BuildContext context) {
final MyInheritedWidgetState state = MyInheritedWidget.of(context);
return new Container(
child: new RaisedButton(
child: new Text('Add Item'),
onPressed: () {
state.addItem('new item');
},
),
);
}
}
class WidgetB extends StatelessWidget {
@override
Widget build(BuildContext context) {
final MyInheritedWidgetState state = MyInheritedWidget.of(context);
return new Text('${state.itemsCount}');
}
}
class WidgetC extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Text('I am Widget C');
}
}
解釋說明:
- 當(dāng)點(diǎn)擊Widget A的時(shí)候筹麸,_MyInherited 會被重新創(chuàng)建一個(gè)
- MyInheritedWidget 相當(dāng)于是一個(gè)購物車活合,它的State通過static MyInheritedWidgetState of(BuildContext context)可以獲取到.
- MyInheritedWidgetState 提供了公開的方法,getter (itemsCount)和addItem竹捉,這兩個(gè)方法可以被子Widget調(diào)用.
- 每次我們添加一項(xiàng)到購物車時(shí)芜辕,MyInheritedWidgetState會rebuild
InheritedWidget是怎么去通知子Widget刷新數(shù)據(jù)的呢尚骄?
當(dāng)一個(gè)子Widget調(diào)用MyInheritedWidget.of(context)時(shí)块差,會走下面的代碼,把它自己的context傳進(jìn)來
static MyInheritedWidgetState of(BuildContext context) {
return (context.inheritFromWidgetOfExactType(_MyInherited) as _MyInherited).data;
}
這個(gè)方法其實(shí)是做了兩件事:
- 獲取MyInheritedWidgetState里面的數(shù)據(jù)
- 會將調(diào)用該方法的Widget加入訂閱者行列倔丈,當(dāng)數(shù)據(jù)發(fā)生改變的時(shí)候憨闰,會通知這些Widget刷新數(shù)據(jù),也就是rebuild.
工作的原理總結(jié)起來如下:
因?yàn)閃idget A和Widget B訂閱了InheritedWidget,所以點(diǎn)擊Widget A的時(shí)候會發(fā)生:
- 觸發(fā)了MyInheritedWidgetState的addItem的方法
- MyInheritedWidgetState的addItem的方法添加了新的一項(xiàng)數(shù)據(jù)到data中
- 觸發(fā)了setState()需五,MyInheritedWidgetState rebuild
- 重新創(chuàng)建了一個(gè) _MyInherited 對象鹉动,傳入了新的數(shù)據(jù),記錄了新的State
- _MyInherited去查看是否需要通知訂閱者宏邮,因?yàn)榉祷氐氖莟rue泽示,所以會通知訂閱者
- Widget A和Widget B 被 rebuild了,Widget C沒有被訂閱蜜氨,所以不會被rebuild
因?yàn)槭聦?shí)上Widget A是不需要被通知的械筛,所以為了解決這個(gè)問題,需要修改代碼
static MyInheritedWidgetState of([BuildContext context, bool rebuild = true]){
return (rebuild ? context.inheritFromWidgetOfExactType(_MyInherited) as _MyInherited
: context.ancestorWidgetOfExactType(_MyInherited) as _MyInherited).data;
}
ancestorWidgetOfExactType 方法只會去獲取Widget飒炎,不會發(fā)生訂閱埋哟,所以Widget A就不會訂閱了。
參考鏈接: https://www.didierboelens.com/2018/06/widget---state---context---inheritedwidget/