🔑 0
我们所做出来的App,不能仅仅是一幅静止不动的画面!想一下你生活中的这些场景:
- 手机健康App上的步数随着你的幸(huang)苦(dong)运(shou)动(bi)而增加;
- 手机时钟App里的秒表计数随着时间的飞快流逝而改变;
- 进度条慢慢延伸;
如果这些App是用Flutter编写成的,那么这些Widget的“状态”就在随着用户的操作而发生改变。
StatefulWidget
就是有状态的Widget
。它在程序运行时,状态被允许发生改变。
只有状态能够发生改变的StatefulWidget
才能让页面不再一成不变。也就是说,要想编写出能够被用户操作的App,就必须要使用StatefulWidget
。
📘 目录
- 📜 Here's the code!
- 💠 理解
StatefulWidget
和State
- 🧱 当我们
setState(() {});
时,Flutter在幕后帮我们做了什么?
📜 1
Here's the code!
我们要实现这样的一个计数器:
我们实现了一个简单的计数器,画面中间的数字随着我们按动左下角的蓝色按钮而递增。
main.dart
如下:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
State<MyApp> createState() {
return MyAppState();
}
}
class MyAppState extends State<MyApp> {
var _counter = 0;
@override
Widget build(BuildContext buildContext) {
return MaterialApp(
title: '计数器',
home: Scaffold(
appBar: AppBar(
title: Text('计数器')
),
body: Center(
child: Text(
'$_counter',
style: TextStyle(fontSize: 30)
)
),
floatingActionButton: FloatingActionButton(
onPressed: () => setState(() => _counter += 1),
child: Icon(Icons.add)
),
)
);
}
}
代码来得太快,就像龙卷风?不要害怕,让我们一点一点剖析它。
💠 2
理解StatefulWidget
和State
开头,我引入了package:flutter/material.dart
,这里面包含了Flutter已经封装好的Material风格的Widget
。
import 'package: flutter/material.dart'
接下来,是经典的runApp()
,这个时候把我编写好的MyApp
(继承了StatefulWidget
)的实例作为参数传入。
void main() => runApp(MyApp());
然后在MyApp
内,我们会发现显示的具体内容并不是在这个类里面实现的!
class MyApp extends StatefulWidget {
@override
State<MyApp> createState() {
return MyAppState();
}
}
在这个类里面,我们重写了一个createState()
函数,然后这个函数返回了一个State<MyApp>
,是用户定义的MyAppState
类的实例!
然后MyAppState
是何方神圣呢?
class MyAppState extends State<MyApp> {
var _counter = 0;
@override
Widget build(BuildContext buildContext) {
return MaterialApp(
title: '计数器',
home: Scaffold(
appBar: AppBar(
title: Text('计数器')
),
body: Center(
child: Text(
'$_counter',
style: TextStyle(fontSize: 30)
)
),
floatingActionButton: FloatingActionButton(
onPressed: () => setState(() => _counter += 1),
child: Icon(Icons.add)
),
)
);
}
}
可以看到了,MyAppState
继承了State<MyApp>
,而在这个类里面,我们重写了build()
函数,并将这个Widget
的内容写在了这里面。
所以,我们可以得知:
StatefulWidget
里面需要重写createState()
函数,这个函数返回一个State<StatefulWidget>
的实例。State<StatefulWidget>
里面需要重写build()
函数,这个函数返回一个Widget
的实例。
但是,我们会发现,仅仅满足了这些,Widget
的状态还不足以发生变化。回看刚才的源码,这里有一行:
onPressed: () => setState(() => _counter += 1)
floatingActionButton
的onPressed
属性定义了这个按钮按完之后会触发什么动作,它的值是一个函数(在Dart内,函数是一等对象)。这个函数它会执行setState(() => _counter += 1)
。那么,这个setState()
是做什么的呢?
🧱 3
当我们setState(() {});
时,Flutter在幕后帮我们做了什么?
setState(fn)
会先执行我们传入的函数fn
。- 接下来,
setState()
会将这个组件标记为“脏的”。 - 下一帧时,Flutter会重新执行这个“脏”组件的
build()
函数,将它重新渲染一遍。
在上述计数器程序中setState(fn);
的执行过程。动图为自制,转载请注明出处。图中组件树为了简洁明了,省略了AppBar
和Icon
等Widget
,只注明参与了这个过程的少数几个Widget
也就是说,setState({} ());
才是本程序的点睛之笔。
🏁 -1
总结
StatefulWidget
就是有状态的Widget
。它在程序运行时,状态被允许发生改变,要想编写出能够被用户操作的App,就必须要使用StatefulWidget
。- 我们实现了一个简单的计数器。
StatefulWidget
里面需要重写createState()
函数,这个函数返回一个State<StatefulWidget>
的实例。State<StatefulWidget>
里面需要重写build()
函数,这个函数返回一个Widget
的实例。当我们执行
setState(fn);
时:setState(fn)
会先执行我们传入的函数fn
。- 接下来,
setState()
会将这个组件标记为“脏的”。 - 下一帧时,Flutter会重新执行这个“脏”组件的
build()
函数,将它重新渲染一遍。
它的用途是刷新一个组件的状态。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。