Flutter 即学即用系列博客——05 StatelessWidget vs StatefulWidget

前言
上一篇我们对 Flutter UI 有了一个基本的了解。
这一篇我们通过自定义 Widget 来了解下如何写一个 Widget?
然而 Widget 有两个,StatelessWidget 和 StatefulWidget,我们要继承哪一个?
下面让我们跟着文章来探索一番。
目录

1. StatelessWidget
我们先来看下继承的 Widget 为 StatelessWidget 的情况。
第一步:新建一个文件 bold_text.dart

这里文件名后面后缀 .dart 可带可不带

文件名多个单词组成用下划线分隔。
这里我们演示直接在 lib 文件夹下面创建,实际项目记得文件夹结构的组织哦~
第二步:import 系统包
一般自定义 Widget 都要 import 下面的一个包。
import 'package:flutter/material.dart';
IDE 有自动提示和补全功能,因此不用死记硬背。

第三步:自定义一个类继承自 StatelessWidget
一般类名跟文件名一致就可以,采用驼峰格式命名。
import 'package:flutter/material.dart';
class BoldText extends StatelessWidget {
}
第四步:实现一个需要 override 的方法 build
import 'package:flutter/material.dart';
class BoldText extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return null;
}
}
一般第三步操作之后 IDE 有提示,直接使用快捷修复自动追加 build 代码即可。如下图:

第五步:实现 Widget
上述代码的 TODO 表示我们要在里面实现对应的 Widget。所以我们删除 TODO,然后在写我们要返回的 Widget 来替换 null 即可。
我们写一个单独的方法 _buildWidget 来返回 Widget,同时返回我们之前写的 Text,如下:
import 'package:flutter/material.dart';
class BoldText extends StatelessWidget {
@override
Widget build(BuildContext context) {
return _buildWidget();
}
Widget _buildWidget() {
return Text(
'Hello, world!',
textDirection: TextDirection.ltr,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontWeight: FontWeight.bold),
);
}
}
可以看到我们这个 Widget 应该会显示成上篇我们界面所见的粗体文本。
但是这里 Hello, world! 写死了,我们要让这个自定义 Widget 通用一些,可以定义一个必传参数文本内容,修改如下:
import 'package:flutter/material.dart';
class BoldText extends StatelessWidget {
final String data;
BoldText(this.data);
@override
Widget build(BuildContext context) {
return _buildWidget();
}
Widget _buildWidget() {
return Text(
data,
textDirection: TextDirection.ltr,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontWeight: FontWeight.bold),
);
}
}
可以看到我们定义了一个变量,通过构造函数让外部传进来。
这里的 BoldText(this.data); 等价于 Android 下面代码:
BoldText(String data) {
this.data = data;
}
可以看到 dart 的语法糖简化了写法。具体更多构造函数写法可以查看 dart 官网。

2. 自定义 Widget 使用
我们以之前的 main.dart 为例进行讲解。
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Text(
'Hello, world!',
textDirection: TextDirection.ltr,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontWeight: FontWeight.bold),
),
);
}
}
第一步:导入我们的自定义 Widget 包
相对路径:
import 'bold_text.dart';
绝对路径:
import 'package:my_flutter/bold_text.dart';
上面任选其一即可。主要是相对路径和绝对路径的区别。
第二步:使用
import 'package:flutter/material.dart';
import 'bold_text.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: BoldText('Hello, world!'),
);
}
}
对比可以看到节省了很多代码行,尤其对于有多个地方用到的公共组件更加可以这样处理。
3. StatelessWidget 通用模板
FileName为你文件名的驼峰形式:
import 'package:flutter/material.dart';
class FileName extends StatelessWidget {
@override
Widget build(BuildContext context) {
return _buildWidget();
}
Widget _buildWidget() {
//TODO build your widget
}
}
4. StatefulWidget
我们再来看下继承的 Widget 为 StatefulWidget 的情况。
第一步:新建 increment.dart 文件
第二步:import 系统包
第三步:自定义一个类继承自 StatefulWidget
第四步:实现一个需要 override 的方法 createState
到这里就有点不一样了。我们先看下目前的代码。
import 'package:flutter/material.dart';
class Increment extends StatefulWidget{
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return null;
}
}
和 StatelessWidget 不一样,这里不是返回 Widget。
我们看下如何操作。
第五步:创建一个类继承 State< T extends StatefulWidget>
这里我们创建 _IncrementState 类继承 State< Increment>,这里尖括号<>里面的类型就是我们一开始写的继承自 StatefulWidget 的类 Increment。
然后我们需要实现一个需要 override 的方法 build。
到这里是不是就是很熟悉了。
直接看代码:
import 'package:flutter/material.dart';
class Increment extends StatefulWidget{
@override
State<StatefulWidget> createState() {
return _IncrementState();
}
}
class _IncrementState extends State<Increment> {
@override
Widget build(BuildContext context) {
// TODO: implement build
return null;
}
}
所以接下来的工作就是类似的。
第六步:实现 Widget
参考一开始的例子我们简单写出下面代码:
import 'package:flutter/material.dart';
class Increment extends StatefulWidget{
@override
State<StatefulWidget> createState() {
return _IncrementState();
}
}
class _IncrementState extends State<Increment> {
int _count = 0;
void _incrementCount() {
setState(() {
_count++;
});
}
@override
Widget build(BuildContext context) {
return _buildPage();
}
Widget _buildPage() {
return MaterialApp(
home: Scaffold(
body: Center(
child : Text('$_count')
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCount,
tooltip: 'Increment',
child: Icon(Icons.add),
),
),
);
}
}
这里面需要说明的是多了一个新的 Widget FloatingActionButton。
可以看到它是作为 Scaffold 自带的一个属性的。
FloatingActionButton 讲解:
onPressed 后面是这个按钮点击之后会回调的一个方法。
tooltip 是长按之后会显示的提示文字。
child 是这个按钮显示的图标。
我们修改 main.dart 文件如下,看下效果:
import 'package:flutter/material.dart';
import 'increment.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Increment();
}
}
效果如下:

这里重点的代码是下面:
setState(() {
_count++;
});
它表示将数字加一之后更新界面。
需要更新界面时需要调用 setState 方法。
更新数据源可以在 setState 方法里面写。
5. StatefulWidget 通用模板
FileName为你文件名的驼峰形式,_FileNameState 里面的 FileName 也是哦~
import 'package:flutter/material.dart';
class FileName extends StatefulWidget{
@override
State<StatefulWidget> createState() {
return _FileNameState();
}
}
class _FileNameState extends State<FileName> {
@override
Widget build(BuildContext context) {
return _buildPage();
}
Widget _buildPage() {
//TODO build your widget
}
}
到了这里你回过头去看新建 Flutter 项目时自动创建的 main.dart 文件就看得懂了。
6. StatelessWidget vs StatefulWidget
好了,上面讲解完了 StatelessWidget 和 StatefulWidget,相信大家应该知道如何自定义一个 Widget 了,也知道如何在其他页面引入了。
但是我们实际上在使用的时候到底是要继承 StatelessWidget 还是 StatefulWidget 呢?
其实根据名称可以看出取决于你这个 Widget 是有状态还是无状态?
不过「状态」这个词也不是好理解。
所以笔者是这样来区分使用 StatelessWidget 还是 StatefulWidget的?
看界面是否需要更新
比如我们上面的例子,点击按钮文本更新了,所以我们选择了 StatefulWidget。
而第一个只是字体调整,界面渲染之后不再需要更新了,所以我们选择了 StatelessWidget。
所以我们可以认为当界面需要更新时,我们的自定义 Widget 就要继承 StatefulWidget 而不是 StatelessWidget。
更多阅读:
Flutter 即学即用系列博客——01 环境搭建
Flutter 即学即用系列博客——02 一个纯 Flutter Demo 说明
Flutter 即学即用系列博客——03 在旧有项目引入 Flutter
Flutter 即学即用系列博客——04 Flutter UI 初窥

Flutter 即学即用系列博客——05 StatelessWidget vs StatefulWidget的更多相关文章
- Flutter 即学即用系列博客——09 EventChannel 实现原生与 Flutter 通信(一)
前言 紧接着上一篇,这一篇我们讲一下原生怎么给 Flutter 发信号,即原生-> Flutter 还是通过 Flutter 官网的 Example 来讲解. 案例 接着上一次,这一次我们让原生 ...
- Flutter 即学即用系列博客——08 MethodChannel 实现 Flutter 与原生通信
背景 前面我们讲了很多 Flutter 相关的知识点,但是我们并没有介绍怎样实现 Flutter 与原生的通信. 比如我在 Flutter UI 上面点击了一个按钮,我希望原生做一些处理,那么原生怎么 ...
- Flutter 即学即用系列博客——06 超实用 Widget 集锦
本篇文章我们来讲讲一些比较常用的 Widget. 大家验证的时候使用下面的代码替换 main.dart 代码,然后在 //TODO 语句返回下面常用 Widget 示例的代码. import 'pac ...
- Flutter 即学即用系列博客总结篇
前言 迟到的总结篇,其实大家看我之前发的系列博客最后一篇,发文时间是 3 月 29 日.距离现在快两个月了. 主要是因为有很多事情在忙,所以这篇就耽搁了. 今天终于可以跟大家会面了. 系列博客背景 F ...
- Flutter 即学即用系列博客——04 Flutter UI 初窥
前面三篇可以算是一个小小的里程碑. 主要是介绍了 Flutter 环境的搭建.如何创建 Flutter 项目以及如何在旧有 Android 项目引入 Flutter. 这一篇我们来学习下 Flutte ...
- Flutter 即学即用系列博客——09 MethodChannel 实现原生与 Flutter 通信(二)
前言 上一篇我们讲解了如何通过 EventChannel 实现 Android -> Flutter 的通信. 并且也看到了 Flutter 内部 EventChannel 源码也是对 Meth ...
- Flutter 即学即用系列博客——03 在旧有项目引入 Flutter
前言 其实如果打算在实际项目中引入 Flutter,完全将旧有项目改造成纯 Flutter 项目的可能性比较小,更多的是在旧有项目引入 Flutter. 因此本篇我们就说一说如何在旧有项目引入 Flu ...
- Flutter 即学即用系列博客——10 混淆
前言 之前的博客我们都是在 debug 的模式下进行开发的. 实际发布到市场或者给到用户的都是 release 包. 而对于 Android 来说,release 包一个重要的步骤就是混淆. Andr ...
- Flutter 即学即用系列博客——02 一个纯 Flutter Demo 说明
前言 上一篇文章我们搭建好了 Flutter 的开发环境. Flutter 即学即用--01 环境搭建 这一篇我们通过 Flutter 的一个 Demo 来了解下 Flutter. 开发系统:MAC ...
随机推荐
- 详解CSS选择器、优先级与匹配原理【转】
作为一个Web开发者,掌握必要的前台技术也是很重要的,特别是在遇到一些实际问题的时候.这里给大家列举一个例子: 给一个p标签增加一个类(class),可是执行后该class中的有些属性并没有起作用.通 ...
- datalist 分页
Asp.net提供了三个功能强大的列表控件:GridView.DataList和Repeater控件,相对GridView,DataList和Repeater控件具有更高的样式自定义性,很多时候我们喜 ...
- python爬虫入门(六) Scrapy框架之原理介绍
Scrapy框架 Scrapy简介 Scrapy是用纯Python实现一个为了爬取网站数据.提取结构性数据而编写的应用框架,用途非常广泛. 框架的力量,用户只需要定制开发几个模块就可以轻松的实现一个爬 ...
- Django入门二之模板语法
一. 模板变量 Context传入的可以是一个str,dict,list,甚至是一个实例对象 在html中如何调用这些对象进行取值呢 1. 变量名 {{ variable }} 返回字符串,无论是st ...
- Selenium2Lib库之鼠标事件常用关键字实战
1.2 鼠标事件常用关键字 1.2.1 Click Button关键字按F5 查看Click Button关键字的说明,如下图: Click Button关键字 是用于点击页面上的按钮.参数locat ...
- Python和Java的硬盘夜话
这是一个程序员的电脑硬盘,在一个叫做"学习"的目录下曾经生活着两个小程序,一个叫做Hello.java,即Java小子:另外一个叫做hello.c ,也就是C老头儿. C老头儿的命 ...
- 并行(Parallelism)与并发(Concurrency)
并行(Parallelism):多任务在同一时刻运行.例如,多个任务在多核处理器上运行. 并发(Concurrency):两个或者两个以上的任务在一段时间内开始.运行.完成,这意味着它们不是在同一时刻 ...
- SSM-SpringMVC-23:SpringMVC中初探异常解析器
------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 本篇博客要讲的是异常解析器,SimpleMappingExceptionResolver简单映射异常解析器 可 ...
- CentOS 安装Python3.x常见问题
CentOS 6.x自带的Python版本是2.6,CentOS 7.x上自带的是2.7,我们要自己安装Python3.X,配置环境,不过一般安装过程不会一帆风顺,往往有些报错,在CentOS以及其他 ...
- jquery的$.extend和$.fn.extend作用及区别/用span实现进度条/腾讯云IIS端口号修改
jQuery为开发插件提拱了两个方法,分别是: jQuery.fn.extend(); jQuery.extend(); 虽然 javascript 没有明确的类的概念,但是用类来理解它,会更方便. ...