有状态与无状态的区别如何影响小部件何时重建?

Posted

技术标签:

【中文标题】有状态与无状态的区别如何影响小部件何时重建?【英文标题】:How does the stateful vs. stateless distinction affect when widgets will be rebuilt? 【发布时间】:2020-03-26 13:05:13 【问题描述】:

我试图了解状态和提升状态在 Flutter 中是如何工作的。在下面的代码中,我制作了一个简单的应用程序,尝试通过多个级别传递或提升字符串和函数。

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget 
  @override
  _MyAppState createState() => _MyAppState();


class _MyAppState extends State<MyApp> 
  String data = 'MyApp class data';

  void setTextField(String s) 
    setState(
      () 
        data = s;
      ,
    );
  

  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      home: MainApp(data, setTextField),
    );
  


class MainApp extends StatelessWidget 
  final String mainAppData;
  final Function setTextField;
  MainApp(this.mainAppData, this.setTextField);
  @override
  Widget build(BuildContext context) 
    return Scaffold(
      appBar: AppBar(title: Text(mainAppData)),
      body: Container(
        child: Level2(mainAppData, setTextField),
      ),
    );
  


class Level2 extends StatelessWidget 
  final String level2Data;
  final Function setTextField;
  Level2(this.level2Data, this.setTextField);

  @override
  Widget build(BuildContext context) 
    return Container(
      child: Level3(level2Data, setTextField),
    );
  


class Level3 extends StatefulWidget 
  final String level3Data;
  final Function setTextField;
  Level3(this.level3Data, this.setTextField);

  @override
  _Level3State createState() 
    print('Level3: $this.level3Data');
    return _Level3State(this.level3Data, this.setTextField);
  


class _Level3State extends State<Level3> 
  String level3Data;
  Function setTextField;
  _Level3State(this.level3Data, this.setTextField);

  @override
  Widget build(BuildContext context) 
    return Column(
      children: <Widget>[
        TextField(
          onChanged: setTextField,
        ),
        Text(level3Data),
      ],
    );
  

当应用程序启动时,_MyAppState 状态类中的字符串data 在构建MainApp 时传递给MainApp StatelessWidget。以类似的方式,然后将信息传递给Level2StatelessWidget 和Level3StatefulWidget。

Level3中的TextField发生变化时,setTextField被触发。 setTextFieldMyApp 中定义,我认为当字符串data 更改(在setState() 内)时,它应该触发重建所有受更改影响的小部件。

MainApp 小部件 (appBar) 中的文本发生变化,显示 MainApp 已按预期重建。但是Level3 小部件没有任何反应;它没有重建。 createState() 方法中的 print 语句不会执行,Text 小部件的文本不会改变。

为什么Level3 StatefulWidget 不重建?

【问题讨论】:

您不应将构造函数添加到State。见***.com/questions/50818770/… 谢谢雷米。在您的链接中,我看到有一条评论“...您通过构造函数传递给 State 对象的任何内容都不会得到更新”这可能与我的问题有关吗? 是的,这是相关的。按照这个答案的建议做(没有构造函数,改用widget 属性)。这应该可以解决您的问题 是的,它确实解决了问题!所以,我猜有状态的小部件对象没有重建——它是不可变的,具有final 属性。但是 State 对象的属性可以改变(?)。如果您在下面解释发生了什么作为答案,我将很乐意接受。 ...再次感谢您的回复。 【参考方案1】:

据我了解,有状态小部件 Level3 已经在树中的给定位置创建。 并根据stateful widget的合同, 小部件实现者有责任使用 State.setState 确保在此类状态更改时及时通知状态。 因此,在您的情况下,您需要在 level3 中设置状态才能再次收到更改通知。 重建只发生在级别 1,因为调用了 setState。

重建过程递归地继续,直到用户界面的描述完全具体。 在第 3 级中,该类有自己的一组字段 字符串级别3数据; 函数 setTextField; 这意味着需要使用 SetState 再次设置这些,或者如果您执行此类操作,则小部件构建将继续。

class Level3 extends StatefulWidget 
  final String level3Data;
  final Function setTextField;
  Level3(this.level3Data, this.setTextField);

  @override
  _Level3State createState() 
    print('Level3: $this.level3Data');
    return _Level3State();
  


class _Level3State extends State<Level3> 
  // String level3Data;
  // Function setTextField;

  @override
  Widget build(BuildContext _) 
    return Column(
      children: <Widget>[
        TextField(
          onChanged: widget.setTextField,
        ),
        Text(widget.level3Data),
      ],
    );
  

【讨论】:

感谢您的回复。据我了解,setState 处于 MyApp 的状态,但小部件 MainApp(即与 setState 所在位置不同的小部件)重建。我知道这是因为标题文本发生了变化......我的理解是否错误? 稍微编辑了我的答案:)。希望对您的理解有所帮助。

以上是关于有状态与无状态的区别如何影响小部件何时重建?的主要内容,如果未能解决你的问题,请参考以下文章

如何调用无状态小部件的重建?

Flutter:何时创建无状态或有状态页面?

我们如何使用 Flutter 中的底部导航栏触发有状态小部件以通过导航重建自身?

当另一个小部件的状态改变时重建小部件

如何在有状态或无状态小部件之外访问 BuildContext?

Flutter:了解无状态/有状态小部件如何布局其子级?