首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何获得StatefulWidget的状态?

如何获得StatefulWidget的状态?
EN

Stack Overflow用户
提问于 2021-03-02 03:28:44
回答 2查看 1.1K关注 0票数 1

我刚开始使用StatefulWidget的状态,它的方法是在小部件中添加一个state属性,例如:

代码语言:javascript
复制
// 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;
    });
  }
}

用法:

代码语言:javascript
复制
final redContainer = _CustomContainer();
final button = FlatButton(
      onPressed: () {
        // call widget function
        redContainer.changeColor();
      },
      child: Text('change color'),
    );

它有效,但我想知道有什么隐藏的危险吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-03-02 05:07:47

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

声明性与祈使性

颤振UI的方法/哲学是声明性UI与命令式UI。

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

  • 创建一个对象
  • 对象保存状态(信息)
  • 对象暴露方法
  • 使用方法将更改强加于对象→UI更改

声明性方法:

  • 对象上方有状态(信息)。
  • 您的对象从该状态声明(创建)。
  • 如果州变了..。
  • 使用已更改的状态重新创建对象。

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

CustomContainer是用color声明的;状态已知/保持在CustomContainer之外&用于其构造。

在构建之后,您不能在CustomContainer上强制进行颜色更改。在命令式框架中,您将公开一个方法,changeColor(color)并调用该方法,该框架将神奇地显示一个新的颜色。

在颤振中,要更改CustomContainer的颜色,可以使用新的颜色声明CustomContainer

代码语言:javascript
复制
import 'package:flutter/material.dart';

/// UI object holds no modifiable state.
/// It configures itself once based on a declared color.
/// If color needs to change, pass a new color for rebuild
class CustomContainer extends StatelessWidget {
  final Color color; 

  CustomContainer(this.color);

  @override
  Widget build(BuildContext context) {
    return Container(
      color: color,
      child: Text('this is a colored Container'),
    );
  }
}

/// A StatefulWidget / screen to hold state above your UI object
class DeclarativePage extends StatefulWidget {
  @override
  _DeclarativePageState createState() => _DeclarativePageState();
}

class _DeclarativePageState extends State<DeclarativePage> {
  var blue = Colors.blueAccent.withOpacity(.3);
  var red = Colors.redAccent.withOpacity(.3);
  
  Color color;
  // state (e.g. color) is held in a context above your UI object
  
  @override
  void initState() {
    super.initState();
    color = blue;
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Declarative Page'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            CustomContainer(color),
            // color "state" ↑ is passed in to build/rebuild your UI object 
            RaisedButton(
              child: Text('Swap Color'),
                onPressed: () {
                  setState(() {
                    toggleColor();
                  });
                }
            )
          ],
        ),
      ),
    );
  }

  void toggleColor() {
    color = color == blue ? red : blue;
  }
}

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

setState()重构与性能

就性能而言,在小部件树下的单个小部件需要重建时,重新构建整个屏幕似乎是浪费的。

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

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

ChangeWrapper将包装CustomContainer (更改颜色)。

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

代码语言:javascript
复制
import 'package:flutter/material.dart';

class ChangeWrapper extends StatefulWidget {
  @override
  _ChangeWrapperState createState() => _ChangeWrapperState();
}

class _ChangeWrapperState extends State<ChangeWrapper> {
  final blue = Colors.blueAccent.withOpacity(.3);
  final red = Colors.redAccent.withOpacity(.3);

  Color _color; // this is state that changes

  @override
  void initState() {
    super.initState();
    _color = blue;
  }
  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        CustomContainer(_color),
        RaisedButton(
            child: Text('Swap Color'),
            onPressed: () {
              setState(() {
                toggleColor();
              });
            }
        )
      ],
    );
  }

  void toggleColor() {
    _color = _color == blue ? red : blue;
  }
}

/// UI object holds no state, it configures itself once based on input (color).
/// If color needs to change, pass a new color for rebuild
class CustomContainer extends StatelessWidget {
  final Color color;

  CustomContainer(this.color);

  @override
  Widget build(BuildContext context) {
    return Container(
      color: color,
      child: Text('this is a colored Container'),
    );
  }
}

class DeclarativePage extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    print('Declarative Page re/built');
    return Scaffold(
      appBar: AppBar(
        title: Text('Declarative Page'),
      ),
      body: Center(
        child: ChangeWrapper(),
      ),
    );
  }
}

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

如果DeclarativePage具有数百个小部件,那么以上述方式重新构建小部件可能是非常重要或有用的。在小屏幕上,如第一个例子中的顶部,甚至是具有几十个小部件的平均屏幕上,节省的差别很可能可以忽略不计。

颤振设计为每秒60帧。如果屏幕能够在16毫秒内构建/重建所有小部件(1000毫秒/ 60帧=16.67ms/帧),用户将不会看到任何jankiness。

当你使用动画时,它们被设计成每秒运行60帧(滴答)。也就是说,动画中的小部件将被重建60次,每秒钟动画运行。

这是正常的颤振操作,它是为之设计和建造的。因此,当您考虑是否可以优化您的小部件体系结构时,考虑它的上下文或如何使用这组小部件是很有用的。如果小部件组不在动画中,则每个屏幕呈现或每个人工按钮点击生成一次.优化可能不是什么大问题。

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

顺便说一下,这个视频系列是颤振体系结构的一个很好的概述。.为了节省CPU周期,实例化、渲染和定位元素对象,我猜Flutter有很多隐藏的优化,比如当Widget没有实质性的/实质性的改变时重新使用元素。

票数 3
EN

Stack Overflow用户

发布于 2021-03-02 04:21:17

添加要添加状态的setState()方法

票数 -2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66432893

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档