Stateとは何か、状態管理と動作原理

著者: IT Sectr 公開日: 2026-07-01 読了時間: 9 分

StateはFlutterにおける中央データ管理オブジェクトであり、StatefulWidgetに関連付けられ、変更可能な情報の保存とインターフェースの構築を担当します。公式Flutterドキュメント(Flutter.dev, 2026)によると、Stateはウィジェットのライフサイクル全体を通じて存在し、その再構築後も存続して、UI更新間のデータ一貫性を保証します。ウィジェット自体とは異なり、Stateはそのフィールドを変更し、setState呼び出しを通じて再構築を開始できます。

重要ポイント

  • State — StatefulWidgetの変更可能なデータを保存し、setStateを介してその再構築を管理するオブジェクト
  • ライフサイクル — StateはinitState、didChangeDependencies、build、didUpdateWidget、disposeを経由し、各段階に明確な目的があります
  • mounted — Stateがまだウィジェットツリーにあり、安全にsetStateを呼び出せることを示すフラグ
  • widget — 関連付けられたStatefulWidgetへの参照で、Stateプロパティを介して親のパラメータを読み取るためにアクセス可能
  • 分離 — Stateは他のStateから分離されています。データ交換にはInheritedWidgetまたは外部の状態管理ツールが使用されます

FlutterにおけるStateとは?

StateはFlutterアーキテクチャのオブジェクトで、StatefulWidgetの変更可能なデータを保存し、このデータがインターフェースにどのように表示されるかを決定します。各StatefulWidgetは、ツリーに挿入されるときに、createStateメソッドを介して正確に1つのStateオブジェクトを作成します。Stateはウィジェットから独立して存在します。親が新しいパラメータでStatefulWidgetを再構築しても、Stateは同じままで、widgetプロパティを介して更新されたウィジェットを受け取ります。

Flutterアーキテクチャ概要(Google, 2026)によると、WidgetとStateの分離は、フレームワークがツリー要素を再利用できるようにする意図的なアーキテクチャ上の決定です。ウィジェット(軽量な記述)は複数回作成および破棄できますが、State(データを持つ重いオブジェクト)は要素がツリーにある限りメモリに残ります。これにより、親ウィジェットの頻繁な再構築中のデータ損失を防ぎます。

Stateはジェネリックを介してStatefulWidgetインターフェースを実装します:class _MyState extends State<MyWidget>。ジェネリックはStateを特定のStatefulWidgetタイプにバインドし、widgetプロパティを介してそのフィールドへの型安全なアクセスを提供します。

Stateはどこに保存されるか?

StateオブジェクトはStatefulElementに保存されます — WidgetとRenderObjectの中間層です。StatefulElementはcreateStateを介してStateを作成し、その参照を保持し、Stateを所有者として渡します。Elementはウィジェットがツリーから削除されたときにのみ破棄されます — それまではStateはメモリに残ります。

Stateのライフサイクル

Stateのライフサイクルは決定論的で、厳密な呼び出し順序で構成されています。この順序を理解することは、正しいリソース管理とメモリリーク防止の基盤です。

initState — 初期化

initStateはState作成時に最初に呼び出されます。このメソッドでは、コントローラー、ストリーム購読、タイマー、フィールドの初期値が初期化されます。最初の行でsuper.initState()を呼び出すことが必須です。initState段階では、ウィジェットツリーはまだ完全にマウントされていないため、MediaQuery.of(context)などのメソッドが正しく動作しない場合があります。

didChangeDependencies

didChangeDependenciesはinitStateの後、およびInheritedWidgetの依存関係が変更されるたびに呼び出されます。MediaQuery.of(context)Theme.of(context)はinitStateではなく、ここで呼び出すべきです。この時点までにツリーは既にマウントされているからです。このメソッドは、ウィジェットがInheritedWidgetが異なる値を提供する別のコンテキストに移動した場合にも呼び出されます。

build — UIの構築

buildはStateの主要メソッドで、ウィジェットツリーを返します。initStateの後、didChangeDependenciesの後、および各setStateの後に呼び出されます。buildメソッドには副作用があってはなりません — 現在のStateフィールド値に基づいてインターフェースを記述するのみです。

didUpdateWidget

didUpdateWidgetは、親が新しいパラメータでStatefulWidgetを再構築するときに呼び出されます。StateはoldWidgetを介して古いウィジェットにアクセスし、新しいものと比較できます。パラメータが変更された場合、状態を更新したり、新しいデータをロードしたり、アニメーションを再開したりできます。

dispose — リソース解放

disposeはすべてのリソース(コントローラー、購読、タイマー)が解放される最終メソッドです。dispose後、Stateは死亡としてマークされます:mountedはfalseを返し、setStateを呼び出すと例外がスローされます。メソッドの最後の行でsuper.dispose()を呼び出すことが必須です。

メソッド呼び出しタイミング必須のsuper
initStateState作成時はい、最初の行
didChangeDependenciesinitState後およびInheritedWidget変更時はい
buildinitState、didChangeDependencies、setState後いいえ
didUpdateWidget親から新しいウィジェットを受信時はい
setState開発者の呼び出し時いいえ
disposeツリーから削除時はい、最後の行

Stateの仕組み

Stateの動作メカニズムは、Elementとの関連付け、setStateによるリアクティビティ、widgetプロパティによる親へのアクセスの3つの主要原則に基づいています。Flutterが要素ツリーを構築し、StatefulElementに遭遇すると、関連付けられたウィジェットのcreateStateを呼び出します。作成されたStateは要素に保存され、要素が削除されるまで存在します。

setStateが呼び出されると、Stateは自身をダーティとしてマークし、次のフレームの再構築をスケジュールします。重要な点:setStateはbuildを即座に呼び出しません — 再構築の必要性を登録するだけです。Flutterは現在のフレームのすべてのダーティ要素を収集し、それらを一括で再構築してパフォーマンスを最適化します。build呼び出し後、Stateはクリーン状態に戻ります。

widgetプロパティにより、StateはStatefulWidgetコンストラクタに渡されたパラメータを読み取ることができます。StatefulWidgetは不変(StatelessWidgetと同様)であるため、そのフィールドは変更されません — パラメータが変更されると、親が新しいウィジェットを作成し、StateはdidUpdateWidgetを介してそれを受け取ります。これにより、Stateが常に最新の親データで動作することが保証されます。

Dartコード例

タイマーで変更されるフィールドを持つ基本的なStateの例。initState、setState、disposeを示します:

dart
class _TimerWidgetState extends State<TimerWidget> {
  int _seconds = 0;
  Timer? _timer;

  @override
  void initState() {
    super.initState();
    _timer = Timer.periodic(
      const Duration(seconds: 1),
      (_) => setState(() => _seconds++),
    );
  }

  @override
  void dispose() {
    _timer?.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Text('$_seconds seconds elapsed');
  }
}

widgetプロパティを使用して親パラメータにアクセスし、didUpdateWidgetを介してその変更に反応する例:

dart
class _GreetingState extends State<GreetingWidget> {
  String _displayName = '';

  @override
  void initState() {
    super.initState();
    _displayName = _formatName(widget.name);
  }

  @override
  void didUpdateWidget(GreetingWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.name != oldWidget.name) {
      setState(() {
        _displayName = _formatName(widget.name);
      });
    }
  }

  String _formatName(String name) => name.trim().isEmpty ? 'Guest' : name;

  @override
  Widget build(BuildContext context) {
    return Text('Hello, $_displayName!');
  }
}

2番目の例では、Stateが入力パラメータnameの変更を追跡し、実際の変更があった場合にのみ表示を再フォーマットします。widget.name != oldWidget.nameのチェックがないと、名前が変更されていなくても、親が再構築されるたびにメソッドが呼び出され、フレームワークにとって不必要な作業が発生します。

State vs StatefulWidget

StateとStatefulWidgetは、Flutterアーキテクチャにおいて異なる役割を果たす2つの異なるクラスです。StatefulWidgetは、ウィジェット設定を記述しStateを作成する軽量な不変のラッパーです。Stateは、変更可能なデータを保存し、購読を管理し、UIを構築する重いオブジェクトです。この分離により、Flutterは状態を失うことなくウィジェットを破棄および作成できます。

StatefulWidgetのすべてのフィールドはfinalでなければならず、コンストラクタで設定されます — 作成後は変更されません。一方、Stateはいつでもそのフィールドを変更できますが、すべての変更の前にsetState呼び出しが必要であり、Flutterが再構築の必要性を認識できるようにします。これが主な違いです:StatefulWidgetは“何を表示するか”、Stateは“どのように表示し、どのデータを使用するか”です。

Flutterソースコード分析(Flutter SDK, 2026)によると、StatefulWidgetには必須フィールドが1つだけ含まれています — createState、一方StateはBuildContextにアクセスでき、ストリームを購読し、アニメーションやコントローラーを管理できます。StatefulWidgetは可能な限りシンプルに保ち、すべてのロジックをStateに移すことが推奨されます。

なぜStatefulWidgetはStateになれないのか?

WidgetとStateの分離は、設定の不変性を保証するアーキテクチャ上の決定です。StatefulWidget自体が状態を保存した場合、親が再構築されるたびに状態が失われます。状態を別のオブジェクトに移動することで、Flutterはデータが再構築後も存続することを保証し、ウィジェットは軽量で比較可能なまま維持されます。

ウィジェット間の状態管理

Stateオブジェクトは分離されています — 他のウィジェットのStateに直接アクセスすることはできません。ウィジェット間のデータ交換には、InheritedWidgetまたは外部の状態管理ツール(Provider、Riverpod、Bloc、Redux)が使用されます。各アプローチは問題を異なる方法で解決します:InheritedWidgetはウィジェットツリーを介して動作し、ProviderはDIコンテナを介して、Blocはイベントストリームを介して動作します。

ツールの選択はプロジェクトの規模によって異なります。小さなアプリケーションには、InheritedWidgetとローカルStateで十分です。中規模から大規模なプロジェクトには、RiverpodまたはBlocが推奨されます — これらはテスト容易性、予測可能性、UIからのロジックの分離を保証します。Stateはローカルウィジェットデータ(フォーカス、スクロール、アニメーション)にのみ使用されます。

Flutterコミュニティ調査2025(Flutter Foundation、2025年12月)によると、Riverpodは新規プロジェクトで最も人気のある状態管理ソリューション(38%)であり、Bloc(31%)、Provider(22%)がそれに続きます。3つのツールすべてがStateと互換性があり、標準のライフサイクルを放棄する必要はありません。

ローカル状態 vs グローバル状態

  • ローカル状態 — 特定のウィジェットのState内(スクロール位置、フォーカス状態)
  • グローバル状態 — 外部ストア(ユーザーデータ、設定、キャッシュ)
  • ルール:データが1つのウィジェットのみで使用される場合 — Stateに保存
  • データが2つ以上のウィジェットで使用される場合 — Riverpod/Bloc/Providerに移動

よくある間違い

最初の間違いは、非同期コールバックでsetStateの前にmountedをチェックするのを忘れることです。ウィジェットがツリーから削除された(ユーザーが画面から移動したなど)が、非同期操作(HTTPリクエスト)がまだ実行中の場合、その完了後にStateは既に死亡しています。死亡したStateでsetStateを呼び出すと例外がスローされます。if (mounted) setState(...)のチェックで問題を解決します。

2番目の間違いは、didChangeDependenciesの代わりにinitStateでInheritedWidget依存関係を初期化することです。initStateではコンテキストがまだマウントされていないため、MediaQuery.of(context)は例外をスローします。すべてのInheritedWidget依存関係はdidChangeDependenciesまたはbuildで設定する必要があります。

3番目の間違いは、setStateを呼び出さずにフィールドを変更することです。開発者がsetStateなしでStateフィールドを変更した場合、Flutterは変更を認識できず、UIは更新されません。例:後続のsetState((){})なしの_list.add(item)はリストを変更しますが、画面は同じままです。

setStateの前にmountedをチェック

Stateでの非同期操作のための安全パターン:

dart
Future<void> _fetchData() async {
  final data = await ApiService.fetch();
  if (mounted) {
    setState(() => _data = data);
  }
}

mountedのチェックにより、setStateが生存しているStateでのみ呼び出され、“setState called after dispose”例外を防ぎます。

よくある質問

StateとStatefulWidgetの違いは何ですか?

StatefulWidgetは不変のウィジェット設定であり、Stateはデータを保存しライフサイクルを管理する可変オブジェクトです。ウィジェットは再作成できますが、Stateはできません。StatefulWidgetはcreateStateを介してStateを作成します。

1つのStatefulWidgetに対していくつのStateオブジェクトが作成されますか?

正確に1つです。createStateメソッドは、StatefulWidgetが最初にツリーに挿入されるときに1回呼び出されます。親が複数回再構築しても、ウィジェットのタイプまたはKeyが変更されない限り、Stateオブジェクトは同じままです。

Stateにおけるmountedとは何ですか?

mountedは、Stateがウィジェットツリーにあるかどうかを示すブールフラグです。disposeを呼び出した後、mountedはfalseになります。例外を避けるために、非同期コールバックでsetStateの前のチェックに使用されます。

StateをStatefulWidgetなしで使用できますか?

いいえ。Stateは常にジェネリックを介して特定のStatefulWidgetにバインドされています:State<T extends StatefulWidget>。ウィジェットとの関連付けなしに直接Stateを作成することは、アーキテクチャ上不可能です。

disposeでsetStateを呼び出すとどうなりますか?

例外がスローされます:“setState called after dispose”。dispose後、Stateは死亡とみなされ、setStateを介してUIを再構築しようとする試みはすべて禁止されます。解決策は、各setStateの前にmountedをチェックすることです。

まとめ

  • State — StatefulWidgetのデータ管理オブジェクトで、変更可能なフィールドを保存し、setStateを介してUI再構築を開始します
  • ライフサイクルには、必須メソッドinitState、didChangeDependencies、build、didUpdateWidget、disposeが含まれ、それぞれに目的があります
  • mounted — ウィジェットがツリーから削除された後のsetState呼び出しを防ぐ重要な安全フラグ
  • widget — 関連付けられたStatefulWidgetパラメータにアクセスするためのStateプロパティ、didUpdateWidgetを介して更新
  • 分離 — Stateは他のStateにアクセスできません。ウィジェット間通信はInheritedWidgetまたは外部ツールを介して実装されます
  • setState — buildを即座に呼び出さず、次のフレームで再構築するためにStateをダーティとしてマークするのみ
  • ルール — ローカルウィジェットデータにはStateを使用し、グローバル状態は外部レイヤー(Riverpod、Bloc)に移動

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください