Flutterに入門してみる (その2)
(↑は「Run」ボタン押してしばらく待つと、動かせます)
前回につづいての2回目。
サンプル実装の中身を順番に解析中。
各クラスの中身を見ていく
1クラス目 MyApp。
class MyApp extends StatelessWidget {
MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
StatelessWidget
を継承したMyApp。
最初にmain関数から
runApp(const MyApp());
のように呼び出している、アプリのルートになるクラスですね。
build
メソッドをオーバライドしている。このメソッドが画面表示時に実行される。StatelessWidget
はインスタンス生成時のみ、buildメソッドが呼ばれる。- インスタンス生成は、以下のタイミングで実行される
- 初回表示
- 親Widgetが更新された時
build
メソッドがMaterialApp
のインスタンスをreturnしている。MaterialApp
の引数home
にMyHomePage
クラスを設定している。実質、ここが入口。MaterialApp
にはタイトルやテーマの他、アプリケーションの様々な設定が行える。- 設定項目はAPIリファレンス見る。
2クラス目 MyHomePage。
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
先にDartのお勉強。
State<MyHomePage> createState() => _MyHomePageState();
=>
はアロー関数。1行で戻り値のみ書くならreturnは省略できる模様。
さて内容。
createState()
メソッドをオーバーライドしている。StatefulWidget
にbuild
メソッドは無い。- Flutter内部で管理されるElementツリーに当Widgetがmountされる際に呼ばれる。(つまり初回)
title
を受け取ってフィールド設定している。- このクラス内で使ってる様子はないが、後述するStateの中で使っている。
- クラス単位でプライベートには出来ないので、この
title
は普通にパブリック公開されている。
StatefulWidgetには特に処理は無く、title
のように設定値を公開されたfinalフィールドとして保持し、実処理を行うState側から使う、という感じで使うらしい。(サンプルのコメントによると)
3クラス目 _MyHomePageState。
ちょい長いので前後半に分けます。
まずは前半。
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() { _counter++; });
}
- Stateを拡張した_MyHomePageStateクラスは
_(アンスコ)
付き。- Flatter内部Stateクラスも大半はアンスコ付いてるので、プライベートとして作るのが慣習ぽい。
- とは言え、付いて無いのもあるから必須ってわけでもない。
- カウンター用の変数
_counter
とメソッド_incrementCounter
が定義されている。- ここで呼び出している
setState()
メソッドが重要。setState
はStateクラスに定義されたメソッド。- これを呼び出す事で、Flutterに状態の変化を伝えることができ、再描画(buildの再呼び出し)が実行される。
- ここで呼び出している
ライフサイクルの全体像は、改めて調査したい。
後半のbuildメソッド。
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('You have pushed the button this many times:'),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
buildメソッドはScaffold
というクラスを返している。
return Scaffold(
appBar: AppBar( ... ),
body: Center( ... ),
floatingActionButton: FloatingActionButton( ... ),
);
意味は英語で「足場」。
画面構成の基礎になるクラスで、ここでは以下が使用されている。
- appBar: 画面ヘッダ部のバー
- body: 画面のメイン要素。ここにレイアウトを作りこんでいく。
- floatingActionButton: フローティングボタン。
他にもたくさんの要素が定義されていて、必要に応じて設定できる。これは改めて整理したい。
さて、appBar。
appBar: AppBar(
title: Text(widget.title),
),
見たままタイトルを表示しているだけですが、widget
はStateに定義されたフィールドで、
class _MyHomePageState extends State<MyHomePage>
でジェネリックに指定されたMyHomePage
が取得できる。
Text
は StatelessWidget で、styleなどの設定項目が他にも色々ある。
次にbody。
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('You have pushed the button this many times:'),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
- Widgetを入れ子にしながらレイアウト作ってる。
- ここでは
Center > Column > [ Text, Text ]
って感じ
- ここでは
- 子が1つしか持てない
SingleChildRenderObjectWidget
のサブクラスはchild
に子を持つ。- ここでは
Center
- ここでは
- 子を複数持てる
MultiChildRenderObjectWidget
のサブクラスはchildren
に子リストを持つ。- ここでは
Column
- ここでは
ここではカウンター数値をTextウィジェットで設定していますね。
Dartのお勉強。
Text('$_counter')
$
で文字列の変数埋め込みができるみたいですね。
あとchildrenに設定しているリストは下記のような書き方になってます。
children: <Widget>[ ... ]
リストにジェネリックで型指定できるんですね。
ちなみに上位のMultiChildRenderObjectWidget
で
final List<Widget> children;
と定義されているので、書かなくてもWidgetしか入れられません。
お勉強終わり。
さて、最後のfloatingActionButton。
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
まぁ見たままですね。ポイントは
onPressed
に定義した_incrementCounter
メソッドを設定- ボタン押下でメソッドが呼ばれ、
setState
される - それによって状態変化がFlutterに伝わり、buildメソッドが再実行されて画面再描画される(数値カウントアップ!)
こんな流れってことですね。
ということで、今回はここまで。