【问题标题】:Flutter ListView doesn't change when setState() is called调用 setState() 时 Flutter ListView 不会改变
【发布时间】:2020-02-02 11:47:13
【问题描述】:

我是使用 Flutter/Dart 的新手,我很难理解为什么以下代码在 _updateResults() 调用 setState() 时不会更新 ListView 内容。我什至添加了一个打印语句来显示_results 的内容,我可以看到项目正在添加到列表中,但显示永远不会更新。

class SearchHomePage extends StatefulWidget {
  SearchHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  createState() => _SearchHomePageState();
}

class _SearchHomePageState extends State<SearchHomePage> {
  List<Widget> _results = <Widget>[];

  @override
  void initState() {
    super.initState();
    _results.add(Text("testing"));
  }

  void _updateResults(String text) {
    setState(() {
      _results.add(Text(text));
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Column(children: [
        Container(
          child: TextField(
            onSubmitted: (String text) {
              _updateResults(text);
            },
          ),
        ),
        Expanded(
          child: ListView(children: _results)
        ),
      ]),
    );
  }
}

如果我将 ListView 部分更改为:

Expanded(
   child: ListView(children: _results.toList())
)

然后它会起作用,我不知道为什么,因为它已经是一个列表。这里到底发生了什么,为什么它不能仅与 ListView 一起使用?

【问题讨论】:

  • 您能在控制台中显示您遇到的错误吗?或者它根本不起作用?

标签: flutter dart


【解决方案1】:

它不起作用,因为您正在将新项目添加到现有列表而不是创建新列表。

这将解决您的问题:


class SearchHomePage extends StatefulWidget {
  SearchHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  createState() => _SearchHomePageState();
}

class _SearchHomePageState extends State<SearchHomePage> {
  List<Widget> _results = <Widget>[];

  @override
  void initState() {
    super.initState();
    _results.add(Text("testing"));
  }

  void _updateResults(String text) {
    setState(() {
      _results = [..._results, Text(text)];
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Column(children: [
        Container(
          child: TextField(
            onSubmitted: (String text) {
              _updateResults(text);
            },
          ),
        ),
        Expanded(
            child: ListView(children: _results)
        ),
      ]),
    );
  }
}

注意:最好执行以下操作,而不是创建小部件并将它们保存在数组中:


class SearchHomePage extends StatefulWidget {
  SearchHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  createState() => _SearchHomePageState();
}

class _SearchHomePageState extends State<SearchHomePage> {
  List<String> _results = <String>[];

  @override
  void initState() {
    super.initState();
    _results.add("testing");
  }

  void _updateResults(String text) {
    setState(() {
      _results = [..._results, text];
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Column(children: [
        Container(
          child: TextField(
            onSubmitted: (String text) {
              _updateResults(text);
            },
          ),
        ),
        Expanded(
            child: ListView(
          children: _results.map((v) => Text(v)).toList(),
        )),
      ]),
    );
  }
}

当您调用 .toList() 时,它将创建一个新的列表实例(旧列表的副本)

【讨论】:

    【解决方案2】:

    来自 StatefulWidget documentation

    StatefulWidget 实例本身是不可变的,并将其可变状态存储在由 createState 方法创建的单独 State 对象中,或者存储在该 State 订阅的对象中 [...]

    来自this文章:

    根据定义,不可变意味着一旦创建,对象/变量就无法更改。因此,您不必更改对象的属性,而是必须复制(或克隆)整个对象,并在此过程中更改相关属性。

    您使用相同的数组创建了 ListView。您更改了数组的内容,但没有更改对该对象的引用。 这就是为什么当您使用 _results.toList() 时它会起作用的原因,因为 .toList() “创建了一个包含此 [Iterable] 元素的 [List]”。

    另一种解决方案可能是:

    setState(() {
      _results = List.from(_results)
        ..add(Text(text));
    });
    

    【讨论】:

      【解决方案3】:
            ListView(
              shrinkWrap: true,
              children: _results.map((one) => one).toList(),
            ),
      
            ListView.builder(
      
              shrinkWrap: true,
              itemCount: _results.length,
              itemBuilder: (context, index) => _results[index],
            ),
      

      如果您努力按照您的要求获得输出,那么您必须更改您的代码,例如上面的代码,而不是下面的代码:

      Expanded(
         child: ListView(children: _results.toList()))
      

      请从两者中放任何一个列表视图...

      【讨论】:

      • 代替map,可以使用children: _results.cast&lt;Widget&gt;()来避免不必要的迭代
      猜你喜欢
      • 2020-12-04
      • 2020-11-16
      • 2023-01-14
      • 2019-10-03
      • 2021-12-18
      • 1970-01-01
      • 2021-04-19
      • 2021-08-23
      • 2020-12-10
      相关资源
      最近更新 更多