83

inside my flutter app I want to check my api every 10 seconds. I found this post to run a function every x amount of time and did the following:

class _MainPage extends State<MainPage> {
  int starter = 0;

  void checkForNewSharedLists(){
    // do request here
    setState((){
      // change state according to result of request
    });

  }

  Widget build(BuildContext context) {
    Timer.periodic(Duration(seconds: 15), (Timer t) => checkForNewSharedLists());
  }
} 

Unfortunately the requests pile up: after restarting the app on the first "round" there are two request to the api, the second round it's four requests, the third it's eight and so on...

Does anybody know how to fix this?

acincognito
  • 1,595
  • 1
  • 13
  • 24
  • You can use like this https://stackoverflow.com/questions/63549244/how-to-execute-a-method-in-a-certain-time-in-flutter/75875791#75875791:~:text=You%20can%20use,print(result)%3B%0A%20%20%7D)%3B – Rahul Kushwaha Mar 29 '23 at 10:08
  • How can i do same thing if app is in background? – amit.flutter Apr 25 '23 at 12:37

3 Answers3

197

build() can and usually will be called more than once and every time a new Timer.periodic is created.

You need to move that code out of build() like

import 'dart:async';

Timer? timer;

@override
void initState() {
  super.initState();
  timer = Timer.periodic(Duration(seconds: 15), (Timer t) => checkForNewSharedLists());
}

@override
void dispose() {
  timer?.cancel();
  super.dispose();
}

Even better would be to move out such code from widgets entirely in an API layer or similar and use a StreamBuilder to have the view updated in case of updated data.

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • 1
    Thanks, works like a charm. I will try the suggestion with the StreamBuilder, too. One question though: the questionmark in the dispose method is it a typo? – acincognito Sep 29 '18 at 17:44
  • 4
    That was intentional, but not absolutely necessary. It's just to avoid an exception in case the timer was not yet set or set to null by some other code. It's the null-save navigation operator. – Günter Zöchbauer Sep 29 '18 at 17:48
  • @acincognito did you try with StreamBuilder? if so please put some hints on it. thanks – khan Oct 11 '18 at 16:54
  • How can i do same thing if app is in background? – amit.flutter Apr 25 '23 at 12:37
  • @amit.flutter not sure what you mean. It's in the background. You just need *some* place to initiate it. It depends what you want to do what the best place is. If you don't want it inside a component, you can initiate it in `main()` before you run the app, but then you might not be able to do what you want when a timer event happens. – Günter Zöchbauer Apr 26 '23 at 17:36
12

Use Cron lib which will be run periodically, but there is a difference between Timer and Cron,

Timer: It's running a task on given specific time intervals whether it is seconds, minutes, or hours.

Cron: It's used for more complex time intervals, eg: if a task needs to be run on a specific time of an hour. let's see the diagram

enter image description here

The above diagram has an asterisk that represents a number that appears in a specific position.

import 'package:cron/cron.dart';

main() {
  var cron = new Cron();
  cron.schedule(new Schedule.parse('*/3 * * * *'), () async {
    print('every three minutes');
  });
  cron.schedule(new Schedule.parse('8-11 * * * *'), () async {
    print('between every 8 and 11 minutes');
  });
}

The above examples are taken from the repository which pretty well explains that the first '*' represents minutes, similar for the hour and so on as shown in the diagram.

Another example of the hour would be Schedule.parse(* 1,2,3,4 * * *), This schedule will run every minute every day during the hours of 1 AM, 2 AM, 3 AM, and 4 AM.

for more reference https://code.tutsplus.com/tutorials/scheduling-tasks-with-cron-jobs--net-8800

Jitesh Mohite
  • 31,138
  • 12
  • 157
  • 147
0

Timer works fine, but you can also use Stream to execute a function periodicly:

final Stream _myStream =
    Stream.periodic(const Duration(seconds: x), (int count) {
       // Do something and return something here
});

see: https://api.flutter.dev/flutter/dart-async/Stream/Stream.periodic.html

I was looking for code to run a function every n amount of seconds for x seconds in total. Also I added functionality to cancel the timer if the periodic Function returns a success before the total timer elapsed. So here is how to solve that:

bool success = false;
bool done = false;
Future.delayed(const Duration(seconds: n), () {
     done = true; 
     print("Timeout");
});
await Stream.periodic(const Duration(seconds: x)).takeWhile((_) => !done).forEach((_) async 
{
     success = await FunctionYouWantToExecutePeriodicly();
     done = success; // only if you want to finish the function earlier
});