0

在我的应用程序中,当用户在 TextField 中键入内容时,我正在搜索结果。我正在使用 Provider ,其中有一个 searchProduct() 函数,每次用户在文本字段中键入内容时都会触发该函数。获取结果后,我将调用 notifyListener() 函数,并且 UI 会相应更新。

我面临的问题是结果是异步获取的,它们没有同时到达。有时最后一个结果出现在之前的结果之一之前。当用户输入速度过快时,尤其会发生这种情况。因此,每次击键都会调用这个 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();
    }
  }
}
4

1 回答 1

1

用户RestartableTimer并设置倒计时持续时间可以说是 2 秒。用户第一次输入字符时,计时器将初始化,然后每次输入字符时,计时器将重置。如果用户停止输入 2 秒,包含网络请求的回调将触发。显然,代码需要改进以考虑其他情况,例如是否应在请求因任何原因触发之前取消请求。

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();
  }
}
于 2020-04-11T13:27:51.513 回答