The setState function is the most basic way to manage state in a Flutter application. Here are some best practices for keeping your application maintainable.
The StatefulWidget
setState
function of ---32bd88e24ba11a250d335ceedd99af73--- is a simple way to manage state in a Flutter application. However, when you want your application to work and perform well, you need to avoid several pitfalls. Here are some best practices you should stick to.
What is setState for?
setState
is how Flutter emits to rebuild the current widget and its descendants. During the rebuild process, the latest variable values will be used to create the user interface. Let's say a user toggles a switch from on to off. The switch has a backing variable that stores the value, so after the change, it's set to false
. The switch itself does not reflect this change until it is rebuilt to the new backing field value.
- change value
- call setState()
- UI has been updated
💡Trick 1: Keep widgets small!
setState
triggered a rebuild of the widget you're currently in. If your whole application contains only one widget, then the whole widget will be rebuilt, which will make your application slow. See the example below.
import 'package:flutter/material.dart';
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
@override
State<Home> createState() => _State();
}
class _State extends State<Home> {
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: <Widget>[
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;
});
})
])));
}
}
Here we have 5 SwitchListTile
widgets in one Column
which are all part of the same widget.
If you toggle any controls, the entire screen is rebuilt. Scaffold
, AppBar
, Column
, ... but just rebuilding the changed widget is enough. Let's look at the next code example:
import 'package:flutter/material.dart';
class Home2 extends StatefulWidget {
const Home2({Key? key}) : super(key: key);
@override
State<Home2> createState() => _State();
}
class _State extends State<Home2> {
@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 <Widget>[
Switch(),
Switch(),
Switch(),
Switch(),
Switch()
])));
}
}
class Switch extends StatefulWidget {
const Switch({Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() => _SwitchState();
}
class _SwitchState extends State<Switch> {
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;
});
});
}
}
Here we wrap SwitchListTile
in a single StatefulWidget
. The page looks the same, but if you click any of the switches in this example, only the clicked widget will rebuild.
💡Trick 2: Don't call setState in build method
From Flutter API Documentation
This method is likely to be called every frame and should not have any side effects other than building a widget.
The build method is designed to build the widget tree, so we should keep it that way. Don't do fancy stuff here, it will slow down your application. Calls to setState may trigger additional rebuilds, and in the worst case, you may end up with an exception telling you that a rebuild is currently in progress.
💡Trick 3: Don't call setState in initState method
initState
will trigger a rebuild after completion, so there is no need to call setState
in this method. This method is intended to initialize state-related properties, such as setting default values or subscribing to a stream. Don't do anything else here!
💡Trick 4: setState() and setState(...) are equal
It's okay to use setState like this
setState((){
_text = “Hello”;
});
or like this
_text = “Hello”;
setState((){});
The result is the same.
💡Trick 5: setState(...) code must be small
Don't do any big calculations inside setState as it will prevent your app from redrawing. See the sample code below:
setState(() {
for (var i = 0; i < 10000; i++) print(i);
_value = true;
});
The widget will rebuild only after the print statement. During this time, your application will not react to user actions, it will perform those actions afterward. Therefore, if the user taps a control multiple times because there is no visual feedback, multiple rebuilds will pile up and make the application slower.
A better approach would be to show a progress indicator while a long running operation is being executed, so the user knows that something is happening and he needs to wait for it to finish.
💡Tip 6: setState(...) code cannot be asynchronous
When running the code
setState(() async {
await Future.delayed(const Duration(seconds: 5));
});
You will end up with an exception message like this:
Do the asynchronous operation outside of the method and then call it.
Finish
I hope these insights help you better understand the mechanics of setState in Flutter. Stick with these tips and you'll have fewer problems and a faster application. Source code examples can be found on GitHub .
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。