使用InheritedWidget的正确方式是什么?到目前为止,我了解到它为您提供了将数据向下传播到Widget树的机会.在极端情况下,如果您将IS设置为RootWidget,则可以从树中的所有Widget在所有路由上访问它,这很好,因为我必须以某种方式使我的ViewModel/Model可供我的Widget访问,而不必求助于全局变量或单例.

但是InheritedWidget是不变的,那么我如何更新它呢?更重要的是,如何触发我的有状态小部件来重新构建它们的子树?

不幸的是,这里的文档非常不清楚,在与许多人讨论之后,似乎没有人真正知道使用它的正确方式是什么.

我引用布莱恩·伊根的话:

是的,我认为这是一种向下传播数据的方式.我发现了什么

"当以这种方式引用继承的小部件时,将导致 当继承的小部件本身更改状态时要重新生成的消费者."

当我第一次读到这篇文章时,我想:

我可以在InheritedWidget中填充一些数据,稍后对其进行Mutations . 当这种变化发生时,它将重新构建所有 引用我发现的My InheritedWidget:

为了改变InheritedWidget的状态,您需要包装 它在一个状态中使你实际上改变了它的状态 StatefulWidget并将此数据向下传递到InheritedWidget,该InheritedWidget 把数据传给它所有的子代.但是在这种情况下呢,它 似乎是在StatefulWidget下重新构建整个树,而不是 仅引用InheritedWidget的小部件.对吗? 或者它会以某种方式知道如何跳过引用 如果updateShouldNotify返回False,是否为InheritedWidget?

推荐答案

问题出在你的报价,这是不正确的.

正如您所说,InheritedWidget和其他小部件一样是不可变的.因此他们不会update岁.它们是重新创造的.

问题是:InheritedWidget is just a simple widget that does nothing but holding data.它没有任何更新或任何逻辑. 但是,与任何其他小部件一样,它与Element相关联. 你猜怎么着?这个东西是可变的,Ffltter会在任何可能的情况下重复使用它!

更正后的报价如下:

以这种方式引用InheritedWidget时,当InheritedWidget与InheritedElement个更改关联时,将导致使用者重新构建.

There's a great talk about how widgets/elements/renderbox are pluged together美元. 但简而言之,它们是这样的(左侧是典型的小部件,中间是"元素",右侧是"渲染框"):

enter image description here

问题是:当你实例化一个新的小部件时;弗利特会把它和旧的相比.重用它的"元素",它指向一个渲染器.以及mutate个RenderBox属性.


Okey, but how does this answer my question ?

当实例化InheritedWidget,然后调用context.inheritedWidgetOfExactType(或基本上相同的MyClass.of)时;这意味着它将监听与您的InheritedWidget相关联的Element.并且每当Element获得新的小部件时,它将强制刷新调用先前方法的任何小部件.

简而言之,当你用一个全新的替换现有的InheritedWidget;弗利特会看到它变了.并将通知绑定的小部件可能的修改.

如果您了解所有内容,您应该已经猜到了解决方案:

把你的InheritedWidget装进StatefulWidget里面,只要有什么变化,它就会创造出一个全新的InheritedWidget

实际代码中的最终结果将是:

class MyInherited extends StatefulWidget {
  static MyInheritedData of(BuildContext context) =>
      context.inheritFromWidgetOfExactType(MyInheritedData) as MyInheritedData;

  const MyInherited({Key key, this.child}) : super(key: key);

  final Widget child;

  @override
  _MyInheritedState createState() => _MyInheritedState();
}

class _MyInheritedState extends State<MyInherited> {
  String myField;

  void onMyFieldChange(String newValue) {
    setState(() {
      myField = newValue;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MyInheritedData(
      myField: myField,
      onMyFieldChange: onMyFieldChange,
      child: widget.child,
    );
  }
}

class MyInheritedData extends InheritedWidget {
  final String myField;
  final ValueChanged<String> onMyFieldChange;

  MyInheritedData({
    Key key,
    this.myField,
    this.onMyFieldChange,
    Widget child,
  }) : super(key: key, child: child);

  static MyInheritedData of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<MyInheritedData>();
  }

  @override
  bool updateShouldNotify(MyInheritedData oldWidget) {
    return oldWidget.myField != myField ||
        oldWidget.onMyFieldChange != onMyFieldChange;
  }
}

But wouldn't creating a new InheritedWidget rebuild the whole tree ?

不,不一定.因为新的InheritedWidget可能具有与以前完全相同的子级.确切地说,我指的是同样的情况. 具有与以前相同实例的小部件不会重新构建.

在大多数情况下(在应用程序的根目录下有一个inheritedWidget),inherited widget是constant.所以没有不必要的重建.

Flutter相关问答推荐

Flutter -设计具有奇怪边界的容器的方法Radius

如何在flutter文本字段中添加要点条目?

如何为Android 12及以上设备以及Android 11及以下设备动态处理Splash Screen?如果可能的话没有包裹

使小部件处于有状态状态会导致hasSize异常

SingleChildScrollView在ErrorWidget.builder中不工作

抖动问题:检测到0个或2个或更多具有相同值的[DropdownMenuItem]

按一下按钮即可更新 fl_chart

TextField 中的富文本并获取 RenderParagraph

没有固定宽度的小部件可以约束文本小部件吗?

点击按钮后重新启动定时器

我对 Flutter Apk 有疑问

Flutter / 为什么当我点击我的图片时我的模型没有更新?

我在手机上找不到我的 Flutter 应用缓存数据

如何与 PostgreSQL 和外部网站交互 flutter apk?

Flutter RIverpod 奇怪地改变状态

在 Flutter 中读取 Firebase 数据库数据时遇到问题

如何在 flutter 中使用 tabbar 作为底部导航栏?

Riverpod 在使用冻结副本更新状态时触发重建,即使没有任何更改

无法列出 com.facebook.android:facebook-login -Facebook 登录问题的版本

在自动完成中 Select 值后保持键盘焦点