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

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

InheritedWidgetの動き

  • Widgetを継承しており、他のWidgetと同じように build() 内で使える
  • 基本的にInheritedWidgetの child はリビルドされることがない
    • InheritedWidget自身は build() メソッドを持たない
    • ReactでいうとchildrenをそのままレンダーするHOC

InheritedWidgetの動き

  • とはいえ他の要因で強制的にリビルドされることはある
    • 「デバイスの向きがPortraitからLandscapeに変わった」など
    • updateShouldNotify() を実装していると、子孫Widgetが再Buildされるべきかどうかを自身の同等性を担保する形で防ぐことができる

InheritedWidgetが使われているもの

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!

Made with Slides.com