如何获得StatefulWidget的状态? [英] How to get StatefulWidget's state?

查看:85
本文介绍了如何获得StatefulWidget的状态?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是扑朔迷离的新手,而我获取StatefulWidget状态的方法是向窗口小部件添加state属性,例如:

I am new to flutter and the way I get StatefulWidget's state is add a state property to widget, eg:

// 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;
    });
  }
}

用法:

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

它有效,但我想知道是否存在任何隐患?

It works, but I wonder is there any hidden danger?

推荐答案

您会发现以强制性方式(如在问题中)操纵Flutter小部件非常尴尬.这是因为Flutter采用了声明性方法来构建屏幕.

You'll notice it's very awkward to manipulate Flutter widgets in an imperative fashion, like in the question. This is because of the declarative approach Flutter has taken to building screens.

Flutter UI的方法/哲学是声明性UI与命令性UI.

The approach / philosophy of Flutter UI is a declarative UI vs. an imperative UI.

上面问题中的示例倾向于命令式方法.

The example in the question above leans toward an imperative approach.

  • 创建对象
  • 对象保存状态(信息)
  • 对象公开方法
  • 使用方法对对象施加更改→UI更改

一种声明式方法:

  • 对象上方有状态(信息)
  • 从该状态声明(创建)您的对象
  • 如果状态发生变化...
  • 以更改后的状态重新创建对象

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

Below I've tried to convert the imperative approach above, into a declarative one.

CustomContainer 声明的,带有 color ;已知状态/保留在 CustomContainer &用于建筑.

CustomContainer is declared with a color; state known / kept outsideCustomContainer & used in its construction.

构造后,不能在 CustomContainer 施加颜色更改.在命令式框架中,您将公开一个方法 changeColor(color)并调用该方法,框架将神奇地显示一种新颜色.

After construction, you cannot impose a color change on CustomContainer. In an imperative framework you would expose a method, changeColor(color) and call that method and the framework would do magic to show a new color.

在Flutter中,要更改 CustomContainer 的颜色,请使用新颜色声明 CustomContainer .

In Flutter, to change color of CustomContainer, you declare CustomContainer with a new color.

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上的声明式与命令式.

在性能方面,当单个小部件需要重建时,重建整个屏幕似乎很浪费.

Performance-wise it seems wasteful to rebuild the entire screen when a single widget, way down in the widget tree needs rebuilding.

在可能的情况下(明智的做法),最好包装具有状态&的特定元素.需要在StatefulWidget中重建,而不是将整个页面包装在StatefulWidget中并重建所有内容.(同样,这甚至不是问题,我将在下面进一步讨论.)

When possible (and sensible) it's better to wrap the particular elements that have state & need rebuilding in a StatefulWidget, rather than wrapping your entire page in a StatefulWidget and rebuilding everything. (Likely, this wouldn't even be a problem, I'll discuss further below.)

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

Below I've modified the above example, moving the StatefulWidget from being the entire DeclarativePage, to a ChangeWrapper widget.

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

ChangeWrapper will wrap the CustomContainer (which changes color).

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

DeclarativePage is now a StatelessWidget and won't be rebuilt when toggling color of CustomContainer.

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小部件.您可以在控制台输出中查看重新构建/声明性页面".在第一次构建/查看屏幕时,仅将其写入调试控制台一次.

When running this version of the code, only the ChangeWrapper widget is rebuilt when swapping colors via button press. You can watch the console output for "Declarative Page re/built" which is written to debug console only once upon first screen build/view.

如果DeclarativePage包含数百个小部件,则以上述方式隔离小部件重建可能是有意义的或有用的.在小屏幕(如第一个示例中的顶部)或什至是带有几十个小部件的普通屏幕上,节省的差异可能微不足道.

If DeclarativePage was huge with hundreds of widgets, isolating widget rebuilds in the above manner could be significant or useful. On small screens like in the first example at top or even or average screens with a couple dozen widgets, the difference in savings are likely negligible.

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

Flutter was designed to operate at 60 frames per second. If your screen can build / rebuild all widgets within 16 milliseconds (1000 milliseconds / 60 frames = 16.67 ms per frame), the user will not see any jankiness.

使用动画时,这些动画被设计为以每秒60帧(滴答声)的速度运行.也就是说,动画中的小部件将每秒运行动画60次.

When you use animations, those are designed to run at 60 frames (ticks) per second. i.e. the widgets in your animation will be rebuilt 60 times each second the animation runs.

这是正常的Flutter操作,它是针对其设计的&内置的.因此,当您考虑是否可以优化窗口小部件体系结构时,考虑其上下文或该组窗口小部件的使用方式将很有用.如果小部件组不在动画中,则为每个屏幕渲染构建一次,或者为每个人类按钮点击构建一次……优化可能不是大问题.

This is normal Flutter operation for which it was designed & built. So when you're considering whether your widget architecture could be optimized it's useful to think about its context or how that group of widgets will be used. If the widget group isn't in an animation, built once per screen render or once per human button tap... optimization is likely not a big concern.

动画中的大量小部件……可能是考虑优化和优化的好人选性能.

A large group of widgets within an animation... likely a good candidate to consider optimization & performance.

顺便说一句,该视频系列很好地概述了Flutter架构.我猜Flutter有很多隐藏的优化功能,例如当Widget并未实质性/实质性地改变时,元素可以重复使用,以节省实例化,渲染和渲染的CPU周期.定位元素对象.

Btw, this video series is a good overview of the Flutter architecture. I'm guessing Flutter has a bunch of hidden optimizations such as Element re-use when a Widget hasn't materially/substantially changed, in order to save on CPU cycles instantiating, rendering & positioning Element objects.

这篇关于如何获得StatefulWidget的状态?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆