Поиск в реальном времени: начните поиск после того, как пользователь закончил печатать - PullRequest
0 голосов
/ 11 апреля 2020

В моем приложении я ищу результаты, когда пользователь вводит что-то в текстовое поле. Я использую Provider, где есть функция searchProduct (), которая запускается каждый раз, когда пользователь вводит что-то в текстовое поле. как только результат получен, я вызываю функцию notifyListener (), и пользовательский интерфейс обновляется соответствующим образом.

Проблема, с которой я сталкиваюсь, заключается в том, что результаты извлекаются асинхронно, а не в одно и то же время. Иногда последний результат предшествует одному из предыдущих. Это особенно происходит, когда пользователь вводит слишком быстро. Таким образом, при каждом нажатии клавиши эта функция searchProduct () вызывается и выполняет сетевой запрос. Этот подход также делает слишком много ненужных сетевых запросов, что не идеально. Как лучше всего решить эту проблему, чтобы в определенный момент, когда пользователь вводит строку поиска, поиск начинался после того, как пользователь закончил печатать?

class ProductService extends ChangeNotifier {
  String _searchText;

  String serverUrl = 'https://api.example.com/api';

  String get searchText => _searchText;
  List<Product> products = [];
  bool searching = false;

  void searchProduct(String text) async {
    searching = true;
    notifyListeners();
    _searchText = text;

    var result = await http
        .get("$serverUrl/product/search?name=$_searchText");
    if (_searchText.isEmpty) {
      products = [];
      notifyListeners();
    } else {
      var jsonData = json.decode(result.body);

      List<Map<String, dynamic>> productsJson =
          List.from(jsonData['result'], growable: true);
      if (productsJson.length == 0) {
        products = [];
        notifyListeners();
      } else {
        products = productsJson
            .map((Map<String, dynamic> p) => Product.fromJson(p))
            .toList();
      }
      searching = false;
      notifyListeners();
    }
  }
}

1 Ответ

1 голос
/ 11 апреля 2020

Пользователь RestartableTimer и установить длительность обратного отсчета, скажем, до 2 секунд. Когда пользователь вводит символ в первый раз, таймер инициализируется, а затем каждый раз, когда вводится символ, он сбрасывает таймер. Если пользователь перестанет набирать текст в течение 2 секунд, сработает обратный вызов, содержащий сетевой запрос. Очевидно, что код нуждается в улучшении для учета других сценариев ios, например, если запрос должен быть отменен, прежде чем он сработает по какой-либо причине.

TextField(
      controller: TextEditingController(),
      onChanged: _lookupSomething,
);


RestartableTimer timer;
static const timeout = const Duration(seconds: 2);

_lookupSomething(String newQuery) {

  // Every time a new query is passed as the user types in characters
  // the new query might not be known to the callback in the timer 
  // because of closures. The callback might consider the first query that was
  // passed during initialization. 
  // To be honest I don't know either if referring to tempQuery this way
  // will fix the issue.  

  String tempQuery = newQuery;

  if(timer == null){
    timer = RestartableTimer(timeout, (){
      myModel.search(tempQuery);
    });
  }else{
    timer.reset();
  }
}
...