Flutter中有状态和无状态小部件之间有什么关系?


104

有状态窗口小部件定义为在其生存期内更改其状态的任何窗口小部件。但是StatelessWidget,将aStatefulWidget作为其子级之一是非常普遍的做法。StatelessWidget如果将其StatefulWidget作为子级之一,是否不会成为有状态的?

我试图寻找到的文件作为代码的一部分StatelessWidget,但无法弄清楚如何StatelessWidget可以Statefulwidget作为它的孩子,但仍保持StatelessWidget

Flutter中有状态和无状态小部件之间的关系和区别是什么?


2
您可以使用不同类型的窗口小部件来组成布局,但这并不意味着您将继承构图的特征来影响每个窗口小部件。我的意思是,您可以拥有一个无状态的容器,该容器具有另一个在其他位置声明为StatefulWidget的容器​​的子容器,该容器的状态仅会影响该组件。因此,这都是由不同类型的小部件组成的,每个小部件都需要它们。
阿齐扎

1
更糟的是,还有第三种小部件:InheritedWidget; 可以进行StatelessWidget更新。
罗米·罗素(RémiRousselet)

Answers:


103

一个StatelessWidget永远不会重建本身(但外部事件可以)。一个StatefulWidget可以。那是黄金法则。

但是任何类型的小部件都可以随时重绘

无状态仅表示其所有属性都是不可变的,并且更改它们的唯一方法是创建该窗口小部件的新实例。例如,它不锁定小部件树。

但是你不应该在乎你的孩子是什么类型。它对您没有任何影响。


11
(相对较新的框架)。是什么区别rebuildrepaint
user462455

同样从flutter框架代码中的注释来看,StateFulWidgets显然也是不可变的。
user462455

3
小部件的构建基本上是对“ build”方法的调用,然后创建/更新相应的renderbox;接下来是绘画过程。它将在屏幕上打印这些渲染框。
罗米·罗素(RémiRousselet)

继承“ StatefulWidget”的类是不可变的。但是状态(State <YourWidget>)本身是可变的。
罗米·罗素(RémiRousselet)

1
@RémiRousselet状态和无状态的小部件都重建每一帧,根据flutter.dev/docs/get-started/flutter-for/...
马特

82

StatefulWidget与StatelessWidget。

在此处输入图片说明

StatelessWidget-不需要可变状态的小部件。

  • 无状态窗口小部件是通过构建其他更具体描述用户界面的窗口小部件的星座来描述用户界面的一部分的窗口小部件。构建过程将以递归方式继续进行,直到用户界面的描述完全具体为止(例如,完全由描述具体RenderObject的RenderObjectWidget组成)。

  • stateless当接口您所描述的用户的部分不依赖于比在对象本身和所述配置信息的任何其他窗口小部件是有用 BuildContext其中插件被充气。对于可能由于内部时钟驱动状态或某些系统状态而动态变化的合成,请考虑使用 StatefulWidget

class GreenFrog extends StatelessWidget {
  const GreenFrog({ Key key }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(color: const Color(0xFF2DBD3A));
  }
}

StatefulWidget-具有可变状态的小部件。

  • 当您描述的用户界面部分可以动态更改时,有状态小部件很有用。

当Flutter构建一个时StatefulWidget,它会创建一个State对象。该对象是该窗口小部件的所有可变状态的保存位置。

状态的概念由两件事定义:

1)小部件使用的数据可能会更改。

2)构建小部件时,无法同步读取数据。(必须在调用build方法时建立所有状态)。

StatefulWidget生命周期

生命周期具有以下简化步骤:

  1. createState() -当指示Flutter构建StatefulWidget时,它将立即调用createState()
  • 在树中的给定位置为此小部件创建可变状态。

  • 子类应重写此方法以返回其关联的State子类的新创建的实例:

@override
_MyState createState() => _MyState();
  1. Mounted == true-所有小部件都具有boolthis.mounted属性。buildContext分配后,它变为true 。setState卸载窗口小部件时调用是错误的。此State对象当前是否在树中。
  • 在创建State对象之后,在调用之前initState,框架通过将State对象与进行关联来“装入” State对象
    BuildContext。State对象将保持安装状态,直到框架
    调用dispose(),之后框架将再也不会要求
    State对象再次构建。

  • 除非mount为true,否则调用setState是错误的。

bool get mounted => _element != null;
  1. initState() -这是在创建窗口小部件时调用的第一个方法(当然,在类构造函数之后)。

initState只被调用一次。它必须调用super.initState().

  • 初始化依赖于特定BuildContext的已创建窗口小部件实例的数据。

  • 初始化依赖于树中这些“父”窗口小部件的属性。

  • 订阅StreamsChangeNotifiers或任何其他可能更改此小部件上的数据的对象。

@override
initState() {
  super.initState();
  // Add listeners to this class
  cartItemStream.listen((data) {
    _updateWidget(data);
  });
}
  1. didChangeDependencies() -当此State对象的依赖项更改时调用。
  • 之后也立即调用此方法initStateBuildContext.inheritFromWidgetOfExactType从此方法调用是安全的。

  • 子类很少重写此方法,因为框架总是在依赖项更改后调用build。一些子类确实重写了此方法,因为当它们的依存关系更改时,它们需要做一些昂贵的工作(例如,网络获取),并且对于每个构建而言,这些工作都将太昂贵。

@protected
@mustCallSuper
void didChangeDependencies() { }
  1. build() -描述小部件代表的用户界面部分。

框架在许多不同的情况下调用此方法:

  • 打电话后initState
  • 打电话后didUpdateWidget
  • 接到的呼叫后setState
  • 此State对象的依存关系发生更改之后(例如,以前的构建所引用的InheritedWidget发生了更改)。
  • 调用停用后,然后将State对象重新插入到另一个位置的树中。
  • 框架通过此方法返回的窗口小部件替换此窗口小部件下的子树,方法是更新现有子树,或者通过删除子树并填充新的子树,具体取决于此方法返回的窗口小部件是否可以更新现有子树的根由呼叫确定 Widget.canUpdate

  • 通常,实现会返回一个新创建的小部件群,这些小部件群配置有该小部件的构造函数,给定的BuildContext和此State对象的内部状态的信息。

@override
  Widget build(BuildContext context, MyButtonState state) {
    ... () { print("color: $color"); } ...
  }
  1. didUpdateWidget() -每当窗口小部件配置更改时调用。
  • 如果父窗口小部件重建并请求更新树中的该位置以显示具有相同运行时类型和Widget.key的新窗口小部件,则框架将更新此State对象的窗口小部件属性以引用新窗口小部件,然后调用此窗口小部件。前一个小部件作为参数的方法。

  • 覆盖此方法以在小部件更改时做出响应(例如,开始隐式动画)。

  • 框架总是在调用didUpdateWidget之后调用build,这意味着在didUpdateWidget中对setState的任何调用都是多余的。

@mustCallSuper
@protected
void didUpdateWidget(covariant T oldWidget) { }
  1. setState() -每当您更改State对象的内部状态时,都要在传递给setState以下函数的函数中进行更改:
  • 调用setState会通知框架此对象的内部状态已更改,该方式可能会影响此子树中的用户界面,这会导致框架
    为该State对象安排构建。

  • 如果仅在不调用setState的情况下直接更改状态,则框架可能不会安排构建,并且可能不会更新此子树的用户界面以反映新状态。

setState(() { _myState = newValue });
  1. deactivate() -当从树中删除State时调用Deactivate,但是可能会在当前帧更改完成之前重新插入它。之所以存在此方法,是因为状态对象可以从树中的一个点移动到另一点。
  • 每当框架从树中删除此State对象时,框架都会调用此方法。在某些情况下,框架会将状态对象重新插入树的另一部分(例如,如果包含该状态对象的子树从树中的一个位置移植到另一位置)。如果发生这种情况,框架将确保它调用build以使State对象有机会适应其在树中的新位置。如果框架确实重新插入了此子树,则它将在从子树中删除该子树的动画帧结束之前这样做。因此,状态对象可以推迟释放大多数资源,直到框架调用其Dispose方法为止。

这很少使用。

@protected
@mustCallSuper
void deactivate() { }
  1. dispose() -当从树中永久删除该对象时调用。
  • 当此State对象永远不再构建时,框架将调用此方法。在框架调用之后dispose(),将State对象视为已卸载,并且mounted属性为false。此时调用setState是错误的。生命周期的这一阶段是终极阶段:无法重新安装已处置的State对象。

  • 子类应重写此方法以释放该对象保留的所有资源(例如,停止任何活动的动画)。

@protected
@mustCallSuper
void dispose() {
  assert(_debugLifecycleState == _StateLifecycle.ready);
  assert(() { _debugLifecycleState = _StateLifecycle.defunct; return true; }());
}

在此处输入图片说明

欲了解更多信息请点击这里 这里在这里


26

flutter.io的文档中:

...这里要注意的重要一点是,无状态小部件和有状态小部件的行为相同。他们重建每个框架,不同之处在于StatefulWidget具有一个State对象,该对象跨框架存储状态数据并进行还原。

如果您有疑问,请始终记住以下规则:如果小部件发生更改(例如,用户与之交互),则该状态为有状态。但是,如果子项对更改做出反应,则如果父项不对更改做出反应,则包含父项仍可以是无状态小部件。


14

正如在flutter文档中提到的

重点是什么?

有些小部件是有状态的,而有些则是无状态的。如果窗口小部件发生了更改(例如,用户与其交互),则该窗口小部件是有状态的。小部件的状态由可以更改的值组成,例如滑块的当前值或是否选中了复选框。窗口小部件的状态存储在State对象中,从而将窗口小部件的状态与其外观分开。当窗口小部件的状态更改时,状态对象将调用setState(),告诉框架重绘窗口小部件。

一个无状态的小工具有没有内部状态来管理。Icon,IconButton和Text是无状态小部件的示例,它们是StatelessWidget的子类。

状态小部件是动态的。用户可以与有状态窗口小部件进行交互(例如,通过键入表单或移动滑块),也可以随时间变化(也许数据馈送会导致UI更新)。Checkbox,Radio,Slider,InkWell,Form和TextField是有状态的小部件的示例,它们是StatefulWidget的子类。

https://flutter.io/tutorials/interactive/#stateful-stateless


10

状态是以下信息:(1)在构建窗口小部件时可以同步读取,并且(2)在窗口小部件的生存期内可能会更改。小部件实现者有责任使用State.setState确保在状态改变时及时通知该状态。

StatefulWidget

有状态窗口小部件是通过构建其他更具体描述用户界面的窗口小部件的星座来描述用户界面的一部分的窗口小部件。构建过程将以递归方式继续进行,直到用户界面的描述完全具体为止(例如,完全由描述具体RenderObject的RenderObjectWidget组成)。

当您描述的用户界面部分可以动态更改(例如,由于具有内部时钟驱动状态或取决于某些系统状态)时,有状态小部件很有用。对于仅依赖于对象本身中的配置信息以及使小部件膨胀的BuildContext的合成,请考虑使用StatelessWidget。

StatefulWidget实例本身是不可变的,并且将其可变状态存储在由createState方法创建的单独的State对象中,或存储在该State所预订的对象中,例如Stream或ChangeNotifier对象,引用存储在StatefulWidget的最终字段中本身。

StatelessWidget

无状态窗口小部件是通过构建其他更具体描述用户界面的窗口小部件的星座来描述用户界面的一部分的窗口小部件。构建过程将以递归方式继续进行,直到用户界面的描述完全具体为止(例如,完全由描述具体RenderObject的RenderObjectWidget组成)。

当您要描述的用户界面部分不依赖于对象本身的配置信息以及该控件在其中膨胀的BuildContext时,无状态控件非常有用。对于可能由于内部时钟驱动状态或某些系统状态而动态变化的合成,请考虑使用StatefulWidget。


9

无状态小部件是静态小部件。在初始化无状态小组件之前,您只需要传递几个属性即可。它们不依赖于任何数据更改或任何行为更改。例如。文本,图标,RaisedButton是无状态小部件。

状态窗口小部件是动态窗口小部件,它们可以在运行时根据用户操作或数据更改进行更新。如果小部件可以在运行时更改其状态,则它将是有状态的小部件。

编辑15/11/2018

如果输入/外部数据发生了变化(外部数据是通过构造函数传递的数据),则无状态小部件可以重新渲染。由于无状态小部件没有状态,因此它们将被渲染一次并且不会自行更新,而只会在外部数据更改时进行更新。

有状态的小工具有一个内部的状态,如果输入的数据改变或如果Widget的状态变化可以重新渲染。

无状态小部件和有状态小部件都具有不同的生命周期。


即使将新数据从外部传递到Stateless窗口小部件,我们也可以在运行时对其进行更改,但是它不称为Stateful窗口小部件(与最后一行相反)。
CopsOnRoad '18

您能否解释一下如何“在外部数据更改时更新无状态”小部件?(“外部数据是通过构造函数传递的数据”。)构造函数是否不仅会被调用一次?通过构造函数传递的数据如何变化?
user1596274

8

我可以想到一个非常简单的类比。您有一些带书,装饰品和电视的家具。家具是无状态的,它什么也不会动。在电视的另一侧,您可以打开,关闭,更改频道,播放附有DVD的电影等。电视的内部状态会影响其行为方式。在家具里你没有状态。家具中电视的存在并没有增加状态。希望这可以帮助。


这不能回答提问者的具体问题。
以赛亚书

1
这是一个很好的类比!
威廉·泰瑞尔

6

解决堆栈溢出问题-有状态与无状态

在Flutter中,区别在于无状态窗口小部件可以仅由所有构造函数参数定义。如果使用相同的参数创建两个无状态窗口小部件,则它们将相同。

但是,有状态的小部件不一定与使用相同构造函数参数构建的其他小部件相同。它可能处于不同的状态。
实际上,有状态窗口小部件本身是不可变的(无状态),但是Flutter管理一个单独的状态对象,并将其与该窗口小部件相关联,如StatefulWidget doc中所述。这意味着Flutter重建有状态的窗口小部件时,将检查是否应重用先前的状态对象,并在需要时将该状态对象附加到窗口小部件。

父窗口小部件是无状态的,因为它不在乎其子状态。有状态的孩子本身(或技术上说是Flutter)会照顾自己的状态。
在较高的层次上,我同意这会使父窗口小部件成为有状态的,因为两个父节点可能包含两个状态不同的子节点,因此它们在技术上也有所不同。但是从Flutter的角度来看,它在构建父窗口小部件时并不关心状态,只有在构建子窗口时才会考虑其状态充分性。


5

什么是有状态和无状态窗口小部件?

TL; DR:允许您刷新屏幕的小部件是有状态小部件。不是非无状态的小部件。

更详细地说,具有可更改内容的动态窗口小部件应该是有状态窗口小部件。无状态窗口小部件只能在更改参数时更改内容,因此需要在其位置在窗口小部件层次结构中的位置上方进行操作。包含静态内容的屏幕或窗口小部件应该是无状态窗口小部件,但是要更改内容,则必须是有状态的。

我在一个有趣的中等故事中发现了这种相对的内容。别客气!


4

无状态:窗口小部件状态仅创建一次,然后可以更新值,但不能显式状态。从那里的结构也很清楚。这就是为什么它只有一个扩展为的类的原因StatelessWidget。因此,如果我说,他们再也无法重新运行build()方法了。

有状态在事件触发时,小部件可以多次更新其STATE(本地)和值。这就是原因,实现方式也有所不同。在此,我们有2个类,一个是StatefulWidget&,另一个是它的State实现处理程序,即State<YourWidget>。因此,如果我说的话,他们可以build()根据触发的事件一次又一次地重新运行方法。

下图将有所帮助。

在此处输入图片说明


1

编写应用程序时,通常会编写新的小部件,这些新的小部件是StatelessWidgetStatefulWidget的子类。

以下是StatelessWidgetStatefulWidget小部件之间的一些区别:

无状态小部件:

  1. 具有不变状态的小部件。
  2. 无状态小部件是静态小部件。
  3. 它们不依赖于任何数据更改或任何行为更改。
  4. 无状态小部件没有状态,它们将被渲染一次并且不会自行更新,而只会在外部数据更改时进行更新。
  5. 例如:TextIconRaisedButton是无状态的窗口小部件。

无状态小部件:

  1. 具有可变状态的小部件。
  2. 有状态小部件是动态小部件。
  3. 可以根据用户操作或数据更改在运行时更新它们。
  4. 有状态的小部件具有内部状态,并且可以在输入数据更改或小部件的状态更改时重新呈现。
  5. 例如:CheckboxRadio ButtonSlider是有状态窗口小部件

0

简单来说:

众所周知,每个小部件都是颤振中的一个视图。里面有自己的类。当我们使用这些类时,我们将为其创建一个对象。我们给它们不同的变量/属性赋值。例如 我们正在创建文本小部件,因此我们可以给它提供字符串,颜色,字体大小,字体系列。因此,通过给出此名称,我们在创建它时定义了它的属性。到目前为止,无状态或有状态小部件都是相同的,但是,

当我们一次又一次地更改/更新它的属性(比如说String或Color)时,它应该是有状态的。

而且,当我们不想在第一次定义后更改其属性时,它是一个无状态的小部件。

这意味着我们关心小部件保存/控制/显示的数据。

因此,无状态意味着数据较少,而有状态意味着数据已满。

现在,如果您定义了一个无状态的类,则意味着该类不在乎/中没有变量,也不在其类中说数据,即类级别,但是其中可能有另一个小部件/类在乎数据,即它是有状态的。因此,它们彼此之间没有任何影响。

如果我错了,请纠正我。


0

什么是有状态和无状态窗口小部件?

无状态窗口小部件:仅当无状态窗口小部件具有父级更改时才构建。

有状态的窗口小部件:状态完整的窗口小部件保存窗口小部件的状态,并且在状态更改时可以重建。


0

免责声明:-从上周开始扑扑:)

无状态和全状态窗口小部件具有自己的生命周期来创建和更新UI。但是,您可以使用无状态或全状态来呈现UI,但实际上,当ui完全或部分依赖于外部数据时(例如,使用api呈现列表),使用全状态更方便,而使用无状态小部件像任何输入屏幕一样呈现静态ui好的做法。


1
我认为作者的意思相反:·D
Roc Boronat
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.