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