is there any way to cancel a dart Future?

How to cancel Future.delayed

A simple way is to use Timer instead :)

Timer _timer;

void _schedule() {
  _timer = Timer(Duration(seconds: 2), () { 
    print('Do something after delay');
  });
}

@override
void dispose() {
  super.dispose();
  _timer?.cancel();
}

You can use CancelableOperation or CancelableCompleter to cancel a future. See below the 2 versions:

Solution 1: CancelableOperation (included in a test so you can try it yourself):

  • cancel a future

test("CancelableOperation with future", () async {

  var cancellableOperation = CancelableOperation.fromFuture(
    Future.value('future result'),
    onCancel: () => {debugPrint('onCancel')},
  );

// cancellableOperation.cancel();  // uncomment this to test cancellation

  cancellableOperation.value.then((value) => {
    debugPrint('then: $value'),
  });
  cancellableOperation.value.whenComplete(() => {
    debugPrint('onDone'),
  });
});
  • cancel a stream

test("CancelableOperation with stream", () async {

  var cancellableOperation = CancelableOperation.fromFuture(
    Future.value('future result'),
    onCancel: () => {debugPrint('onCancel')},
  );

  //  cancellableOperation.cancel();  // uncomment this to test cancellation

  cancellableOperation.asStream().listen(
        (value) => { debugPrint('value: $value') },
    onDone: () => { debugPrint('onDone') },
  );
});

Both above tests will output:

then: future result
onDone

Now if we uncomment the cancellableOperation.cancel(); then both above tests will output:

onCancel

Solution 2: CancelableCompleter (if you need more control)

test("CancelableCompleter is cancelled", () async {

  CancelableCompleter completer = CancelableCompleter(onCancel: () {
    print('onCancel');
  });

  // completer.operation.cancel();  // uncomment this to test cancellation

  completer.complete(Future.value('future result'));
  print('isCanceled: ${completer.isCanceled}');
  print('isCompleted: ${completer.isCompleted}');
  completer.operation.value.then((value) => {
    print('then: $value'),
  });
  completer.operation.value.whenComplete(() => {
    print('onDone'),
  });
});

Output:

isCanceled: false
isCompleted: true
then: future result
onDone

Now if we uncomment the cancellableOperation.cancel(); we get output:

onCancel
isCanceled: true
isCompleted: true

Be aware that if you use await cancellableOperation.value or await completer.operation then the future will never return a result and it will await indefinitely if the operation was cancelled. This is because await cancellableOperation.value is the same as writing cancellableOperation.value.then(...) but then() will never be called if the operation was cancelled.

Remember to add async Dart package.

Code gist