はじめに
FlutterのInheritedWidgetについての記事です。
詳しい原理などは省いて、使い方について言及します。
(詳しい原理については別途記事を作成しようと思います)
InheritedWidget
データを子Widgetへ効率的に伝えるWidgetです
特徴
- 子WidgetはO(1)でInheritedWidgetへアクセスが可能
- InheritedWidgetが提供するデータに変化があった場合、必要に応じて子Widgetのリビルドが可能
使い方
- InheritedWidget継承クラスを作成する
- 子WidgetからInheritedWidgetのメソッドを使用してデータを参照する
InheritedWidget継承クラスを作成する
inherited_sample.dart
class InheritedText extends InheritedWidget {
const InheritedText({
super.key,
required this.text,
required super.child,
});
final String text;
/// InheritedWidget継承クラスを取得する為の関数です
/// 参照元Widgetから最も近いInheritedWidget継承クラスを取得します
static InheritedText? maybeOf(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<InheritedText>();
}
static InheritedText of(BuildContext context) {
final InheritedText? result = maybeOf(context);
assert(result != null, 'No InheritedCounter found in context');
return result!;
}
/// InheritedWidget継承クラスがリビルドされた時に
/// データを参照しているWidgetをリビルドさせるかを判定するメソッドです
@override
bool updateShouldNotify(InheritedText oldWidget) =>
text != oldWidget.text;
}
子WidgetからInheritedWidgetのメソッドを使用してデータを参照する
inherited_sample.dart
class TextDisplayWidget extends StatelessWidget {
const TextDisplayWidget({super.key});
@override
Widget build(BuildContext context) {
///
/// InheritedTextのofメソッドを使用して、textを参照しています
final text = InheritedText.of(context).text;
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text("InheritedWidgetお試し"),
),
body: Center(
child: Text(text),
),
);
}
}
コード全体
inherited_sample.dart
void main() {
runApp(const InheritedSampleApp());
}
class InheritedText extends InheritedWidget {
const InheritedText({
super.key,
required this.text,
required super.child,
});
final String text;
/// InheritedWidget継承クラスを取得する為の関数です
/// 参照元Widgetから最も近いInheritedWidget継承クラスを取得します
static InheritedText? maybeOf(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<InheritedText>();
}
static InheritedText of(BuildContext context) {
final InheritedText? result = maybeOf(context);
assert(result != null, 'No InheritedCounter found in context');
return result!;
}
/// InheritedWidget継承クラスがリビルドされた時に
/// データを参照しているWidgetをリビルドさせるかを判定するメソッドです
@override
bool updateShouldNotify(InheritedText oldWidget) => text != oldWidget.text;
}
class InheritedSampleApp extends StatelessWidget {
const InheritedSampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const InheritedText(
text: "テストだよ",
child: TextDisplayWidget(),
),
);
}
}
class TextDisplayWidget extends StatelessWidget {
const TextDisplayWidget({super.key});
@override
Widget build(BuildContext context) {
///
/// InheritedTextのofメソッドを使用して、textを参照しています
final text = InheritedText.of(context).text;
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text("InheritedWidgetお試し"),
),
body: Center(
child: Text(text),
),
);
}
}
注意点
ofメソッド、というよりもdependOnInheritedWidgetOfExactTypeメソッドは下記では呼ばないようにしましょう
- StatefulWidgetのinitStateメソッド内
- initStateはStatefulWidgetのリビルドで毎回呼ばれるものではない為です
- 上位のinheritedWidgetの更新を正しく受け取りたい場合はdidChangeDependencies内で呼ぶ必要があります
- StatefulWidgetのdisposeメソッド内
- dispose時点ではElementツリーが既に不安定に成っている為です
- BuildContextを使用して参照を得ているのでElementツリーの状態は常に考慮する必要があると思います
まとめ
以上がinheritedWidgetの基本的な使用方法です。
今回の例はinheritedWidgetのデータは常に変化しないようになっていますが、StatefulWidget等と併用する事で変更が可能です。
適切ではない点などあればご指摘いただけると幸いです。
参考資料