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

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

问题描述

使用InheritedWidget的正确方法是什么?到目前为止,我了解到它为您提供了沿Widget树向下传播数据的机会。在极端情况下,如果您将其作为RootWidget放置,则可以从所有路径上的树中的所有Widget进行访问,这很好,因为我必须以某种方式使Widget可以访问ViewModel / Model,而不必诉诸于Globals或Singletons。



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



不幸的是,此处的文档非常不清楚,与很多人讨论之后,似乎没人真正知道什么是正确的。



我添加了Brian Egan的报价:


是的,我将其视为在树上传播数据的一种方式。我从API文档中发现
令人困惑:



继承的窗口小部件以这种方式引用时,将导致
使用者在重新构建时继承的窗口小部件本身会更改状态。



当我初读此书时,我想:



我可以装东西InheritedWidget中的一些数据并在以后对其进行突变。
当发生这种突变时,它将重建
引用我的InheritedWidget的所有小部件。我发现的内容:



为了使某个状态更改为InheritedWidget,您需要将
包裹在StatefulWidget中,然后实际更改
StatefulWidget的状态,并将此数据传递给InheritedWidget,后者将
的数据传递给所有子项。但是,在那种情况下,
似乎重建了StatefulWidget下的整个树,而不是
只是引用了InheritedWidget的Widget。那是对的吗?
或者,如果updateShouldNotify返回false,它是否会以某种方式知道如何跳过引用
InheritedWidget的窗口小部件?



解决方案

问题出在您的报价上,这是不正确的。


正如您所说,InheritedWidgets与其他widgets一样是不可变的。因此,它们不会更新。它们是重新创建的。


事情是: InheritedWidget只是一个简单的小部件,除了保存数据外什么也不做。它没有任何更新逻辑或任何逻辑。
但是,与其他任何小部件一样,它与 Element 关联。
你猜怎么着?这东西是易变的,颤振会在可能的情况下重用它!


更正后的报价为:


InheritedWidget when when以这种方式引用时,与 InheritedElement 相关联的InheritedWidget更改时,将导致使用者重建。



问题是:实例化新的小部件时;扑将它与旧的比较。重用它的元素,它指向RenderBox。和 mutate RenderBox属性。




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


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


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


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


将您的<$ StatefulWidget 内的c $ c> InheritedWidget ,它将在任何情况下创建全新的 InheritedWidget


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

 类MyInherited扩展了StatefulWidget {
static MyInheritedData of(BuildContext context)=>
context.inheritFromWidgetOfExactType(MyInheritedData)作为MyInheritedData;

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

个最终Widget子对象;

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

类_MyInheritedState扩展了State< MyInherited> {
String myField;

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

@override
小部件构建(BuildContext上下文){
return MyInheritedData(
myField:myField,
onMyFieldChange:onMyFieldChange,
child:widget.child,
);
}
}

类MyInheritedData继承了InheritedWidget {
final String myField;
final ValueChanged< String> onMyFieldChange;

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

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

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




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


否,不一定。由于新的InheritedWidget可能具有与以前完全相同的子级。确切地说,我的意思是同一实例。具有相同实例的
窗口小部件不会重建。


在大多数情况下(在您应用的根目录中有一个InheritedWidget),继承的窗口小部件是恒定。因此,不需要进行任何重建。


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天全站免登陆