如何用ProgressIndicator展示Isolate中异步任务的进度?
在Flutter Isolate中实现进度更新的正确方式
Isolate之间内存完全隔离,不能直接传递回调函数,必须通过消息传递机制(SendPort/ReceivePort)实现主Isolate(UI线程)和子Isolate的通信。下面是修改后的完整代码,实现进度实时更新:
import 'package:flutter/material.dart'; import 'dart:isolate'; class IsolateProgress extends StatefulWidget { const IsolateProgress({super.key}); @override State<IsolateProgress> createState() => _IsolateProgressState(); } class _IsolateProgressState extends State<IsolateProgress> { double _progress = 0.0; Map<int, bool>? _results; Isolate? _isolate; final ReceivePort _receivePort = ReceivePort(); @override void initState() { super.initState(); _startHeavyTask(); } void _startHeavyTask() async { final List<int> listToCheck = [1, 11, 111, 1111, 11111, 96953, 111111, 99971, 11111111, 999999487]; // 监听子Isolate发来的消息 _receivePort.listen((message) { if (message is double) { // 更新进度 setState(() => _progress = message); } else if (message is Map<int, bool>) { // 任务完成,接收结果 setState(() => _results = message); // 关闭资源 _receivePort.close(); _isolate?.kill(priority: Isolate.immediate); } }); // 手动创建Isolate,传入SendPort和任务参数 _isolate = await Isolate.spawn( _heavyTask, _TaskParams( sendPort: _receivePort.sendPort, listToCheck: listToCheck, ), ); } @override void dispose() { _receivePort.close(); _isolate?.kill(priority: Isolate.immediate); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Isolate Progress')), body: _buildContent(), ); } Widget _buildContent() { if (_results != null) { return ListView( children: _results!.entries .map((e) => Padding( padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16), child: Text('${e.key}: ${e.value ? '是质数' : '不是质数'}'), )) .toList(), ); } return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircularProgressIndicator(value: _progress), const SizedBox(height: 16), Text('进度: ${(_progress * 100).toStringAsFixed(1)}%'), ], ), ); } } // 用于传递给Isolate的参数类(必须是可序列化的) class _TaskParams { final SendPort sendPort; final List<int> listToCheck; _TaskParams({required this.sendPort, required this.listToCheck}); } // 子Isolate执行的任务(必须是顶层函数或static函数) void _heavyTask(_TaskParams params) { final SendPort sendPort = params.sendPort; final List<int> listToCheck = params.listToCheck; final Map<int, bool> result = {}; final int total = listToCheck.length; for (int index = 0; index < total; index++) { final int num = listToCheck[index]; // 模拟异步延迟(实际业务中替换为真实重型任务) Future.delayed(const Duration(milliseconds: 100)).then((_) { if (num == 1) { result[num] = false; } else { result[num] = true; for (int i = 2; i * i <= num; i++) { // 优化质数判断逻辑,减少循环次数 if (num % i == 0) { result[num] = false; break; } } } // 计算进度并发送给主Isolate final double progress = (index + 1) / total; sendPort.send(progress); // 所有任务完成后发送结果 if (index == total - 1) { sendPort.send(result); } }); } }
关键说明:
- 必须使用StatefulWidget:需要维护进度和结果的状态,触发UI更新。
- 消息传递机制:通过
ReceivePort监听子Isolate消息,SendPort实现子Isolate向主Isolate发送进度、结果数据。 - 参数序列化:传递给Isolate的参数必须可序列化,这里用
_TaskParams封装避免传递非序列化对象。 - 资源清理:在页面销毁或任务完成时关闭端口、销毁Isolate,防止内存泄漏。
- 任务优化:将质数判断的循环条件改为
i * i <= num,大幅减少计算量。
内容的提问来源于stack exchange,提问作者Florian
相关产品推荐
相关产品推荐

