お願い
本記事の内容に誤りや改善点がございましたら、コメント等でご指摘いただけますと幸いです。
なお、本記事は個人の学習記録として作成したものです。業務でご利用の際は、公式ドキュメントもあわせてご確認ください。
1. はじめに
会社でFlutterを使うことになりましたが、まったくの初心者だったため、学習の一環として気になったFlutterの特徴を整理しました。
補足
Dartの特徴で気になったところは下記の記事でまとめました。
Flutter 学習メモ(Dartの基本仕様)(Java経験者向け)
Flutterのディレクトリ構成、Widgetツリーは下記の記事でまとめました。
Flutter 学習メモ(Flutterの基本・ディレクトリ構成, Widgetツリー)
2. 非同期処理
2-1. 同期処理と非同期処理
プログラムの実行は大きく二つに分けられます。
- 同期処理:前の処理が終わらないと次の処理に進めない(順次実行)
- 非同期処理:処理を「開始」しておいて、終わるのを待たずに次の処理を進める
Flutter(dart)の非同期処理は3つに分けられます。
- Future:一度だけ結果を返す
- Stream:複数回のイベントや値を非同期に受け取る
- Isolate:メインスレッドとは別スレッドで処理を行う
2-2. 処理の使い分け
| 処理 | 使用手段 | スレッド | 影響・動作 |
|---|---|---|---|
| 即座に完了する軽い処理 | 同期処理 | メインスレッド | 非同期の管理がいらず、処理がシンプル |
| I/O待機を伴う処理 | Future / Stream | メインスレッド | 軽い処理であればUIフリーズなし |
| 負荷が高い処理でUI影響を避けたい | Isolate | 別スレッド | UIフリーズなし |
1. UIの動きと合わせて順番を守りつつ、シンプルに実装したい処理
- 例:ユーザー入力後に即座に処理して結果を表示する場合
- 手段:同期処理(通常の関数呼び出し)
- 理由:即座に完了する軽い処理の場合、async/awaitのオーバーヘッドが不要でコードもシンプルになる
// lib\features\sync_processing\sync_screen.dart
/// ボタンが押されたときの処理
void _executeCalculation() {
// 同期処理:この計算が終わるまで次に進まない
int result = _service.calculate(10, 5);
// 結果を画面に反映
setState(() {
_result = '10 × 5 = $result';
});
}
// lib\features\sync_processing\sync_service.dart
/// 2つの数値を掛け算する(同期処理)
///
/// [a] 1つ目の数値
/// [b] 2つ目の数値
/// 返り値:計算結果
int calculate(int a, int b) {
// 即座に計算して結果を返す
return a * b;
}
2. 画面に影響を出したくない処理(軽い非同期処理)
- 例:ネットワーク通信、ファイル読み込み、タイマー処理など
- 手段:Future や Stream
- 理由:I/O待ちやイベント待ちを非同期で行えるので、待機時間が主体の処理の場合メインスレッドをブロックせずにUIは動き続ける
// lib\features\future_processing\future_screen.dart
/// データ取得ボタンが押されたときの処理
Future<void> _fetchData() async {
// ローディング開始
setState(() {
_status = 'サーバーと通信中...';
_isLoading = true;
});
// 非同期処理:サーバーからデータを取得
// await を使うことで、処理が完了するまで待つ
String data = await _service.fetchDataFromServer();
// 取得完了後に画面を更新
setState(() {
_status = data;
_isLoading = false;
});
}
// lib\features\future_processing\future_service.dart
/// サーバーからデータを取得する(非同期処理)
///
/// 実際のアプリでは、ここでAPIを呼び出します
/// 例:http.get('https://api.example.com/data')
Future<String> fetchDataFromServer() async {
// 2秒待機(ネットワーク通信を模擬)
await Future.delayed(Duration(seconds: 2));
// データを返す
return 'データ取得完了!(ID: 12345)';
}
3. 画面に影響を出したくない 且つ 負荷が高い処理の場合
- 例:大きな計算、データ加工、画像処理など
- 手段:Isolate
- 理由:メインスレッドとは別スレッドで処理されるため、UIのフリーズを防げる
// lib\features\isolate_processing\isolate_screen.dart
/// 重い計算を実行するボタンが押されたときの処理(Compute使用)
Future<void> _runHeavyCalculationCompute() async {
setState(() {
_result = '計算中... (Compute使用)\n(UIは動き続けます)';
_isRunning = true;
});
// Computeで別スレッドで実行(Web・モバイル両対応)
int result = await _computeService.calculateSum();
setState(() {
_result = '計算完了! (Compute使用)\n結果: $result';
_isRunning = false;
});
}
// lib\features\isolate_processing\isolate_service.dart
/// 大量の計算をcomputeを使って実行する
///
/// 10億回のループ計算を行います
/// computeを使うことでプラットフォームを問わずIsolateで実行
Future<int> calculateSum() async {
try {
// computeを使って別スレッドで実行
final result = await compute(_heavyTask, null);
return result;
} catch (e) {
print('Compute error: $e');
return -1;
}
}
/// 別スレッドで実行される重い計算処理
///
/// computeで実行されるため、トップレベル関数である必要がある
static int _heavyTask(void _) {
int sum = 0;
// 10億回のループ(重い処理)
for (int i = 0; i < 1000000000; i++) {
sum += i;
}
return sum;
}
3. サンプルコード
私はこれまでの業務で非同期処理に触れる機会がほとんどなかったため、実際にデモアプリを動かしながら理解を深めるためにサンプルコードを作成しました。
実際に動作を確認することで、非同期処理の流れや挙動を直感的に理解できるようになっていると思います。
サンプルコード:flutter_sample_async