Flutter未経験者に初期サンプルコードの説明をしてみた

Flutter未経験者の方から「実際どんなコードなん？」と質問があったので、説明したみた内容を投稿します。

以下はお馴染みのあのコード。

Flutterの新しいプロジェクトを作成すると、すでにプログラムが入っています。
そのまま実行するとボタンに反応してカウントアップするアプリが実行されます。
ある程度土台ができているので、それを使って説明します。

```dart:main.dart

import 'package:flutter/material.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.display1,
            ),
            FloatingActionButton(
              onPressed: _incrementCounter,
              tooltip: 'Increment',
              child: Icon(Icons.add),
            )
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), 
    );
  }
}

```


#### import文 

```dart:main.dart
import 'package:flutter/material.dart';
```
一番最初の行に`import`文があります。
Googleのアプリっぽいデザインやそれに合ったツールが使えるようになります。

#### main()関数と MyApp

```dart:main.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'),
    );
  }
}
```

`main()`関数があって、`MyApp`をスクリーンに表示するように書かれています。
`MyApp`はクラスで名前は何でもいいです。

`MyApp` は `StatelessWidget` を継承しています。
ウィジェットは画面の一要素、それのステートレスなものです。
つまり 「変更が起きない要素」 のようなものだと思ってください。


その中でウィジェットのビルドが行われています。

引数に `context` が呼ばれています。
日本語に訳すと「状況」や「文脈」などと言いますが、この `context` の中にどこでどういう風に作成されたかの情報を含んでいます。

`build` 文で `MaterialApp` が返されています。
一番最初の行にimport文で指定した `material.dart` が早速使われています。GoogleのUIっぽいデザインを返すようにしています。

引数にタイトル、全体の色などのテーマ情報、初期表示のホームとなるクラスを指定します。
ここでは `MyHomePage` が指定されてます。

実際の MyHomePage の中を見てみます。

#### MyHomePage

```dart:main.dart
class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}
```

`StatefulWidget` を継承しています。先ほど説明した Stateless とは違って、Stateful なウィジェット、つまり見た目の変化や持っている値の変化などが起きるウィジェットです。

コンストラクタ に `key` と `title` が渡されています。
`MyApp` の stateless ウィジェットで `title` を渡していますが、`key` はデフォルトでユニークなものが渡されます。

そのあとに `: super(key: key)` と書かれています。
[Redirecting constructors](https://dart.dev/codelabs/dart-cheatsheet#redirecting-constructors) と呼ばれる手法で、別のコンストラクタの処理を追加で行なっています。親クラスにもキーを渡しているんですね。

そして、`createState()` という関数でステートを作成しています。
Statefulウィジェットなので、ステート部分の作成は必須です。

ここでは  `_MyHomePageState` というステートを作成しています。

では実際にステートの中身を見てみます。

#### _MyHomePageState

`_MyHomePageState` クラスは `MyHomePage` の型を持ったステートを継承しています。
「これは`MyHomePage`内のある一つの状態を管理するクラスですよ」と宣言していると思ってください。

では何の状態を管理しているのかを説明していきます。


```dart:main.dart
class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }
  //〜〜〜〜　以下、あとで説明　〜〜〜〜
}
```

まず、変数定義の部分です。
`_counter` という `int` 型の変数と、文字列型を格納する配列の `_data` があります。
 
次に `_incrementCounter()` 関数があります。
`void`型なので値は返しません。`setState`という関数があって、その中で `_counter` をインクリメントしています。

この `setState` で画面の再描画を行います。
 `_counter` の値が変更されたものを再描画したいので、 `setState` の中に`_counter` を入れています。

では、`_incrementCounter()` 関数はどこで呼ばれるのでしょうか？
ボタンを押した時に呼びたいので、これからデザイン部分を作っていきます。

#### _MyHomePageState の build 部分

```dart:main.dart
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      //個々の内容はあとで説明
      appBar: xxxxxxxx,
      body: xxxxxxxx,
      floatingActionButton: xxxxxxxx,
    );
  }
```

ウィジェットのビルドを行っています。
この中でボタンの配置や変化する数字など、内容を作っていきます。

`build` メソッドは`StatelessWidget`の中にもありました。
今回の`build`メソッドは、`StatefulWidget`の中に定義するのではなく、`State`の中に定義します。
（Stateの中で定義する理由は色んな記事があるので、今は割愛します。）

`return` で `Scaffold` を返しています。
`Scaffold` は「足場」という意味で、デザインする際に便利な機能が沢山入ったものだと思ってください。
その中で `appBar`、`body`、`floatingActionButton` という要素が入っています。
それぞれ順番に説明していきます。

#### appBar 部分
```dart:main.dart
      appBar: AppBar(
        title: Text(widget.title),
      ),
```

appBar はアプリケーション上部に表示するバー部分です。属性である `title` の内容を表示します。
今回は文字列要素の `Text` があり、`widget.title` が指定されています。

この `widget.title` は `StatefulWidget` である `MyHomePage` に渡された `title` を持ってきています。
`State`の中にいても、それを呼び出したウィジェットの情報をこうやって取得することができます。

#### body 部分

```dart:main.dart
      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.display1,
            ),
            FloatingActionButton(
              onPressed: _incrementCounter,
              tooltip: 'Increment',
              child: Icon(Icons.add),
            )
          ],
        ),
      ),
```
`body` について説明します。内容の中心となるものを配置していきます。
`Center` で中央に配置する要素を作っています。コンテンツを中央寄せにしたいんですね。
その中に `child` という属性があります。`Center`はただの入れ物の要素なので、子要素が必要な要素は、`child` や `children` が必須です。
今回の場合でいうと、`Column`が子要素で、それを中央寄せにしています。

`Column`はリストや表のような枠組みを作っていくのに便利な要素です。
`mainAxisAlignment` がありますが、属性を変更することで、縦に子要素を並べていったり、横に並べていったりを設定できます。
これらはHTMLのタグのようなもので、種類が沢山あるので、その都度調べて取り込んでいく必要があります。

`Column`も子要素が必要です。
並べる子要素は複数なので、`child` ではなく、複数形の `children` になります。
プログラムでは `Text` で文字を設定しています。

#### style について

```dart:main.dart
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
```
`Text` には `style` が設定されています。文字の大きさや色を設定する箇所です。
しかしここでは、「blue」とか「red」とか「12px」のような表現がありません。

`Theme.of(context)` で親のテーマも持ってきています。`context` は「文脈」なので、親のテーマ情報も持っています。それの「普通のテキスト」に関するテーマである `textTheme` の `display1` タイプを取得しています。
もちろん `display2` も `display3` もあり、数字が上がる毎に文字が大きくなっていきます。


#### floatingActionButton 部分

```dart:main.dart
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
```

最後に`floatingActionButton` があります。画面の一番下に浮動しているボタンなので、body の外に定義します。`onPressed` にボタンが押された時の動作を記述しますが、ここでずっと説明を放置されてきた`_incrementCounter()` を設定します。

最後に`tooltip` や アイコン を設定して終了です。