
Efficient task execution is crucial in Flutter application development, as it directly affects the user experience. Developers need to implement code that ensures tasks are performed quickly and seamlessly.
Why does it matter ?
This is important because, in a Flutter application with UI rendering, there is a specific time allocated to execute the callbacks responsible for rendering the frame. If your code takes too long, these callbacks may be delayed, causing some frames to be missed.
In Flutter, there are multiple approaches to executing tasks, each with varying levels of efficiency and suitability depending on the specific use case. Choosing the right method is essential to optimize performance and deliver a smooth, responsive application.
1. Vanilla approach
We need to start by this approach as it is the most common one and having it in mind will help to better understand the difference with the others.
The vanilla approach is the traditional method of executing code. You create a function and run it without considering efficiency.

Pros
- Simple to implement
- Simple to understand
- Easy to port to different platforms
Cons
- Can become highly inefficient and performance-intensive
2. scheduleMicrotask / Future.microtask / SchedulerBinding.instance.scheduleTask
This approach requires the Flutter dependency and lets you schedule a task to be executed after frame rendering. It delays the execution of the scheduled task, allowing your function to return sooner and enabling the event handler to quickly process the next task, such as UI rendering.
The same code as above with this approach will look like

Few things to note there that changed.
- The update of the variable _likedCount is now done by incrementing/decrementing its value instead of relying on the size of the _hugeLikedItems variable. This is because the _hugeLikedItems will be updated asynchronously and so its size is no more reliable.
- We wrap the “delayable” code inside SchedulerBinding.instance.scheduleTask that will be executed asynchronously.
- As this code, depends on a task scheduled by the SchedulerBinding which on its side depends on the frame build this code is worth only in a UI code execution.
Pros
- High gain in execution time.
- Low impact on frames rendering for some tasks.
Cons
- Depends on frame building and so could still affect it.
- Asynchronous execution makes the data unreliable for any read operation.
scheduleMicrotask works the same way but offers much better execution time. So this function should be prioritized over the scheduleTask.
SchedulerBinding.instance.scheduleTask was used in the previous code as example of use but should not be used for code that takes more than a millisecond. So it would be good for the dislike task but not good for the fillList task.

3. Timer.run
This approach also requires the Flutter dependency and is less efficient than the previous one, as tasks created with scheduleMicrotask have higher priority than those created with Timer.run. However, it is still worth mentioning because it allows for some asynchrony and the possibility to return earlier.
The same code as above with this approach will look like

The only thing changed was the use of Timer.run instead of SchedulerBinding.instance.scheduleTask.
Pros
- High gain in execution time.
- Low impact on frame rendering.
- A Timer can be cancelled ;).
Cons
- Low priority of execution so if you have a lot of frame rendering (like animations) your task could take time to be executed.
- Asynchronous execution makes the data unreliable for any read operation.
4. compute / Isolate.run
This approach relies on the compute function from Flutter or Isolate.run from Dart (used in recent Flutter releases). Unlike the previous methods, the callback/function is executed asynchronously on a different Isolate, allowing for an earlier return
It is a particular one from the others cause the callback will be executed on different isolate and so can add more complexity but also add much more value.
This method is then highly effective when you manipulate huge data that are not used in/for the UI.

Few changes to note there :
- We rely on the function compute
- We didn’t use it for the dislike operation because the complexity of the operation will be too high due to the next point.
- We had to fill the list using another list because as the code will be executed in another Isolate, new instances of the global variables are created and so we cannot update directly (add/remove items) the same list variable.
Pros
- Don’t depend on frame building and so, can be used without UI operations.
- High gain in execution time.
- No impact on frame rendering, as the operations are done on another Isolate than the one responsible of rendering.
Cons
- Use more memory, as variables shared between the Isolates will be recreated.
- Cannot be used for operations that update global variables.
So depending on the needs and/ or requirements of your application, you can use different ways of task execution that will be highly efficient on your Flutter application and have less impact in the frame rendering.
If your task is short (around a milliseconds) you can use scheduleMicrotask or SchedulerBinding.instance.scheduleTask otherwise Timer.run is a better option. If your task is really huge then Isolate.run or compute are the way to go.