InheritedWidgetは、コンストラクターを介して明示的に渡すことなく、ウィジェットツリーの下方向にデータを渡すFlutterの特別なウィジェットです。子ウィジェットはBuildContextを通じてデータにアクセスし、自動的に更新を購読します。InheritedWidgetのデータが変更されると、依存するすべてのウィジェットが再構築されます。Flutter API Reference, 2025によると、InheritedWidgetはTheme、MediaQuery、Localizations、およびほとんどの状態管理ライブラリの基盤となっています。
重要なポイント
InheritedWidgetは、Widget Tree内のすべての子孫がデータにアクセスできるようにするウィジェットです。コンストラクターを介して子要素にのみデータを渡す通常のウィジェットとは異なり、InheritedWidgetはサブツリー内の任意のウィジェットがパラメータチェーンなしでデータにアクセスできるようにします。これにより、“prop drilling”問題(このデータを必要としない多くの中間ウィジェットを介してデータを渡すこと)を解決します。
Flutterにはいくつかの組み込みInheritedWidgetが含まれています:Theme(カラースキームとスタイル)、MediaQuery(画面サイズ、向き、ピクセル密度)、Localizations(ローカライズ文字列)、Directionality(テキスト方向)、DefaultTextStyle(デフォルトのテキストスタイル)。これらのウィジェットはMaterialAppなどのルートウィジェットによって設定され、アプリケーション全体で利用できます。
InheritedWidgetには独自の状態がなく、コンストラクターを介して渡されたデータを保存します。InheritedWidgetの親が新しいデータで再構築されると、updateShouldNotifyメソッドが呼び出され、古いデータと新しいデータを比較します。メソッドがtrueを返すと、依存するすべてのウィジェットが再構築用にマークされます。これはシンプルですが効果的なリアクティブ更新メカニズムです。
InheritedWidgetによるデータ受け渡しメカニズムはElement Treeに基づいています。ウィジェットがdependOnInheritedWidgetOfExactTypeを呼び出すと、対応する要素がInheritedElementへの依存関係を登録します。InheritedWidgetが変更されると、InheritedElementは依存するすべての要素に通知し、それらは次のフレームで再構築されます。
dependOnInheritedWidgetOfExactTypeメソッドは、ツリー内でInheritedWidgetを見つけるだけでなく、現在の要素を通知に購読させます。dependOnの代わりにfindAncestorWidgetOfExactTypeを使用した場合、ウィジェットはデータを取得できますが、データ変更時に再構築されません。これは重要な違いです。dependOnは購読であり、findAncestorは1回限りの検索です。
ウィジェットがInheritedWidgetをリクエストすると、Flutterは現在の要素からルートまでElement Treeを上昇し、各InheritedElementが型に一致するかチェックします。最初に一致したInheritedElementが返されます。つまり、ツリー内で最も近いInheritedWidgetが優先されます。InheritedWidgetを子孫の近くに配置することで、特定のレベルでデータをオーバーライドできます。
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の作成は2つのステップで構成されます:InheritedWidgetを拡張するクラスの定義と、子孫からアクセスするための静的メソッドofの実装です。データはコンストラクターを介して渡され、updateShouldNotifyメソッドが依存ウィジェットの再構築タイミングを決定します。
クラスはInheritedWidgetを拡張し、必須のchildパラメータを持つコンストラクターを介してデータを受け入れる必要があります。データはプリミティブ、オブジェクト、関数など任意の型にできます。主なルールは、古い値と新しい値を確実に比較できるように、データが不変(immutable)でなければならないことです。
静的ofメソッドはBuildContextを受け取り、InheritedWidgetデータを返します。内部でdependOnInheritedWidgetOfExactTypeを呼び出し、ツリー内で指定された型の最も近いInheritedWidgetを見つけます。InheritedWidgetが見つからない場合、メソッドは実装に応じて例外をスローするかデフォルト値を返します。
データにアクセスするには、ウィジェットがbuildメソッド内でMyWidget.of(context)を呼び出します。Flutterは自動的にウィジェットを更新に購読させます。データが変更されると、ウィジェットは次のフレームで再構築されます。これにより、不要なパラメータなしでクリーンで宣言的なコードを記述できます。
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は、データ変更について依存ウィジェットに通知する必要があるかどうかを判断するInheritedWidgetの主要メソッドです。メソッドがfalseを返す場合、依存ウィジェットは再構築されません。たとえInheritedWidget自体が同じデータを持つ新しいインスタンスを受け取ったとしてもです。これはパフォーマンスにとって非常に重要です。
実際に変更され、表示に影響を与えるフィールドのみを比較します。InheritedWidgetに10個のフィールドがあっても、UIに影響するのが1つだけの場合は、そのフィールドのみをチェックします。コレクションの場合は、ディープ比較または不変データ構造を使用します。ListやMapは参照で比較されるため、==を使用しないでください。
最も一般的な間違いは、比較なしでtrueを返すことです。これにより、データが変更されていない場合でも、すべての依存ウィジェットが親の更新ごとに再構築されます。2つ目の間違いは、データが変更されたときにfalseを返すことで、UIが古くなります。3つ目は、すべてのフレームで実行されパフォーマンスを低下させる複雑な比較です。
InheritedWidgetとコールバック(コンストラクターを介した関数の受け渡し)は異なる問題を解決します。InheritedWidgetはツリーのさまざまなレベルの多くのウィジェットで必要とされるデータに適しています。コールバックは、親から特定の子へ、またはその逆の単方向イベント受け渡しに便利です。選択はアプリケーションアーキテクチャと更新頻度によって異なります。
異なるネストレベルで多くのウィジェットがデータを必要とする場合にInheritedWidgetを使用します:アプリのテーマ、ユーザー設定、デバイス情報、現在のセッションデータ。InheritedWidgetは、めったに変更されないがUIのさまざまな部分で必要とされる“グローバル”データに特に効果的です。
コールバック(コールバック関数)は、子ウィジェットから親へのイベント受け渡しに適しています:ボタン押下、リスト項目の選択、フォーム送信。コールバックは子が実行できるアクションを明示的に示し、隠れた依存関係を作成しません。少数のレベルのツリー下方向にデータを渡す場合は、コンストラクターパラメータを使用する方が簡単です。
| 基準 | InheritedWidget | コールバック |
|---|---|---|
| 方向 | 上から下(親→子孫) | 下から上(子→親)または直接 |
| 範囲 | サブツリー全体 | 特定のウィジェット |
| 再構築 | データ変更時に自動 | 手動のsetStateが必要 |
| 複雑さ | 中(InheritedWidgetクラスが必要) | 低(単なる関数) |
ProviderとRiverpodは、InheritedWidgetの上に構築されたFlutterで人気の状態管理ライブラリです。これらは機能を拡張します:ChangeNotifierサポート、アンマウント時の自動破棄、遅延初期化、ジェネリックによる簡略化された構文を追加します。
ProviderはInheritedWidgetを使用して、任意の型のオブジェクトをツリーの下方向に渡します。ChangeNotifierProviderはChangeNotifierを介して変更を追跡し、notifyListenersが呼び出されるとupdateShouldNotifyを呼び出します。これにより、開発者は手動でInheritedWidgetを作成したりupdateShouldNotifyを実装したりする必要がなくなります。
直接のInheritedWidgetはより多くの制御を提供し、外部依存関係を必要としません。ProviderはConsumer、Selector、MultiProvider、ProxyProviderといった既製のインフラストラクチャを提供します。選択はアプリケーションの複雑さに依存します。シンプルなプロジェクトでは直接のInheritedWidgetで十分です。大規模なプロジェクトでは、ProviderやRiverpodがボイラープレートコードを削減します。
// 直接の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はBuildContextを介してすべての子孫がデータを利用できるようにしますが、通常のウィジェットはコンストラクターを介してのみデータを渡します。InheritedWidgetはまた、子孫をデータ更新に購読させます。
依存ウィジェットはupdateShouldNotifyがtrueを返す場合にのみ再構築されます。メソッドが正しく実装されていれば、再構築はデータが実際に変更された場合にのみ発生し、親の再構築ごとには発生しません。
はい、同じツリーで任意の数のInheritedWidgetを使用できます。それぞれが特定の型のデータを提供し、ウィジェットは複数のInheritedWidgetから同時にデータを取得できます。
dependOnはウィジェットを更新に購読させます — データが変更されるとウィジェットが再構築されます。findAncestorは購読なしの1回限りの検索を実行し、ウィジェットはデータ変更を認識しません。
単純な状態(テーマ、設定)にはInheritedWidgetで十分です。ビジネスロジックを伴う複雑な状態には、Provider、Riverpod、またはBLoCを使用してください — これらはInheritedWidget上に構築され、必要なインフラストラクチャを追加します。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。