Answer
Overview
Concurrency is running multiple tasks at the same time (or appearing to). Isolates are Dart's mechanism for achieving true parallelism by running code on separate threads.
Concurrency vs Parallelism
Concurrency
Multiple tasks making progress (not necessarily simultaneously).
Example: Single-threaded event loop (async/await).
dartFuture<void> fetchData() async { print('Fetching...'); await Future.delayed(Duration(seconds: 2)); print('Done'); } // Tasks are concurrent but run on single thread (event loop)
Parallelism
Multiple tasks running simultaneously on different CPU cores.
Example: Isolates (separate threads).
dart// Task 1 runs on Core 1 // Task 2 runs on Core 2 (simultaneously)
Dart's Concurrency Model
Single-Threaded Event Loop
Dart is single-threaded by default (like JavaScript).
dartvoid main() { print('Start'); Future.delayed(Duration(seconds: 1), () { print('Task 1'); }); Future.delayed(Duration(seconds: 1), () { print('Task 2'); }); print('End'); } // Output: // Start // End // Task 1 // Task 2
Limitation: Heavy computation blocks the UI thread.
dart// ❌ Blocks UI for 5 seconds void main() { heavyComputation(); // Freezes UI print('Done'); } void heavyComputation() { int sum = 0; for (int i = 0; i < 10000000000; i++) { sum += i; } }
Isolates: True Parallelism
Isolates are separate threads with their own memory.
Key Concepts
- Each isolate has its own memory (no shared state)
- Isolates communicate via message passing (ports)
- Isolates run on separate CPU cores
Creating Isolates
Method 1: compute() Function (Simplest)
dartimport 'package:flutter/foundation.dart'; // Heavy computation function int heavyComputation(int n) { int sum = 0; for (int i = 0; i < n; i++) { sum += i; } return sum; } void main() async { print('Start'); // Run in isolate (separate thread) final result = await compute(heavyComputation, 1000000000); print('Result: $result'); print('Done'); }
Benefits:
- ✅ UI remains responsive
- ✅ Simple API
- ✅ Automatic message passing
Method 2: Isolate.spawn() (Manual)
dartimport 'dart:isolate'; // Entry point for isolate void heavyComputationIsolate(SendPort sendPort) { int sum = 0; for (int i = 0; i < 1000000000; i++) { sum += i; } // Send result back to main isolate sendPort.send(sum); } void main() async { print('Start'); // Create receive port final receivePort = ReceivePort(); // Spawn isolate await Isolate.spawn(heavyComputationIsolate, receivePort.sendPort); // Wait for result final result = await receivePort.first; print('Result: $result'); print('Done'); }
Method 3: Bidirectional Communication
dartimport 'dart:isolate'; // Isolate entry point void workerIsolate(SendPort mainSendPort) async { // Create receive port for isolate final workerReceivePort = ReceivePort(); // Send isolate's send port to main mainSendPort.send(workerReceivePort.sendPort); // Listen for messages from main await for (var message in workerReceivePort) { if (message == 'stop') { break; } // Process message final result = message * 2; // Send result back mainSendPort.send(result); } } void main() async { // Create receive port for main final mainReceivePort = ReceivePort(); // Spawn isolate await Isolate.spawn(workerIsolate, mainReceivePort.sendPort); // Get isolate's send port final workerSendPort = await mainReceivePort.first as SendPort; // Send messages to isolate workerSendPort.send(5); workerSendPort.send(10); workerSendPort.send(15); // Receive results mainReceivePort.listen((result) { print('Result: $result'); }); // Stop isolate await Future.delayed(Duration(seconds: 2)); workerSendPort.send('stop'); }
Flutter Example: Image Processing
dartimport 'package:flutter/material.dart'; import 'package:flutter/foundation.dart'; import 'dart:ui' as ui; // Heavy image processing function ui.Image processImage(ui.Image image) { // Apply filters, transformations, etc. // (Simplified example) return image; } class ImageProcessorScreen extends StatefulWidget { _ImageProcessorScreenState createState() => _ImageProcessorScreenState(); } class _ImageProcessorScreenState extends State<ImageProcessorScreen> { ui.Image? _processedImage; bool _isProcessing = false; Future<void> _processImage(ui.Image image) async { setState(() { _isProcessing = true; }); // Run in isolate (doesn't block UI) final result = await compute(processImage, image); setState(() { _processedImage = result; _isProcessing = false; }); } Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Image Processor')), body: Center( child: _isProcessing ? CircularProgressIndicator() : _processedImage != null ? RawImage(image: _processedImage) : Text('No image'), ), floatingActionButton: FloatingActionButton( onPressed: () { // Load and process image loadImage().then(_processImage); }, child: Icon(Icons.image), ), ); } Future<ui.Image> loadImage() async { // Load image from assets/network // (Simplified) throw UnimplementedError(); } }
Common Use Cases
1. JSON Parsing
dartimport 'package:flutter/foundation.dart'; import 'dart:convert'; List<User> parseUsers(String jsonString) { final List<dynamic> jsonList = jsonDecode(jsonString); return jsonList.map((json) => User.fromJson(json)).toList(); } // Usage final users = await compute(parseUsers, jsonString);
2. Database Operations
dartFuture<List<User>> queryDatabase() async { return await compute(_queryDatabaseIsolate, null); } List<User> _queryDatabaseIsolate(_) { // Perform heavy database query return database.query('SELECT * FROM users'); }
3. Encryption/Decryption
dartString encryptData(String data) { // Heavy encryption return encrypted; } final encrypted = await compute(encryptData, sensitiveData);
Isolate Limitations
No Shared Memory
dart// ❌ Cannot access shared variables int counter = 0; void isolateFunction(SendPort sendPort) { counter++; // ❌ Error: counter is not accessible }
Only Primitive Types
Isolates can only pass simple types (int, String, List, Map).
dart// ✅ Works compute(myFunction, 42); compute(myFunction, 'hello'); compute(myFunction, [1, 2, 3]); // ❌ Doesn't work (complex objects) compute(myFunction, MyCustomClass());
Solution: Serialize to JSON.
dart// Serialize final jsonString = jsonEncode(user.toJson()); final result = await compute(processUser, jsonString); // Deserialize in isolate User processUser(String jsonString) { final user = User.fromJson(jsonDecode(jsonString)); // Process user return user; }
Performance Comparison
Test: Calculate sum of 1 billion numbers
| Method | Time | UI Blocked? |
|---|---|---|
| Main thread | 5000ms | ✅ Yes (freezes UI) |
| async/await | 5000ms | ✅ Yes (still on main thread) |
| Isolate (compute) | 5000ms | ❌ No (UI responsive) |
Best Practices
dart// ✅ Use compute() for simple tasks final result = await compute(heavyFunction, data); // ✅ Use Isolate.spawn() for long-running tasks await Isolate.spawn(workerIsolate, sendPort); // ✅ Use isolates for heavy computation // - JSON parsing (large files) // - Image processing // - Encryption/decryption // - Database queries // ❌ Don't use isolates for simple tasks (overhead) // await compute(() => 1 + 1); ❌ Overkill // ❌ Don't access UI from isolates // Isolates cannot call setState() or access widgets
Isolate Pool (Advanced)
For multiple concurrent tasks.
dartimport 'dart:isolate'; class IsolatePool { final int size; final List<SendPort> _workers = []; IsolatePool({this.size = 4}); Future<void> init() async { for (int i = 0; i < size; i++) { final receivePort = ReceivePort(); await Isolate.spawn(_workerIsolate, receivePort.sendPort); final sendPort = await receivePort.first as SendPort; _workers.add(sendPort); } } static void _workerIsolate(SendPort mainSendPort) { final receivePort = ReceivePort(); mainSendPort.send(receivePort.sendPort); receivePort.listen((message) { // Process message final result = message * 2; mainSendPort.send(result); }); } void execute(int data) { final worker = _workers[data % size]; worker.send(data); } }
Summary
| Concept | Description |
|---|---|
| Concurrency | Multiple tasks making progress (single thread) |
| Parallelism | Multiple tasks running simultaneously (multiple threads) |
| Isolate | Separate thread with own memory |
| compute() | Simple API for running functions in isolates |
| Message Passing | Isolates communicate via SendPort/ReceivePort |
When to use isolates:
- ✅ Heavy computation (> 100ms)
- ✅ JSON parsing (large files)
- ✅ Image processing
- ✅ Encryption/database operations
- ❌ Simple async operations (use async/await)
Learn more: