Flutter Context
@axross
Introduction to
Contextとは
- ビルドの"文脈"
- 子孫Widgetに値を伝搬するもう1つの仕組み
- Widgetの引数と違い、直接の親Widgetがその値を明示的に取り回している必要はない
- つまり直接の親を飛び越えていずれかの祖先Widgetからの値を受け取れるということ
- ReactのContextとできることは同じ
代表的な例: Theme
@override
Widget build(BuildContext context) => new MaterialApp(
title: 'ピザの提供はユーザベース様です',
theme: new ThemeData(primarySwatch: Colors.blue),
initialRoute: '/',
routes: routes,
);
color: Theme.of(context).dividerColor,
代表的な例: Theme
- この Material App のコンストラクト時に渡した ThemeData インスタンスは子孫Widgetのどこからでも参照できる
- Theme.of(BuildContext context) によって祖先でContextにある ThemeData を取得している
Themeの実装 (抜粋)
class Theme extends StatelessWidget {
const Theme({Key key, this.data this.child}) ...;
final ThemeData data;
@override Widget build(BuildContext context) =>
new _InheritedTheme(theme: this, child: child);
static ThemeData of(BuildContext context) =>
context.inheritFromWidgetOfExactType(_InheritedTheme);
}
class _InheritedTheme extends InheritedWidget {
const _IheritedTheme({Key key, this.theme, Widget child}):
super(key: key, child: child);
final Theme theme;
@override updateShouldNotify(_InheritedTheme old) =>
theme.data != old.theme.data;
}
スペース不足のため色々割愛
InheritedWidget
- 子孫WidgetがContextを取得できるようにするためのWidget
- BuildContext#inheritFromWidgetOfExactType() で自身を取得させられる
- ReactでいうとContextを定義/供給しているReact Component
- 例) react-redux の <Provider>
- 例) react-router の <Router>
InheritedWidgetの動き
- Widgetを継承しており、他のWidgetと同じように build() 内で使える
- 基本的にInheritedWidgetの child はリビルドされることがない
- InheritedWidget自身は build() メソッドを持たない
- ReactでいうとchildrenをそのままレンダーするHOC
InheritedWidgetの動き
- とはいえ他の要因で強制的にリビルドされることはある
- 「デバイスの向きがPortraitからLandscapeに変わった」など
- updateShouldNotify() を実装していると、子孫Widgetが再Buildされるべきかどうかを自身の同等性を担保する形で防ぐことができる
InheritedWidgetが使われているもの
- DefaultTextStyle
- IconTheme
- MediaQuery
- etc.
- その他、できそうなこと
- Dependency Injection
Dependency Injection
class Container {
Container({ @required this.foo });
final Foo foo;
}
class Provider extends InheritedWidget {
static Container of(BuildContext context) =>
context.inheritFromWidgetOfExactType(Provider).container;
Provider({Key key, Widget child, this.container}):
super(key: key, child: child);
final Container container;
@override bool updateShouldNotify(Provider old) =>
container != old.container;
}
スペース不足のため色々割愛
Contextを扱う
ベストプラクティス
static Foo of()
- というStaticメソッドを実装するのが定番
-
Contextを返す
-
Theme の例であれば ThemeData を返す
-
Dependency Injectionの例であれば注入されたコンテナを返す
-
-
ほとんどのFlutter標準APIではこの形になっている
責務とサイズを最小限に
- 1つのInheritedWidgetに何でもかんでも詰め込まない
- updateShouldNotify() の実行コストが高くなる
- 責務を適切に分離するのは言わずもがな大切
できるだけ const
-
子孫Widgetのキャッシュが効く
- InheritedWidgetのメリットを最大限享受できるようになる
- というか、Flutter一般的にもベター
Thank you
for
listening!
Introduction to Flutter Context
By Kohei Asai
Introduction to Flutter Context
Talked at UZABASE, Inc. on 2017-03-29.
- 874