Flutter开发者

StatefulWidget

2018-03-21

上期回顾

在前面的文章中我们学习了许多的控件,但是无一例外他们都不能处理界面的动态变更,我们无法在界面初始化后根据用户的操作去更改界面的展示方式。是的这是一个很大的问题,在Flutter我们可以借助于StatefulWidget组件来完成界面的变更。

StatefulWidget又被称为有状态组件,开发者可以根据用户的操作来选择性的更新界面上的组件。

大家是否还记得我们每次创建项目,系统都会默认给我们生成的程序

每次当点击下面的FloatActionButton上面的数字就会+1

还是来看下官方代码是怎么实现的(为了便于大家理解我们去除了英文注释和无用参数设置)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import 'package:flutter/material.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}

class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);

final String title;

@override
_MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;

void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(

title: new Text(widget.title),
),
body: new Center(
child:
new Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
),
floatingActionButton: new FloatingActionButton(
onPressed: _incrementCounter,
child: new Icon(Icons.add),
),
);
}
}

代码很长对吧,希望大家不要被上面的代码吓到,其实还是跟原来的StatelessWidget的流程是一样的,只不过多了一个设置State的中间件而已。咱们来一点一点看下代码。

首先新建了一个App对象继承于StatelessWidget,返回了一哥MyHomePage对象。

1
2
3
4
5
6
7
8
9
10
void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}

MyHomePage对象继承于StateFulWdiget,但是继承StateFulWidget组件默认要返回继承于State的Wdiget,所以,MyHomePage又返回了一个_MyHomePageState对象

1
2
3
4

@override
_MyHomePageState createState() => new _MyHomePageState();
}

_MyHomePageState继承于State,同样它需要返回真正页面上展示的内容,但是这个组件内你可以调用 setState(() {})的回调函数来实现界面效果的更新。

1
2
3
4
5
void _incrementCounter() {
setState(() {
_counter++;
});
}

每当我们点击FloatActionButton按钮都会调用_incrementCounter方法,_incrementCounter方法会调用setState方法更新计数器的counter使其值+1,并且更新界面上View的状态。

由于界面上的Text的值是依据_counter的,所以每次更新界面都会显示最新的_counter的值,这样就达到了更新界面的效果。

关于点击事件

在上面的代码中大家可以看到floatingActionButton的onPressed点击事件我们传入了_incrementCounter,甚至连“()”也没有写。

从floatingActionButton的构造方法中我们可以看到,onPress需要传入的是一个没有返回值,没有参数的方即VoidCallback。

正好 _incrementCounter方法就是这么一个方法,所以我们可以直接把方法当做参数传入onPressed中。

举个例子

为了方便大家理解这个有状态组件,我们再来举个例子

内容区域是一个IndexedStack(栈里面依次放几个图标),默认加载第一个入栈的元素。下面是一个FloAtionButton,没当点击这个按钮是更新IndexedStack当前现实的元素位置,在这里我们使用Random来随机显示栈内Icons。

接下来,我们来看下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13

void main() {
runApp(new MaterialApp(
home: new MyHome(),
));
}

class MyHome extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return new MyHomeState();
}
}

首先,我们创建一个MyHome Widget继承于StatefulWidget,返回一个具有状态的MyhomeState组件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class MyHomeState extends State<MyHome> {
int currentIndex = 0;

void _onFloatActionButtonPressed() {
setState(() {
currentIndex =new Random().nextInt(4);
});
}

@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("StatefulWidget"),
),
body: new Center(
child: new IndexedStack(
index: currentIndex,
children: <Widget>[
new Icon(
Icons.cloud_download,
size: 100.0,
color: Colors.blue,
),
new Icon(
Icons.error_outline,
size: 100.0,
color: Colors.red,
),
new Icon(
Icons.check_circle,
size: 100.0,
color: Colors.green,
),
new Icon(
Icons.help,
size: 100.0,
color: Colors.yellowAccent,
)
],
)),
floatingActionButton: new FloatingActionButton(
onPressed: _onFloatActionButtonPressed,
child: new Icon(Icons.track_changes),
backgroundColor: Colors.blueAccent,
),
);
}
}

在MyHomeState中我们声明了一个变量currentIndex来记录栈内当前位置的值。

内部body是一个居中的IndexedStack,下面是一个floatingActionButton,每当我们点击这个按钮都会调用 _onFloatActionButtonPressed方法,更新currentIndex的值,然后更细UI显示效果。

上面的例子很简单,大家在平时的开发中可以根据用户操作的状态来显示指定的页面,如加载中、加载成功、加载失败、网络异常等状态。

小结

  1. StateFulWidget是有状态组件,负责处理有状态的界面
  2. 使用setState回调可以很轻松的更新界面上UI的显示效果
  3. 有状态组件变更归根到底是根据数据变更来做的操作
  4. VoidCallback是一个没有参数没有返回值的方法

试一试

今天的内容很简单,但是还是需要熟悉Flutter中有状态组件的用法,因为这个是最重要的概念哦。
那么大家试一试官方Demo让中间计数器的字体大小随着计数器的值变化。

使用支付宝打赏
使用微信打赏

若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏

打赏请备注姓名或者昵称,方便我后期统计哦

关注公众号,及时查阅最新文章