Flutterの記事を整理し本にしました
- 本稿の記事を含む様々な記事を体系的に整理し本にまとめました
- 今後はこちらを最新化するため、最新情報はこちらをご確認ください
- 10万文字を超える超大作になっています(笑)
はじめに
- 最近、Flutterの3つのツリーをまとめたので、BuildContextについても整理してみました。
まとめ
BuildContext
Flutterで一番良く使うメソッドと言っても過言ではない、buildメソッドは引数にBuildContextを持っています。
Widget build(BuildContext context){ ・・・ }
はおなじみだと思います。
また、Theme.of(context)
のように、何となくおまじないのようにcontext
は登場したりもします。
まず、context
とは文脈という意味です。
近くにやまがあるので登ると明日が試験なのでやまをはるといった場合、おなじやまという文字ですが、持っている意味は変わってきます。
context
はその対象が置かれた、前後関係や環境や条件などの文脈のことを指しています。
では、Flutternにおける文脈
とは何を指すでしょうか?
答えから言ってしまうと、Widgetツリーの親子関係になります。
context
とは親のElementへの参照となっています。
Elementについては、よくわからないという方は、3つのツリーの記事をご参照ください
ビルドメソッドを持つ、WidgetのElementを参照しているというのは、ElementへのAPIが提供されているという意味になります。
実際には、後述するofメソッドなどで利用されています。
その前に、本当にcontext
が親のElementを保有しているのか、ソースコードとデバッグで確認していきます。
サンプルにはHelloWorldを使い、「+」ボタンが押された時のイベントにブレイクポイントを張ってみます。
import 'package:flutter/material.dart';
import 'package:hello_world/BusinessLogic.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, this.title}) : super(key: key);
final String? title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++; // ここにブレイクポイントをはる
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title!),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
デバッグで変数を表示させていますが、context
がStateful Element(MyHomePage)
となっていることが確認できるかと思います。
またchild
にはScaffold
があり、ツリー構造に従ってchild
が連なっています。
つまり、context
があることで、buildがあるWidgetの根本からWidgetをたどることができるというわけです。
ofメソッド
しかし、逆に根本から祖先をさかのぼりたいときもあります。
そのようなニーズに対応するのが、ScaffoldやThemeのof
メソッドになります。
このメソッドは、context
から祖先に向かって(child方向ではなく)最も近い目的のWidgetを探すことができます。
これを応用し、全体の設定の取得や変更を行うことができます。
Scaffold.ofの場合はScaffoldState。Theme.ofの場合はThemeDataが返ります。
/* 中略 */
void _incrementCounter() {
+ print(Theme.of(context));
+ print(Theme.of(context).primaryColor);
+ print(Theme.of(context).brightness);
setState(() {
_counter++;
});
}
/* 中略 */
I/flutter (13681): ThemeData#a7f8e
I/flutter (13681): MaterialColor(primary value: Color(0xff2196f3))
I/flutter (13681): Brightness.light
Scaffold.ofの注意点
Scaffold.ofには1つ注意点があります。
Scaffoldの定義と、Scaffold.ofを使う場所が同じbuild関数の中にある場合は、うまく見つけることができません。
それは、ofメソッドがbuildを呼び出すWidgetから祖先をたどっていくためです。
Scaffoldは子孫のため探し出すことができません。
そこで、Builderを使って探すという手段が一般的に行われます。
contextの探索開始点をツリーの下にする目的です。
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Demo')
),
body: Builder(
// Create an inner BuildContext so that the onPressed methods
// can refer to the Scaffold with Scaffold.of().
builder: (BuildContext context) {
return Center(
child: RaisedButton(
child: Text('SHOW A SNACKBAR'),
onPressed: () {
Scaffold.of(context).showSnackBar(SnackBar(
content: Text('Hello!'),
));
},
),
);
},
),
);
}