Flutter Daily - Isolates for Heavy Computation

Isolates for Heavy Computation

What You’ll Learn

Dart runs on a single thread by default, which means heavy computations can freeze your UI. Isolates let you run code in separate threads, keeping your app smooth and responsive even during intensive tasks like parsing large JSON files or processing images.

The Problem: UI Blocking

When you run heavy computations on the main thread, Flutter can’t render frames, causing your app to freeze:

// BAD: This will freeze the UI
void processLargeData() {
  // Simulating heavy computation
  for (int i = 0; i < 1000000000; i++) {
    // Complex calculation
  }
}

The Solution: Isolates

Isolates are Dart’s way of achieving true parallelism. Each isolate has its own memory and event loop:

import 'dart:isolate';

// Function that runs in separate isolate
Future<int> heavyComputation(int value) async {
  return await Isolate.run(() {
    // This runs on a separate thread
    int result = 0;
    for (int i = 0; i < value; i++) {
      result += i;
    }
    return result;
  });
}

// Usage in your widget
class ComputationDemo extends StatefulWidget {
  @override
  State<ComputationDemo> createState() => _ComputationDemoState();
}

class _ComputationDemoState extends State<ComputationDemo> {
  int? result;
  bool isLoading = false;

  Future<void> runComputation() async {
    setState(() => isLoading = true);
    
    // UI stays responsive during this
    final value = await heavyComputation(100000000);
    
    setState(() {
      result = value;
      isLoading = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        ElevatedButton(
          onPressed: runComputation,
          child: Text('Run Heavy Computation'),
        ),
        if (isLoading) CircularProgressIndicator(),
        if (result != null) Text('Result: $result'),
      ],
    );
  }
}

Real-World Example: JSON Parsing

Here’s a practical example parsing large JSON data:

import 'dart:convert';
import 'dart:isolate';

// Parse JSON in isolate
Future<List<User>> parseUsersInIsolate(String jsonString) async {
  return await Isolate.run(() {
    final List<dynamic> jsonList = jsonDecode(jsonString);
    return jsonList.map((json) => User.fromJson(json)).toList();
  });
}

// Your model
class User {
  final String name;
  final String email;

  User({required this.name, required this.email});

  factory User.fromJson(Map<String, dynamic> json) {
    return User(name: json['name'], email: json['email']);
  }
}

Try It Yourself

  1. Create a Flutter app with a button that runs a heavy computation using Isolate.run()
  2. Add an animation (like a rotating circle) to verify the UI stays smooth during computation
  3. Compare it with the same computation running on the main thread - notice the difference!

Bonus challenge: Use compute() helper function (from Flutter foundation library) which is a simpler wrapper around isolates:

import 'package:flutter/foundation.dart';

final result = await compute(heavyFunction, inputData);

Tip of the Day

When to use isolates:

When NOT to use isolates:

Debugging tip: Isolates can’t be debugged with breakpoints in the same way. Use print() statements or logging to debug code running in isolates.