首页 > 解决方案 > 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 里面的一些数据。因此,当列表状态发生变化时,它会重新渲染与列表相关的任何内容。

这是真的吗?

如果是,是否有任何其他解决方案如何更改我的代码?如果没有,我的代码或我的逻辑是否有任何错误?

谢谢你。真的很期待对此的一些解决方案,因为我真的陷入了这个困境,而且已经一个星期了:(

标签: dartflutter

解决方案


简单地移动你的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) {

重复的原因:我们可以调用setStateSlider 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"}
);

推荐阅读