dart - Fluter setState() 使循环始终被调用
问题描述
我有一个像下面这样的代码,简单的流程是我从一个对象列表中创建一个循环来创建一些小部件。
class ScoringAttribute {
int _id;
bool _isdelete;
double _scorehigh, _scorelow, _scorevalue;
String _name, _scoretype, _description, _title;
}
class HomePageState extends State<HomePage> with TickerProviderStateMixin {
List dataScoringAttributes;
List<ScoringAttribute> listScoringAttributeObjects = new List<ScoringAttribute>();
final String urlPresentation = ".../.resentations/getPresentations";
final String urlScoringAttribute = ".../.scoringattributes/getScoringattributes";
Future<String> getPresentationData() async {
var responseScoringAttribute = await http.get(
Uri.encodeFull(urlScoringAttribute),
headers: {"Accept": "application/json"}
);
var scoringAttributeJson = json.decode(responseScoringAttribute.body);
dataScoringAttributes = scoringAttributeJson['scoringattributes'];
for(int i = 0; i < dataScoringAttributes.length; i++) {
var scoringAttributeObject = new ScoringAttribute();
scoringAttributeObject._id = dataScoringAttributes[i]["id"];
scoringAttributeObject._description = dataScoringAttributes[i]["iddescription"];
scoringAttributeObject._isdelete = dataScoringAttributes[i]["isdelete"];
scoringAttributeObject._name = dataScoringAttributes[i]["name"];
scoringAttributeObject._scorehigh = double.parse(dataScoringAttributes[i]["scorehigh"].toString());
scoringAttributeObject._scorelow = double.parse(dataScoringAttributes[i]["scorelow"].toString());
scoringAttributeObject._scoretype = dataScoringAttributes[i]["scoretype"];
scoringAttributeObject._title = dataScoringAttributes[i]["title"];
scoringAttributeObject._scorevalue = double.parse(dataScoringAttributes[i]["scorelow"].toString());
listScoringAttributeObjects.add(scoringAttributeObject);
}
return "Success";
}
List<Widget> scoringAttributeList() {
List<Widget> list = new List();
for(int i = 0; i < listScoringAttributeObjects.length; i++) {
if(listScoringAttributeObjects[i]._scoretype == "slider") {
list.add(
new Container(
child: new Column(
children: <Widget>[
new Column(
children: <Widget>[
//THE SLIDER VALUE TEXT
new Text(
//CONVERT DOUBLE TYPE TO STRING WITHOUT DECIMAL POINTS
listScoringAttributeObjects[i]._scorevalue.toStringAsFixed(listScoringAttributeObjects[i]._scorevalue.truncateToDouble() == listScoringAttributeObjects[i]._scorevalue ? 0 : 0),
style: new TextStyle(
fontSize: 28.0,
),
),
//THE SLIDER
new Slider(
activeColor: Colors.blueAccent,
inactiveColor: const Color(0xFFb7d2e0),
min: double.parse(listScoringAttributeObjects[i]._scorelow.toString()),
max: double.parse(listScoringAttributeObjects[i]._scorehigh.toString()),
value: double.parse(listScoringAttributeObjects[i]._scorevalue.toString()),
onChanged: (double value) {
setState(() {
listScoringAttributeObjects[i]._scorevalue = double.parse(value.round().toString());
});
},
),
],
),
],
),
),
);
}
else if(listScoringAttributeObjects[i]._scoretype == "text_field") {
list.add(...);
}
else if(listScoringAttributeObjects[i]._scoretype == "stars") {
list.add(...);
}
else if(listScoringAttributeObjects[i]._scoretype == "thumb") {
list.add(new Container(...);
}
}
return list;
}
@override
Widget build(BuildContext context) {
return new Scaffold(
body: FutureBuilder<String> (
future: getPresentationData(),
builder: (context, snapshot) {
if(snapshot.hasData) {
return new Column(
children: <Widget>[
new Column(
children: scoringAttributeList(),
),
],
),
}
},
),
);
}
}
有一些不同的小部件取决于类型,有 4 种类型,一种类型中可能有超过 1 个小部件,所以我使循环依赖于 iIgot 从 DB 获取的数据。
问题是我不知道为什么每次我setState()
在循环内使用时,它总是再次处理循环,所以创建一个新的小部件将是一个无限循环,它会从头开始复制小部件(仅在setState() 被调用)。
例如:List 里面有 4 个数据,如果setState()
调用它会显示 8 个数据(显示前 4 个数据两次)
这是我如何将 setState() 放入列表中的数据的示例
onChanged: (double value) {
setState(() {
listScoringAttributeObjects[i]._scorevalue = double.parse(value.round().toString());
});
},
我认为问题是因为我setState()
进入了 List 里面的一些数据。因此,当列表状态发生变化时,它会重新渲染与列表相关的任何内容。
这是真的吗?
如果是,是否有任何其他解决方案如何更改我的代码?如果没有,我的代码或我的逻辑是否有任何错误?
谢谢你。真的很期待对此的一些解决方案,因为我真的陷入了这个困境,而且已经一个星期了:(
解决方案
简单地移动你的getPresentationData()
状态变量。这样它只会triggered
得到一次。
class HomePageState extends State<HomePage> with TickerProviderStateMixin {
Future<String> _presentationFuture;
initState() {
_presentationFuture = getPresentationData()
}
//other contents
@override
Widget build(BuildContext context) {
return new Scaffold(
body: FutureBuilder<String> (
future: _presentationFuture,
builder: (context, snapshot) {
if(snapshot.hasData) {
重复的原因:我们可以调用setState
Slider dataChange 这将重新渲染HomePageState
将再次触发network
调用 ( getPresentationData()
)
注意:如果要在滑块更改时触发网络,请在进行网络调用之前清除列表
Future<String> getPresentationData() async {
listScoringAttributeObjects = new List<ScoringAttribute>(); // clear data
var responseScoringAttribute = await http.get(
Uri.encodeFull(urlScoringAttribute),
headers: {"Accept": "application/json"}
);
推荐阅读
- sql-server - TFS 仪表板小部件:查询 TFS_Warehouse 数据库?
- python - 自动完成 ipython 结束方/圆括号
- django - Django-compressor & S3 Boto:静态文件未压缩
- data-structures - 完美哈希表子表构建成功概率
- javascript - 如何使用 v-for 从数组中随机选取特定数量的项目?
- angular - ng gc c-name => 指定的模块不存在
- npm - 为什么我无法升级 NPM?
- reactjs - 在整个应用程序中导航时显示返回按钮的单个应用程序应用栏
- sql-server - Join 仅返回所有行的 t2 中的第一项
- php - 当 datediff = 0 时如何更新数据库中的用户状态