【Flutterレイアウト】Flex、Row、Column、Wrap、Stackについて

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のBoxConstraintsを理解する](https://qiita.com/ling350181/items/9e927826f1164c9205b1)

この記事はFlutterの複数子ウェジェットを持つレイアウトウェジェットを理解するため書いたものです。

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

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

# Flex
RowとColumnはFlexを継承していて、directionはAxis.horizontalとAxis.verticalのウェジェットである、すなわち、RowとColumnはFlexのdirection属性を固定したもの。
## コンストラクター
```dart
  Flex({
    Key? key,
    required this.direction,
    this.mainAxisAlignment = MainAxisAlignment.start,
    this.mainAxisSize = MainAxisSize.max,
    this.crossAxisAlignment = CrossAxisAlignment.center,
    this.textDirection,
    this.verticalDirection = VerticalDirection.down,
    this.textBaseline, // NO DEFAULT: we don't know what the text's baseline should be
    this.clipBehavior = Clip.none,
    List<Widget> children = const <Widget>[],
  }) : assert(direction != null),
       assert(mainAxisAlignment != null),
       assert(mainAxisSize != null),
       assert(crossAxisAlignment != null),
       assert(verticalDirection != null),
       assert(crossAxisAlignment != CrossAxisAlignment.baseline || textBaseline != null, 'textBaseline is required if you specify the crossAxisAlignment with CrossAxisAlignment.baseline'),
       assert(clipBehavior != null),
       super(key: key, children: children);
```

## 属性
| 属性 | 説明 |
| -- | -- |
|direction|主軸の方向 |
|mainAxisAlignment| メイン軸/主軸に対する整列|
|mainAxisSize|主軸のサイズ|
|crossAxisAlignment|クロス軸/交差軸の整列|
|textDirection|文字列の方向|
|verticalDirection|垂直方向|
|textBaseline|文字列のベースライン|

## direction
- Axis.horizontal
子ウェジェットたちの並び方向を水平にする。Axis.horizontalに定義した時はRowと同じものと見られる。
- Axis.vertical
子ウェジェットたちの並び方向を垂直にする。Axis.verticalに定義した時はColumnと同じものと見られる。

## mainAxisAlignment
メイン軸/主軸に対する整列、Rowの場合は横方向の整列、Columは縦方向整列
```dart
/// How the children should be placed along the main axis in a flex layout.
///
/// See also:
///
///  * [Column], [Row], and [Flex], the flex widgets.
///  * [RenderFlex], the flex render object.
enum MainAxisAlignment {
```
- MainAxisAlignment.start
始点から整列
- MainAxisAlignment.end
末尾から整列
- MainAxisAlignment.center
センターから整列
- MainAxisAlignment.spaceBetween
両端を揃える。つまり、最初の子が左側にあり、最後の子が右側にあり、残りの子が中央に均等に分散される。
- MainAxisAlignment.spaceAround
各子の左右の間隔を等しくする、つまりmarginは等しい。
- MainAxisAlignment.spaceEvenly
各子は均等に分散される。つまり、幅は同じ。

Rowを例として確認します。
|MainAxisAlignment.start|MainAxisAlignment.end|MainAxisAlignment.center|
| -- | -- | -- |
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/51720b67-c5bb-6f8b-760b-9603a5fcb308.png"width=180>|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/cd1d6890-fb50-1aec-9103-25ee0698594e.png"width=180>|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/9420dc1e-8881-7a6b-5c16-a70212bd1f86.png"width=180>|
|MainAxisAlignment.spaceBetween|MainAxisAlignment.spaceAround|MainAxisAlignment.spaceEvenly|
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/802d2b0e-db51-e883-faba-0081b0336377.png"width=180>|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/88856a0c-157b-a9fe-5557-845651a65d01.png"width=180>|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/66094759-5da7-19cd-39bf-efcd86bd852c.png"width=180>|

## mainAxisSize
主軸のサイズ、デフォルトはMainAxisSize.max
制約条件による機能する、親の制約は緩い場合のみ機能する。
下記のソースコードで区別を確認しよう、Alignで制約を緩める(詳細は前の[記事](https://qiita.com/ling350181/items/9e927826f1164c9205b1)を確認しよう)
MainAxisSize.maxは制約サイズ最大に伸ばせる
```dart
return Scaffold(
      body: SafeArea(
        child: Container(
          color: Colors.lightGreen,
          child: Align(
            child: Container(
              color: Colors.white,
              child: Row(
                mainAxisSize: MainAxisSize.max,
                children: [
                  SizedBox(
                    width: 100,
                    height: 100,
                    child: Container(
                      color: Colors.yellow,
                    ),
                  ),
                  SizedBox(
                    width: 100,
                    height: 200,
                    child: Container(
                      color: Colors.green,
                    ),
                  ),
                  SizedBox(
                    width: 100,
                    height: 100,
                    child: Container(
                      color: Colors.red,
                    ),
                  )
                ],
              ),
            ),
          ),
        ),
      ),
    );
```
|MainAxisSize.max|MainAxisSize.min|
| -- | -- |
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/4a5adce7-32dc-edc2-3cd8-dbce6cb0a5da.png"width="180">|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/bfe639b8-96a5-9d49-eee3-dd4f025ca7a5.png"width="180">|

## verticalDirection
子ウェジェットの整列順序、デフォルトはdown
down：topからbottom
up：bottomからtop
## crossAxisAlignment
クロス軸/交差軸の整列,Rowの場合は縦方向の整列、Columは横方向の整列
```dart
/// How the children should be placed along the cross axis in a flex layout.
///
/// See also:
///
///  * [Column], [Row], and [Flex], the flex widgets.
///  * [RenderFlex], the flex render object.
enum CrossAxisAlignment {
  /// Place the children with their start edge aligned with the start side of
  /// the cross axis.
```
- CrossAxisAlignment.start
Rowは縦方向の上から整列
- CrossAxisAlignment.end
Rowは縦方向の下から整列
- CrossAxisAlignment.center
Rowは縦方向の中心より、整列する 
- CrossAxisAlignment.stretch
子を拡大し、親のレイアウトを埋める
- CrossAxisAlignment.baseline
ベースラインを基準に整列する

※クロス軸のcrossAxisAlignment.startとendはverticalDirectionの属性により、影響を与える。verticalDirectionデフォルトはVerticalDirection.downだが、upの時、crossAxisAlignment.startとendの効果は逆になる。
ベースラインを使う際、textBaselineの属性を定義しないといけない

```dart
return Scaffold(
      body: SafeArea(
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            SizedBox(
              width: 100,
              height: 100,
              child: Container(
                color: Colors.yellow,
              ),
            ),
            SizedBox(
              width: 100,
              height: 200,
              child: Container(
                color: Colors.green,
              ),
            ),
            SizedBox(
              width: 100,
              height: 100,
              child: Container(
                color: Colors.red,
              ),
            )
          ],
        ),
      ),
    );
```
|CrossAxisAlignment.start|CrossAxisAlignment.end|CrossAxisAlignment.center|
| -- | -- | -- |
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/81b5352c-b16e-aadd-bbbe-cff7b5b26b8f.png"width="180">|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/891a407d-2085-e986-42db-06d1f5671f64.png"width="180">|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/684ca20f-8045-b800-3ccc-c0137846e44c.png"width="180">|
|CrossAxisAlignment.stretch|
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/35ab08ac-4e05-925b-cb63-4e94d3b4c32b.png"width="180">|

# Wrap
## コンストラクター
```dart
class Wrap extends MultiChildRenderObjectWidget {
  /// Creates a wrap layout.
  ///
  /// By default, the wrap layout is horizontal and both the children and the
  /// runs are aligned to the start.
  ///
  /// The [textDirection] argument defaults to the ambient [Directionality], if
  /// any. If there is no ambient directionality, and a text direction is going
  /// to be necessary to decide which direction to lay the children in or to
  /// disambiguate `start` or `end` values for the main or cross axis
  /// directions, the [textDirection] must not be null.
  Wrap({
    Key? key,
    this.direction = Axis.horizontal,
    this.alignment = WrapAlignment.start,
    this.spacing = 0.0,
    this.runAlignment = WrapAlignment.start,
    this.runSpacing = 0.0,
    this.crossAxisAlignment = WrapCrossAlignment.start,
    this.textDirection,
    this.verticalDirection = VerticalDirection.down,
    this.clipBehavior = Clip.none,
    List<Widget> children = const <Widget>[],
  }) : assert(clipBehavior != null), super(key: key, children: children);
```

## 属性
| 属性 | 説明 |
| -- | -- |
|direction|主軸の方向|
|alignment|新しく一行できたとき、主軸に対する整列|
|spacing|主軸の方向の子ウェジェットの間隔|
|runAlignment|runの整列。行ごとのクロス軸の整列方式|
|runSpacing|runの間隔|
|crossAxisAlignment|行の子ウェジェットのクロス軸の整列方向|
|textDirection| Flexと同じく、文字列の整列方向|
|verticalDirection|垂直方向の整列順序|
|clipBehavior|端を超えてエッジをトリミングする方法|

## direction
WrapのdirectionデフォルトAxis.horizontal(水平方向)
|Axis.horizontal|Axis.vertical|
| -- | -- |
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/1f44697b-5d73-ac34-755b-c90147bee143.png"width="180">|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/9342ff5e-0afd-a5ec-d620-15b4492903ba.png"width="180">|

## alignment
新しく一行できたとき、主軸に対する整列
- WrapAlignment.start
始点から整列
- WrapAlignment.end
末尾から整列
- WrapAlignment.center
センターから整列
- WrapAlignment.spaceBetween
両端を揃える。つまり、最初の子が左側にあり、最後の子が右側にあり、残りの子が中央に均等に分散される。
- WrapAlignment.spaceAround
各子の左右の間隔を等しくする、つまりmarginは等しい。
- WrapAlignment.spaceEvenly
各子は均等に分散される。つまり、幅は同じ。

|WrapAlignment.start|WrapAlignment.end|WrapAlignment.center|
| -- | -- | -- |
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/913420e8-470b-a9e7-b456-52937570046f.png"width="180">|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/a0c73c9c-cf4f-f7bf-9143-d300d86fe6ab.png"width="180">|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/f64f3645-0618-2ac3-14ea-46d7b6e156f0.png"width="180">|
|WrapAlignment.spaceBetween|WrapAlignment.spaceAround|WrapAlignment.spaceEvenly|
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/9541fabc-a291-4394-27ec-8987840d3a13.png"width="180">|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/90fcff16-4332-1f6b-be38-01bdd3c7bc6b.png"width="180">|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/5752e87a-39eb-3972-b874-1b5267e59c6d.png"width="180">|

## runAlignment
runの整列。行ごとのクロス軸の整列方式。alignmentと同じ列挙型のWrapAlignmentを使われている。
|WrapAlignment.start|WrapAlignment.end|WrapAlignment.center|
| -- | -- | -- |
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/6cd2b3a9-5e55-35b6-1f3f-483acde72290.png"width="180">|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/d12bb1ed-a0a9-2ba2-a317-91c153244293.png"width="180">|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/1c28e5ff-cc2e-34f2-0f06-1116b58bf5c6.png"width="180">|
|WrapAlignment.spaceBetween|WrapAlignment.spaceAround|WrapAlignment.spaceEvenly|
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/eea3d67f-1461-4ea7-feda-9d1e22274fb8.png"width="180">|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/0719fa51-605b-3f31-997a-ff636d6e6c6f.png"width="180">|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/f36eb8a3-4df1-0580-3211-a35942127fb6.png"width="180">|

## crossAxisAlignment
各行の子ウェジェットのクロス軸整列方式
```dart
enum WrapCrossAlignment {
  start,
  end,
  center,
}
```
|WrapCrossAlignment.start|WrapCrossAlignment.center|WrapCrossAlignment.end|
| -- | -- | -- |
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/63d0b949-71e4-730d-0051-daeb49fc33dc.png"width=180>|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/af0d4fb7-4768-0d6f-87e0-26a90b52a3a3.png"width="180">|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/33ae9f68-084d-c474-2676-183e6a217b46.png"width="180">|

## textDirection
文字列の整列方向
|TextDirection.ltr|TextDirection.rtl|
| -- | -- |
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/5a065306-2242-7228-6453-9484ef7490f7.png"width="180">|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/b183b311-0770-4d2b-c207-bb049ed5d332.png"width="180">|
## verticalDirection
垂直方向の整列順序
|VerticalDirection.down|VerticalDirection.up|
| -- | -- |
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/5a065306-2242-7228-6453-9484ef7490f7.png"width="180">|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/f22a7191-616d-811b-40bb-541dd75fd067.png"width="180">|

## 間隔spacingとrunSpacing
spacingは主軸の各子ウェジェットの間隔を定義する、runSpacingは各行の間隔を定義する。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/47d77e78-8579-8a58-31f4-1c93f5fb7c1b.png"width="180">
# Stack
Stackについて、前の記事で説明したため、ここの[リンク](https://qiita.com/ling350181/items/f5719272a516889bd3bb)をどうぞ！！！