Widget 中的 State 解析
StatefulWidget 应对有交互、需要动态变化视觉效果的场景
StatelessWidget 则用于处理静态的、无状态的视图展示
那么,StatelessWidget 是否有存在的必要?StatefulWidget 是否是 Flutter 中的万金油?
UI 编程范式
在 Flutter 中,如何调整一个控件(Widget)的展示样式,即 UI 编程范式。
原生系统(Android、iOS)或原生 JavaScript 开发中,视图开发是命令式的,需要精确地告诉操作系统或浏览器用何种方式去做事情。比如,如果想要变更界面的某个文案,则需要找到具体的文本控件并调用它的控件方法命令,才能完成文字变更。
下述代码分别展示在 Android、iOS 及原生 Javascript 中,如何将一个文本控件的展示文案更改为 Hello World:
// Android 设置某文本控件展示文案为 Hello World
TextView textView = (TextView) findViewById(R.id.txt);
textView.setText("Hello World"); // iOS 设置某文本控件展示文案为 Hello World
UILabel *label = (UILabel *)[self.view viewWithTag:1234];
label.text = @"Hello World"; // 原生 JavaScript 设置某文本控件展示文案为 Hello World
document.querySelector("#demo").innerHTML = "Hello World!";
与此不同的是,Flutter 的视图开发是声明式的,其核心设计思想就是将视图和数据分离,这与 React 的设计思路完全一致。
Flutter 要实现同样的需求,则要麻烦点:除了设计好 Widget 布局方案之外,还需要提前维护一套文案数据集,并为需要变化的 Widget 绑定集中的数据,使 Widget 根据这个数据集完成渲染。
但是,当需要变更界面的文案时,只要改变数据集中的文案数据,并通知 Flutter 框架触发 Widget 的重新渲染即可。比起命令式的视图开发方式需要挨个设置不同组建(Widget)的视觉属性,这种方式要便捷得多。
总结来说,命令式编程强调精确控制过程细节,而声明式编程强调通过意图输出结果整体。对应到 Flutter 中,意图是绑定来组件状态的 State,结果则是重新渲染后的组件。在 Widget 的声明周期内,应用到 State 中的任何更改都将强制 Widget 重新构建。
其中,对于组件完成创建后就无需变更的场景,状态的绑定是可选项。这里的“可选”就区分出了 Widget 的两种类型,即:StatelessWidget 不带绑定状态,而 StatefulWidget 带绑定状态。当所要构建的用户界面不随任何状态信息的变化而变化时,需要选择使用 StatelessWidget,反之则选用 StatefulWidget。前者一般用于静态内容的展示,而后者则用于存在交互反馈的内容呈现中。
StatelessWidget
在 Flutter 中,Widget 采用由父到子、自顶向下的方式进行构建,父 Widget 控制着 Widget 的显示样式,其样式配置由父 Widget 在构建时提供。
用如上方式构建出的 Widget,有些(比如 Text、Container、Row、Column 等)在创建时,除了这些配置参数之外不依赖于任何其他信息,换句话说,它们一旦创建成功就不再关心、也不响应任何数据变化进行重绘。在 Flutter 中,这样的 Widget 被称为 StatelessWidget(无状态组件)。
以 Text 的部分源码为例,说明 StatelessWidget 的构建过程。
class Text extends StatelessWidget { // 构造方法及属性声明部分
const Text(this.data, {
key key,
this.textAlign,
this.textDirection,
// 其他参数
...
}) : assert(data != null),
textSpan = null,
super(key: key); final string data;
final TextAlign textAlign;
final TextDirection textDirection;
// 其他属性
... @override
Widget build(BuildContext context) {
...
Widget result = RichText(
// 初始化配置
...
);
...
return result;
}
}
代码中可以看到,在构造方法将其属性列表赋值后,build 方法随即将子组件 RichText 通过其属性列表(如文本 data、对齐方式 textAlign、文本展示方向 textDirection 等)初始化后返回,之后 Text 内部不再响应外部数据的变化。
那么,什么应用场景下应该使用 StatelessWidget 呢?
简单的判断规则:父 Widget 是否能够通过初始化参数完全控制其 UI 展示效果? 如果能,那么就可以使用 StatelessWidget 来设计构造函数接口了。
比如错误信息提示的弹框控件,可以采用继承 StatelessWidget 的方式,来进行组件自定义。计数器按钮需要通过继承 StatefulWidget 的方式,来进行组件自定义。
StatefulWidget
有一些 Widget(比如 Image、Checkbox)的展示,除了父 Widget 初始化时传入的静态配置之外,还需要处理用户的交互(比如,用户点击按钮)或其内部数据的变化(比如,网络数据回包),并体现在 UI 上。换句话说,这些 Widget 创建完成后,还需要关心和响应数据变化来进行重绘。在 Flutter 中,这一类 Widget 被称为 StatefulWidget(有状态组件)。
StatefulWidget 是以 State 类代理 Widget 构建的设计方式实现的。接下来,以 Image 的部分源码为例,说明 StatefulWidget 的构造过程。
和上面的 Text 一样,Image 类的构造函数会接收要被这个类使用的属性参数。然而,不同的是,Image 类并没有 build 方法来创建视图,而是通过 createState 方法创建来一个类型为 _ImageState 的 state 对象,然后由这个对象负责视图的构建。
这个 state 对象持有并处理来 Image 类中的状态变化,所以就以 _imageInfo 属性为例来展开说明。
_imageInfo 属性用来给 Widget 加载真实的图片,一旦 State 对象通过 _handleImageChanged 方法监听到 _imageInfo 属性发生来变化,就会立即调用 _ImageStage 类的 setState 方法通知 Flutter 框架:“数据变了,请使用更新后的 _imageInfo 数据重新加载图片!”。Flutter 框架则会标记视图状态,更新 UI。
StatefulWidget 不是万金油,要慎用
事实上,StatefulWidget 的滥用会直接影响 Flutter 应用的渲染性能。
Widget 是不可变的,更新则意味着销毁 + 重建(build)。StatelessWidget 是静态的,一旦创建则无需更新;而对于 StateFulWidget 来说,在 State 类中调用 setState 方法更新数据,会触发视图的销毁和重建,也将间接地触发其每个子 Widget 的销毁和重建。
如果根布局是一个 StatefulWidget,在其 State 中每调用一次更新 UI,都将是一整个页面所有 Widget 的销毁和重建。
虽然 Flutter 内部通过 Element 层可以最大程度地降低对真实渲染视图的修改,提高渲染效率,而不是销毁整个 RenderObject 树重建。但,大量 Widget 对象的销毁重建是无法避免的。如果某个子 Widget 的重建涉及到一些耗时操作,那页面的渲染性能将会急剧下降。
因此,**正确评估视图展示需求,避免无谓的 StatefulWidget 使用,是提高 Flutter 应用渲染性能最简单也是最直接的手段。
Widget 中的 State 解析的更多相关文章
- Flutter Widget中的State
一.Flutter 的声明式视图开发 在原生系统(Android.iOS)或原生JavaScript 开发的话,应该知道视图开发是命令式的,需要精确地告诉操作系统或浏览器用何种方式去做事情. 比如,如 ...
- Postgres中postmaster代码解析(中)
今天我们对postmaster的以下细节进行讨论: backend的启动和client的连接请求的认证 客户端取消查询时的处理 接受pg_ctl的shutdown请求进行shutdown处理 2.与前 ...
- 沉淀再出发:java中线程池解析
沉淀再出发:java中线程池解析 一.前言 在多线程执行的环境之中,如果线程执行的时间短但是启动的线程又非常多,线程运转的时间基本上浪费在了创建和销毁上面,因此有没有一种方式能够让一个线程执行完自己的 ...
- java中采用dom4j解析xml文件
一.前言 在最近的开发中用到了dom4j来解析xml文件,以前听说过来解析xml文件的几种标准方式:但是从来的没有应用过来,所以可以在google中搜索dmo4j解析xml文件的方式,学习一下dom4 ...
- 转:在java中使用dom4j解析xml
JAVA 使用Dom4j 解析XML Java DOM4J Parser - Parse XML Document Dom4j下载及使用Dom4j读写XML简介 在java中使用dom4j解析xml ...
- Android中的XML解析
在安卓中主要有三种XML文档解析方式:DOM(Document Object Model), SAX(Simple API for XML), PULL 他们的主要特点如下表: 特点 主要类 DO ...
- Android中使用Gson解析JSON数据的两种方法
Json是一种类似于XML的通用数据交换格式,具有比XML更高的传输效率;本文将介绍两种方法解析JSON数据,需要的朋友可以参考下 Json是一种类似于XML的通用数据交换格式,具有比XML更高的 ...
- julia与python中的列表解析.jl
julia与python中的列表解析.jl #=julia与python中的列表解析.jl 2016年3月16日 07:30:47 codegay julia是一门很年轻的科学计算语言 julia文档 ...
- Hadoop中的InputFormat解析
1.InputFormat InputFormat是Hadoop平台上Mapreduce输入的规范,仅有两个抽象方法. List<InputSplit> getSplits(), 获取由输 ...
随机推荐
- 基础部分之System、Object、Calendar、StringBuffer
System.out.println(obj)和 System.out.println(obj.toString())区别 public static void main(String[] args) ...
- 一篇文章让你马上入门Hibernate
在前面我们学完了Struts2,接下来我们就要去学习第二个框架Hibernate. 那什么是Hibernate? Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对 ...
- Nginx总结(二)基于ip的虚拟主机配置
前面讲了如何安装配置Nginx,大家可以去这里看看nginx系列文章:https://www.cnblogs.com/zhangweizhong/category/1529997.html 今天要说的 ...
- 如何比较装X地回答问题 | 面试系列.1
公众号后台比较多同学让我写面试相关的文章,在<从面试官的角度谈谈大数据面试>也聊了很多,但是有同学吐槽说我没有把答案写上去,当时我的心里是拒绝写的,这有啥好写的,又不难,Google一大堆 ...
- Docker学习总结(一)--Docker简介
什么是虚拟化 在计算机中,虚拟化是一种资源管理技术,是将计算机的各种实体资源,如服务器.网络.内存等,以抽象.转换后呈现出来,打破实体结构间的不可切割的障碍,使用户可以比之前更好的应用这些资源. 在实 ...
- linux的一些简单命令
简单学习了一些linux相关的知识,自己做一个简单的总结,可以在一般工作中命令行状态下装装B,哈哈 正则相关 grep grep xxx yyy.file 查找出yyy文件中存在xxx的行 通配符 * ...
- eclipse中导入外部包却无法查看对应源码或Javadoc的入坑指南
eclipse中导入外部包却无法查看对应源码或Javadoc的 入坑指南 出现这个错误的原因是,你虽然导入了.jar包,但没有配置对应的Javadoc或源码路径,所以在编辑器中无法查看源 码和对应AP ...
- 2019nc#4
题号 标题 已通过代码 题解 通过率 团队的状态 A meeting 点击查看 树直径 604/2055 B xor 点击查看 线段树维护线性基交 81/861 未通过 C sequence 点击 ...
- Could not delete D:/workspace/.metadata/.plugins/org.eclipse.wst.server.core/tmp1/wtpwebapps/platform/WEB-INF/lib
再把之前的maven工程删掉时,出现了如下错误: Could not delete D:/workspace/.metadata/.plugins/org.eclipse.wst.server.cor ...
- 实验吧CTF练习题---WEB---FALSE解析
实验吧web之FALSE 地址:http://www.shiyanbar.com/ctf/1787 flag值:CTF{t3st_th3_Sha1} 解题步骤: 1.点开题目链接,观察题意 2.题 ...