Flutter備忘録

# Flutterの書き方で注意すべきことをベースにメモ【随時更新】
- ※登場するソース・イメージは一部[公式](https://flutter.dev/)より拝借してます
- ドキュメントが充実しているので公式見る方がいいと思います

>日本語で見たいんだ

という人は是非見てください。



# StatefulWidgetの構成
  - 2つのクラスから成る
    - Widgetクラス：Stateを作成して返す
    - Stateクラス：状態を保持し、Widgetを描画（build）する


# `Widgetクラス：Stateを作成して返す`
### StatefulWidgetで必ず行うこと
- `createdState`メソッドを定義して`State`を作成し、返す

```dart


class FavoriteWidget extends StatefulWidget {
  @override
  _FavoriteWidgetState createState() => _FavoriteWidgetState();
}
```

# `Stateクラス：状態を保持し、Widgetを描画（build）する`
- extendsには`State<上記で作ったWidgetクラスを入れる>`

- `build`メソッド内にWidgetの内容を記述する

```dart


class _FavoriteWidgetState extends State<FavoriteWidget> {
  // ···
  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisSize: MainAxisSize.min,
      children: [
        Container(
          padding: EdgeInsets.all(0),
          child: IconButton(
            icon: (_isFavorited ? Icon(Icons.star) : Icon(Icons.star_border)),
            color: Colors.red[500],
            onPressed: _toggleFavorite,
          ),
        ),
        SizedBox(
          width: 18,
          child: Container(
            child: Text('$_favoriteCount'),
          ),
        ),
      ],
    );
  }
}
```

### 状態管理している値を変更したいとき
- 必ず`setState`メソッドを使う（これを怠ると<font color="Salmon">flutterが変更を検知してくれない</font>）

```dart


class _FavoriteWidgetState extends State<FavoriteWidget> {
  bool _isFavorited = true;
  int _favoriteCount = 41;
  // ···

  void _toggleFavorite() {
  setState(() {
    if (_isFavorited) {
      _favoriteCount -= 1;
      _isFavorited = false;
    } else {
      _favoriteCount += 1;
      _isFavorited = true;
    }
  });
}
}
```

# row・columnを使ったレイアウト
- その名の通り行・列を使用しレイアウトを構成していく
- childにアイコン・画像を入れ込むことも可能

<img width="986" alt="スクリーンショット 2020-05-05 12.40.13.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303093/a912a095-9ecd-fcab-94ae-00ec0097cd34.jpeg">

### rowのchildrenの挙動はこんな感じ（横に連なる）

```dart

Row(
  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  children: [
    Image.asset('images/pic1.jpg'),
    Image.asset('images/pic2.jpg'),
    Image.asset('images/pic3.jpg'),
  ],
);
```

<img width="302" alt="スクリーンショット 2020-05-05 13.00.08.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303093/8d9b4f5e-8f06-f71c-8655-57bacf576d56.jpeg">

### columnのchildrenの挙動はこんな感じ（縦に連なる）

```dart

Column(
  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  children: [
    Image.asset('images/pic1.jpg'),
    Image.asset('images/pic2.jpg'),
    Image.asset('images/pic3.jpg'),
  ],
);
```

<img width="133" alt="スクリーンショット 2020-05-05 13.02.01.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303093/03fcadc3-73de-34d3-411c-47e518df7939.jpeg">


# サイジング
- レイアウトが大きすぎてデバイスに収まらない場合は、影響を受けるエッジに沿って黄色と黒の縞模様が表示されます。

<img width="434" alt="スクリーンショット 2020-05-05 13.11.40.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303093/8017a53f-39f1-a50d-8dcc-196cd16bfa94.jpeg">

- `Expanded`Widgetを使用することでこの問題を回避できるようです

```dart


Row(
  crossAxisAlignment: CrossAxisAlignment.center,
  children: [
    Expanded(
      child: Image.asset('images/pic1.jpg'),
    ),
    Expanded(
      child: Image.asset('images/pic2.jpg'),
    ),
    Expanded(
      child: Image.asset('images/pic3.jpg'),
    ),
  ],
);
```

- 補正後の画像

<img width="303" alt="スクリーンショット 2020-05-05 13.17.16.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303093/e8d060f0-0e6f-08bd-dae6-f9ef2b4a6230.jpeg">

### 特定の要素のサイズを変更する
- これを実現する方法は簡単でExpandedWidgetの`flex`プロパティを使用することで可能になります。
- 真ん中の写真だけ２倍の大きさにする例

```dart


Row(
  crossAxisAlignment: CrossAxisAlignment.center,
  children: [
    Expanded(
      child: Image.asset('images/pic1.jpg'),
    ),
    // 真ん中だけ２倍にする
    Expanded(
      flex: 2,
      child: Image.asset('images/pic2.jpg'),
    ),
    Expanded(
      child: Image.asset('images/pic3.jpg'),
    ),
  ],
);
```

### SafeArea
- 端末に応じたレイアウトをいい感じにやってくれる。使い方は`SafeArea`でラップするだけ

```dart

body: SafeArea(
        child: ListView.builder(itemBuilder: (BuildContext context, int index) {
          return Text(
            "This text is safe.",
            style: TextStyle(
              fontSize: 30,
            ),
          );
        }),
      ),
```

# Widget間のデータの受け渡し
- 上位ツリーから下位ツリーへコンストラクタとしてデータを渡すことができる（Javaとかと似ているので割愛）

# 上位・下位ツリーの直接参照
- GlobalKeyを用いると下位に存在する`StatefulWidget`のStateへアクセスが可能になる
### 実装手順

- 1・GlobalKeyのインスタンス化とkeyプロパティの追加
  - 1-1・下位ツリーを識別するためのGlobalKeyをインスタンス化
  - 1-2・下位ツリーインスタンスを生成する際（`buildメソッド`）のkeyプロパティに追加する
- 2・GlobalKeyを生成した上位ツリーで`globalKey.currentState.widget`で参照する


```dart:1-1〜2
class _MyWidgetState extends State<MyWidget> {
  ImportantData importantData = ImportantData();
  // 1-1 ※型は参照する下位ツリーのStateクラスを指定する
  GlobalKey<AnotherWidgetState> anotherWidgetStateGlobalKey = GlobalKey();

  _doImportantThings() {
    setState(() {
      importantData.increment();
    });
  }

  @override
  Widget build(BuildContext context) {
    debugPrint("MyWidget is built");
    return Inherited(
        importantData: importantData,
        child: Scaffold(
          appBar: AppBar(title: Text(widget.title)),
          body: Center(
            child: Column(
              children: <Widget>[
                Text("MyWidget"),
                // 2・宣言したGlobalKey.currentState.widget.参照したい値で下位ツリーの値を取得できる
                Text("Another Widget 直接参照： 
                ${anotherWidgetStateGlobalKey.currentState?.widget?.importantData?.count ?? "empty"}"),
               // 1-2・AnotherWidgetを識別するkeyプロパティに1-1で宣言した変数を追加する
                AnotherWidget(
                    key: anotherWidgetStateGlobalKey,
                    importantData: importantData),
                const NoRefToImportantDataWidget()
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: _doImportantThings,
            tooltip: 'Increment',
            child: Icon(Icons.add),
          ),
          backgroundColor: Colors.green,
        ));
  }
}
```

# 上位ツリーへの参照
- BuildContextの`ancestorWidgetOfExactType`を利用する
- `myWidget.state.参照したい値`で取得可能

```dart
// コレを参照したいとする
import 'package:inherited_widget_sample/my_widget.dart';


class AnotherWidgetState extends State<AnotherWidget> {
  ImportantData get _importantData => widget.importantData;

  @override
  Widget build(BuildContext context) {
    debugPrint("AnotherWidget is built");
    final MyWidget myWidget = context.ancestorWidgetOfExactType(MyWidget);
    return Container(
        height: 400,
        decoration: BoxDecoration(color: Colors.cyan),
        child: Column(children: <Widget>[
          Text("AnotherWidget"),
          Text(
              "Parent Direct Reference ${myWidget.state?.importantData?.count ?? "empty"}"),
          YetAnotherWidget(importantData: _importantData)
        ]));
  }
}
```