Isolates are Dart's only way to use multiple cores and keep a Flutter UI at 60+ fps during heavy work.
Dart code runs in isolates: each has its own memory heap and event loop, and isolates share no mutable state. That's why Dart needs no locks — and why parallelism requires message passing.
The simplest API is `Isolate.run` (Dart 2.19+):
final result = await Isolate.run(() => parseHugeJson(text));It spawns an isolate, runs the function, returns its result and shuts the isolate down. Flutter's compute(fn, arg) is a wrapper around the same idea. Use it for work that takes more than a few milliseconds: JSON decoding of large payloads, image processing, encryption, search indexing.
For long-lived workers, use Isolate.spawn with a `SendPort`/`ReceivePort` pair: the worker sends back its own SendPort, and both sides exchange messages.
What can be sent: most objects are copied (deep copy), including lists, maps, records and instances of your classes. Exceptions: objects holding native resources such as Socket, ReceivePort or DynamicLibrary can't be sent. Closures can be sent only if what they capture is sendable — capturing a big object by accident copies it too. Isolate.exit transfers the result without copying when ending a worker; TransferableTypedData moves byte buffers cheaply.
Isolates spawned from the same isolate group share code and start quickly (tens of microseconds to milliseconds). On the web, isolates aren't available in the same form; use web workers.
Example
import 'dart:isolate';
int countPrimes(int limit) {
var count = 0;
for (var n = 2; n < limit; n++) {
var prime = true;
for (var d = 2; d * d <= n; d++) {
if (n % d == 0) { prime = false; break; }
}
if (prime) count++;
}
return count;
}
Future<void> main() async {
final sw = Stopwatch()..start();
final results = await Future.wait([
Isolate.run(() => countPrimes(2000000)),
Isolate.run(() => countPrimes(1000000)),
]);
print(results); // [148933, 78498] — computed in parallel
print('main isolate stayed free: ${sw.elapsedMilliseconds} ms');
}Remember
- Isolates have separate heaps and communicate only by messages
Isolate.run(or Flutter'scompute) offloads one CPU-heavy task- Messages are copied; native resources can't be sent
Watch out for
- Capturing a large object in the closure passed to
Isolate.run, silently copying it to the new isolate.