(↑は「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の引数homeMyHomePageクラスを設定している。実質、ここが入口。
    • 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()メソッドをオーバーライドしている。
    • StatefulWidgetbuildメソッドは無い。
    • 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メソッドが再実行されて画面再描画される(数値カウントアップ!)

こんな流れってことですね。



ということで、今回はここまで。