如何处理Flutter中的大列表搜索?

时间:2019-03-07 11:51:45

标签: dart flutter

我想问一下我应该如何在Flutter中处理大量清单。当我在要搜索的列表中确实很深的数据项上时,我的应用程序变得超级慢。我的清单是一个数据结构大的70,000+个对象。

以下是我“搜索”列表的方式。

Future<Iterable<SomeDataStruct>> _getAllData() async {
    return allData.where((a) => (a.dataTitle.toLowerCase().contains(querySearch.toLowerCase().trim())));
}

使用FutureBuilder内部的ListView.builder构建列表。

当我搜索并填充列表深处的一个或多个结果时,该应用程序的运行速度非常慢,我单击列表项的时间很短,然后才执行onTap几秒钟。而且,如果我需要更改搜索查询,则在单击TextField之后,需要一些时间才能使软键盘重新显示。

我在哪里犯错或处理错了,我应该怎么做才能在不使应用无法忍受的情况下搜索庞大的列表。

编辑: 更改代码后,我如何不降低应用程序运行速度。这是正确的吗?

String tempQuery;
List<SomeDataStruct> searchResults = [];
Future<List<SomeDataStruct>> _getAllData() async {
    if(querySearch!=tempQuery) {
      tempQuery = querySearch;
      searchResults = allData.where((a) => (a.dataTitle.toLowerCase().contains(querySearch.toLowerCase().trim()))).toList();
    }
    return searchResults;
  }

3 个答案:

答案 0 :(得分:1)

包含的物品很贵

包含查询非常昂贵,因为如果可以找到搜索词,则需要在每个位置(最多value.length-searchTerm.length)检查每个条目。

将搜索支持限制在字符串的开头将大大提高性能。另外,您可以创建辅助程序数据结构,在该结构中,值的整个列表以开头的相同字符分割为多个部分。如果块仍然太大,则可以为第二个字符添加另一个级别。由于字符数有限,因此查找会很快。

使用数据库可能会减轻一些编程工作(维护索引)。像SQLite这样的数据库可以与专门用于您的查询的索引一起使用。

分成较小的工作块,以允许框架执行其工作

如果您不能只限于“字符串开始”搜索,您仍然可以将数据结构分成较小的块,并为每个异步块调用搜索。这样,在搜索下一个块之前,UI会“呼吸一些”以重新呈现UI。搜索结果将逐步更新。

移出UI线程

另一种方法是启动另一个隔离并在那里进行搜索。另一个隔离可以在另一个CPU(核心)上运行,因此在搜索时不会阻塞UI线程。这样,就不必拆分成大块了。尽管逐步更新UI而不是让用户等到整个搜索结果可用之前,仍然可能是有利的。

另请参见

缓存

将搜索结果保存在内存中也可能有助于提高性能。例如,如果用户输入foo,然后按退格键,则可以对先前已经计算过的fo重新使用搜索结果,但这仅在某些情况下有用。

测量

当然,另一个重要的点是进行基准测试。无论您尝试如何提高性能,都应创建基准以了解哪些措施具有什么效果,以及是否值得。您将学到很多有关您的方案,数据,Dart等的信息,这将使您能够做出明智的决定。

答案 1 :(得分:0)

如果列表足够大,搜索列表会使您的应用崩溃。我测试了搜索 150,000 个项目的列表,但我的应用程序崩溃了。如果您搜索的内容靠近列表的开头,它似乎可以工作,但如果您搜索的内容可能位于列表的深处,例如索引 100,000,那么事情会显着减慢,应用程序将冻结。

为了解决这个问题,我使用基于 sqlite 构建的 moor 库转向了数据库解决方案。您只需按照文档设置样板内容,不需要很长时间。

您可以创建一个简单的数据库表,其中只有 2 个字段,id 和另一个用于保存列表值。在本例中,我将另一个字段称为 itemName

在你的数据库类中,你可以有一个函数来进行过滤,例如

Future<List<Item>> getFilteredItems(search) => (select(Items)..where((t) => t.itemName.like(search))).get();

然后您的代码将如下所示:

String tempQuery;
List<SomeDataStruct> searchResults = [];
Future<List<SomeDataStruct>> _getAllData() async {
    if(querySearch!=tempQuery) {
      tempQuery = querySearch;
      query = await MyDatabaseClass().getFilteredItems("%$querySearch%"); 
      for (int i = 0; i<query.length; i++) {
        searchResults.add(query[i].itemName);  //itemName 
        }
    }
    return searchResults;
}

此后不再崩溃或死机。

答案 2 :(得分:-1)

也许是这样:

ListView.builder(
  itemBuilder: (BuildContext context, int index) {
    return Text(data[index]);
  },
)