Answer
Overview
Isolates are Dart's concurrency mechanism -- each isolate is an independent worker with its own memory heap. Unlike threads in other languages, isolates cannot share memory; they communicate only via message passing.
Why Isolates?
Dart's main thread runs the UI at 60fps. CPU-heavy operations (parsing large JSON, image processing, encryption) block the UI thread and cause jank. Isolates solve this by offloading work.
textMain Isolate (UI Thread) Background Isolate -- Widget rendering -- Parses 50MB JSON -- Animations -- Processes images -- User events -- Encrypts files -- 'Send me the result' -- 'Here is your result' Message Passing
Simple Isolate with compute()
Flutter provides
text
compute()dartimport 'package:flutter/foundation.dart'; // Top-level or static function (required for isolates) List<int> heavyComputation(List<int> numbers) { return numbers.map((n) => n * n).toList(); // CPU intensive } // In widget Future<void> _process() async { final result = await compute(heavyComputation, [1, 2, 3, 4, 5]); print(result); // [1, 4, 9, 16, 25] setState(() => _data = result); }
Isolate.run() (Dart 2.19+)
dartimport 'dart:isolate'; // Simple background task final result = await Isolate.run(() { // Runs in background isolate return parseHeavyJson(rawData); });
Manual Isolate with SendPort/ReceivePort
dartimport 'dart:isolate'; void isolateWorker(SendPort sendPort) { // Works in background final result = expensiveCalculation(); sendPort.send(result); // Send result to main isolate } Future<void> runHeavyTask() async { final receivePort = ReceivePort(); // Spawn isolate await Isolate.spawn(isolateWorker, receivePort.sendPort); // Wait for result final result = await receivePort.first; print('Result: $result'); receivePort.close(); }
Two-Way Communication
dartFuture<void> twoWayExample() async { final receivePort = ReceivePort(); await Isolate.spawn(echoIsolate, receivePort.sendPort); final sendPort = await receivePort.first as SendPort; // Send data to isolate final responsePort = ReceivePort(); sendPort.send({'data': 'hello', 'replyPort': responsePort.sendPort}); final response = await responsePort.first; print('Response: $response'); } void echoIsolate(SendPort sendPort) { final port = ReceivePort(); sendPort.send(port.sendPort); port.listen((message) { final msg = message as Map; final replyPort = msg['replyPort'] as SendPort; replyPort.send('Echo: ${msg['data']}'); }); }
When to Use Isolates
| Task | Isolate? |
|---|---|
| Parse large JSON (>1MB) | Yes |
| Image/video processing | Yes |
| Encryption/hashing | Yes |
| Database queries (Hive, Isar) | Yes -- built-in isolate support |
| Regular API calls | No -- async is enough |
| setState, UI updates | No -- main isolate only |
Key Rule: Isolates cannot share memory. All data passed between them must be copyable (primitives, lists, maps). UI updates must happen on the main isolate via
after receiving the result.textsetState()