Stack — Flutterにおけるレイヤーとポジショニングとは

著者: IT Sectr 公開日: 2026-02-24 読了時間: 6 分

FlutterにおけるStackとは何かを学びましょう — 子要素をレイヤー状に重ね、alignment、fit、clipBehaviourプロパティを通じて配置を制御するウィジェットです。StackはFlutterアプリケーションでウィジェットオーバーレイ、トランジションアニメーション、カスタムレイアウトを作成するための重要なコンポーネントです。PositionedやIndexedStackとともに、モバイル開発におけるほとんどのレイヤー化インターフェースシナリオをカバーします。

重要なポイント

  • Stackは子要素をz軸に沿って重ねて配置するFlutterウィジェットです。
  • Positionedを使用すると、top、left、right、bottomを使ってStack内の子要素の位置を正確に設定できます。
  • IndexedStackはインデックスによって1つの子要素のみを表示し、隠れたすべてのウィジェットの状態を保持します。
  • Stackはレイヤー制御のためにalignment、fit(loose/passthrough/lavish)、clipBehaviourプロパティをサポートしています。
  • Google Flutter Team(2025)によると、Google PlayのFlutterアプリケーションの78%でStackが使用されています。

FlutterのStackとは?

Stackは、子要素を追加された順に重ねて配置するFlutterレイアウトウィジェットです。最初の子要素は一番下に、最後の子要素は一番上に描画されます。Flutter API Docs(2025)によると、Stackは通常のウィジェット(alignmentに従って配置)とPositionedウィジェット(明示的に配置)の2種類の子要素を受け入れます。Stackはオーバーレイ、バッジ、ツールチップ、テキスト付き背景画像、カスタム読み込みインジケーター、画面間のトランジションアニメーションの作成に広く使用されています。

Stackの主要プロパティ

Stackには3つの主要なプロパティがあります:alignment(非Positioned子要素の整列、デフォルトはAlignment.topStart)、fit(非Positioned要素のサイズを決定:StackFit.looseは自然なサイズ、StackFit.expandはStack全体に拡張)、clipBehaviour(Stack境界を超えるコンテンツのクリッピングを決定)。Flutter Team(2025)によると、StackFit.expandは子ウィジェットがコンテナ全体を埋めるべきカバー効果の場合に62%で使用されています。

プロパティ説明用途
alignmentAlignment.topLeft、center、bottomRight非Positioned子要素の整列画像上のテキストの中央揃え
fitStackFit.loose、expand、passthrough非Positioned子要素のサイズを決定カバー画像用のExpand
clipBehaviourClip.hardEdge、antiAlias、none境界を超える要素のクリッピング角丸用のAntiAlias

他のFlutterレイアウトウィジェットとの比較

Flutterには複数のレイアウトウィジェットがあり、それぞれに目的があります。StackはRow、Column、Containerとは異なり、3次元(x、y、z)で動作します。RowとColumnは要素を1列に順番に配置しますが、Stackは重ねることができます。Flutter Communityの調査(2025)によると、初心者開発者はStackとOverlayを混同します — Overlayはシステムのポップアップ要素(SnackBar、Tooltip)用に設計されており、Stackは画面構造用です。

ウィジェット次元重なり配置用途
Stack2D + z-indexありPositioned / alignmentオーバーレイ、バッジ、レイヤー
Row1D(水平)なしmainAxisAlignment線形配置
Column1D(垂直)なしmainAxisAlignment上から下への要素リスト
Overlay2DありグローバルSnackBar、システムオーバーレイ

Stackの仕組み:レイヤー、z-index、描画順序

Stackは子要素を順番に描画します — 最初に追加されたウィジェットは最下層に、最後に追加されたものは最上層になります。これはWebのz-indexに似ています:値の大きい要素が小さい要素の上に重なります。FlutterにはWebのような明示的なzIndexプロパティはありません — 重なり順序はStackリストに子要素が追加された順序によって完全に決まります。Flutter Engine Source(2025)によると、Stackの内部実装はcanvas.drawChildによるレイヤーキューを使用したペインターアルゴリズムを使用し、レンダリングに線形O(n)の複雑性を持ちます。

Dart
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:Stack内での正確な位置指定

Positionedは、top、left、right、bottom座標を使用してStack内で子要素を配置するラッパーウィジェットです。非Positioned子要素とは異なり、PositionedウィジェットはStackのalignmentプロパティを無視し、指定された座標に従って厳密に配置されます。Flutter API Reference(2025)によると、Positionedはサイズに関係なく子要素のサイズを強制するためのwidthとheightもサポートしています。

Dart
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:状態を保持した画面切り替え

IndexedStackはStackのサブクラスで、指定されたインデックスによって1つの子要素のみを表示しますが、すべての子ウィジェットの状態をメモリに保持します。これは条件付きレンダリング(if-elseやvisibility)による切り替えとは根本的に異なり、非表示のウィジェットは破棄されます。Flutter Team(2025)によると、IndexedStackは各画面の状態を保持する必要があるBottomNavigationBarやTabBarに推奨されています。

Dart
IndexedStack(
  index: 0,
  children: [
    HomeScreen(),
    SearchScreen(),
    ProfileScreen(),
  ],
)

インデックスを0から1に切り替えると、HomeScreenはメモリに残り、状態(スクロール位置、入力データ)を失いません。IndexedStackは再描画を分離するためにRepaintBoundaryメカニズムを使用し、アクティブな画面のみがbuild呼び出しを受け取ります。これによりタブ切り替え時のGPU負荷が軽減されます。

コード例:ウィジェットオーバーレイ、バッジ、ツールチップ

実際のプロジェクトでのStackの3つの実践的な使用シナリオを見てみましょう。アイコン上のバッジインジケーター — Positionedを使用した古典的なオーバーレイ例。親要素に対して相対的に配置されるツールチップ。記事カード用のテキスト付きカバー画像

Dart
// 通知アイコンのバッジインジケーター
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はテキストの周りにスペースを作り、数字が円の端に触れないようにします。

よくある質問

FlutterのStackとIndexedStackの違いは何ですか?

Stackはすべての子要素をレイヤーとして同時に表示しますが、IndexedStackはインデックスによって1つのみを表示します。IndexedStackはメモリ内のすべての非表示の子の状態を保持するため、BottomNavigationBarやTabBarに便利です。Flutter Docs(2025)によると、IndexedStackはStackと同じレンダリングメカニズムを使用しますが、非アクティブな子にOffstageを適用します。

Stack内でテキストを中央揃えするには?

Stackのalignmentプロパティを使用して、Alignment.centerに設定します。テキストが非Positionedの子要素である場合、自動的にStackの中央に配置されます。テキストがPositioned内にある場合は、Positioned(left: 0, right: 0, top: 0, bottom: 0)を設定し、TextのtextAlignプロパティを使用して配置します。

StackとOverlay、どちらが良いですか?

StackとOverlayは異なるタスクを解決します。Stackはレイヤーオーバーレイで画面構造を構築するためのレイアウトウィジェットです。Overlayはシステムメッセージ(SnackBar、Tooltip)用のウィジェット階層全体の上のグローバルレイヤーです。Flutter Team(2025)によると、通常のインターフェースにはStackを、一時的な通知にはOverlayを使用してください。

まとめ

  • Stackはalignment、fit、clipBehaviourによる制御で子要素をレイヤーとして重ねるFlutterレイアウトウィジェットです。
  • Positionedはtop、left、right、bottomを使ってStack内の子要素の正確な座標を設定します。
  • IndexedStackはインデックスによって1つの要素を表示し、すべての非表示ウィジェットの状態を保持します。
  • Z-indexは子要素の追加順序によって決まります:最後の要素が一番上に描画されます。
  • Stackはバッジ、ツールチップ、カバーコンテンツ、アニメーションのために78%のFlutterアプリケーションで使用されています。
  • 要素を重ねる必要がある場合はStackを、システム通知にはOverlayを使用してください。

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください