Flutterの公式ドキュメントを読んで理解する 〜Building layouts/Dealing with box constraints〜

# 今回読んだページ
https://flutter.dev/docs/development/ui/layout/box-constraints

# 個人的なまとめ
* 「制約」は最小・最大の幅と高さで構成され、「サイズ」は特定の幅と高さで構成されている。
* 制約の処理方法に関しては3種類のボックスがある
    * 可能な限り大きくなろうとするもの。例えば、`Center`や`ListView`によって使われるボックス。
    * 自身のchildrenと同じ大きさになろうとするもの。例えば、`Transform`や`Opacity`によって使われるボックス。
    * ある特定のサイズになろうとするもの。例えば、`Image`や`Text`によって使われるボックス。
* `Center`のように、制約を緩和させる(最大値はそのままで、最小値を0にする)widgetがある
* Unbouded box(無制限のボックス)を使う際は注意が必要

# Dealing with box constraints
>Note: フレームワークがBox Constraintに関係する問題を検出したとき、このページが表示されることがあります。

>Note: Flutterでの制約、サイズ変更、配置の仕組みについて混乱している場合は、[Understanding constraints](https://flutter.dev/docs/development/ui/layout/constraints)を参照してください。

Flutterでは、Widgetsはそれらの基礎となる`RenderBox`オブジェクトによってレンダリングされます。
Render boxesはそれらの親から制約を与えられ、その制約の中で自身のサイズを調整します。
制約は最小・最大の幅と高さで構成され、サイズは特定の幅と高さで構成されています。

一般的には、制約の処理方法に関して、3種類のボックスがあります：

* 可能な限り大きくなろうとするもの。例えば、`Center`や`ListView`によって使われるボックス。
* 自身のchildrenと同じ大きさになろうとするもの。例えば、`Transform`や`Opacity`によって使われるボックス。
* ある特定のサイズになろうとするもの。例えば、`Image`や`Text`によって使われるボックス。

例えば、`Container`のようないくつかのWidgetsは、コンストラクタの引数に基づいてタイプが異なります。
`Container`の場合、デフォルトではできるだけ大きくなろうとしますが、例えば`width`を与えた場合、それを優先して特定のサイズになろうとします。

例えば、`Row`や`Column`(flex boxes)といった他のwidgetは、Flexセクションで後述するように、受け取った制約に基づいて変化します。

制約は「厳しく」なる場合があります。つまり、レンダーボックスがサイズを決める余地がなくなります(例えば、最小と最大の幅が同じ場合、厳しい幅を持つと言います)。
この例は、`RenderView`classに含まれている`App`widgetです。
アプリケーションの`build`関数によって返されたchildが使用するボックスには、アプリケーションのコンテンツ領域を正確に埋めるように(たいていはスクリーン全体)強制する制約が与えられます。
Flutterのボックスの多く、特に1つのchildしか持たないボックスは、自身の制約をchildrenに渡します。
つまり、アプリケーションのレンダーツリーのRootで、たくさんのボックスを入れ子にした場合、これらは厳しい制約によって強制され、互いにフィットします。

一部のボックスは制約を緩和します。つまり、最大値は維持されますが、最小値は削除されます。
例えば、`Center`です。

#### 補足
`MaterialApp`の`home`に直接`Container`を使って`width, height`を指定しても、適用されない。

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

void main() {
  runApp(MaterialApp(
      home: Container(
        width: 50,
        height: 50,
        color: Colors.cyan  
      ),
    )
  );
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526994/84907e47-7228-eacb-2e7c-11ee7a82865f.png)

`Center`などでラップすると、制約が緩められ、`Container`に指定した制約が適用されるようになる。

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

void main() {
  runApp(MaterialApp(
      home: Center(
        child: Container(
          width: 50,
          height: 50,
          color: Colors.cyan  
        ),
      )
    )
  );
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526994/85cc25ff-3bcd-abb1-d87e-8c73369ddd10.png)

## Unbounded constraints
ある状況では、ボックスに与えられた制約は、「無制限」あるいは無限になります。
つまり、最大の幅または最大の高さに`double.INFINITY`がセットされるということです。

無制限の制約が指定されている場合、できるだけ大きくなろうとするボックスは有効に機能せず、
デバッグモードでは、そのような組み合わせはそのファイルを指す例外を投げます。

レンダーボックスが無制限の制約を持っているとわかる最も一般的なケースは、flex boxes(`Row`や`Column`)の中や、**スクロール可能な領域**(`ListView`や他の`ScrollView`subclasses)内にあります。

特に、`ListView`は、利用可能なスペースを埋めようと、そのcross-directionに広がろうとします。(例えば、垂直方向のスクロールブロックの場合、親と同じ幅になろうとします。)
垂直スクロールの`ListView`を水平スクロールの`ListView`にネストする場合、中のものはできるだけ広くなり、外のものはその方向にスクロールできるため、無限に広くなります。

## Flex
Flex boxes(`Row`と`Column`)自体の動作は、それらが指定された方向に境界のある制約なのか無制限の制約なのかによって異なります。

境界がある制約の場合、その方向にできるだけ大きくなろうとします。

境界がない制約の場合、childrenをその方向にフィットさせようとします。
この場合childrenの`flex`を0(デフォルト値)以外に設定することはできません。
Widget Libraryでは、これは、Flex boxが他のFlex boxまたはスクロール可能なオブジェクト内にある場合、`Expanded`を使用できないことを意味します。
そうすると、このドキュメント示す例外メッセージが表示されます。

cross-direction、例えば、`Column`(垂直のflex)の幅や`Row`(水平のflex)の高さでは、決して境界なしにしてはいけません。
そうでなければ、それらはchildrenを合理的に整列させることができません。