Sobes.tech
Middle

How to create two threads in Dart in the context of Flutter?

sobes.tech AI

Answer from AI

To create independent execution threads in Dart/Flutter, Isolates are used. They represent separate tasks with their own dedicated memory space, preventing issues with concurrent data access and blocking the main thread (UI thread).

There are several ways to create and interact with isolates:

  1. Isolate.spawn(): The lowest-level method for creating an isolate. It requires passing a top-level function or static class method to execute in the new isolate and a port for message exchange.

    import 'dart:isolate';
    
    // Function to run in the isolate
    void isolateEntry(SendPort sendPort) {
      // Perform some long operation
      int result = 0;
      for (int i = 0; i < 1000000000; i++) {
        result += i;
      }
      // Send the result back to the main isolate
      sendPort.send(result);
    }
    
    void main() async {
      ReceivePort receivePort = ReceivePort();
      // Create a new isolate
      Isolate isolate = await Isolate.spawn(isolateEntry, receivePort.sendPort);
    
      // Listen for messages from the isolate
      receivePort.listen((message) {
        print('Result from isolate: $message');
        // Kill the isolate after receiving the message
        isolate.kill();
      });
    
      print('Main thread continues to work...');
    }
    
  2. compute(): A simplified function from the flutter/foundation package that internally uses Isolate.spawn(). Ideal for simple background tasks that do not require complex interaction with the isolate.

    import 'package:flutter/foundation.dart';
    
    // Function to run with compute
    int heavyComputation(int limit) {
      int result = 0;
      for (int i = 0; i < limit; i++) {
        result += i;
      }
      return result;
    }
    
    void main() async {
      print('Starting heavy computations in background...');
      // Run the function in a separate isolate
      int result = await compute(heavyComputation, 1000000000);
      print('Computation result: $result');
      print('Main thread continues after computations.');
    }
    
  3. Isolate.spawnUri(): Used to run code from a separate Dart file in a new isolate.

Interaction between isolates:

Isolates communicate only through message passing via SendPort and ReceivePort instances. These are atomic operations that do not require locks or other synchronization mechanisms.

  • SendPort: Used to send messages from one isolate to another.
  • ReceivePort: Used to listen for incoming messages.

When passing objects between isolates, they must be "transferable," which includes:

  • Primitive data types (int, double, String, bool)
  • Lists and maps containing transferable types
  • Certain Flutter-specific types that can be serialized

Non-transferable objects will be copied, and subsequent changes in one isolate will not reflect in the other.

Key points:

  • Isolates cannot directly access each other's memory.
  • Isolates do not have access to shared state, including variables, classes, and widgets of the main isolate (UI thread).
  • Communication between isolates is asynchronous.
  • When working with isolates in Flutter, it is important to manage their lifecycle properly (e.g., using isolate.kill() when necessary).

Using isolates allows performing long-running or resource-intensive operations in the background, preventing UI freezing and ensuring smooth app operation.