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 future
myCancelableFuture.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.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);
@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 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"
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.