How to solve: Dart - execute a function after x seconds unless cancelled by event

Question

Asked by killerbird on January 05, 2022 (source).

I am currently writing an app using Flutter and Dart. On a button onPressed event I would like to invoke an action that executes after timeLeft seconds unless it is cancelled by correctly entering a pin. Additionally, I would like to use the value timeLeft in a Text widget.

This would require a structure with the following functionality:

  • executing a function after an x amount of seconds
  • this function should execute unless some event (e.g. entering a pin correctly) has occurred.
  • the timeLeft value should be accessible to be used in a Text widget and should update as the timer progresses.

I am wondering how to do this according to flutter's and dart's best practices. For state management I am using the provider pattern so preferably this approach is compatible with the provider pattern.

This is what I have tried so far:

class Home extends ChangeNotifier {
  int secondsLeft = 10;

  void onPressedEmergencyButton(BuildContext context) {
    countDown();
    showDialog<void>(
      context: context,
      builder: (context) {
        return ScreenLock(
          title: Text(
              "Sending message in ${context.read<Home>().secondsLeft} seconds"),
          correctString: '1234',
          canCancel: false,
          didUnlocked: () {
            Navigator.pop(context);
          },
        );
      },
    );
  }

  void countDown() {
    Future.delayed(const Duration(seconds: 1), () {
      secondsLeft =- 1;
      notifyListeners();
      if (secondsLeft <= 0) {
        // Do something
        return;
      }
    });
  }
}

Answer

Question answered by Yeasin S (source).

You can use CancelableOperation from async package.

Simplifying code-snippet and about _cancelTimer(bool) , this bool used to tell widget about true = time end, and on cancel false like _cancelTimer(false);, rest are described on code-comments.

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

  @override
  State<TS> createState() => _TSState();
}

class _TSState extends State<TS> {
  Timer? _timer;
  final Duration _refreseRate = const Duration(seconds: 1);

  CancelableOperation? _cancelableOperation;

  Duration taskDuration = const Duration(seconds: 5);

  bool isSuccess = false;

  _initTimer() {
    if (_cancelableOperation != null) {
      _cancelTimer(false);
    }

    _cancelableOperation = CancelableOperation.fromFuture(
      Future.delayed(Duration.zero),
    ).then((p0) {
      _timer = Timer.periodic(_refreseRate, (timer) {
        setState(() {
          taskDuration -= _refreseRate;
        });
        if (taskDuration <= Duration.zero) {
          /// task complete on end of duration

          _cancelTimer(true);
        }
      });
    }, onCancel: () {
      _timer?.cancel();

      setState(() {});
    });
  }

  _cancelTimer(bool eofT) {
    // cancel and reset everything
    _cancelableOperation?.cancel();
    _timer?.cancel();
    _timer = null;
    taskDuration = const Duration(seconds: 5);
    isSuccess = eofT;
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            if (isSuccess)
              Container(
                height: 100,
                width: 100,
                color: Colors.green,
              ),
            if (_timer != null)
              Text("${taskDuration.inSeconds}")
            else
              const Text("init Timer"),
          ],
        ),
      ),
      floatingActionButton: Row(
        mainAxisSize: MainAxisSize.min,
        children: [
          FloatingActionButton(
            child: const Text("init"),
            onPressed: () {
              _initTimer();
            },
          ),
          FloatingActionButton(
            child: const Text("Cancel"),
            onPressed: () {
              _cancelTimer(false);
            },
          ),
        ],
      ),
    );
  }
}
COUNTER DART FLUTTER
SHARE: