MediaQuery.sizeOf(context)ではなくFractionallySizedBoxを使ってみる


普段Flutterでレスポンシブな対応をする場合、`MediaQuery`を用いて以下のように書いておりました。

```Dart
Widget build(BuildContext context) {
  final Size size = MediaQuery.sizeOf(context);
  return Scaffold(
    appBar: AppBar(
      title: const Text('MediaQuery Demo'),
    ),
    body: SizedBox(
      width: size.width * 0.8,
      height: size.height * 0.1,
      child: Container(
        color: Colors.amber,
      ),
    ),
  );
}
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/735195/07d801db-432b-12f0-a648-0b317438c436.png" width="250" >


ただ、もう少し最適なウィジェットが公式で用意されていました。

## FractionallySizedBox

https://youtu.be/PEsY654EGZ0

https://api.flutter.dev/flutter/widgets/FractionallySizedBox-class.html

`FractionallySizedBox`ウィジェットを用いると、以下の様にいちいちメディアクエリを書かなくて良くなり、さらにこのウィジェットは少し応用を効かせることも可能です。
さらに、パラメーターにalignmentを指定できるため、簡単に簡単に中央寄せの設定も可能です。

```dart
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: const Text('MediaQuery Demo'),
    ),
    body: SizedBox.expand(
      child: FractionallySizedBox(
        alignment: Alignment.topCenter,
        widthFactor: 0.8,
        heightFactor: 0.1,
        child: Container(
          color: Colors.amber,
        ),
      ),
    ),
  );
}
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/735195/1c4403a9-8d59-7953-52e9-b119eabe3c17.png" width="250" >

## ちょっとした応用

公式などにも記載がありますが、いくつか応用ができそうでした。


### 1. 余白として使う
`FractionallySizedBox`はchildパラメーターがrequiredではないため、空の余白としても使えます。

```dart
FractionallySizedBox(
    alignment: Alignment.topCenter,
    heightFactor: 0.1, // 高さの余白のみ指定
),
```

### 2. FlexibleなどでラップしてColumnやRowで使える
`FractionallySizedBox`をそのままRowやColumnウィジェットで使ってしまうと、よくみる
`Failed assertion: line 1966 pos 12: 'hasSize'`のエラーに陥ってしまいます。しかし、`Flexible`でラップすることで簡単に使うことが可能です。

```dart
Column(
  children: [
    Flexible(
      child: FractionallySizedBox(
        alignment: Alignment.topCenter,
        widthFactor: 0.8,
        heightFactor: 0.1,
        child: Container(
          color: Colors.amber,
        ),
      ),
    ),
    // FractionallySizedBox(
    //   alignment: Alignment.topCenter,
    //   widthFactor: 0.8,
    //   heightFactor: 0.1,
    //   child: Container(
    //     color: Colors.amber,
    //   ),
    // ),
  ],
),
```

### 3. 簡易アニメーション
`AnimatedFractionallySizedBox`を使うことで、簡易的にアニメーションの実装もすることができます。
領域の伸び縮みをするアニメーションや、widthFactor, heightFactorを動的にしたい場合に有効です。

```dart
SizedBox.expand(
  child: Column(
    children: [
      Flexible(
        child: AnimatedFractionallySizedBox(
          alignment: Alignment.topCenter,
          widthFactor: _sliderValue,
          heightFactor: 0.2,
          duration: const Duration(milliseconds: 500),
          curve: Curves.easeInOut,
          child: Container(
            color: Colors.amber,
          ),
        ),
      ),
      const Flexible(
        child: FractionallySizedBox(
          heightFactor: 0.1,
        ),
      ),
      ElevatedButton(
        onPressed: () {
          setState(() {
            if (_sliderValue < 1.0) {
              _sliderValue += 0.05;
            } else {
              _sliderValue = 0.1;
            }
          });
        },
        child: const Text('Animation'),
      ),
    ],
  ),
),
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/735195/9f6dc0ac-36e9-1856-104f-622bd5da447c.gif" width="250" >

## 注意点
今回の記事で、`MediaQuery.sizeOf`より`FractionallySizedBox`を推奨しましたが、もしこれから移行する方はお気をつけください。
`FractionallySizedBox`の`heightFactor: 0.5`は、親要素の対する50%の高さですが、`MediaQuery.sizeOf(context).height * 0.5`は基本的に画面全体に対する50%となります。
この記事と1枚目の画像と２枚目の画像を比較していただければ若干差があることに気づけると思います。
そのため、一概にMediaQueryがダメというわけではなく、用途に応じて使い分けていただければと思います。


|  MediaQuery（１枚目）  |  FractionallySizedBox（２枚目）  |
|----|----|
| <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/735195/07d801db-432b-12f0-a648-0b317438c436.png" width="250" > | <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/735195/1c4403a9-8d59-7953-52e9-b119eabe3c17.png" width="250" > |