Como Usar Isolates no Flutter para Processamento em Segundo Plano

Introdução

No Flutter, a execução de tarefas pesadas pode causar travamentos na interface do usuário, pois o Flutter utiliza uma única thread para a UI e a lógica de negócios. Para evitar isso, podemos utilizar Isolates, que permitem rodar código em segundo plano sem bloquear a interface.

Neste artigo, exploraremos o que são Isolates, como usá-los e um exemplo prático de processamento intensivo fora da thread principal.

1. O Que São Isolates?

No Dart, os Isolates são uma forma de execução concorrente onde cada isolate tem sua própria memória e não compartilha estado com outros isolates. Diferente de threads convencionais, que podem acessar a mesma memória global, os isolates precisam trocar mensagens entre si.

Isso torna os isolates ideais para executar tarefas como:

  • Processamento pesado de dados (exemplo: manipulação de imagens).
  • Parsing de arquivos grandes (exemplo: JSON, CSV).
  • Cálculos matemáticos complexos.

2. Criando um Isolate Simples

Podemos criar um Isolate manualmente usando o método Isolate.spawn().

Exemplo básico


import 'dart:isolate';

void backgroundTask(SendPort sendPort) {
  int sum = 0;
  for (int i = 0; i < 1000000; i++) {
    sum += i;
  }
  sendPort.send(sum);
}

void main() async {
  ReceivePort receivePort = ReceivePort();
  await Isolate.spawn(backgroundTask, receivePort.sendPort);

  receivePort.listen((message) {
    print('Resultado do Isolate: \$message');
  });
}

Aqui, o isolate executa um loop pesado de soma sem travar a UI.

3. Usando Isolates no Flutter

No Flutter, podemos usar Isolates para manipular dados sem bloquear a interface. Vamos criar um exemplo que simula o carregamento de uma lista grande.

Criando um Isolate para Processar Dados


import 'dart:async';
import 'dart:isolate';
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: const IsolateExample(),
    );
  }
}

class IsolateExample extends StatefulWidget {
  const IsolateExample({Key? key}) : super(key: key);

  @override
  _IsolateExampleState createState() => _IsolateExampleState();
}

class _IsolateExampleState extends State {
  List _numbers = [];
  bool _loading = false;

  Future _generateNumbers() async {
    setState(() {
      _loading = true;
    });

    ReceivePort receivePort = ReceivePort();
    await Isolate.spawn(_heavyTask, receivePort.sendPort);
    _numbers = await receivePort.first;

    setState(() {
      _loading = false;
    });
  }

  static void _heavyTask(SendPort sendPort) {
    List numbers = List.generate(5000, (index) => index);
    sendPort.send(numbers);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Isolates no Flutter')),
      body: _loading
          ? const Center(child: CircularProgressIndicator())
          : ListView.builder(
              itemCount: _numbers.length,
              itemBuilder: (context, index) {
                return ListTile(title: Text('Número: \${_numbers[index]}'));
              },
            ),
      floatingActionButton: FloatingActionButton(
        onPressed: _generateNumbers,
        child: const Icon(Icons.refresh),
      ),
    );
  }
}

4. Melhorando o Código com compute()

Para casos mais simples, podemos usar a função compute() da biblioteca flutter/foundation. Ela cria um isolate automaticamente e facilita o uso.

Exemplo com compute()


import 'package:flutter/foundation.dart';

Future<List> generateNumbersInIsolate() async {
  return compute(_generateNumbers, 5000);
}

Essa função facilita a execução de tarefas pesadas sem precisar lidar diretamente com `Isolate.spawn()`.

5. Boas Práticas

  • Use `compute()` para operações simples e `Isolate.spawn()` para maior controle.
  • Evite passar grandes quantidades de dados entre isolates, pois a comunicação entre eles pode ser lenta.
  • Utilize `ReceivePort` e `SendPort` para trocar mensagens entre a UI e os isolates.

Publicar comentário