【Flutterレイアウト】BoxConstraintsを理解する

Flutterレイアウトシリーズのその他の記事
- [Stackの使い方](https://qiita.com/ling350181/items/f5719272a516889bd3bb)
- [FlutterレイアウトのOverlayの使用](https://qiita.com/ling350181/items/cb3623b04a3328f2b125)
- [Flutterのレイアウトを理解する](https://qiita.com/ling350181/items/bfb4b715b56f17f75bb6)
- [FlutterレイアウトのExpanded、Flexible、Spacer](https://qiita.com/ling350181/items/40d723da418ebe64d8d5)
- [FlutterレイアウトのFlex、Row、Column、Wrap、Stackについて](https://qiita.com/ling350181/items/47c8029e53af392f0e5f)

この記事はFlutterのBoxConstraintsを理解するため書いたものです。

※本記事は下記のZenn本にまとめました。

https://zenn.dev/cxj/books/07def04506182d

## BoxConstraintsとは
Flutter開発の中、多少遭遇したことあると思いますが、画面が設定したサイズ通りに描画してくれません。( 例１)それはBoxConstraintsのせいだから、BoxConstraintsに文言を言いたい一方で、そのおかげで、Flutter開発の中多くのものはサイズを定義しなくて、簡単に画面描画できました。

BoxConstraintsはFlutterのサイズ制約ロジックです。親から子へ渡される、幅の最大値・最小値と高さの最大値・最小値のことです。 あるWidgetは親に決められた最小値と最大値の間で、自身のSizeを決めることになります。

例１：幅100,高さ30の青色のボックスを作りたいですが、画面全体が青色になってしまった！！！
```dart
return SizedBox(
      width: 100,
      height: 30,
      child: Container(
        color: Colors.blue,
      ),
    );
```
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/01266fd1-eaa4-69e4-bfea-40f24767947e.png" width="160">

## BoxConstraintsのtightコンストラクター
上記の現象起こる理由はBoxConstraintsのコンストラクターです。幅と高さの最小値・最大値はsizeのwidthとheightです。要は使えるサイズは１つしかないです。
```dart
BoxConstraints.tight(Size size)
  : minWidth = size.width,
    maxWidth = size.width,
    minHeight = size.height,
    maxHeight = size.height;
```
例１のUIをDevToolsのUI　Tree（下図）で確認しましょう。サイズが幅375、高さ667になっていることが分かりました。それは端末のサイズです。
親から継承した制約で、子はSizedBoxのみで制約突破は不可です。ですので、サイズを変更することは不可能です。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/93e1abf8-d83b-d0f7-ad18-a0d6ab872167.png" width="160">

## BoxConstraintsの制約突破
1. UnconstrainedBox
下記のコードのように、上述のUIコードにUnconstrainedBoxを追加することで、BoxConstraintsの制約突破することできました。UIはコードの下部で貼り付けます。
```dart
return UnconstrainedBox(
      child: SizedBox(
        width: 100,
        height: 30,
        child: Container(
          color: Colors.blue,
        ),
      ),
    );
```
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/b4d2b8b5-a2d5-553a-d9ad-031cd4e7c300.png"width="160">
では、DevToolsのUIツリーでサイズを確認しましょう。SizedBoxは親のUnconstrainedから制約を継承し、Unconstrainedになっており、自由定義することができました。Boxconstraintsのサイズが定義して通りになっていることが分かりました。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/3ef3698c-75f2-b19a-e5ca-a575b5fc7fb9.png"width="160"><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/17caf739-bbad-f4c0-2e40-d560c780be09.png"width="160">
2. Align、Flex、Column、Row、Wrap、Stack
AlignなどのWigetで制約を突破することもできます。
```dart
return Align(
      child: SizedBox(
        width: 100,
        height: 30,
        child: Container(
          color: Colors.blue,
        ),
      ),
    );
```

3. 制約のカスタマイズ
SingleChildLayoutDelegateを継承し、CustomSingleChildLayoutを使うことで、BoxConstraintsのカスタマイズもできます。

```
...
return CustomSingleChildLayout(
      delegate: CustomizeLayoutDelegate(),
      child: Container(
        color: Colors.blue,
      ),
    );
...

class CustomizeLayoutDelegate extends SingleChildLayoutDelegate {
  
  @override
  bool shouldRelayout(covariant SingleChildLayoutDelegate oldDelegate) => false;

  @override
  BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
    return BoxConstraints.tight(const Size(100, 100));
  }
}
```
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/51055be1-a938-ef4f-857f-6dc6304c4e6e.png"width="160">

## まとめ
BoxConstraints.tigh()の制約を突破する方法は３つあります。
1. UnconstrainedBoxによる制約解除
1. Align、Flex、Column、Row、Wrap、StackなどWidgetによる制約解除
1. CustomSingleChildLayoutによる制約解除

## 補足
ScaffoldはWidgetの集合体であり、ScaffoldはBoxConstraints制約を突破できる理由はCustomMultiChildLayoutを使って突破しているからです。