为什么更新ListView中使用的List <statefulwidget>不能更新显示?

时间:2018-10-17 03:51:17

标签: dart flutter

我正在尝试创建一个应用程序以显示搜索结果。我有以下非常简单的StatefulWidget。它只有一个状态变量data,该变量在构造函数中传递。

class Entry extends StatefulWidget {
  Entry({Key key, @required this.data}) : super(key: key);

  final String data;

  @override
  _EntryState createState() => _EntryState(data);
}

class _EntryState extends State<Entry> {
  _EntryState(this.data) : super();
  final String data;

  @override
  Widget build(BuildContext context) {
    return Text(data);
  }
}

要生成随机长度(0-9之间)的数据集,我使用以下辅助函数,该函数基本上跟踪DateTime和传递给它的字符串:

List<String> render(String searchTerm) {
  final Random rng = new Random();
  final String date = DateTime.now().toString();
  return List<String>.generate(
    rng.nextInt(10),
    (int i) => '$searchTerm $i $date',
  );
}

最后,我的主要小部件就是这样。它使用状态变量_items来跟踪Entry的列表。可以通过搜索栏进行新的查询,它可以返回随机长度的数据集。然后通过ListView显示该列表。

class MyWidget extends StatefulWidget {
  MyWidget({Key key}) : super(key: key);

  final String title = "Demo";

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

class _State extends State<MyWidget> {
  List<String> _items = [];

  Future<Null> getData(String searchTerm) async {
    final entriesToAdd = render(searchTerm);
    setState(() {
      _items = entriesToAdd;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: TextField(
            onSubmitted: getData,
            decoration:
                InputDecoration(hintText: 'Search', icon: Icon(Icons.search)),
          ),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Expanded(
                  child: ListView.builder(
                      itemCount: _items.length,
                      itemBuilder: (context, index) {
                        return Container(
                            padding: EdgeInsets.all(8.0), child: Entry(data: _items[index]));
                      })),
            ],
          ),
        ));
  }
}

所以我的问题是我希望当我通过按钮进行搜索(即调用getData)时,显示的列表将使用新查询的结果进行更新,从而完全覆盖旧查询的结果。这不会发生。显示的列表是正确的长度,但是项目通常是各种先前/当前搜索的组合,如以下屏幕截图所示,其中显示了2个搜索的组合。但是,在itemBuilder的{​​{1}}函数中执行打印语句时,ListView变量似乎正在正确更新,并且无论是否应该混合使用两种不同的搜索结果。是什么原因导致这种现象? bugged

这是完整的代码:

_item

1 个答案:

答案 0 :(得分:3)

不要缓存小部件!您应该在每次调用build时重新构建它们。 (如果您担心这种方法效率低下,请不要这样做。这是Flutter进行设计和优化的方式,以支持此方法。)只需保留字符串列表,然后每次都重新构建Text。

避免只使用静态函数的类。这些可以替换为顶级功能。

尝试以下方法:

import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

void main() => runApp(App());

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Demo',
      theme: ThemeData(
        primarySwatch: Colors.green,
      ),
      initialRoute: '/',
      routes: <String, WidgetBuilder>{
        '/': (BuildContext context) => const MyWidget(),
      },
    );
  }
}

class MyWidget extends StatefulWidget {
  const MyWidget({Key key}) : super(key: key);

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

class MyWidgetState extends State<MyWidget> {
  List<String> _items = <String>[];

  void getData(String searchTerm) {
    final List<String> newEntries = helper(searchTerm);
    setState(() {
      _items = newEntries;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: TextField(
          onSubmitted: getData,
          decoration: const InputDecoration(
            hintText: 'Search',
            icon: Icon(Icons.search),
          ),
        ),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Expanded(
              child: ListView.builder(
                itemCount: _items.length,
                itemBuilder: (BuildContext context, int index) => Container(
                      padding: const EdgeInsets.all(8.0),
                      child: Text(_items[index]),
                    ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

List<String> helper(String searchTerm) {
  final Random rng = new Random();
  final String date = DateTime.now().toString();
  return List<String>.generate(
    rng.nextInt(10),
    (int i) => '$searchTerm $i $date',
  );
}

以下是说明使用StatefulWidget的版本:

class MyWidget extends StatefulWidget {
  const MyWidget({Key key}) : super(key: key);

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

class MyWidgetState extends State<MyWidget> {
  List<String> _items = <String>[];

  void getData(String searchTerm) {
    final List<String> newEntries = helper(searchTerm);
    setState(() {
      _items = newEntries;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: TextField(
          onSubmitted: getData,
          decoration: const InputDecoration(
            hintText: 'Search',
            icon: Icon(Icons.search),
          ),
        ),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Expanded(
              child: ListView.builder(
                itemCount: _items.length,
                itemBuilder: (BuildContext context, int index) => Container(
                      padding: const EdgeInsets.all(8.0),
                      child: Entry(data: _items[index]),
                    ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class Entry extends StatefulWidget {
  const Entry({Key key, @required this.data}) : super(key: key);

  final String data;

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

class _EntryState extends State<Entry> {
  bool bold = true;

  @override
  Widget build(BuildContext context) => GestureDetector(
        onTap: () {
          setState(() {
            bold = !bold;
          });
        },
        child: Text(
          widget.data,
          style: TextStyle(
            fontWeight: bold ? FontWeight.bold : FontWeight.normal,
          ),
        ),
      );
}