🔍

【初心者向け】Dartにおけるconst contextの伝搬について【Flutterでよく使う】

2025/03/09に公開

こんにちは、ワニかず@40歳 出戻りエンジニアです。
Flutterを勉強していると、

const myApp = MyApp(key: ValueKey('static-key'));
という、インスタンスを生成する処理をよく見かけると思いますが、
これは、

const myApp = MyApp(key: const ValueKey('static-key'));
として解釈される(ことが多い)(条件あり)、というのです。

この意味が分からず、調べていると、
Dartの言語仕様として「const contextの伝搬」というものがある、
と知りました。

ということで今回は、
「Dartにおけるconst contextの伝搬」についてまとめました。

Dartにおけるconst contextの伝搬について

Dartプログラミング言語には、コンパイル時に値が決定される定数を表すconstキーワードがあります。この記事では、Dartの興味深い仕様である「const contextの伝搬」について解説します。

const contextとは何か?

Dartでは、外側の式にconstキーワードを付けると、その中のネストされた式も暗黙的にconstとして解釈されるという仕様があります。これが「const contextの伝搬」(propagation of const context)です。

簡単に言えば、外側の式がconstであれば、内側の式も明示的にconstを書かなくても、自動的にconstと解釈されるということです。

具体例で理解する

基本的な例

// 以下の2つは同等です
const list1 = [1, 2, 3];
const list2 = const [1, 2, 3];

上記の例では、list1の定義で内側のリストにconstを明示していませんが、外側のconstが伝搬するため、内側のリストも定数として扱われます。

ネストした例

// 以下の2つは同等です
const nestedList1 = [[1, 2], [3, 4]];
const nestedList2 = const [const [1, 2], const [3, 4]];

ネストしたリストでも同様に、最も外側のconstが内側のすべての式に伝搬します。

オブジェクトの例

class Point {
  final int x;
  final int y;
  
  const Point(this.x, this.y);
}

// 以下の2つは同等です
const points1 = [Point(1, 2), Point(3, 4)];
const points2 = const [const Point(1, 2), const Point(3, 4)];

オブジェクトの配列でも、外側のconstが内側のコンストラクタ呼び出しに伝搬します。

const contextの伝搬のメリット

  1. コードの簡潔さ: 多層のネストがある場合に、すべての層にconstを書く必要がないため、コードがすっきりします。

  2. 可読性の向上: 不必要な繰り返しを避けることで、コードの本質的な部分に集中できます。

  3. エラーの防止: 内部の式にconstを書き忘れるミスを防止できます。

注意点

  1. 部分的なconst: 外側がconstでも、内側の一部だけを変数にすることはできません。
// これは動作しません
const list = [1, someVariable, 3]; // エラー: constの中で非constの値は使えません

// これなら可能
final list = [1, someVariable, 3];
  1. コンパイル時定数のみ: const contextに含まれる全ての値はコンパイル時に決定可能でなければなりません。
// これは動作しません
const result = [DateTime.now()]; // エラー: DateTime.now()はコンパイル時に決定できません

Dartのバージョンによる違い

Dartの古いバージョンでは、const contextの伝搬の動作が異なる場合があります。特にDart 2以降で一貫した動作になりました。新しいバージョンのDartでは、この仕様がより堅牢になっています。

まとめ

const contextの伝搬は、Dartの便利な言語仕様の一つです。特に複雑なデータ構造を定数として定義する際に、コードを簡潔に保つのに役立ちます。この仕様を理解することで、より読みやすく保守しやすいDartコードを書くことができるでしょう。

効果的な使用例としては、設定値や定数データ、テストデータなどが挙げられます。これらのケースでは、const contextの伝搬を活用することで、コードの冗長性を減らしつつ、パフォーマンスの恩恵も受けることができます。

Discussion