Flutter:如何正确使用继承的小部件? [英] Flutter: How to correctly use an Inherited Widget?

查看:24
本文介绍了Flutter:如何正确使用继承的小部件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

不幸的是,这里的文档非常不清楚,经过大量讨论后,似乎没有人真正知道使用它的正确方法是什么.

我添加了 Brian Egan 的一句话:

<块引用>

是的,我认为这是一种将数据向下传播的方式.我发现了什么令人困惑,来自 API 文档:

"继承的小部件,当以这种方式引用时,将导致消费者在继承的小部件本身改变状态时重建."

当我第一次读到这里时,我想:

我可以在 InheritedWidget 中填充一些数据,然后对其进行变异.当这种变化发生时,它将重建所有参考我的 InheritedWidget 我发现了什么:

为了改变一个 InheritedWidget 的状态,你需要包装它在 StatefulWidget 中然后你实际上改变了状态StatefulWidget 并将这些数据传递给 InheritedWidget,后者将数据传递给所有的孩子.然而,在这种情况下,它似乎在 StatefulWidget 下重建整个树,而不是只是引用 InheritedWidget 的小部件.那是对的吗?或者它会以某种方式知道如何跳过引用InheritedWidget 如果 updateShouldNotify 返回 false?

解决方案

问题来自于您的引用,这是不正确的.

如您所说,InheritedWidgets 与其他小部件一样,是不可变的.因此他们不会更新.它们是重新创建的.

问题是:InheritedWidget 只是一个简单的小部件,除了保存数据什么都不做.它没有任何更新逻辑或任何内容.但是,像任何其他小部件一样,它与 Element 相关联.你猜怎么着?这个东西是可变的,flutter 会尽可能地重用它!

更正后的报价为:

<块引用>

InheritedWidget,当以这种方式引用时,将导致消费者在关联到 InheritedElement 的 InheritedWidget 发生变化时重建.

事情是:当你实例化一个新的小部件时;flutter 会将它与旧的进行比较.重用它的元素",它指向一个 RenderBox.并改变 RenderBox 属性.


好的,但这如何回答我的问题?

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

简而言之,当您用全新的InheritedWidget 替换现有的InheritedWidget 时;flutter 会看到它改变了.并将通知绑定的小部件潜在的修改.

如果你理解了一切,你应该已经猜到了解决方案:

将您的 InheritedWidget 包裹在 StatefulWidget 中,它会在发生变化时创建一个全新的 InheritedWidget

实际代码的最终结果是:

class MyInherited extends StatefulWidget {静态 MyInheritedData of(BuildContext context) =>context.inheritFromWidgetOfExactType(MyInheritedData) as MyInheritedData;const MyInherited({Key key, this.child}) : super(key: key);最终的 Widget 子项;@覆盖_MyInheritedState createState() =>_MyInheritedState();}class _MyInheritedState 扩展 State{字符串 myField;void onMyFieldChange(String newValue) {设置状态((){myField = newValue;});}@覆盖小部件构建(BuildContext 上下文){返回 MyInheritedData(我的领域:我的领域,onMyFieldChange: onMyFieldChange,孩子:widget.child,);}}类 MyInheritedData 扩展 InheritedWidget {最终字符串 myField;最终 ValueChangedonMyFieldChange;我的继承数据({钥匙钥匙,this.myField,this.onMyFieldChange,小部件孩子,}) : super(key: key, child: child);静态 MyInheritedData of(BuildContext context) {返回 context.dependOnInheritedWidgetOfExactType();}@覆盖bool updateShouldNotify(MyInheritedData oldWidget) {返回 oldWidget.myField != myField ||oldWidget.onMyFieldChange != onMyFieldChange;}}


但是创建一个新的 InheritedWidget 不会重建整个树吗?

不,不一定.因为您的新 InheritedWidget 可能具有与以前完全相同的孩子.确切地说,我的意思是同一个实例.拥有与之前相同实例的小部件不会重建.

并且在大多数情况下(在您的应用的根目录中有一个继承的Widget),继承的小部件是常量.所以没有不必要的重建.

What is the correct way to use an InheritedWidget? So far I understood that it gives you the chance to propagate data down the Widget tree. In extreme if you put is as RootWidget it will be accessible from all Widgets in the tree on all Routes, which is fine because somehow I have to make my ViewModel/Model accessible for my Widgets without having to resort to globals or Singletons.

BUT InheritedWidget is immutable, so how can I update it? And more important how are my Stateful Widgets triggered to rebuild their subtrees?

Unfortunately the documentation is here very unclear and after discussion with a lot nobody seems really to know what the correct way of using it.

I add a quote from Brian Egan:

Yes, I see it as a way to propagate data down the tree. What I find confusing, from the API docs:

"Inherited widgets, when referenced in this way, will cause the consumer to rebuild when the inherited widget itself changes state."

When I first read this, I thought:

I could stuff some data in the InheritedWidget and mutate it later. When that mutation happens, it will rebuild all the Widgets that reference my InheritedWidget What I found:

In order to mutate the State of an InheritedWidget, you need to wrap it in a StatefulWidget You then actually mutate the state of the StatefulWidget and pass this data down to the InheritedWidget, which hands the data down to all of it's children. However, in that case, it seems to rebuild the entire tree underneath the StatefulWidget, not just the Widgets that reference the InheritedWidget. Is that correct? Or will it somehow know how to skip the Widgets that reference the InheritedWidget if updateShouldNotify returns false?

解决方案

The problem comes from your quote, which is incorrect.

As you said, InheritedWidgets are, like other widgets, immutable. Therefore they don't update. They are created anew.

The thing is: InheritedWidget is just a simple widget that does nothing but holding data. It doesn't have any logic of update or whatsoever. But, like any other widgets, it's associated with an Element. And guess what? This thing is mutable and flutter will reuse it whenever possible!

The corrected quote would be :

InheritedWidget, when referenced in this way, will cause the consumer to rebuild when InheritedWidget associated to an InheritedElement changes.

There's a great talk about how widgets/elements/renderbox are pluged together. But in short, they are like this (left is your typical widget, middle is 'elements', and right are 'render boxes') :

The thing is: When you instantiate a new widget; flutter will compare it to the old one. Reuse it's "Element", which points to a RenderBox. And mutate the RenderBox properties.


Okey, but how does this answer my question ?

When instantiating an InheritedWidget, and then calling context.inheritedWidgetOfExactType (or MyClass.of which is basically the same) ; what's implied is that it will listen to the Element associated with your InheritedWidget. And whenever that Element gets a new widget, it will force the refresh of any widgets that called the previous method.

In short, when you replace an existing InheritedWidget with a brand new one; flutter will see that it changed. And will notify the bound widgets of a potential modification.

If you understood everything, you should have already guessed the solution :

Wrap your InheritedWidget inside a StatefulWidget that will create a brand new InheritedWidget whenever something changed!

The end result in the actual code would be :

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 ?

No, it won't necessarily. As your new InheritedWidget can potentially have the exact same child as before. And by exact, I mean the same instance. Widgets who have the same instance they had before don't rebuild.

And in the most situation (Having an inheritedWidget at the root of your app), the inherited widget is constant. So no unneeded rebuild.

这篇关于Flutter:如何正确使用继承的小部件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆