4

I am new to flutter and I want to translate some text from an InputField by calling an API. However I don't want to call it on every key stroke, but instead only when the user paused typing.

On Android I would just use the Handler class with postDelay() with beforehand calling removeAllCallbacksAndMessages(null). Is there a way to do something similar on Dart?

Here is my current code:

Future<String> getTranslation(String query, Language from, Language to) async {
    // cancel here if a call to this function was less than 500 millis ago.
    return Future.delayed(const Duration(milliseconds: 500), () {
      return _translator.translate(query, from: from.code, to: to.code)
    });
  }

Edit 1

I'm calling the code from my Bloc like so:

@override
  Stream<State> mapEventToState(Event event) async* {
    if (event is QueryChangeEvent) {
      yield TextTranslationChangeState(
          query: event.query ?? "",
          translation: await _repo.getTranslation(event.query, currentState.fromLang, currentState.toLang));
  }

This is why I cannot call .then() on the future because I wouldn't be able to yield the new state from the block of the nested function.

Any help is appreciated!

  • 2
    Possible duplicate of [is there any way to cancel a dart Future?](https://stackoverflow.com/questions/17552757/is-there-any-way-to-cancel-a-dart-future) – jamesdlin May 28 '19 at 16:12

3 Answers3

4

You can achieve cancelling the Future async operation by using CancelableOperation.

Here is an example (p.s I simplified your method signature for me to test it easily)

  CancelableOperation cancellableOperation;

  Future<dynamic> fromCancelable(Future<dynamic> future) async {
    cancellableOperation?.cancel();
    cancellableOperation = CancelableOperation.fromFuture(future, onCancel: () {
      print('Operation Cancelled');
    });
    return cancellableOperation.value;
  }

  Future<dynamic> getTranslation(String query, String from, String to) async {
    return Future.delayed(const Duration(milliseconds: 1000), () {
      return "Hello";
    });
  }

On Text Changed Listener:

  onTextChanged() {
    fromCancelable(getTranslation("query", "EN", "TR")).then((value) {
      print("Then called: $value");
    });
  }

Sample output:

I/flutter ( 7312): Operation Cancelled
I/flutter ( 7312): Operation Cancelled
I/flutter ( 7312): Operation Cancelled
I/flutter ( 7312): Operation Cancelled
I/flutter ( 7312): Then called: Hello
Gunhan
  • 6,807
  • 3
  • 43
  • 37
2

Yes there is, it is called Timer

https://api.dartlang.org/stable/2.3.1/dart-async/Timer-class.html

You can delay execution as well as cancel the trigger.

Antoniossss
  • 31,590
  • 6
  • 57
  • 99
0

Assuming that you're using a TextField for your input, you can call getTranslation() on the onSubmitted, which will be called when the user finishes editing :

 TextField(
   onSubmitted: (value) {
     getTranslation(value, 'en', 'ru'); 
   },
 );
Mazin Ibrahim
  • 7,433
  • 2
  • 33
  • 40
  • Thanks but I need to call it when the user paused typing, not when he explicitly submitted his query. The delay is just to prevent calling the api for every single character and prevent race conditions. –  May 28 '19 at 20:45