推荐文章:
View Tree直接通信
父View如何拿到子View的状态
- 
在这里假设我们子View需要拿到父View的数据,父View与子View的关系图层如下所示。

 
处理方式
方案一
- 在父View中创建State时候把变量引用给保存下来。
 
class ParentWidgetView extends StatefulWidget {
   ParentStateView myState;
    
   @override
   ParentStateView createState(){
      myState = new ParentStateView();
      return myState;
   }
}
//提供一个color给子View使用
class ParentStateView extends State<ParentStateView>{
   Color _color;
   Color get color => _color;
   ...
}
- 在子View中通过BuildContext关系获取父View的State
 
class ChildrenView extends StatelessWidget {
   @override
   Widget build(BuildContext context){
      final ParentWidgetView widget = context.ancestorWidgetOfExactType(ParentWidgetView);
      final ParentStateView state = widget?.myState;
        
      return new Container(
         //拿到对应的state,state里面的所有属性变量自然能获取到了
         color: state == null ? Colors.blue : state.color,
      );
   }
}
缺点:该方案当父View刷新时候,子View怎么知道何时应该刷新呢?唯有子View重建的时候才能达到刷新效果,所以不是很方便。下面方案二将讨论“InheritedWidget”的用法,该类可以解决此问题。
方案二
- InheritedWidget是一个特殊的Widget,您将它作为另一个子树的父级放入Widgets树中。该子树的所有子View都必须具有与该InheritedWidget公开的数据进行交互的能力。所以首先声明一个InheritedWidget。
 
class MyInheritedWidget extends InheritedWidget {
   MyInheritedWidget({
      Key key,
      @required Widget child,
      this.data,
   }): super(key: key, child: child);
    
   final data;
    
   static ParentWidgetView of(BuildContext context) {
      return context.inheritFromWidgetOfExactType(MyInheritedWidget);
   }
  //updateShouldNotify”重写方法用于告诉InheritedWidget如果对数据进行了修改,是否必须将通知传递给所有子小部件(已注册/已预订)
   @override
   bool updateShouldNotify(MyInheritedWidget oldWidget) => data != oldWidget.data;
}
- 在ParentWidgetView时候构建时候,我们用MyInheritedWidget作为最外层的View。让其在ParentWidgetView的子View能获取到对应数据
 
class ParentWidgetView... {
   ...
   @override
   Widget build(BuildContext context){
      return new MyInheritedWidget(
        //关键关联数据
         data: widget.mState,
         child: new Row(
            children: <Widget>[
               ...
            ],
         ),
      );
   }
}
- 子View如何从父View中获取数据呢?生成子对象时,后者将获得对InheritedWidget的引用,如下
 
class ChildrenView... {
   ...
    
   @override
   Widget build(BuildContext context){
      //通过静态方法获取对应MyInheritedWidget句柄
      final MyInheritedWidget inheritedWidget = MyInheritedWidget.of(context);
      return new Container(
          //通过句柄的data访问父View的数据,当父View数据更新后,data也会相应更新
         color: inheritedWidget.data.color,
      );
   }
}
子View和子View如何能拿到对方数据
- 
关系树形图如下

 - 为了说明互动的类型,我们假设以下内容:
 
- A的Widget是将商品添加到购物车的按钮;
 - B的Widget是显示购物车中商品数量的文本;
 - C的Widget 位于小部件B旁边,是一个文本,其中包含任何文本;
 - 我们希望“B”能够在按下“A”后,自动在购物车中自动显示正确数量的商品,但我们不希望重新构建“小部件C”
 
- 代码如下
MyTree类作为View Tree的入口,以MyInheritedWidget作为树的父级。然后各自A、B通过 
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(
      //将数据直接指向自己,供其子View获取state里面的方法
      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');
  }
}
问题
- 当MyInheritedWidget的数据改变后,A的widget也会被重建,但是我们的A其实不需要重建的,那么怎样阻止某些小部件在能访问继承的小部件的同时,不进行重建呢?
 
解决办法
防止这种自动订阅同时仍允许Widget A访问 MyInherited WidgetState的解决方案是如下更改MyInheritedWidget的静态方法:
static MyInheritedWidgetState of([BuildContext context, bool rebuild = true]){
    return (rebuild ? context.inheritFromWidgetOfExactType(_MyInherited) as _MyInherited
                    : context.ancestorWidgetOfExactType(_MyInherited) as _MyInherited).data;
  }
通过增加布尔值,让其是否自动添加到订阅者队列中
- 如果“ rebuild”参数为true(默认情况下),我们将使用常规方法(并且Widget将被添加到订户列表中)
 - 如果“ rebuild”参数为false,则仍然可以访问数据,但无需使用InheritedWidget的内部实现
 
因此,要完成该解决方案,我们还需要按如下所示稍微更新小部件A的代码(我们添加了false额外参数)
class WidgetA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final MyInheritedWidgetState state = MyInheritedWidget.of(context, false);
    return new Container(
      child: new RaisedButton(
        child: new Text('Add Item'),
        onPressed: () {
          state.addItem('new item');
        },
      ),
    );
  }
}
总结
- 参考View Tree直接通信。
 - 多动手试试,这次通过state拿到引用后是否能拿到对应的context的位置信息。
 










