Post

Isolates and Concurrency in Flutter

A quick guide to using isolates in Flutter to keep your UI responsive during heavy computations.

Isolates and Concurrency in Flutter

When your Flutter app processes large amounts of data or performs heavy computations, the UI can freeze. This happens because Dart runs on a single thread by default, so all your code executes on the main isolate that also handles UI rendering.

What are Isolates?

Isolates let you run code in parallel. Each isolate has its own memory space and runs independently, allowing you to offload heavy work from the main UI thread.

Using compute()

The easiest way to use isolates is with Flutter’s compute() function. It spawns an isolate, runs your function, and returns the result.

Here’s a simple example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

// This function runs in a separate isolate
int _heavyComputation(int number) {
  int result = 0;
  for (int i = 0; i < number; i++) {
    result += i * i; // Heavy computation
  }
  return result;
}

class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  int? _result;
  bool _isProcessing = false;

  Future<void> _processData() async {
    setState(() => _isProcessing = true);
    
    // Heavy computation runs in isolate - UI stays responsive!
    final result = await compute(_heavyComputation, 10000000);
    
    setState(() {
      _result = result;
      _isProcessing = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Isolates Example')),
      body: Column(
        children: [
          if (_isProcessing) LinearProgressIndicator(),
          ElevatedButton(
            onPressed: _isProcessing ? null : _processData,
            child: Text('Process Data'),
          ),
          if (_result != null)
            Text('Result: $_result'),
        ],
      ),
    );
  }
}

The compute() function runs _heavyComputation in a separate isolate. While that’s running, your UI thread stays free to handle user interactions, scrolling, and animations.

Important Notes

  • The function you pass to compute() must be top-level or static. Instance methods won’t work.
  • Data gets copied between isolates since they don’t share memory. Keep this in mind for large datasets.
  • compute() is great for one-off tasks. For long-running work with progress updates, use Isolate.spawn() instead.

When to Use Isolates

Use isolates for:

  • Parsing large JSON files
  • Image processing
  • Complex calculations
  • Any operation taking more than 16ms (one frame at 60fps)

Don’t use isolates for:

  • Simple operations (the overhead isn’t worth it)
  • UI updates (isolates can’t access UI directly)
  • Frequent small tasks (too much overhead)

That’s the basics of using isolates in Flutter. They’re straightforward and make a huge difference in keeping your app responsive during heavy computations.

This post is licensed under CC BY 4.0 by the author.