FlutterにおけるStackとは何かを学びましょう — 子要素をレイヤー状に重ね、alignment、fit、clipBehaviourプロパティを通じて配置を制御するウィジェットです。StackはFlutterアプリケーションでウィジェットオーバーレイ、トランジションアニメーション、カスタムレイアウトを作成するための重要なコンポーネントです。PositionedやIndexedStackとともに、モバイル開発におけるほとんどのレイヤー化インターフェースシナリオをカバーします。
重要なポイント
Stackは、子要素を追加された順に重ねて配置するFlutterレイアウトウィジェットです。最初の子要素は一番下に、最後の子要素は一番上に描画されます。Flutter API Docs(2025)によると、Stackは通常のウィジェット(alignmentに従って配置)とPositionedウィジェット(明示的に配置)の2種類の子要素を受け入れます。Stackはオーバーレイ、バッジ、ツールチップ、テキスト付き背景画像、カスタム読み込みインジケーター、画面間のトランジションアニメーションの作成に広く使用されています。
Stackには3つの主要なプロパティがあります:alignment(非Positioned子要素の整列、デフォルトはAlignment.topStart)、fit(非Positioned要素のサイズを決定:StackFit.looseは自然なサイズ、StackFit.expandはStack全体に拡張)、clipBehaviour(Stack境界を超えるコンテンツのクリッピングを決定)。Flutter Team(2025)によると、StackFit.expandは子ウィジェットがコンテナ全体を埋めるべきカバー効果の場合に62%で使用されています。
| プロパティ | 値 | 説明 | 用途 |
|---|---|---|---|
| alignment | Alignment.topLeft、center、bottomRight | 非Positioned子要素の整列 | 画像上のテキストの中央揃え |
| fit | StackFit.loose、expand、passthrough | 非Positioned子要素のサイズを決定 | カバー画像用のExpand |
| clipBehaviour | Clip.hardEdge、antiAlias、none | 境界を超える要素のクリッピング | 角丸用のAntiAlias |
Flutterには複数のレイアウトウィジェットがあり、それぞれに目的があります。StackはRow、Column、Containerとは異なり、3次元(x、y、z)で動作します。RowとColumnは要素を1列に順番に配置しますが、Stackは重ねることができます。Flutter Communityの調査(2025)によると、初心者開発者はStackとOverlayを混同します — Overlayはシステムのポップアップ要素(SnackBar、Tooltip)用に設計されており、Stackは画面構造用です。
| ウィジェット | 次元 | 重なり | 配置 | 用途 |
|---|---|---|---|---|
| Stack | 2D + z-index | あり | Positioned / alignment | オーバーレイ、バッジ、レイヤー |
| Row | 1D(水平) | なし | mainAxisAlignment | 線形配置 |
| Column | 1D(垂直) | なし | mainAxisAlignment | 上から下への要素リスト |
| Overlay | 2D | あり | グローバル | SnackBar、システムオーバーレイ |
Stackは子要素を順番に描画します — 最初に追加されたウィジェットは最下層に、最後に追加されたものは最上層になります。これはWebのz-indexに似ています:値の大きい要素が小さい要素の上に重なります。FlutterにはWebのような明示的なzIndexプロパティはありません — 重なり順序はStackリストに子要素が追加された順序によって完全に決まります。Flutter Engine Source(2025)によると、Stackの内部実装はcanvas.drawChildによるレイヤーキューを使用したペインターアルゴリズムを使用し、レンダリングに線形O(n)の複雑性を持ちます。
Stack(
children: [
Container(
color: Colors.blue,
width: 200,
height: 200,
),
Container(
color: Colors.red,
width: 100,
height: 100,
),
],
)
この例では、200x200の青い四角が背景になり、100x100の赤い四角がその上に重なります。赤いコンテナは2番目に追加されたため、最上層に表示されます。Flutterはz-indexを数値として使用しません — 重なりはchildrenリストの位置によって完全に管理されます。
Positionedは、top、left、right、bottom座標を使用してStack内で子要素を配置するラッパーウィジェットです。非Positioned子要素とは異なり、PositionedウィジェットはStackのalignmentプロパティを無視し、指定された座標に従って厳密に配置されます。Flutter API Reference(2025)によると、Positionedはサイズに関係なく子要素のサイズを強制するためのwidthとheightもサポートしています。
Stack(
children: [
Container(
color: Colors.grey.shade200,
width: 300,
height: 300,
),
Positioned(
top: 20,
left: 20,
child: Text('左上隅'),
),
Positioned(
bottom: 20,
right: 20,
child: Text('右下隅'),
),
],
)
Positionedは論理ピクセル(デバイス非依存ピクセル)で値を受け入れます。Positioned(left: 0, right: 0, top: 0, bottom: 0)とContainer(width: double.infinity, height: double.infinity)の違いは、Positionedは子要素をStackの境界まで正確に伸ばすのに対し、ContainerはStack内で利用可能な最大サイズまで伸びることです。
IndexedStackはStackのサブクラスで、指定されたインデックスによって1つの子要素のみを表示しますが、すべての子ウィジェットの状態をメモリに保持します。これは条件付きレンダリング(if-elseやvisibility)による切り替えとは根本的に異なり、非表示のウィジェットは破棄されます。Flutter Team(2025)によると、IndexedStackは各画面の状態を保持する必要があるBottomNavigationBarやTabBarに推奨されています。
IndexedStack(
index: 0,
children: [
HomeScreen(),
SearchScreen(),
ProfileScreen(),
],
)
インデックスを0から1に切り替えると、HomeScreenはメモリに残り、状態(スクロール位置、入力データ)を失いません。IndexedStackは再描画を分離するためにRepaintBoundaryメカニズムを使用し、アクティブな画面のみがbuild呼び出しを受け取ります。これによりタブ切り替え時のGPU負荷が軽減されます。
実際のプロジェクトでのStackの3つの実践的な使用シナリオを見てみましょう。アイコン上のバッジインジケーター — Positionedを使用した古典的なオーバーレイ例。親要素に対して相対的に配置されるツールチップ。記事カード用のテキスト付きカバー画像。
// 通知アイコンのバッジインジケーター
Stack(
children: [
Icon(Icons.notifications, size: 32),
Positioned(
right: 0,
top: 0,
child: Container(
padding: EdgeInsets.all(4),
decoration: BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
child: Text('3',
style: TextStyle(
color: Colors.white,
fontSize: 10,
),
),
),
),
],
)
この例では、Positionedが通知アイコンの右上隅に赤い丸いバッジを配置します。right: 0とtop: 0のパラメータがバッジをStackの境界に固定します。Container内のPaddingはテキストの周りにスペースを作り、数字が円の端に触れないようにします。
よくある質問
Stackはすべての子要素をレイヤーとして同時に表示しますが、IndexedStackはインデックスによって1つのみを表示します。IndexedStackはメモリ内のすべての非表示の子の状態を保持するため、BottomNavigationBarやTabBarに便利です。Flutter Docs(2025)によると、IndexedStackはStackと同じレンダリングメカニズムを使用しますが、非アクティブな子にOffstageを適用します。
Stackのalignmentプロパティを使用して、Alignment.centerに設定します。テキストが非Positionedの子要素である場合、自動的にStackの中央に配置されます。テキストがPositioned内にある場合は、Positioned(left: 0, right: 0, top: 0, bottom: 0)を設定し、TextのtextAlignプロパティを使用して配置します。
StackとOverlayは異なるタスクを解決します。Stackはレイヤーオーバーレイで画面構造を構築するためのレイアウトウィジェットです。Overlayはシステムメッセージ(SnackBar、Tooltip)用のウィジェット階層全体の上のグローバルレイヤーです。Flutter Team(2025)によると、通常のインターフェースにはStackを、一時的な通知にはOverlayを使用してください。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。