3 Ways to Cancel a Future in Flutter and Dart

Get into the habit of writing together! This is the 5th day of my participation in the "Nuggets Daily New Plan · April Update Challenge", click to view the details of the event .

3 Ways to Cancel a Future in Flutter and Dart

Author: Nuts

Public account: " Big Front-End Tour "

Huawei cloud sharing expert, InfoQ contract author, Alibaba Cloud expert blogger, 51CTO blog chief experience officer, one of the members of the open source project GVA , focusing on the sharing of big front-end technologies, including Flutter, applet, Android, VUE, JavaScript.

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

Use async package (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 to cancel a Future is the CancelableOperation class:

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

For more clarity, see the actual example below.

Complete example

App preview

animation 1

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

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

A demo is worth over a thousand words:

code

1. Install the async package by doing the following:

flutter pub add async
复制代码

Then run:

flutter pub get
复制代码

2. Complete 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),
      ),
    );
  }
}
复制代码

Using the timeout() method

This method is quick and easy. However, it is not very flexible.

Using the timeout() method, you can limit the Future's time (eg 3 seconds). 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:

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';
}
复制代码

Set a timeout of 3 seconds:

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

Convert Future to Stream

You can use the asStream() method of the Future class to create a stream containing the raw Future results. Now you can unsubscribe from this 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"
    print(data);
 });
// cancel the stream subscription
_sub.cancel();
复制代码

Note that this quick example only briefly describes how things work. You must modify it to work in an existing project.

in conclusion

You've learned more than one way to cancel a Future in Flutter. Choose one of these to implement in your application to make it more robust and attractive when dealing with asynchronous tasks.

Guess you like

Origin juejin.im/post/7083506236112502814