Flutterの動的ページを作る基礎的なお話

# はじめに
何かを作るのにはやっぱり動的なページだなということで勉強したことのまとめです。

# 環境
Android Studio
Flutter 1.0

# StatefulWidget
Flutterは動的なWidgetを作成するために状態を持つ`StatefulWidget`を使用することがベースとなります。（他のWidgetを使うこともあるようなので、初期の勉強としてはです）

ただし`StatefulWidget`をそのまま大元の子供としてエラーとなってしまうようです。

``` Dart
import 'package:flutter/material.dart';

void main() => runApp(App());

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MyApp();
  }

}

class MyApp extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return TestState();
  }
}
```

```
I/flutter (11861): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter (11861): The following assertion was thrown building MyApp(state: TestState#0a289):
I/flutter (11861): MediaQuery.of() called with a context that does not contain a MediaQuery.
I/flutter (11861): No MediaQuery ancestor could be found starting from the context that was passed to MediaQuery.of().
I/flutter (11861): This can happen because you do not have a WidgetsApp or MaterialApp widget (those widgets introduce
I/flutter (11861): a MediaQuery), or it can happen if the context you use comes from a widget above those widgets.
I/flutter (11861): The context used was:
I/flutter (11861):   Scaffold(dirty, state: ScaffoldState#18d75(lifecycle state: initialized, tickers: tracking 1
I/flutter (11861):   ticker))
```

正常に動作させるためにはいくつか方法があるようですが、最初は`MaterialApp`を使って覚えていくのが一番楽そうです。
`WidgetApp`もいけるようですが、`builder`の概念がまだよく分かってないので、いったん置いておきます。
ということで`MaterialAPP`を使って何かしら表示させることができたコードが以下となります。

``` Dart
class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: MyApp());
  }

}

class MyApp extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return TestState();
  }
}

class TestState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Demo"),
      ),
      body: Center(
        child: Text('Stateful Demo'),
      ),
    );
  }

}
```

# State
`StatefulWidget`を継承したクラスでは`createState`を実装し`State<T>`を返却する必要があります。
`State`自体は`Widget`を返却するため、ここで状態で変化する`Widget`を作成します。

今回は`Button`を作って押したらその日時が表示されるような変化を起こす実装をしてみます。

`State`の作成方法としては基本は`Widget`を作成するため、そこは特に違いがありません。
違いとしては状態が変更されるというトリガーがあるのと、そのトリガーを持って状態を変化させる実装する点にあります。

状態変化時の動きについては`setState`に変化時に動作するべき関数を渡すことで実現します。`setState`は引数に`void`が戻り値となるfunctionを渡す必要があるため、この内部で何かしらの副作用を与える形となります。
`setState`外部で状態を変更して渡すという訳ではないので、その点は注意が必要です。

トリガーは`Button`なので、Widget CatalogにあるBasicsにあった`RaisedButton`を使用します。
各種ボタンには押された際に動作する`onPressed`が用意されているため、ここに引数なしかつ`void`が戻り値となるfunctionを渡すことで押下時の動作を指定することができます。
今回はここに`setState`を実装したfunctionを渡すことでトリガーとなります。
`setState`と`onPressed`の引数が違うため、`onPressed`に直接実装できないため、何かしらでラッピングさせる必要がある点に注意ください。

そうして出来た`State`が以下のコードとなります。
これを実行して右下にあるボタンを押すと真ん中のテキストが現在日時で更新されていきます。

変数の文字列展開は`$変数`とすればできるので、今回は変数に日時の文字列を入れてその文字列を展開することで実現しています。

``` Dart
class TestState extends State<MyApp> {
  String _dateText = DateTime.now().toString();

  void _updateDateText() {
    setState(() {
      _dateText = DateTime.now().toString();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Demo'),
      ),
      body: Center(
        child: Text('$_dateText'),
      ),
      floatingActionButton: RaisedButton(
        onPressed: _updateDateText,
        child: Text("Button"),
      ),
    );
  }
}
```

# 動的ページを作るためのまとめ
`StatefulWidget`と`State`の組み合わせで動的ページを作成できることが分かったので、いったんここで動的なページを作るためのまとめを書きます。
ここでは上で作ったような最小限な構成とします。

Widget構成は以下画像のようになり、実装としては`MaterialApp`配下の組み立てとなることが多くなりそうです。
状態については`StatefulWidget`を間にかませて`State<T>`配下に設定したイベントを検知して変更するロジックを組むことになります。
状態変更については副作用を与えることが前提となるため(現状わかっている範囲では)、メンバ変数の変更で対応していくことになります。

![Untitled Diagram (2).png](https://qiita-image-store.s3.amazonaws.com/0/71288/c1146dc0-e414-9754-8213-67673654ab45.png)