使用预先输入的颤振搜索

时间:2021-06-01 21:02:13

标签: flutter dart

我从资产中加载了大量 json 数据。我需要使用 recipeName 从食谱中进行搜索,我正在使用 flutter typeahead 创建搜索栏和功能。

我正在应用启动时加载所有配方对象。下面的代码基本上返回了所有的食谱。

  List<RecipeModel> getAllRecipes() {
    for (int i = 0; i < _appData.recipeCategories!.length; i++) {
      for (int j = 0; j < _appData.recipeCategories![i].recipes!.length; j++) {
        _recipeList.add(_appData.recipeCategories![i].recipes![j]);
      }
    }
    return _recipeList;
  }

这段代码负责搜索(Search.dart):

class Search {
  List<RecipeModel> _recipeList = Store.instance.getAllRecipes(); // Store loads data during splash screen
  late RecipeModel recipe;
  RecipeModel returnRecipe(String? suggestion) {
    for (int i = 0; i < _recipeList.length; i++) {
      if (suggestion == _recipeList[i].recipeName) {
        return _recipeList[i];
      }
    }

    return recipe;
  }
}

现在是预输入部分。 Flutter typeahead 需要 4 个参数 textFieldConfigurationsuggestionsCallbackitemBuilderonSuggestionSelected

对于SuggestionsCallback,当屏幕加载并使用该列表搜索时,我将所有配方名称加载到列表中:

  Future<List<String>> getSuggestions(String str) async {
    return List.of(recipeNamesList).where(
      (recipe) {
        final recipeLower = recipe.toLowerCase();
        final queryLower = str.toLowerCase();

        return recipeLower.contains(queryLower);
      },
    ).toList();
  }

这里是整个预输入小部件:

  Widget buildSearchTextInput() {
    return Container(
      padding: EdgeInsets.all(10),
      child: TypeAheadFormField<String?>(
        textFieldConfiguration: TextFieldConfiguration(
          controller: _textEditingController,
          focusNode: _focusNode,
          autofocus: true,
          decoration: InputDecoration(
            hintText: "Enter recipe name",
          ),
        ),
        suggestionsCallback: (query) async {
          print("query: $query");
          if (query.length == 0) return [];
          return await getSuggestions(query);
        },
        hideOnEmpty: true,
        itemBuilder: (context, String? suggestion) =>
            SearchResultCard(suggestion: suggestion),
        onSuggestionSelected: (String? suggestion) {
          openRecipeDetailsPage(
            context,
            Search().returnRecipe(suggestion),
          );
        },
      ),
    );
  }

如您所见,我正在使用上述类进行临时搜索。现在对于 openSuggestionsSelected,我必须将用户发送到一个 recipeDetailsScreen,它接受​​一个 RecipeModel 对象。

  1. 我一直在想,虽然我正在为此进行临时搜索,但我可以在一定程度上减少混乱吗?我的意思是除了加载商店中所有食谱的功能之外,我还有两个列表可以工作。一个带有食谱的名称,一个带有所有的食谱。有没有一种方法可以使用一个列表来做到这一点?

  2. 我可以提高搜索效率吗?我认为使用两个列表不是一个好主意。

注意:我编写的解决方案有效。我只需要一些可以消除混乱并希望降低搜索时间复杂度或至少提高功能效率的东西。

1 个答案:

答案 0 :(得分:1)

您可以通过两种方式降低代码的复杂性:

  1. 在搜索部分使用 Map<String, RecipeModel>
class Search {
  // This will still be O(n)
  Map<String, RecipeModel> _map = Map.fromIterable(
    Store.instance.getAllRecipes(),
    key: (recipe) => recipe.name);

  late RecipeModel recipe;

  RecipeModel returnRecipe(String? suggestion) {
    if (suggestion == null) return recipe;
    // This will be O(1) instead of O(n) [better]
    final RecipeModel? found = _map[suggestion];
    return found ?? recipe;
  }
}
  1. 在预先输入部分使用 Trie(这里我使用 trie,但您可以使用 autotrie):
 class Search {
  final Map<String, RecipeModel> _map = Map.fromIterable(
    Store.instance.getAllRecipes(),
    key: (recipe) => recipe.name);

  final late Trie trie;

  Search() {
    // This will be O(n)
    trie = Trie.list(map.keys().toList());
  }

  // ...

  List<String> returnSuggestions(String prefix) {
    // This will be O(W*L) instead of O(n^2) [better]
    return trie.getAllWordsWithPrefix(prefix);
  }
}

更改您的代码:

final Search search = Search();

// ...

Widget buildSearchTextInput() {
  return Container(
    padding: EdgeInsets.all(10),
    child: TypeAheadFormField<String?>(
      // ...
      suggestionsCallback: (query) async {
        if (query.isEmpty) return [];
        return search.returnSuggestions(query);
      },
      // ...
      onSuggestionSelected: (String? suggestion) {
        openRecipeDetailsPage(context, search.returnRecipe(suggestion));
      },
    ),
  );
}

您仍然会有两个“列表”,但总体而言您的复杂性会有所改善。