What is the difference between isolates and compute ?
Answer
Overview
Both Isolates and run code in a separate thread to avoid blocking the UI. The difference is that textcompute()
text
compute()Flow Diagrams
compute() Flow
text┌─────────────────────────────────────────────────────────────┐ │ Main Isolate (UI) │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 1. Call compute(function, data) │ │ ↓ │ │ 2. Flutter creates new isolate automatically │ │ ↓ │ └──────────────────────┬──────────────────────────────────────┘ │ │ Spawn & Send Data ↓ ┌─────────────────────────────────────────────────────────────┐ │ Background Isolate │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 3. Receive data parameter │ │ ↓ │ │ 4. Execute function(data) │ │ ↓ │ │ 5. Return result │ │ ↓ │ │ 6. Isolate automatically terminated │ │ │ └──────────────────────┬──────────────────────────────────────┘ │ │ Send Result Back ↓ ┌─────────────────────────────────────────────────────────────┐ │ Main Isolate (UI) │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 7. Receive result via Future │ │ ↓ │ │ 8. Update UI with result │ │ │ └─────────────────────────────────────────────────────────────┘ Lifetime: Short (one task, then exit) Communication: One-way (data in → result out) Use case: Simple, one-shot heavy computation
Raw Isolate Flow
text┌─────────────────────────────────────────────────────────────┐ │ Main Isolate (UI) │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 1. Create ReceivePort │ │ ↓ │ │ 2. Spawn isolate with SendPort │ │ ↓ │ └──────────────────────┬──────────────────────────────────────┘ │ │ Spawn with SendPort ↓ ┌─────────────────────────────────────────────────────────────┐ │ Worker Isolate │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 3. Receive Main's SendPort │ │ ↓ │ │ 4. Create own ReceivePort │ │ ↓ │ │ 5. Send back own SendPort to Main │ │ ↓ │ │ 6. Listen for messages (loop) │ │ │ └──────────────────────┬──────────────────────────────────────┘ │ │ Send Own SendPort ↓ ┌─────────────────────────────────────────────────────────────┐ │ Main Isolate (UI) │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 7. Receive Worker's SendPort │ │ ↓ │ │ 8. Send Message #1 ────────────────────┐ │ │ │ │ │ 9. Send Message #2 ──────────────────┐ │ │ │ │ │ │ │ 10. Send Message #N ────────────────┐│ │ │ │ ││ │ │ └──────────────────────────────────────┼┼─┼───────────────────┘ ││ │ Messages Queue ↓↓ ↓ ┌─────────────────────────────────────────────────────────────┐ │ Worker Isolate │ ├─────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────────────────────────────┐ │ │ │ Message Loop (continuous) │ │ │ │ │ │ │ │ 11. Receive Message #1 │ │ │ │ ↓ │ │ │ │ 12. Process & Send Result #1 ───────┼──────┐ │ │ │ ↓ │ │ │ │ │ 13. Receive Message #2 │ │ │ │ │ ↓ │ │ │ │ │ 14. Process & Send Result #2 ───────┼──────┤ │ │ │ ↓ │ │ │ │ │ 15. Keep listening... (loop) │ │ │ │ │ │ │ │ │ └──────────────────────────────────────┘ │ │ │ │ │ └─────────────────────────────────────────────────┼───────────┘ │ Results Back ↓ ┌─────────────────────────────────────────────────────────────┐ │ Main Isolate (UI) │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 16. Receive Result #1 │ │ ↓ │ │ 17. Update UI │ │ ↓ │ │ 18. Receive Result #2 │ │ ↓ │ │ 19. Update UI │ │ ↓ │ │ 20. Continue... (ongoing) │ │ │ │ Later: isolate.kill() to terminate │ │ │ └─────────────────────────────────────────────────────────────┘ Lifetime: Long-lived (until explicitly killed) Communication: Bidirectional (continuous messaging) Use case: Persistent worker, multiple tasks, ongoing processing
Side-by-Side Comparison
text┌──────────────────────────┬──────────────────────────┐ │ compute() │ Raw Isolate │ ├──────────────────────────┼──────────────────────────┤ │ │ │ │ Main Isolate │ Main Isolate │ │ │ │ │ │ │ │ compute() │ │ Isolate.spawn() │ │ ↓ │ ↓ │ │ ┌─────────┐ │ ┌─────────┐ │ │ │ Background│ │ │ Worker │ │ │ │ Isolate │ │ │ Isolate │ │ │ │ │ │ │ │ │ │ │ Run once │ │ │ Run loop│ │ │ │ Exit auto│ │ │ Stay alive│ │ │ └─────────┘ │ └─────────┘ │ │ │ │ │ ↑ │ │ │ Result │ │ │ Multiple │ │ ↓ │ │ │ Messages │ │ Main Isolate │ ↓ │ │ │ │ Main Isolate │ │ ✅ Simple │ │ │ │ │ ✅ Automatic cleanup │ │ │ │ │ ❌ One-shot only │ ✅ Bidirectional │ │ │ ✅ Long-lived │ │ │ ❌ Complex setup │ │ │ │ └──────────────────────────┴──────────────────────────┘
Memory Model
text┌─────────────────────────────────────────────────────────────┐ │ Dart Memory Model │ ├─────────────────────────────────────────────────────────────┤ │ │ │ Main Isolate Worker Isolate │ │ ┌──────────────────┐ ┌──────────────────┐ │ │ │ Heap Memory │ │ Heap Memory │ │ │ │ │ │ │ │ │ │ • UI Objects │ │ • Compute Data │ │ │ │ • State │ NO │ • Work Results │ │ │ │ • Controllers │ SHARED │ • Temp Objects │ │ │ │ • Variables │ MEMORY! │ • Variables │ │ │ │ │ │ │ │ │ └──────────────────┘ └──────────────────┘ │ │ │ │ │ │ │ │ │ │ │ Message Passing (Copy) │ │ │ └────────────────────────────────┘ │ │ │ │ ⚠️ Important: Data is COPIED between isolates │ │ - No shared memory access │ │ - Prevents race conditions │ │ - Each isolate has its own heap │ │ │ └─────────────────────────────────────────────────────────────┘
compute() — Simple Background Work
text
compute()- Spawns a new isolate
- Runs your function in it
- Returns the result
- Closes the isolate automatically
dartimport 'package:flutter/foundation.dart'; // The function to run in background (must be top-level or static) List<Post> _parsePosts(String jsonStr) { final list = jsonDecode(jsonStr) as List; return list.map((e) => Post.fromJson(e)).toList(); } // Usage — clean and simple Future<List<Post>> loadPosts(String jsonStr) async { return await compute(_parsePosts, jsonStr); // ↑ Runs _parsePosts in a new isolate with jsonStr as argument }
✅ One function, one argument, one return value — that's it.
Isolate — Full Control
Raw
text
Isolate.spawn()dartimport 'dart:isolate'; void workerIsolate(SendPort sendPort) { // This runs in a separate isolate final receivePort = ReceivePort(); sendPort.send(receivePort.sendPort); // Send back our port receivePort.listen((message) { if (message is int) { // Do heavy work int result = message * message; sendPort.send(result); // Send result back } }); } Future<void> runWithIsolate() async { final receivePort = ReceivePort(); await Isolate.spawn(workerIsolate, receivePort.sendPort); final SendPort workerPort = await receivePort.first; // Send multiple messages workerPort.send(5); workerPort.send(10); workerPort.send(20); await for (final result in receivePort) { print('Result: $result'); } }
Key Differences
| Feature | text | Raw text |
|---|---|---|
| Ease of use | ✅ Very simple | ⚠️ Complex setup |
| Direction | One-way: input → output | Bidirectional messaging |
| Lifetime | Short-lived (exits after return) | Can be long-lived |
| Multiple messages | ❌ One call, one result | ✅ Many messages |
| Function signature | text | Any function |
| Under the hood | Uses Isolate internally | Direct Isolate API |
| Best for | One-time heavy computation | Ongoing background worker |
When to Use Which
| Use Case | Recommendation |
|---|---|
| Parse large JSON from API | text |
| Compress/resize images | text |
| Run FFT or math calculations | text |
| Background download manager | Raw text |
| Persistent worker (multiple requests) | Raw text |
| Real-time audio/video processing | Raw text |
Flutter 3.7+ — Isolate.run()
Flutter added
text
Isolate.run()text
compute()dart// Flutter 3.7+ — cleaner syntax final result = await Isolate.run(() { // Heavy computation return heavyProcess(data); });
Example: compute vs raw Isolate
dart// ✅ Use compute for simple one-shot heavy task Future<String> encryptData(String data) async { return await compute(_encrypt, data); } String _encrypt(String input) { // CPU-heavy encryption return performEncryption(input); } // ✅ Use raw Isolate for ongoing background processing class BackgroundProcessor { late Isolate _isolate; late SendPort _sendPort; Future<void> start() async { final receivePort = ReceivePort(); _isolate = await Isolate.spawn(_worker, receivePort.sendPort); _sendPort = await receivePort.first; } void processItem(dynamic item) => _sendPort.send(item); void stop() => _isolate.kill(); }
Summary: Use
for simple, one-shot background tasks. Use rawtextcompute()when you need a persistent worker, bidirectional communication, or multiple message exchanges.textIsolate