前言

上一篇我们对 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的更多相关文章

  1. Flutter 即学即用系列博客——09 EventChannel 实现原生与 Flutter 通信(一)

    前言 紧接着上一篇,这一篇我们讲一下原生怎么给 Flutter 发信号,即原生-> Flutter 还是通过 Flutter 官网的 Example 来讲解. 案例 接着上一次,这一次我们让原生 ...

  2. Flutter 即学即用系列博客——08 MethodChannel 实现 Flutter 与原生通信

    背景 前面我们讲了很多 Flutter 相关的知识点,但是我们并没有介绍怎样实现 Flutter 与原生的通信. 比如我在 Flutter UI 上面点击了一个按钮,我希望原生做一些处理,那么原生怎么 ...

  3. Flutter 即学即用系列博客——06 超实用 Widget 集锦

    本篇文章我们来讲讲一些比较常用的 Widget. 大家验证的时候使用下面的代码替换 main.dart 代码,然后在 //TODO 语句返回下面常用 Widget 示例的代码. import 'pac ...

  4. Flutter 即学即用系列博客总结篇

    前言 迟到的总结篇,其实大家看我之前发的系列博客最后一篇,发文时间是 3 月 29 日.距离现在快两个月了. 主要是因为有很多事情在忙,所以这篇就耽搁了. 今天终于可以跟大家会面了. 系列博客背景 F ...

  5. Flutter 即学即用系列博客——04 Flutter UI 初窥

    前面三篇可以算是一个小小的里程碑. 主要是介绍了 Flutter 环境的搭建.如何创建 Flutter 项目以及如何在旧有 Android 项目引入 Flutter. 这一篇我们来学习下 Flutte ...

  6. Flutter 即学即用系列博客——09 MethodChannel 实现原生与 Flutter 通信(二)

    前言 上一篇我们讲解了如何通过 EventChannel 实现 Android -> Flutter 的通信. 并且也看到了 Flutter 内部 EventChannel 源码也是对 Meth ...

  7. Flutter 即学即用系列博客——03 在旧有项目引入 Flutter

    前言 其实如果打算在实际项目中引入 Flutter,完全将旧有项目改造成纯 Flutter 项目的可能性比较小,更多的是在旧有项目引入 Flutter. 因此本篇我们就说一说如何在旧有项目引入 Flu ...

  8. Flutter 即学即用系列博客——10 混淆

    前言 之前的博客我们都是在 debug 的模式下进行开发的. 实际发布到市场或者给到用户的都是 release 包. 而对于 Android 来说,release 包一个重要的步骤就是混淆. Andr ...

  9. Flutter 即学即用系列博客——02 一个纯 Flutter Demo 说明

    前言 上一篇文章我们搭建好了 Flutter 的开发环境. Flutter 即学即用--01 环境搭建 这一篇我们通过 Flutter 的一个 Demo 来了解下 Flutter. 开发系统:MAC ...

随机推荐

  1. HTML 标签小细节

    简书地址:https://www.jianshu.com/p/03a23aa28a34 今天重新学习了一下HTML中标签的用法,补充并记录一下自己新学到的知识. a中的href href Contai ...

  2. JS入门熟知

    JS是面向对象的语言 封装 继承 多态 聚集(对象中具有引用其他对象的能力) JS使用中绝大多数情况不需要进行面向对象的设计,很多情况是使用已经设计好,准备好的对象,基于对象的语言. JS的使用(引入 ...

  3. NOIP2017Day1题解

    Day1 T1.小学奥数... 代码: #include<iostream> #include<cstring> #include<string> #include ...

  4. Python_字符串之删除空白字符或某字符或字符串

    ''' strip().rstrip().lstrip()分别用来删除两端.右端.左端.连续的空白字符或字符集 ''' s='abc ' s2=s.strip() #删除空白字符 print(s2) ...

  5. VC++中字符串编码处理的一些相关问题

    前言 什么是tchar? 百度百科对其的定义如下": 因为C++支持两种字符串,即常规的ANSI编码(使用""包裹)和Unicode编码(使用L""包 ...

  6. linux 用户空间与内核空间——高端内存详解

    摘要:Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对 ...

  7. 使用EHCache需要注意的几个问题(转)

    最近做一个小项目,需要对一批数据进行缓存,且要求持久化到磁盘.使用ehcache非常简单和直观,一般来说只需要配置ehcache.xml文件,接着直接使用@Cacheable, @Cacheput, ...

  8. JS奇淫巧技:防抖函数与节流函数

    应用场景 实际工作中,我们经常性的会通过监听某些事件完成对应的需求,比如: 通过监听 scroll 事件,检测滚动位置,根据滚动位置显示返回顶部按钮 通过监听 resize 事件,对某些自适应页面调整 ...

  9. 关于PHP处理Json数据的例子

    最近工作需要在原来静态看板(大屏)页面的基础上,实现数据的动态展示,而且需要定时刷新. 接到任务后就马不停蹄的开始修改页面: 显然这个需求最好的解决方法就是用Ajax对后台数据进行定时请求,在前端页面 ...

  10. BZOJ_1662_[Usaco2006 Nov]Round Numbers 圆环数_数位DP

    BZOJ_1662_[Usaco2006 Nov]Round Numbers 圆环数_数位DP Description 正如你所知,奶牛们没有手指以至于不能玩“石头剪刀布”来任意地决定例如谁先挤奶的顺 ...