一、主線程:默認的執行環境
所有代碼默認運行在主線程。下面的例子展示了一個會阻塞主線程的錯誤示范:
import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});Widget build(BuildContext context) {return MaterialApp(home: HomePage(),);}
}class HomePage extends StatefulWidget {State<HomePage> createState() => _HomePageState();
}class _HomePageState extends State<HomePage> {String _result = '點擊按鈕開始計算';bool _isLoading = false;// 一個模擬的、非常耗時的同步計算函數// 這會嚴重阻塞主線程!int _expensiveTask(int number) {int sum = 0;for (int i = 0; i < number; i++) {sum += i;}return sum;}void _doTaskOnMainThread() {setState(() {_isLoading = true;});// 這個計算直接在UI線程上運行,會導致界面卡死int result = _expensiveTask(10000000000); // 一個很大的數setState(() {_result = '計算結果: $result';_isLoading = false;});}Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('線程模型實戰')),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [if (_isLoading) const CircularProgressIndicator(),Text(_result, style: Theme.of(context).textTheme.headlineSmall),ElevatedButton(onPressed: _doTaskOnMainThread,// 點擊這個按鈕后,UI會完全卡住,連Loading圈都無法轉動child: const Text('在主線程執行耗時任務 (錯誤示范)'),),],),),);}
}
現象:點擊按鈕后,即使看到了 Loading 圓圈,它也會完全凍結,無法動畫,直到計算完成。這就是阻塞主線程的后果。
二、異步操作:處理 I/O 任務(網絡請求為例)
使用 async/await 和 Future 處理不會阻塞線程的“等待型”任務。
// ... 承接上面的 State class ...// 在 _HomePageState 類中添加新方法
Future<void> _fetchDataAsync() async {setState(() {_isLoading = true;_result = '正在加載...';});// 使用 async/await 進行異步網絡請求// http.get 是非阻塞的,它向系統發出請求后就直接返回了try {// 記得添加 http 包: flutter pub add http// import 'package:http/http.dart' as http;final response = await http.get(Uri.parse('https://api.example.com/data'));// 這里的代碼會在網絡請求成功后,由事件循環調度執行// 它仍然運行在主線程,但等待期間主線程是空閑的,可以處理其他事件(如動畫)if (response.statusCode == 200) {setState(() {_result = '獲取成功: ${response.body.substring(0, 100)}...';});} else {setState(() {_result = '請求失敗: ${response.statusCode}';});}} catch (e) {setState(() {_result = '發生錯誤: $e';});} finally {setState(() {_isLoading = false;});}
}// 在 build 方法的 Column 中添加新的按鈕
ElevatedButton(onPressed: _fetchDataAsync,child: const Text('執行異步網絡請求 (正確示范)'),
),
現象:點擊按鈕后,Loading 圓圈流暢旋轉,UI 可以正常響應。數據獲取完成后,結果會更新到屏幕上。
三、Isolate:處理 CPU 密集型任務
使用 compute 函數將耗時計算任務移到后臺 Isolate。
// 1. 首先,在文件頂部導入 foundation.dart
import 'package:flutter/foundation.dart';// 2. 確保耗時函數是頂級函數或靜態方法
// 因為 Isolate 不能訪問原實例中的非靜態成員
int _expensiveTaskInIsolate(int number) {int sum = 0;for (int i = 0; i < number; i++) {sum += i;}return sum;
}// 3. 在 _HomePageState 類中添加新方法
void _doTaskInIsolate() async {setState(() {_isLoading = true;_result = '在Isolate中計算...';});// 使用 compute 將函數和參數拋到新的Isolate中執行// 這是一個非阻塞調用,會立即返回一個Futureint result = await compute(_expensiveTaskInIsolate, 10000000000);// compute 完成后,這里的代碼會在主線程執行,可以安全地更新UIsetState(() {_result = 'Isolate計算結果: $result';_isLoading = false;});
}// 4. 在 build 方法的 Column 中添加新的按鈕
ElevatedButton(onPressed: _doTaskInIsolate,child: const Text('使用Isolate執行計算 (正確示范)'),
),
現象:點擊按鈕后,Loading 圓圈流暢旋轉,UI 操作完全正常,毫無卡頓。計算完成后,結果會更新到屏幕上。
最終效果對比
你的 build 方法中的 Column 最終會有三個按鈕:
Column(mainAxisAlignment: MainAxisAlignment.center,children: [if (_isLoading) const CircularProgressIndicator(),Text(_result, style: Theme.of(context).textTheme.headlineSmall),ElevatedButton(onPressed: _doTaskOnMainThread,child: const Text('在主線程執行耗時任務 (錯誤示范)'),),SizedBox(height: 10),ElevatedButton(onPressed: _fetchDataAsync,child: const Text('執行異步網絡請求 (正確示范)'),),SizedBox(height: 10),ElevatedButton(onPressed: _doTaskInIsolate,child: const Text('使用Isolate執行計算 (正確示范)'),),],
)
總結一下:
· 錯誤示范按鈕:會讓你體驗到應用卡死的糟糕感覺。
· 異步網絡請求按鈕:展示了如何用 async/await 正確處理 I/O 操作。
· Isolate 計算按鈕:展示了如何用 compute 正確處理 CPU 密集型任務。
通過這個實戰對比,你能清晰地看到三種方式的巨大差異,并理解在正確場景使用正確工具的重要性。