如何获取StatefulWidget的状态?

无夜之*_*之星辰 3 dart flutter

我是 flutter 的新手,获取 StatefulWidget 状态的方式是向 widget 添加状态属性,例如:

// ignore: must_be_immutable
class _CustomContainer extends StatefulWidget {
  _CustomContainer({Key key}) : super(key: key);

  @override
  __CustomContainerState createState() {
    state = __CustomContainerState();
    return state;
  }

  __CustomContainerState state;

  void changeColor() {
    if (state == null) return;
    // call state's function
    this.state.changeColor();
  }
}

class __CustomContainerState extends State<_CustomContainer> {
  var bgColor = Colors.red;

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 200,
      height: 200,
      color: bgColor,
    );
  }

  void changeColor() {
    setState(() {
      bgColor = Colors.blue;
    });
  }
}

Run Code Online (Sandbox Code Playgroud)

用法:

final redContainer = _CustomContainer();
final button = FlatButton(
      onPressed: () {
        // call widget function
        redContainer.changeColor();
      },
      child: Text('change color'),
    );
Run Code Online (Sandbox Code Playgroud)

可以用,但是不知道有没有什么隐患?

Bak*_*ker 6

您会注意到以命令式方式操作 Flutter 小部件非常尴尬,就像问题中一样。这是因为 Flutter 采用声明式方法来构建屏幕。

\n

声明式与命令式

\n

Flutter UI 的方法/理念是声明式 UI 与命令式 UI。

\n

上面问题中的例子倾向于命令式方法。

\n
    \n
  • 创建一个对象
  • \n
  • 对象保存状态(信息)
  • \n
  • 对象公开方法
  • \n
  • 使用方法对对象 \xe2\x86\x92 UI 更改进行更改
  • \n
\n

声明式方法:

\n
    \n
  • 你的物体上方有状态(信息)
  • \n
  • 您的对象是从该状态声明(创建)的
  • \n
  • 如果状态发生变化...
  • \n
  • 您的对象将使用更改后的状态重新创建
  • \n
\n
\n

下面我尝试将上面的命令式方法转换为声明式方法。

\n

CustomContainercolor;声明​ 状态已知/保留在外部CustomContainer并在其构造中使用。

\n

施工后,您无法对 进行颜色更改CustomContainer。在命令式框架中,您将公开一个方法,changeColor(color)并调用该方法,框架将神奇地显示新的颜色。

\n

在 Flutter 中,要更改 的颜色CustomContainer,您可以声明 CustomContainer一个新颜色。

\n
import \'package:flutter/material.dart\';\n\n/// UI object holds no modifiable state.\n/// It configures itself once based on a declared color.\n/// If color needs to change, pass a new color for rebuild\nclass CustomContainer extends StatelessWidget {\n  final Color color; \n\n  CustomContainer(this.color);\n\n  @override\n  Widget build(BuildContext context) {\n    return Container(\n      color: color,\n      child: Text(\'this is a colored Container\'),\n    );\n  }\n}\n\n/// A StatefulWidget / screen to hold state above your UI object\nclass DeclarativePage extends StatefulWidget {\n  @override\n  _DeclarativePageState createState() => _DeclarativePageState();\n}\n\nclass _DeclarativePageState extends State<DeclarativePage> {\n  var blue = Colors.blueAccent.withOpacity(.3);\n  var red = Colors.redAccent.withOpacity(.3);\n  \n  Color color;\n  // state (e.g. color) is held in a context above your UI object\n  \n  @override\n  void initState() {\n    super.initState();\n    color = blue;\n  }\n  \n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        title: Text(\'Declarative Page\'),\n      ),\n      body: Center(\n        child: Column(\n          mainAxisAlignment: MainAxisAlignment.center,\n          children: [\n            CustomContainer(color),\n            // color "state" \xe2\x86\x91 is passed in to build/rebuild your UI object \n            RaisedButton(\n              child: Text(\'Swap Color\'),\n                onPressed: () {\n                  setState(() {\n                    toggleColor();\n                  });\n                }\n            )\n          ],\n        ),\n      ),\n    );\n  }\n\n  void toggleColor() {\n    color = color == blue ? red : blue;\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

在 Flutter.dev 上阅读有关声明式与命令式的更多信息。

\n

setState() 重建和性能

\n

从性能角度来看,当小部件树中的单个小部件需要重建时,重建整个屏幕似乎很浪费。

\n

如果可能(并且合理),最好将具有状态且需要重建的特定元素包装在 StatefulWidget 中,而不是将整个页面包装在 StatefulWidget 中并重建所有内容。(很可能,这根本不是问题,我将在下面进一步讨论。)

\n

下面我修改了上面的示例,将 StatefulWidget 从整个 DeclarativePage 移动到 ChangeWrapper 小部件。

\n

ChangeWrapper 将包装 CustomContainer(它会改变颜色)。

\n

DeclarativePage 现在是一个 StatelessWidget,并且在切换 CustomContainer 的颜色时不会被重建。

\n
import \'package:flutter/material.dart\';\n\nclass ChangeWrapper extends StatefulWidget {\n  @override\n  _ChangeWrapperState createState() => _ChangeWrapperState();\n}\n\nclass _ChangeWrapperState extends State<ChangeWrapper> {\n  final blue = Colors.blueAccent.withOpacity(.3);\n  final red = Colors.redAccent.withOpacity(.3);\n\n  Color _color; // this is state that changes\n\n  @override\n  void initState() {\n    super.initState();\n    _color = blue;\n  }\n  @override\n  Widget build(BuildContext context) {\n    return Column(\n      mainAxisAlignment: MainAxisAlignment.center,\n      children: [\n        CustomContainer(_color),\n        RaisedButton(\n            child: Text(\'Swap Color\'),\n            onPressed: () {\n              setState(() {\n                toggleColor();\n              });\n            }\n        )\n      ],\n    );\n  }\n\n  void toggleColor() {\n    _color = _color == blue ? red : blue;\n  }\n}\n\n/// UI object holds no state, it configures itself once based on input (color).\n/// If color needs to change, pass a new color for rebuild\nclass CustomContainer extends StatelessWidget {\n  final Color color;\n\n  CustomContainer(this.color);\n\n  @override\n  Widget build(BuildContext context) {\n    return Container(\n      color: color,\n      child: Text(\'this is a colored Container\'),\n    );\n  }\n}\n\nclass DeclarativePage extends StatelessWidget {\n\n  @override\n  Widget build(BuildContext context) {\n    print(\'Declarative Page re/built\');\n    return Scaffold(\n      appBar: AppBar(\n        title: Text(\'Declarative Page\'),\n      ),\n      body: Center(\n        child: ChangeWrapper(),\n      ),\n    );\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

运行此版本的代码时,通过按下按钮交换颜色时仅重建 ChangeWrapper 小部件。您可以观看“声明性页面重新/构建”的控制台输出,该输出仅在第一个屏幕构建/查看时写入调试控制台一次。

\n

如果 DeclarativePage 非常庞大,包含数百个小部件,则以上述方式隔离小部件重建可能会很重要或有用。在小屏幕上,例如第一个示例中的顶部或带有几十个小部件的平均屏幕,节省的差异可能可以忽略不计。

\n

Flutter 设计为以每秒 60 帧的速度运行。如果您的屏幕可以在 16 毫秒内构建/重建所有小部件(1000 毫秒/60 帧 = 每帧 16.67 毫秒),用户将不会看到任何卡顿。

\n

当您使用动画时,它们被设计为以每秒 60 帧(滴答声)的速度运行。即动画运行时每秒将重建 60 次动画中的小部件。

\n

这是正常的 Flutter 操作,它是为此设计和构建的。因此,当您考虑是否可以优化您的小部件架构时,考虑其上下文或如何使用该组小部件会很有用。如果小部件组不在动画中,则每次屏幕渲染构建一次或每次人工按钮点击构建一次......优化可能不是一个大问题。

\n

动画中的一大组小部件...可能是考虑优化和性能的良好候选者。

\n

顺便说一句,这个视频系列很好地概述了 Flutter 架构。我猜测 Flutter 有很多隐藏的优化,例如当 Widget 没有发生重大/实质性更改时重新使用 Element,以节省实例化、渲染和定位 Element 对象的 CPU 周期。

\n