【Flutterレイアウト】Stackの使い方について

Flutterレイアウトシリーズのその他の記事
- [FlutterレイアウトのFlex、Row、Column、Wrap、Stackについて](https://qiita.com/ling350181/items/47c8029e53af392f0e5f)
- [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のレイアウトWidgetのStackの使い方を分かりやすく理解するため書いたものです。

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

https://zenn.dev/cxj/books/07def04506182d
## Stackとは
Stack、Row、およびColumnは、Flutterで最も一般的に使用されるレイアウトコンポーネントです。RowとColumnは最も一般的なFlexレイアウトのアイデアですが、スタックは、上下左右の制約を指定できる絶対レイアウトコンポーネントとしてより理解できます。複数の子をオーバーラップさせたい場合に役立ちます。
## コンストラクター
```dart
  const Stack({
    super.key,
    this.alignment = AlignmentDirectional.topStart,
    this.textDirection,
    this.fit = StackFit.loose,
    this.clipBehavior = Clip.hardEdge,
    super.children,
  });
```
## 属性
| 属性 | 説明 |
| -- | -- |
| alignment |　アラインメント形式、制約なしの子に適用する|
| textDirection |　レイアウト方向、制約なしの子に適用する |
| fit |　制約設定していない子ウィジェットの充填方式 |
| overflow | スタックの端を超えた子の端の部分がクリップされているかどうか|
| clipBehavior |スタックの端を超えてエッジをトリミングする方法 |

- alignment
(0.0, 0.0)を中心に、９のアラインメント形式がある
    -  AlignmentDirectional topStart = AlignmentDirectional(-1.0, -1.0)
    -  AlignmentDirectional topCenter = AlignmentDirectional(0.0, -1.0)
    -  AlignmentDirectional topEnd = AlignmentDirectional(1.0, -1.0)
    -  AlignmentDirectional centerStart = AlignmentDirectional(-1.0, 0.0)
    -  AlignmentDirectional center = AlignmentDirectional(0.0, 0.0)
    -  AlignmentDirectional centerEnd = AlignmentDirectional(1.0, 0.0)
    -  AlignmentDirectional bottomStart = AlignmentDirectional(-1.0, 1.0)
    -  AlignmentDirectional bottomCenter = AlignmentDirectional(0.0, 1.0)
    -  AlignmentDirectional bottomEnd = AlignmentDirectional(1.0, 1.0)
- textDirection
    - TextDirection.rtl(右から左)
    - TextDirection.ltr（左から右）
- fit
    - StackFit.loose（ゆるい）
    - StackFit.expand（拡大）
    - StackFit.passthrough（通り過ぎる）
- overflow
    - Overflow.visible(溢れ出る子供たちが見える)
    - Overflow.clip(溢れ出る子供たちが親の境界にクリップされる)
- clipBehavior
    - Clip.none(クリップしない)
    - Clip.hardEdge（クリップするが、アンチエイリアシングは適用しない）
    - Clip.antiAlias(アンチエイリアシングでクリップする)
    - Clip.antiAliasWithSaveLayer(クリップの直後にアンチエイリアシングとsaveLayerを使用してクリップする)

## 使い方
RowとColumnと比較して、StackとPositionedコンビネーションで使用できます。上、下、左、右、および幅と高さの制約を設定して、絶対レイアウトすることができます。
### alignment
```dart
return Scaffold(
      body: Stack(
        alignment: Alignment.center,
        children: <Widget>[
          Container(
            width: 200,
            height: 200,
            color: Colors.red,
          ),
          Container(
            width: 120,
            height: 120,
            color: Colors.green,
          ),
          Container(
            width: 60,
            height: 60,
            color: Colors.blue,
          ),
        ],
      ),
    );
```
|AlignmentDirectional.topStart|AlignmentDirectional.topCenter|AlignmentDirectional.topEnd|
| -- | --| -- |
| <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/8541f77d-db72-d63c-8ba3-78ded564bb9b.png"width="180">|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/745c9ba2-fa4b-1cab-5a87-e1f68e4f5b55.png"width="180">|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/06afda13-cf19-41e7-02ed-a2f5475ac8fe.png"width="180">|
|AlignmentDirectional.centerStart|AlignmentDirectional.center|AlignmentDirectional.centerEnd|
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/4da0c2c1-6af1-694d-f5ea-784a569a3073.png"width="180">|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/ff982f57-7a81-5b0b-7cf5-27c6e1cfdd9f.png"width="180">|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/a05bffd9-271e-dc02-ad53-40aff823d85e.png"width="180">|
|AlignmentDirectional.bottomLeft|AlignmentDirectional.bottomCenter|AlignmentDirectional.bottomRight|
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/b4648b75-6a5a-35e6-2f93-8550015df57f.png"width="180">| <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/bae51e5b-f55a-2650-d210-32153a8896b7.png"width="180">|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/9ee6049b-01b7-dc9b-0f5d-40331053e988.png"width="180">|

### textDirection
|TextDirection.ltr|TextDirection.rtl|
| -- | -- |
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/9f97e8de-84b2-bd8c-3e55-27d60ee6e6a5.png"width="180">|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/466e30ec-eb9d-6bf4-1788-2cb1afcfafd1.png"width="180">|
### fit
サイズや位置制約してなウィジェットに対する充填方式の定義です。
```dart
return Scaffold(
      body: Stack(
        alignment: AlignmentDirectional.centerStart,
        fit: StackFit.loose,
        children: <Widget>[
          Container(
            width: 200,
            height: 200,
            color: Colors.red,
          ),
          Positioned(
            left: 30,
            top: 30,
            child: Container(
              width: 120,
              height: 120,
              color: Colors.yellow,
            ),
          ),
          Positioned(
            left: 50,
            top: 50,
            child: Container(
              width: 60,
              height: 60,
              color: Colors.blue,
            ),
          ),
        ],
      ),
    );
```
|StackFit.loose|StackFit.expand|
| -- | -- |
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/375ac306-e9f9-3b26-5cd5-acea7eaf4e7b.png"width="180">|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/6f8615d2-86f8-c6ad-cee5-13deba0d5305.png"width="180">|
- StackFit.passthrough
親からスタックに渡される制約は、位置やサイズ制約されていない子に変更されずに渡されます。
下記の例では、RowのExpandedの制約を子の赤色のContainerに渡されます。
```dart
return Scaffold(
      body: Row(
        children: [
          Expanded(
            child: Stack(
              alignment: AlignmentDirectional.centerStart,
              fit: StackFit.passthrough,
              children: <Widget>[
                Container(
                  width: 200,
                  height: 200,
                  color: Colors.red,
                ),
                Positioned(
                  left: 30,
                  top: 30,
                  child: Container(
                    width: 120,
                    height: 120,
                    color: Colors.yellow,
                  ),
                ),
                Positioned(
                  left: 50,
                  top: 50,
                  child: Container(
                    width: 60,
                    height: 60,
                    color: Colors.blue,
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
```
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/e6bfb5f3-bf9c-c9da-3b54-54c0c05b71dc.png"width="180">
### overflow
Flutter 2.10 以降、overflowは非推奨になり、ついに最新バージョンのdartには削除されることになりました。その変わり、clipBehaviorの使用が推奨されています。

### clipBehavior
```dart
return Scaffold(
      body: Stack(
        alignment: AlignmentDirectional.centerStart,
        clipBehavior: Clip.antiAlias,
        children: <Widget>[
          Container(
            width: 200,
            height: 200,
            color: Colors.red,
          ),
          Positioned(
            left: 30,
            top: 30,
            child: Container(
              width: 120,
              height: 120,
              color: Colors.yellow,
            ),
          ),
          Positioned(
            left: 50,
            top: 50,
            child: Container(
              width: 60,
              height: 260,
              color: Colors.blue,
            ),
          ),
        ],
      ),
    );
```
|Clip.none|Clip.hardEdge|
| -- | -- |
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/599b266a-b811-921b-3197-e903d3a46871.png"width="180">|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/bea0726a-09af-af41-0bca-655e8fc38725.png"width="180">|