3 Ways to Cancel a Future in Flutter and Dart

Last updated on March 21, 2022 Pennywise Loading... Post a comment

This article walks you through 3 different ways to cancel a future in Flutter and Dart.

Using async package (recommended)

The async package is developed and published by the authors of the Dart programming language. It provides utility classed in the style of dart:async to enhance asynchronous computations. The thing that can help us cancel a future is the CancelableOperation class:

var myCancelableFuture = CancelableOperation.fromFuture(
  Future<T> inner, 
  { FutureOr onCancel()? }

// call the cancel() method to cancel the future

For more clarity, please see the practical example below.

Complete Example

App Preview

The app we’re going to build has a floating button. When this button gets pressed, an asynchronous operation will start (this takes 5 seconds to finish). The button’s background changes from indigo to red and its label changes from “Start” to “Cancel” and now you can use it to cancel the future.

  • If you touch the Cancel button within 5 seconds before the future is completed, the screen will show “Future has been canceled”.
  • If you do nothing then after 5 seconds the screen will show “Future completed”.

A demo is worth more than a thousand words:

The Code

1. Install the async package by performing this:

flutter pub add async

Then run:

flutter pub get

2. The full source code in main.dart (with explanations):

// main.dart
import 'package:flutter/material.dart';
import 'package:async/async.dart';

void main() {
  runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
  Widget build(BuildContext context) {
    return MaterialApp(
        // Remove the debug banner
        debugShowCheckedModeBanner: false,
        title: 'KindaCode.com',
        theme: ThemeData(
          primarySwatch: Colors.indigo,
        home: const HomePage());

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  _HomePageState createState() => _HomePageState();

class _HomePageState extends State<HomePage> {

  // this future will return some text once it completes
  Future<String?> _myFuture() async {
    await Future.delayed(const Duration(seconds: 5));
    return 'Future completed';

  // keep a reference to CancelableOperation
  CancelableOperation? _myCancelableFuture;

  // This is the result returned by the future
  String? _text;

  // Help you know whether the app is "loading" or not
  bool _isLoading = false;

  // This function is called when the "start" button is pressed
  void _getData() async {
    setState(() {
      _isLoading = true;

    _myCancelableFuture = CancelableOperation.fromFuture(
      onCancel: () => 'Future has been canceld', 
    final value = await _myCancelableFuture?.value;

    // update the UI
    setState(() {
      _text = value;
      _isLoading = false;

  // this function is called when the "cancel" button is tapped
  void _cancelFuture() async {
    final result = await _myCancelableFuture?.cancel();
    setState(() {
      _text = result;
      _isLoading = false;

  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('KindaCode.com')),
      body: Center(
        child: _isLoading
            ? const CircularProgressIndicator()
            : Text(
                _text ?? 'Press Start Button',
                style: const TextStyle(fontSize: 28),
      // This button is used to trigger _getDate() and _cancelFuture() functions
      // the function is called depends on the _isLoading variable
      floatingActionButton: ElevatedButton(
        onPressed: () => _isLoading ? _cancelFuture() : _getData(),
        child: Text(_isLoading ? 'Cancel' : 'Start'),
        style: ElevatedButton.styleFrom(
            padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 30),
            primary: _isLoading ? Colors.red : Colors.indigo),

Using timeout() method

This approach is quick and simple. However, it isn’t very flexible.

With the timeout() method, you can limit time (e.g. 3 seconds) for a future. If the future completes in time, its value will be returned. On the other hand, if the future exceeds the limit time, the onTimeout function will be executed instead:

Future<T> timeout(
   Duration timeLimit,
  {FutureOr<T> onTimeout()?}

Quick Example

Create a dummy future:

Future<String?> _myFuture() async {
    await Future.delayed(const Duration(seconds: 10));
    return 'Future completed';

Setting a timeout of 3 seconds:

      const Duration(seconds: 3),
      onTimeout: () =>
          'The process took too much time to finish. Please try again later',

Converting the future to a stream

You can use the asStream() method of the Future class to create a stream that contains the result of the original future. Now you can cancel a subscription to that stream.

Quick Example

// don't forget to import this
import 'dart:async';

// Create a demo future
Future<dynamic> _loadData() async {
    await Future.delayed(const Duration(seconds: 10));
    return 'Some Data';

// a reference to the stream subscription
// so that we can call _sub.cancel() later
StreamSubscription<dynamic>? _sub;

// convert the future to a stream
_sub = _loadData().asStream().listen((data) {
    // do something with "data"

// cancel the stream subscription

Note that this quick example only briefly describes how things work. You have to modify it to make it runable in your existing project.


You’ve learned more than one approach to cancel a future in Flutter. Choose one from them to implement in your app to make it more robust and engaging when dealing with asynchronous tasks. If you’d like to explore more new and interesting things in Flutter, take a look at the following articles:

You can also take a tour around our Flutter topic page and Dart topic page to see the latest tutorials and examples.

Notify of
Inline Feedbacks
View all comments

Related Articles