flutter中InheritedWidget的介绍和运用

2019-08-07  本文已影响0人  萤火虫离别的礼物

InheritedWidget 不继承自StatefulWidget,而是 InheritedWidget -> ProxyWidget -> Widget 这样的继承关系。简单来说,InheritedWidget 的作用是向它的子 Widget 有效地传播和分享数据,当 InheritedWidget 作为一个Parent Widget时,它下面的Widget tree的所有Widget都可以去和 InheritedWidget 发生数据传递和交互。当数据发生改变时,一部分控件需要 rebuild,另外的控件不需要 rebuild 的时候,可以使用 InheritedWidget,具体的介绍结合代码来看。
下面是 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中根据类型去找响应的Widget,但这个方法还有其他的作用,会在后面详细介绍。
初始化方法中,会传一个Child,并传递给super,也就是它的子Widget。

使用这个类:

class MyParentWidget... {
   ...
   @override
   Widget build(BuildContext context){
      return new MyInheritedWidget(
         data: counter,
         child: new Row(
            children: <Widget>[
               ...
            ],
         ),
      );
   }
}

子Widget怎么获取数据?

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,
      );
   }
}

应用场景:

代码如下:

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');
  }
}

解释说明:

InheritedWidget是怎么去通知子Widget刷新数据的呢?

当一个子Widget调用MyInheritedWidget.of(context)时,会走下面的代码,把它自己的context传进来

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

这个方法其实是做了两件事:

工作的原理总结起来如下:
因为Widget A和Widget B订阅了InheritedWidget,所以点击Widget A的时候会发生:

因为事实上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,不会发生订阅,所以Widget A就不会订阅了。

参考链接: https://www.didierboelens.com/2018/06/widget---state---context---inheritedwidget/

上一篇下一篇

猜你喜欢

热点阅读