はじめに
Dart で関数を理解するうえで欠かせないのが
「スコープ(変数の有効範囲)」 と 「クロージャ(関数の中の関数)」 です。
Flutter 開発でも、setState() や onPressed: () => {...} のような匿名関数を多用しますが、
その背後には スコープとクロージャの仕組み が深く関係しています。
1. スコープとは?
スコープ(scope) とは、「変数や関数が有効な範囲」のことです。
Dart では主に以下の2種類があります。
| 種類 | 説明 |
|---|---|
| グローバルスコープ | ファイル全体からアクセスできる範囲 |
| ローカルスコープ | 関数・ブロック内だけで有効な範囲 |
例:スコープの基本
int globalValue = 10; // グローバルスコープ
void main() {
int localValue = 5; // ローカルスコープ
print(globalValue); // ✅ OK
print(localValue); // ✅ OK
}
void show() {
print(globalValue); // ✅ OK(グローバルにはアクセス可能)
// print(localValue); // ❌ エラー:定義されていない
}
ポイント
-
globalValueはどこからでもアクセス可能 -
localValueはmain()の中だけで有効です
2. ブロックスコープ(if / for など)
Dart では、if や for の中に定義した変数も
そのブロック内でのみ有効 です。
void main() {
if (true) {
var message = 'Hello';
print(message); // ✅ OK
}
// print(message); // ❌ Error: message is not defined
}
for文も同様:
for (int i = 0; i < 3; i++) {
print(i);
}
// print(i); // ❌ Error
3. クロージャ(Closure)とは?
クロージャ は、
「関数の外の変数を記憶して使える関数」 のことです。
基本例
Function makeCounter() {
int count = 0;
// 無名関数を返す
return () {
count++;
print(count);
};
}
void main() {
var counter = makeCounter();
counter(); // => 1
counter(); // => 2
counter(); // => 3
}
ポイント
-
countはmakeCounter()内で定義されていますが、 - 返された無名関数は その変数を覚えている(これがクロージャ)。
-
makeCounter()が終了しても、countは消えません。
図で理解する
+----------------------+
| makeCounter() |
| int count = 0; |
| return () { ... }; |
+----------┬-----------+
↓
[クロージャが count を記憶]
4. 複数のインスタンスを作るとどうなる?
void main() {
var counter1 = makeCounter();
var counter2 = makeCounter();
counter1(); // 1
counter1(); // 2
counter2(); // 1 ← 別のスコープを保持
}
各 makeCounter() 呼び出しは、
独立したスコープ(count) を持ちます。
5. クロージャの活用例
コールバックで使う
void main() {
var prefix = '[LOG]';
var log = (String msg) {
print('$prefix $msg');
};
log('Start'); // => [LOG] Start
log('End'); // => [LOG] End
}
ここで log() は prefix を外側スコープから参照しています。
これもクロージャの一種です。
UIイベントで使う(Flutterでも同様)
void main() {
var name = 'Anna';
var greet = () => print('こんにちは、$name さん!');
greet(); // => こんにちは、Anna さん!
}
このように 外部変数を参照した匿名関数 はすべてクロージャです。
Flutter の onPressed: () => doSomething(context) も同じ仕組みです。
6. スコープとクロージャの関係まとめ
| 概念 | 例 | 説明 |
|---|---|---|
| スコープ | int x = 5; |
変数の有効範囲 |
| ブロックスコープ | { var y = 1; } |
if や for 内だけ有効 |
| クロージャ | return () { x++; }; |
外部変数を保持する関数 |
| 独立スコープ | makeCounter() |
呼び出しごとに別々の環境を保持 |
まとめ
- スコープ = 変数の「見える範囲」
- クロージャ = 「外の変数を記憶する関数」
- Dart の関数は「第一級オブジェクト」なので、自由にネスト・保持・返却が可能
「関数が変数を覚えている」──
これがクロージャの魔法です。
Dart / Flutter 開発で匿名関数や setState、onPressed を理解するうえで、
この仕組みを理解しておくと、バグの原因やライフサイクルの挙動を正しく捉えられるようになります。