调用 setState() 时 Flutter ListView 不会改变

Jos*_*osh 5 dart flutter

我刚开始使用 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)
        ),
      ]),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

如果我将 ListView 部分更改为:

Expanded(
   child: ListView(children: _results.toList())
)
Run Code Online (Sandbox Code Playgroud)

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

ale*_*sam 5

来自 StatefulWidget文档

\n\n
\n

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

\n
\n\n

这篇文章:

\n\n
\n

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

\n
\n\n

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

\n\n

另一个解决方案可能是:

\n\n
setState(() {\n  _results = List.from(_results)\n    ..add(Text(text));\n});\n
Run Code Online (Sandbox Code Playgroud)\n


Sah*_*evs 3

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

这将解决你的问题:


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)
        ),
      ]),
    );
  }
}

Run Code Online (Sandbox Code Playgroud)

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


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(),
        )),
      ]),
    );
  }
}

Run Code Online (Sandbox Code Playgroud)

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