美文网首页Flutter
Flutter视图树之间的数据传递

Flutter视图树之间的数据传递

作者: Magic旭 | 来源:发表于2020-04-02 14:59 被阅读0次

推荐文章:
View Tree直接通信

父View如何拿到子View的状态

  1. 在这里假设我们子View需要拿到父View的数据,父View与子View的关系图层如下所示。


    层级关系
处理方式
方案一
  1. 在父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;
   ...
}
  1. 在子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”的用法,该类可以解决此问题。

方案二
  1. 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;
}
  1. 在ParentWidgetView时候构建时候,我们用MyInheritedWidget作为最外层的View。让其在ParentWidgetView的子View能获取到对应数据
class ParentWidgetView... {
   ...
   @override
   Widget build(BuildContext context){
      return new MyInheritedWidget(
        //关键关联数据
         data: widget.mState,
         child: new Row(
            children: <Widget>[
               ...
            ],
         ),
      );
   }
}
  1. 子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如何能拿到对方数据

  1. 关系树形图如下


    关系树形图
  2. 为了说明互动的类型,我们假设以下内容:
  • A的Widget是将商品添加到购物车的按钮;
  • B的Widget是显示购物车中商品数量的文本;
  • C的Widget 位于小部件B旁边,是一个文本,其中包含任何文本;
  • 我们希望“B”能够在按下“A”后,自动在购物车中自动显示正确数量的商品,但我们不希望重新构建“小部件C”
  1. 代码如下
    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');
  }
}
问题
  1. 当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');
        },
      ),
    );
  }
}

总结

  1. 参考View Tree直接通信
  2. 多动手试试,这次通过state拿到引用后是否能拿到对应的context的位置信息。

相关文章

网友评论

    本文标题:Flutter视图树之间的数据传递

    本文链接:https://www.haomeiwen.com/subject/gbntphtx.html