Lesson 5 of 77 intermediate

Isolates, compute() & Concurrency

When Single-Threaded Isn't Enough

Open interactive version (quiz + challenge)

Real-world analogy

Dart's single thread is like one chef in a kitchen. For most meals (UI tasks), one chef is fine. But for a huge banquet (heavy computation like image processing), you need to hire a temporary chef (Isolate) who works in their OWN kitchen with their OWN ingredients — they can't touch the main chef's stuff. When done, they send the result back via a window (SendPort).

What is it?

Isolates are Dart's concurrency primitive — separate execution threads with their own memory that communicate via message passing. compute() and Isolate.run() provide simple APIs for offloading CPU-heavy work. They prevent UI jank by keeping heavy computation off the main thread, while the event loop handles I/O-bound async work.

Real-world relevance

In an offline-first field operations app processing thousands of survey records, syncing with a remote server while the user continues working requires an isolate. The main thread keeps the UI responsive while a worker isolate crunches the sync logic, resolves conflicts, and sends progress updates back via a SendPort.

Key points

Code example

// Isolates & Concurrency — Interview Essentials

import 'dart:isolate';
import 'package:flutter/foundation.dart';

// 1. compute() — simplest approach (Flutter)
Future<List<Map<String, dynamic>>> heavyJsonParse(
  String rawJson,
) async {
  // Runs in a separate isolate, returns the result
  return await compute(_parseJson, rawJson);
}

// Must be top-level or static — NOT a closure
List<Map<String, dynamic>> _parseJson(String raw) {
  // This runs in a separate isolate
  // Heavy parsing happens here without blocking UI
  return jsonDecode(raw) as List<Map<String, dynamic>>;
}

// 2. Isolate.run() — modern Dart 2.19+
Future<int> computeHash(String data) async {
  return await Isolate.run(() {
    // Heavy computation in separate isolate
    var hash = 0;
    for (var i = 0; i < data.length; i++) {
      hash = (hash * 31 + data.codeUnitAt(i)) & 0xFFFFFFFF;
    }
    return hash;
  });
}

// 3. Full Isolate with bidirectional communication
Future<void> workerIsolateExample() async {
  final receivePort = ReceivePort();

  // Spawn isolate with our receive port
  await Isolate.spawn(
    _workerEntryPoint,
    receivePort.sendPort,
  );

  // Listen for messages from the worker
  await for (final message in receivePort) {
    if (message is SendPort) {
      // Worker sent us its SendPort — now we can talk to it
      message.send('Process this data');
    } else if (message == 'done') {
      receivePort.close();
      break;
    } else {
      print('Worker result: $message');
    }
  }
}

void _workerEntryPoint(SendPort mainSendPort) {
  final workerReceivePort = ReceivePort();
  // Send our port so main can talk back
  mainSendPort.send(workerReceivePort.sendPort);

  workerReceivePort.listen((message) {
    // Process the work
    final result = 'Processed: $message';
    mainSendPort.send(result);
    mainSendPort.send('done');
    workerReceivePort.close();
  });
}

// WHEN TO USE vs NOT USE:
// USE Isolates:        NOT Isolates:
// - JSON parse 1MB+    - API calls (I/O bound)
// - Image resize       - File reads (I/O bound)
// - Encryption         - Simple state updates
// - Complex sort/filter - Timer/periodic tasks
// - PDF generation     - Database queries (I/O)

Line-by-line walkthrough

  1. 1. Using compute() to run heavy JSON parsing off the main thread
  2. 2. The function must be top-level — this is the isolate entry point
  3. 3. Inside the isolate: heavy work happens here without blocking UI
  4. 4. Isolate.run() — modern, cleaner API for one-shot isolate tasks
  5. 5. The closure runs in a completely separate memory space
  6. 6. Full isolate setup: create a ReceivePort to get messages
  7. 7. Spawn an isolate with an entry point function and our SendPort
  8. 8. Listen for messages from the worker isolate
  9. 9. The worker sends us its SendPort so communication is bidirectional
  10. 10. Worker entry point: receives the main thread's SendPort
  11. 11. Creates its own ReceivePort for incoming messages
  12. 12. Sends results back to the main isolate via message passing

Spot the bug

// In a StatefulWidget
void processData() async {
  final result = await Isolate.run(() {
    final data = widget.largeDataList; // Accessing widget state
    return data.where((x) => x > 100).toList();
  });
  setState(() => processed = result);
}
Need a hint?
Can an isolate access the parent's memory and objects?
Show answer
Isolates have separate memory — you cannot access 'widget.largeDataList' from inside an isolate closure. The data must be passed as a parameter. Fix: capture the data before the isolate call: 'final list = widget.largeDataList; final result = await Isolate.run(() => list.where((x) => x > 100).toList());' — Dart will serialize 'list' to the new isolate.

Explain like I'm 5

Imagine you're coloring a really big picture. If you try to color the whole thing yourself, you can't talk to your friends or play until it's done. But what if you tear off a section and give it to a friend? They color their part in their OWN room with their OWN crayons (isolate with separate memory). When they finish, they slide the colored section back under the door (message passing). Now you kept playing while they colored!

Fun fact

The name 'Isolate' comes from the concept of isolation in concurrent programming. Unlike threads which share memory (and need locks, mutexes, and semaphores to avoid race conditions), Dart isolates are completely isolated from each other — making concurrent Dart code inherently safer than most other languages.

Hands-on challenge

Create a function that takes a large list of integers (100,000+) and finds all prime numbers using Isolate.run(). Compare the execution time with and without isolate usage. Observe UI jank in a Flutter app when running the same function on the main isolate.

More resources

Open interactive version (quiz + challenge) ← Back to course: Flutter Interview Mastery