InheritedWidget — 概要、ツリー内のデータ受け渡しと動作の仕組み

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

InheritedWidgetは、コンストラクターを介して明示的に渡すことなく、ウィジェットツリーの下方向にデータを渡すFlutterの特別なウィジェットです。子ウィジェットはBuildContextを通じてデータにアクセスし、自動的に更新を購読します。InheritedWidgetのデータが変更されると、依存するすべてのウィジェットが再構築されます。Flutter API Reference, 2025によると、InheritedWidgetはTheme、MediaQuery、Localizations、およびほとんどの状態管理ライブラリの基盤となっています。

重要なポイント

  • InheritedWidgetは、すべてのウィジェットを明示的に通過させることなく、Widget Treeの下方向にデータを渡します。
  • 自動購読 — dependOnInheritedWidgetOfExactTypeを使用するウィジェットは、データ変更時に再構築されます。
  • ThemeとMediaQueryは、InheritedWidgetの組み込み例であり、すべてのFlutterアプリケーションで利用できます。
  • ProviderとRiverpodはInheritedWidgetの上に構築されており、状態管理の機能を拡張します。
  • 適切な実装では、不要な再構築を防ぐためにupdateShouldNotifyをオーバーライドする必要があります。

FlutterのInheritedWidgetとは?

InheritedWidgetは、Widget Tree内のすべての子孫がデータにアクセスできるようにするウィジェットです。コンストラクターを介して子要素にのみデータを渡す通常のウィジェットとは異なり、InheritedWidgetはサブツリー内の任意のウィジェットがパラメータチェーンなしでデータにアクセスできるようにします。これにより、“prop drilling”問題(このデータを必要としない多くの中間ウィジェットを介してデータを渡すこと)を解決します。

組み込みのInheritedWidget

Flutterにはいくつかの組み込みInheritedWidgetが含まれています:Theme(カラースキームとスタイル)、MediaQuery(画面サイズ、向き、ピクセル密度)、Localizations(ローカライズ文字列)、Directionality(テキスト方向)、DefaultTextStyle(デフォルトのテキストスタイル)。これらのウィジェットはMaterialAppなどのルートウィジェットによって設定され、アプリケーション全体で利用できます。

InheritedWidgetのライフサイクル

InheritedWidgetには独自の状態がなく、コンストラクターを介して渡されたデータを保存します。InheritedWidgetの親が新しいデータで再構築されると、updateShouldNotifyメソッドが呼び出され、古いデータと新しいデータを比較します。メソッドがtrueを返すと、依存するすべてのウィジェットが再構築用にマークされます。これはシンプルですが効果的なリアクティブ更新メカニズムです。

InheritedWidgetによるデータ受け渡しの仕組み

InheritedWidgetによるデータ受け渡しメカニズムはElement Treeに基づいています。ウィジェットがdependOnInheritedWidgetOfExactTypeを呼び出すと、対応する要素がInheritedElementへの依存関係を登録します。InheritedWidgetが変更されると、InheritedElementは依存するすべての要素に通知し、それらは次のフレームで再構築されます。

依存関係の登録

dependOnInheritedWidgetOfExactTypeメソッドは、ツリー内でInheritedWidgetを見つけるだけでなく、現在の要素を通知に購読させます。dependOnの代わりにfindAncestorWidgetOfExactTypeを使用した場合、ウィジェットはデータを取得できますが、データ変更時に再構築されません。これは重要な違いです。dependOnは購読であり、findAncestorは1回限りの検索です。

InheritedWidgetのツリー探索

ウィジェットがInheritedWidgetをリクエストすると、Flutterは現在の要素からルートまでElement Treeを上昇し、各InheritedElementが型に一致するかチェックします。最初に一致したInheritedElementが返されます。つまり、ツリー内で最も近いInheritedWidgetが優先されます。InheritedWidgetを子孫の近くに配置することで、特定のレベルでデータをオーバーライドできます。

dart
class ThemeData {
  final Color primaryColor;
  final TextTheme textTheme;

  const ThemeData({required this.primaryColor, required this.textTheme});
}

class MyTheme extends InheritedWidget {
  final ThemeData data;

  const MyTheme({required this.data, required Widget child}) : super(child: child);

  static MyTheme of(BuildContext context) {
    final widget = context.dependOnInheritedWidgetOfExactType<MyTheme>();
    assert(widget != null, "MyTheme not found in tree");
    return widget!;
  }

  @override
  bool updateShouldNotify(MyTheme oldWidget) => oldWidget.data != data;
}

この例では、MyThemeが静的メソッドofを使用して子孫にデータを提供しています。dependOnInheritedWidgetOfExactTypeメソッドが依存関係を登録し、updateShouldNotifyが古いデータと新しいデータを比較して、依存ウィジェットの再構築が必要かどうかを判断します。

カスタムInheritedWidgetの作成

カスタムInheritedWidgetの作成は2つのステップで構成されます:InheritedWidgetを拡張するクラスの定義と、子孫からアクセスするための静的メソッドofの実装です。データはコンストラクターを介して渡され、updateShouldNotifyメソッドが依存ウィジェットの再構築タイミングを決定します。

ステップ1:InheritedWidgetクラスの定義

クラスはInheritedWidgetを拡張し、必須のchildパラメータを持つコンストラクターを介してデータを受け入れる必要があります。データはプリミティブ、オブジェクト、関数など任意の型にできます。主なルールは、古い値と新しい値を確実に比較できるように、データが不変(immutable)でなければならないことです。

ステップ2:静的メソッドof

静的ofメソッドはBuildContextを受け取り、InheritedWidgetデータを返します。内部でdependOnInheritedWidgetOfExactTypeを呼び出し、ツリー内で指定された型の最も近いInheritedWidgetを見つけます。InheritedWidgetが見つからない場合、メソッドは実装に応じて例外をスローするかデフォルト値を返します。

ステップ3:ウィジェットでの使用

データにアクセスするには、ウィジェットがbuildメソッド内でMyWidget.of(context)を呼び出します。Flutterは自動的にウィジェットを更新に購読させます。データが変更されると、ウィジェットは次のフレームで再構築されます。これにより、不要なパラメータなしでクリーンで宣言的なコードを記述できます。

dart
class UserPreferences extends InheritedWidget {
  final String languageCode;
  final bool darkMode;

  const UserPreferences({
    required this.languageCode,
    required this.darkMode,
    required Widget child,
  }) : super(child: child);

  static UserPreferences of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<UserPreferences>()!;
  }

  @override
  bool updateShouldNotify(UserPreferences oldWidget) =>
    oldWidget.languageCode != languageCode || oldWidget.darkMode != darkMode;
}

この例では、UserPreferencesがユーザー設定を保存しています。updateShouldNotifyメソッドは各フィールドを個別に比較するため、1つのパラメータのみが変更された場合の不要な再構築を防ぎます。複数のフィールドを持つ独自のInheritedWidgetにも同様のアプローチを使用してください。

updateShouldNotifyメソッドと不要な再構築の防止

updateShouldNotifyは、データ変更について依存ウィジェットに通知する必要があるかどうかを判断するInheritedWidgetの主要メソッドです。メソッドがfalseを返す場合、依存ウィジェットは再構築されません。たとえInheritedWidget自体が同じデータを持つ新しいインスタンスを受け取ったとしてもです。これはパフォーマンスにとって非常に重要です。

updateShouldNotifyの適切な実装

実際に変更され、表示に影響を与えるフィールドのみを比較します。InheritedWidgetに10個のフィールドがあっても、UIに影響するのが1つだけの場合は、そのフィールドのみをチェックします。コレクションの場合は、ディープ比較または不変データ構造を使用します。ListやMapは参照で比較されるため、==を使用しないでください。

  • プリミティブ — 直接比較を使用:oldWidget.value != value。
  • 不変オブジェクト — オーバーライドされた==を使用:oldWidget.data != data(dataが==をオーバーライドする場合)。
  • コレクション — package:flutter/foundation.dartのlistEquals、mapEqualsを使用。

updateShouldNotify実装のエラー

最も一般的な間違いは、比較なしでtrueを返すことです。これにより、データが変更されていない場合でも、すべての依存ウィジェットが親の更新ごとに再構築されます。2つ目の間違いは、データが変更されたときにfalseを返すことで、UIが古くなります。3つ目は、すべてのフレームで実行されパフォーマンスを低下させる複雑な比較です。

InheritedWidget vs コールバック:どちらを選ぶべきか

InheritedWidgetとコールバック(コンストラクターを介した関数の受け渡し)は異なる問題を解決します。InheritedWidgetはツリーのさまざまなレベルの多くのウィジェットで必要とされるデータに適しています。コールバックは、親から特定の子へ、またはその逆の単方向イベント受け渡しに便利です。選択はアプリケーションアーキテクチャと更新頻度によって異なります。

InheritedWidgetを使用する場合

異なるネストレベルで多くのウィジェットがデータを必要とする場合にInheritedWidgetを使用します:アプリのテーマ、ユーザー設定、デバイス情報、現在のセッションデータ。InheritedWidgetは、めったに変更されないがUIのさまざまな部分で必要とされる“グローバル”データに特に効果的です。

コールバックを使用する場合

コールバック(コールバック関数)は、子ウィジェットから親へのイベント受け渡しに適しています:ボタン押下、リスト項目の選択、フォーム送信。コールバックは子が実行できるアクションを明示的に示し、隠れた依存関係を作成しません。少数のレベルのツリー下方向にデータを渡す場合は、コンストラクターパラメータを使用する方が簡単です。

基準InheritedWidgetコールバック
方向上から下(親→子孫)下から上(子→親)または直接
範囲サブツリー全体特定のウィジェット
再構築データ変更時に自動手動のsetStateが必要
複雑さ中(InheritedWidgetクラスが必要)低(単なる関数)

InheritedWidgetと状態管理ライブラリ

ProviderRiverpodは、InheritedWidgetの上に構築されたFlutterで人気の状態管理ライブラリです。これらは機能を拡張します:ChangeNotifierサポート、アンマウント時の自動破棄、遅延初期化、ジェネリックによる簡略化された構文を追加します。

InheritedWidgetベースのProvider

ProviderはInheritedWidgetを使用して、任意の型のオブジェクトをツリーの下方向に渡します。ChangeNotifierProviderはChangeNotifierを介して変更を追跡し、notifyListenersが呼び出されるとupdateShouldNotifyを呼び出します。これにより、開発者は手動でInheritedWidgetを作成したりupdateShouldNotifyを実装したりする必要がなくなります。

直接のInheritedWidgetとの比較

直接のInheritedWidgetはより多くの制御を提供し、外部依存関係を必要としません。ProviderはConsumer、Selector、MultiProvider、ProxyProviderといった既製のインフラストラクチャを提供します。選択はアプリケーションの複雑さに依存します。シンプルなプロジェクトでは直接のInheritedWidgetで十分です。大規模なプロジェクトでは、ProviderやRiverpodがボイラープレートコードを削減します。

dart
// 直接のInheritedWidget
class UserProvider extends InheritedWidget {
  final UserData userData;
  const UserProvider({required this.userData, required Widget child}) : super(child: child);
  static UserData of(BuildContext context) => context.dependOnInheritedWidgetOfExactType<UserProvider>()!.userData;
  @override
  bool updateShouldNotify(UserProvider old) => old.userData != userData;
}

// Provider相当
return ChangeNotifierProvider<UserData>(
  create: (_) => UserData(),
  child: MyApp(),
);

例の両方のアプローチは同じ問題を解決します — UserDataをツリーの下方向に渡すことです。Providerはコード量を削減しますが、InheritedWidgetのメカニズムを隠します。直接のInheritedWidgetは完全な制御と動作の理解を提供し、これはFlutterの学習や複雑な再構築問題のデバッグにおいて特に重要です。

よくある質問

InheritedWidgetは通常のウィジェットとどう違うのですか?

InheritedWidgetはBuildContextを介してすべての子孫がデータを利用できるようにしますが、通常のウィジェットはコンストラクターを介してのみデータを渡します。InheritedWidgetはまた、子孫をデータ更新に購読させます。

依存ウィジェットはどのくらいの頻度で再構築されますか?

依存ウィジェットはupdateShouldNotifyがtrueを返す場合にのみ再構築されます。メソッドが正しく実装されていれば、再構築はデータが実際に変更された場合にのみ発生し、親の再構築ごとには発生しません。

同じツリーで複数のInheritedWidgetを使用できますか?

はい、同じツリーで任意の数のInheritedWidgetを使用できます。それぞれが特定の型のデータを提供し、ウィジェットは複数のInheritedWidgetから同時にデータを取得できます。

dependOnInheritedWidgetOfExactTypeとfindAncestorWidgetOfExactTypeの違いは?

dependOnはウィジェットを更新に購読させます — データが変更されるとウィジェットが再構築されます。findAncestorは購読なしの1回限りの検索を実行し、ウィジェットはデータ変更を認識しません。

InheritedWidgetは複雑な状態管理に適していますか?

単純な状態(テーマ、設定)にはInheritedWidgetで十分です。ビジネスロジックを伴う複雑な状態には、Provider、Riverpod、またはBLoCを使用してください — これらはInheritedWidget上に構築され、必要なインフラストラクチャを追加します。

まとめ

  • InheritedWidgetは、自動更新購読を備え、ツリー下方向にデータを渡すFlutterの特別なウィジェットです。
  • 動作メカニズムはElement Treeに基づく:InheritedElementが依存要素を登録し、変更を通知します。
  • updateShouldNotify — 依存ウィジェットの不要な再構築を防ぐための主要メソッド。
  • 組み込みInheritedWidget:Theme、MediaQuery、Localizations、Directionality、DefaultTextStyle。
  • カスタム作成にはクラスの継承、コンストラクターを介したデータ受け渡し、静的メソッドofが含まれます。
  • ProviderとRiverpodはInheritedWidget上に構築され、ChangeNotifier、Consumer、Selector、簡略化された構文を追加します。
  • InheritedWidgetはprop drilling問題を解決し、Flutterにおけるリアクティブ状態管理の基盤です。

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

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

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

こちらもお読みください