How to make Timer.periodic cancel itself when a condition is reached?

19,009

Solution 1

You get the timer passed into the callback. You can just call cancel() on it:

Timer.periodic(const Duration(seconds: 1), (timer) {
  if(condition) {
    timer.cancel();
  }
});

or

Timer timer;

startTimer() {
  timer = Timer.periodic(const Duration(seconds: 1), (timer) {
    if(condition) {
      cancelTimer();
    }
  });
}

cancelTimer() {
  timer.cancel();
}

this way the timer can be cancelled independent of a timer event.

Solution 2

You could instead use Stream.periodic with takeWhile

  bool condition = true;

  Future.delayed(const Duration(seconds: 5), () => condition = false);

  Stream.periodic(const Duration(seconds: 1))
      .takeWhile((_) => condition)
      .forEach((e) {
    print('event: $e');
  });

Solution 3

One solution I found is to pass a function in to the timer that returns a bool and then use timer.cancel() when it's satisfied. Here's a generalized way of doing this:

periodically(int milliseconds, Function repeat, [Function cancel]) {
  return Timer.periodic(
    Duration(milliseconds: milliseconds),
    (Timer t) {
      repeat();
      if (cancel != null) {
        cancel() ? t.cancel() : null;
      }
    }
  );
}
Share:
19,009

Related videos on Youtube

Matt S.
Author by

Matt S.

Updated on June 04, 2022

Comments

  • Matt S.
    Matt S. almost 2 years

    Timer.periodic() is great to have a function repeatedly execute, but is it possible to have the the timer cancel itself if an arbitrary condition is reached outside the function being executed by the timer?

  • Maritn Ge
    Maritn Ge about 3 years
    If I do option b, the Timer timer will always be null, even while the timer is running. it will run, but when i cancel it, it gives me a null exception
  • Günter Zöchbauer
    Günter Zöchbauer about 3 years
    That's probably not related to the code in my answer. I guess cancel() is called before the timer is initialized.
  • Maritn Ge
    Maritn Ge about 3 years
    You're kinda right, I did another thing wrong, you're code works fine