Flutter 中使用 Isolate 进行耗时计算并显示 Loading 状态的完整学习笔记,
🧠 Flutter Isolate 异步计算与 Loading 状态笔记
一、问题背景
在 Flutter 中,UI 渲染和逻辑代码默认运行在同一个主 Isolate(线程)中 。
当我们执行 大量计算或复杂循环 时,主 Isolate 会被阻塞,导致:
- 页面卡死;
CircularProgressIndicator()
不旋转;- 用户交互卡顿。
例如:
csharp
Future<String> performLargeCalculation() async {
await Future.delayed(Duration(seconds: 2));
return "Result";
}
如果在 FutureBuilder
或按钮中直接执行大计算,会出现 UI 无法刷新 的问题。
原因是:Dart 虽然是异步语言,但其异步是 事件循环模型 ,并非多线程。
一旦有耗时同步任务,就会卡住整个 UI 渲染。
二、根本原因
- Flutter 的动画、绘制、事件分发都依赖 主 Isolate 的事件循环;
- 耗时任务阻塞 event loop;
- 结果:UI 停止响应。
三、解决方案 ------ 使用 Isolate
🧩 概念理解
-
Isolate 是 Dart 实现真正多线程的机制;
-
每个 Isolate 有自己的内存堆、事件循环;
-
不共享内存,依靠 SendPort / ReceivePort 通信;
-
非常适合执行:
- 大量计算;
- 数据转换;
- JSON 解析;
- 图像处理;
- 训练算法等任务。
四、基本使用步骤
1️⃣ 创建通信通道
ini
final responsePort = ReceivePort();
2️⃣ 启动新 Isolate
ini
Isolate.spawn(_isolateEntry, _IsolateData(responsePort.sendPort));
Isolate.spawn()
的第二个参数是新 Isolate 的入口参数,必须是"可发送对象"。
3️⃣ 在 _isolateEntry
中执行耗时任务
javascript
static void _isolateEntry(_IsolateData isolateData) {
// 执行大量计算
var list = [];
for (var i = 0; i < 100000000; i++) {
list.add('');
}
// 发送结果
isolateData.responsePort.send("done");
}
4️⃣ 主线程监听结果
dart
await for (var message in responsePort) {
print("结果: $message");
break;
}
五、完整示例:加载对话框 + Isolate 计算
scala
import 'dart:isolate';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Loading Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: LoadingDemo(),
);
}
}
class LoadingDemo extends StatefulWidget {
@override
_LoadingDemoState createState() => _LoadingDemoState();
}
class _LoadingDemoState extends State<LoadingDemo> {
Future<void> performLargeCalculation() async {
final responsePort = ReceivePort();
// 显示 Loading 弹窗
showDialog(
context: context,
barrierDismissible: false,
builder: (_) => const Dialog(
child: Padding(
padding: EdgeInsets.all(20),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
CircularProgressIndicator(),
SizedBox(width: 20),
Text("计算中,请稍候...")
],
),
),
),
);
// 启动新 Isolate
Isolate.spawn(_isolateEntry, _IsolateData(responsePort.sendPort));
// 等待结果
await for (var _ in responsePort) {
Navigator.pop(context); // 关闭弹窗
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('计算完成!')),
);
break;
}
}
static void _isolateEntry(_IsolateData isolateData) {
// 模拟大量计算
var list = [];
for (var i = 0; i < 100000000; i++) {
list.add('');
}
isolateData.responsePort.send(null);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Loading Demo")),
body: Center(
child: ElevatedButton(
onPressed: performLargeCalculation,
child: const Text('开始计算'),
),
),
);
}
}
class _IsolateData {
final SendPort responsePort;
_IsolateData(this.responsePort);
}
六、向 Isolate 传递参数
如果想传递多个参数,可以创建一个封装类:
ini
class IsolateData {
final SendPort responsePort;
final TrainingPlanInfo trainingPlanInfos;
IsolateData(this.responsePort, this.trainingPlanInfos);
}
void isolateEntry(IsolateData data) {
var responsePort = data.responsePort;
var info = data.trainingPlanInfos;
// 在这里使用 info
responsePort.send("done");
}
void startIsolate() {
final port = ReceivePort();
final info = TrainingPlanInfo(...);
Isolate.spawn(isolateEntry, IsolateData(port.sendPort, info));
}
七、从 Isolate 返回结果
Isolate 不共享内存,因此不能直接返回值。
正确的做法是 通过 SendPort
发送结果:
scss
void _isolateEntry(IsolateData data) {
var result = computeHeavyTask();
data.sendPort.send(result); // 发送计算结果
}
void main() async {
final receivePort = ReceivePort();
await Isolate.spawn(_isolateEntry, IsolateData(receivePort.sendPort));
receivePort.listen((message) {
print('收到结果: $message');
});
}
⚠️ 发送规则 :只能发送"可序列化"数据类型
(int、double、bool、String、List、Map、null)或这些类型的组合。
八、实战场景示例
你项目中用的写法更贴近实战👇
scss
static void _isolateEntry(_IsolateData isolateData) {
...
isolateData.responsePort.send(dayActions);
}
Future<void> _submit(BuildContext context) async {
final responsePort = ReceivePort();
showDialog(
context: context,
barrierDismissible: false,
builder: (_) => const Dialog(
child: Padding(
padding: EdgeInsets.all(20),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
CircularProgressIndicator(),
SizedBox(width: 20),
Text("正在生成处方预览...")
],
),
),
),
);
Isolate.spawn(
_isolateEntry,
_IsolateData(responsePort.sendPort, _trainingPlanInfos),
);
await for (var e in responsePort) {
Navigator.pop(context);
// 处理 dayActions
...
break;
}
}
九、总结对比
方案 | 优点 | 缺点 | 场景 |
---|---|---|---|
Future / async | 实现简单 | 会阻塞主 Isolate | 网络请求、短延时 |
Isolate | 真正并行,不阻塞 UI | 通信复杂、数据需可序列化 | 大量计算、循环、图像处理 |
🧭 小结
✅ 记忆重点:
- Isolate = Dart 的多线程;
- 不共享内存,只能消息传递;
- 使用
ReceivePort
/SendPort
通信; - 耗时任务一定要放在 Isolate 内;
FutureBuilder
只适合短异步,不适合重计算;- Loading 动画必须放在主 Isolate 控制中。