Flutter ListView.builder()动态列表-显示无限重复的条目

时间:2019-02-14 03:29:07

标签: listview dart flutter

我有一个很大的〜5K物品清单。

我想做的就是为这些物品写一个搜索视图。

我尝试了以下-

   List<String> items;
   String query;

   ListView.builder(
    itemBuilder: (context, index) {
      for (int i = index; i < items.length; i++) {
        var item = items[i];
        if (item.contains(query)) {
          return ItemTile(item);
        }
      }
    }

这有效地渲染和搜索元素,但是问题在于它最终无限地重复了列表中的最后一项。

我想那是因为我没有提供itemCount


因此,我尝试自己在Stateful小部件中跟踪已过滤项目的数量。

   var _count = 1;

   _queryController.addListener(() {
      setState(() => _count = 1);
   });

   ListView.builder(
    itemBuilder: (context, index) {
      for (int i = index; i < items.length; i++) {
        var item = items[i];
        if (item.contains(query)) {
          setState(() => _count += 1);
          return ItemTile(item);
        }
     }
     itemCount: _count;
   }

但是随后我收到一条错误消息,说-setState() or markNeedsBuild() called during build.

什么是正确的方法,而不必完全为每个查询搜索项目?

(我的目标是搜索按需输入UX)

3 个答案:

答案 0 :(得分:1)

您可以在将列表传递给构建器之前尝试过滤列表。

List<String> items;
List<String> _queryResults;
String query;

_queryResults = items.where((item) => item.contains(query)).toList();

然后可以将其传递给ListView.builder,而不必一遍又一遍地设置状态。不确定与其他选项相比其性能如何,应该不会有太大区别,因为在大多数情况下,过滤后的列表可能会很短。这比在构建器中进行过滤和设置状态要干净得多,所以我至少要对其进行测试。

如果您要使用“按需搜索”解决方案,也应该对过滤进行反跳处理,这意味着您需要在每次输入后等待500毫秒或一段合适的持续时间,以查看用户在进行过滤之前是否输入更多内容。将为您节省很多不必要的电话,并使您的解决方案性能更好。

答案 1 :(得分:1)

这是添加去抖动的一种可能方法:-

var _controller = TextEditingController();
List<dynamic> _filtered;

@override
void initState() {
  _filtered = widget.items;

  _controller.addListener(() {
    var query = _controller.text;
    Future.delayed(Duration(milliseconds: 250), () {
      if (!mounted) {
        return;
      }
      if (_controller.text == query) {
        setState(() {
          _filtered = widget.items
              .where((item) => item.contains(query)).toList())
              .toList();
        });
      }
    });
  });

  super.initState();
}

...

ListView.builder(
  itemBuilder: (context, index) => ItemTile(_filtered[index]),
  itemCount: _filtered.length,
)

答案 2 :(得分:0)

您正在Listview.builder内部使用for循环,这不是必需的。 ListView.builder将自动循环到列表中的项目数...

尝试一下。

List<String> items;
String query;

ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
  var item = items[index];
  if (item.contains(query)) {
      setState(() => _count += 1);
      return ItemTile(item);
  }
}