This article will walk you through 3 different ways to cancel future in Flutter and Dart.

using asynchronous packages (recommended)

The async package was developed and published by the authors of the Dart programming language. It provides dart:async-style utilities to enhance asynchronous computation. What can help us cancel Future is the CancelableOperation class:

var myCancelableFuture = CancelableOperation.fromFuture(  Future<T> inner,   { FutureOr onCancel()? })
// call the cancel() method to cancel the futuremyCancelableFuture.cancel();

copy the code

for more clarity, see the actual example below.

complete example

app preview

The application we are going to build has a floating button. When this button is pressed, an asynchronous operation begins (this takes 5 seconds to complete). The background of the button changed from indigo to red, and its label changed from Start to Cancel, and you can now use it to cancel Future.

  • If you click the Cancel button within 5 seconds before Future completes, the screen will display “Future Canceled.”.
  • If you do nothing, the screen will display “Future completed” after 5 seconds.

a demo worth more than a thousand words:

code

1. install the asynchronous package by doing the following:

flutter pub add async

copy the code

then run:

flutter pub get

copy the code

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

// main.dartimport '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); @override Widget build(BuildContext context) { return MaterialApp( // Remove the debug banner debugShowCheckedModeBanner: false, title: '大前端之旅', theme: ThemeData( primarySwatch: Colors.indigo, ), home: const HomePage()); }}
class HomePage extends StatefulWidget { const HomePage({Key? key}) : super(key: key);
@override _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( _myFuture(), 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; }); }
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('大前端之旅')), 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), ), ); }}

copy the code

use the timeout() method

this method is both quick and simple. however, it is not very flexible.

Using the timeout() method, you can limit the time of Future (for example, 3 seconds). If future completes in time, its value will be returned. On the other hand, if Future exceeds the limit time, the onTimeout function: will be executed

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

copy the code

quick example

Create a virtual Future:

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

copy the code

set the timeout to 3 seconds:

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

copy the code

Convert Future to a stream

You can use the asStream() method of the Future class to create a stream that contains the original Future result. You can now unsubscribe from the flow.

quick example

// don't forget to import thisimport 'dart:async';
// Create a demo futureFuture<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() laterStreamSubscription<dynamic>? _sub;
// convert the future to a stream_sub = _loadData().asStream().listen((data) { // do something with "data" print(data); });
// cancel the stream subscription_sub.cancel();

copy the code

note that this quick example only briefly describes how things work. you must modify it so that it can run in an existing project.

conclusion

You’ve learned more than one way to cancel Future in Flutter. Choose one of them to implement in your application to make it more robust and engaging when dealing with asynchronous tasks.