setState 函数是在 Flutter 应用程序中管理状态的最基本方法。以下是一些保持应用可维护性的最佳实践。
StatefulWidget
的 setState
函数是一种在 Flutter 应用程序中管理状态的简单方法。但是,当您希望您的应用程序正常工作和高性能时,您需要避免几个陷阱。以下是您应该坚持的一些最佳实践。
setState 有什么用?
setState
是Flutter发出 rebuild (重建)当前 widget 及其后代的方式。在 rebuild 过程中,最新的变量值将被用于创建用户界面。比方说,一个用户将一个开关从打开切换到关闭。该开关有一个存储该值的支持变量,所以在改变之后,它被设置为 false
。开关本身并不反映这一变化,直到它被重建为新的支持字段值。
- 更改值
- 调用 setState()
- 用户界面已更新
技巧 1:保持 widgets 小!
setState
触发了对你当前所在的小组件的重建。如果你的整个应用程序只包含一个widget,那么整个widget将被重建,这将使你的应用程序变得缓慢。请看下面的例子。
import 'package:flutter/material.dart';
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
@override
State createState() => _State();
}
class _State extends State {
bool _tile1 = false;
bool _tile2 = false;
bool _tile3 = false;
bool _tile4 = false;
bool _tile5 = false;
@override
Widget build(BuildContext context) {
print("built method Home"); // <-- setState triggers build here!
return Scaffold(
appBar: AppBar(title: const Text("Demo")),
body: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SwitchListTile(
activeColor: Colors.green,
inactiveThumbColor: Colors.red,
title: Text("Switch is ${_tile1 ? "on" : "off"}"),
value: _tile1,
onChanged: (_) {
setState(() {
_tile1 = !_tile1;
});
}),
SwitchListTile(
activeColor: Colors.green,
inactiveThumbColor: Colors.red,
title: Text("Switch is ${_tile2 ? "on" : "off"}"),
value: _tile2,
onChanged: (_) {
setState(() {
_tile2 = !_tile2;
});
}),
SwitchListTile(
activeColor: Colors.green,
inactiveThumbColor: Colors.red,
title: Text("Switch is ${_tile3 ? "on" : "off"}"),
value: _tile3,
onChanged: (_) {
setState(() {
_tile3 = !_tile3;
});
}),
SwitchListTile(
activeColor: Colors.green,
inactiveThumbColor: Colors.red,
title: Text("Switch is ${_tile4 ? "on" : "off"}"),
value: _tile4,
onChanged: (_) {
setState(() {
_tile4 = !_tile4;
});
}),
SwitchListTile(
activeColor: Colors.green,
inactiveThumbColor: Colors.red,
title: Text("Switch is ${_tile5 ? "on" : "off"}"),
value: _tile5,
onChanged: (_) {
setState(() {
_tile5 = !_tile5;
});
})
])));
}
}
这里我们在一个 Column
中有 5 个 SwitchListTile
小部件,它们都是同一个小部件的一部分。
如果您切换任何控件,整个屏幕都会被重建。 Scaffold
, AppBar
, Column
, ... 但只要重建已更改的小部件就足够了。让我们看下一个代码示例:
import 'package:flutter/material.dart';
class Home2 extends StatefulWidget {
const Home2({Key? key}) : super(key: key);
@override
State createState() => _State();
}
class _State extends State {
@override
Widget build(BuildContext context) {
print("built method Home2");
return Scaffold(
appBar: AppBar(title: const Text("Demo")),
body: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Switch(),
Switch(),
Switch(),
Switch(),
Switch()
])));
}
}
class Switch extends StatefulWidget {
const Switch({Key? key}) : super(key: key);
@override
State createState() => _SwitchState();
}
class _SwitchState extends State {
bool _value = false;
@override
Widget build(BuildContext context) {
print("build method Switch"); // <-- setState triggers build here!
return SwitchListTile(
activeColor: Colors.green,
inactiveThumbColor: Colors.red,
title: Text("Switch is ${_value ? "on" : "off"}"),
value: _value,
onChanged: (_) {
setState(() {
_value = !_value;
});
});
}
}
在这里,我们将 SwitchListTile
包装在单个 StatefulWidget
中。页面看起来相同,但如果您单击此示例中的任何开关,则只有单击的小部件将重建。
技巧 2:不要在构建方法中调用 setState
这个方法有可能在每一帧中被调用,除了建立一个小部件外,不应该有任何副作用。
build 方法旨在构建小部件树,因此我们应该保持这种方式。不要在这里做花哨的事情,它会减慢你的应用程序。对setState的调用可能会触发额外的重建,在最坏的情况下,你可能最终会出现一个异常,告诉你目前有一个重建正在进行。
技巧 3:不要在 initState 方法中调用 setState
initState
将在完成后触发重建,因此无需在此方法中调用 setState
。此方法旨在初始化与状态相关的属性,例如设置默认值或订阅流。不要在这里做任何其他事情!
技巧4:setState()和setState(...)是相等的
像这样使用 setState 没关系
setState((){
_text = “Hello”;
});
或者像这样
_text = “Hello”;
setState((){});
结果是一样的。
技巧 5:setState(...) 代码必须很小
不要在setState内做任何大的计算,因为它将阻止你的应用程序重绘。请看下面的示例代码:
setState(() {
for (var i = 0; i < 10000; i++) print(i);
_value = true;
});
只有在打印语句之后,小部件才会重建。在这段时间里,你的应用程序不会对用户的操作做出反应,它将在之后执行这些操作。因此,如果用户因为没有视觉反馈而多次点击一个控件,多次重建就会堆积起来,会使应用程序的速度更慢。
一个更好的方法是在执行一个长期运行的操作时显示一个进度指示器,这样用户就知道正在发生一些事情,他需要等待完成。
技巧 6:setState(...) 代码不能是异步的
运行代码时
setState(() async {
await Future.delayed(const Duration(seconds: 5));
});
你最终会得到一个类似这样的异常信息:
在方法之外执行异步操作,然后调用它。
结束
我希望这些见解能帮助你更好地理解Flutter中setState的机制。坚持这些技巧,你会有更少的问题和更快的应用程序。源代码例子可以在GitHub上找到。