What are Isolates?
In Dart, all Dart code runs inside isolates. An isolate is a separate execution context with its own memory heap and its own event loop. Isolates do not share memory; they communicate by passing messages (like how processes communicate). This design avoids many concurrency bugs (like race conditions) and allows Dart to run on multiple cores efficiently.
The main isolate (where main() runs) is created automatically. You can spawn additional isolates to perform heavy computations in parallel without blocking the main event loop.
Why Use Isolates?
- CPU‑intensive tasks – image processing, JSON parsing, complex calculations.
- Keeping UI responsive – move heavy work off the main isolate (especially important in Flutter).
- True parallelism – on multi‑core devices, isolates can run simultaneously.
Spawning an Isolate
To create a new isolate, use Isolate.spawn(entryPoint, message). The entry point must be a top‑level function or a static method that takes a single argument. The new isolate starts running that function.
Note: Isolate.spawn is asynchronous and returns a future that completes with the isolate reference. The new isolate runs independently.
Communicating Between Isolates
Because isolates don't share memory, you need to pass messages using ports. A ReceivePort listens for incoming messages, and a SendPort sends messages to a specific receive port. Typically, the main isolate creates a receive port and sends its send port to the child isolate.
Two‑Way Communication
Often you need the child isolate to send data back. You can set up a dedicated receive port in the child and send its send port to the main isolate. The main isolate then uses that send port to send messages to the child.
Long‑Lived Isolates and Streaming
You can keep an isolate alive for multiple tasks. The pattern above sets up a permanent communication channel. For simpler one‑off tasks, you can create a short‑lived isolate that exits after sending a result.
The compute() Function (Flutter)
In Flutter, the compute() function provides a convenient way to run a function in a separate isolate and get a single result. It's ideal for offloading a one‑time heavy task.
Note: compute is Flutter‑specific; it wraps isolate creation and communication for you.
What Can Be Sent Between Isolates?
Messages must be serializable. You can send primitive types, lists, maps, strings, and custom objects if they are serializable (e.g., using dart:convert). You cannot send objects with native resources (like file handles) or closures.
Common Pitfalls
- Forgetting to close ports – unused ports prevent garbage collection.
- Sending unsendable objects – leads to runtime errors.
- Main isolate blocking – isolates help, but if the main isolate is blocked, the UI freezes anyway.
- Too many isolates – spawning isolates has overhead; reuse them if you have many tasks.
Key Takeaways
- Isolates are independent workers with their own memory and event loop.
- Use
Isolate.spawnto create a new isolate.
- Use
- Communication is via
SendPortandReceivePort.
- Communication is via
- Use isolates for CPU‑intensive tasks to keep the UI responsive.
- In Flutter,
computesimplifies one‑off isolate tasks.
- In Flutter,
- Always close ports when done to avoid leaks.