flutter中InheritedWidget的介紹和運用

InheritedWidget 不繼承自StatefulWidget,而是 InheritedWidget -> ProxyWidget -> Widget 這樣的繼承關(guān)系。簡單來說,InheritedWidget 的作用是向它的子 Widget 有效地傳播和分享數(shù)據(jù),當 InheritedWidget 作為一個Parent Widget時,它下面的Widget tree的所有Widget都可以去和 InheritedWidget 發(fā)生數(shù)據(jù)傳遞和交互。當數(shù)據(jù)發(fā)生改變時,一部分控件需要 rebuild,另外的控件不需要 rebuild 的時候,可以使用 InheritedWidget,具體的介紹結(jié)合代碼來看。
下面是 InheritedWidget 的一個實例:

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 是必須重寫的一個方法,這個方法來決定什么時候需要去rebuild 控件
of 是一個類方法,返回了它自己,inheritFromWidgetOfExactType這個方法,我理解的是從父Widget中根據(jù)類型去找響應(yīng)的Widget,但這個方法還有其他的作用,會在后面詳細介紹。
初始化方法中,會傳一個Child,并傳遞給super,也就是它的子Widget。

使用這個類:

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是一個按鈕,點擊的時候購物車數(shù)量加1.
  • Widget B是一個顯示購物車數(shù)量的文本.
  • Widget C是顯示一個固定的文本
  • 在點擊Widget A的時候,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');
  }
}

解釋說明:

  • 當點擊Widget A的時候,_MyInherited 會被重新創(chuàng)建一個
  • MyInheritedWidget 相當于是一個購物車,它的State通過static MyInheritedWidgetState of(BuildContext context)可以獲取到.
  • MyInheritedWidgetState 提供了公開的方法,getter (itemsCount)和addItem,這兩個方法可以被子Widget調(diào)用.
  • 每次我們添加一項到購物車時,MyInheritedWidgetState會rebuild

InheritedWidget是怎么去通知子Widget刷新數(shù)據(jù)的呢?

當一個子Widget調(diào)用MyInheritedWidget.of(context)時,會走下面的代碼,把它自己的context傳進來

static MyInheritedWidgetState of(BuildContext context) {
    return (context.inheritFromWidgetOfExactType(_MyInherited) as _MyInherited).data;
  }

這個方法其實是做了兩件事:

  • 獲取MyInheritedWidgetState里面的數(shù)據(jù)
  • 會將調(diào)用該方法的Widget加入訂閱者行列,當數(shù)據(jù)發(fā)生改變的時候,會通知這些Widget刷新數(shù)據(jù),也就是rebuild.

工作的原理總結(jié)起來如下:
因為Widget A和Widget B訂閱了InheritedWidget,所以點擊Widget A的時候會發(fā)生:

  • 觸發(fā)了MyInheritedWidgetState的addItem的方法
  • MyInheritedWidgetState的addItem的方法添加了新的一項數(shù)據(jù)到data中
  • 觸發(fā)了setState(),MyInheritedWidgetState rebuild
  • 重新創(chuàng)建了一個 _MyInherited 對象,傳入了新的數(shù)據(jù),記錄了新的State
  • _MyInherited去查看是否需要通知訂閱者,因為返回的是true,所以會通知訂閱者
  • Widget A和Widget B 被 rebuild了,Widget C沒有被訂閱,所以不會被rebuild

因為事實上Widget A是不需要被通知的,所以為了解決這個問題,需要修改代碼

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/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容