FlutterのStackを使ってみた

FlutterのStackを使ってみました。自分がハマったところと合わせて使い方をご紹介します。
2018.08.16

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

大阪オフィスの山田です。道に落ちてるセミが急に動き出して必要以上にビビります。今回もFlutterです。Stackを使ってみます。ちょっと描画時の規則でハマったのでそちらも合わせてご紹介します。

開発環境

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel beta, v0.5.1, on Mac OS X 10.13.5 17F77, locale ja-JP)
[✓] Android toolchain - develop for Android devices (Android SDK 28.0.1)
[✓] iOS toolchain - develop for iOS devices (Xcode 9.3)
[✓] Android Studio (version 3.1)
    ✗ Flutter plugin not installed; this adds Flutter specific functionality.
    ✗ Dart plugin not installed; this adds Dart specific functionality.
[!] VS Code (version 1.25.1)
[✓] Connected devices (2 available)

VSCodeの警告が出ていますが、Extensionインストール済みです。

Flutterのバージョンは以下の通りです。

Flutter 0.5.1 • channel beta • https://github.com/flutter/flutter.git
Framework • revision c7ea3ca377 (2 months ago) • 2018-05-29 21:07:33 +0200
Engine • revision 1ed25ca7b7
Tools • Dart 2.0.0-dev.58.0.flutter-f981f09760

前回、「この次はバージョンアップしよう」と書いてましたが、beta 6がまだ来てなかったので保留です。

今回やること

FlutterのWidget、Stackの基本的な使い方のご紹介、それと私がハマったポイントについてご紹介します。

Stackの使い方

Stackは以下の事ができます。

  • 子のWidgetをStackの端から位置を指定して子を配置する
  • Widgetを重ねる場合に使用します。
  • それでは使い方です。まずは基本です。

        new Stack(
          children: <Widget>[
            // ここに子の要素を配列で書いていく
          ],
        );

    子の要素としてはpositionednon-positionedなWidgetを設定することができます。positionedPositioned Widgetでラップされたものを指します。それ以外はnon-positionedとなります。positionednon-positionedの関係性については後述します。子の要素は一番上が最背面、一番下が最前面になります。

    Positioned Widgetを配置してみる

    子の要素としてPositioned Widgetを使って配置します。

    Widget _buildStack() {
        return Stack(
          children: <Widget>[
            Positioned(
              left: 20.0,
              top: 20.0,
              width: 100.0,
              height: 100.0,
              child: Container(color: Colors.indigo,),
            ),
            Positioned(
              left: 100.0,
              top: 100.0,
              right: 100.0,
              bottom: 200.0,
              child: Container(color: Colors.cyan,),
            ),
          ],
        );
      }

    Positioned Widgetは、lefttoprightbottomwidthheightを指定する事ができます。先ほどのサンプルプログラムの1つ目では幅と高さを決めて上から20、左から20を始点にして配置しています。2つ目は幅と高さを指定せず、上下左右の位置を指定しています。

    non-positionedなWidgetを配置してみる

    Stack内の要素がPositioned Widgetでラップされている場合、その要素はStack内でpositionedとみなされます。それ以外はnon-positionedです。この事を踏まえた上でソースコードと、結果の画像です。

      Widget _buildStack() {
        return Stack(
          alignment: Alignment.bottomRight,
          children: <Widget>[
            SizedBox(
              width: 200.0,
              height: 200.0,
              child: Container(color: Colors.orange,),
            ),
            SizedBox(
              width: 100.0,
              height: 100.0,
              child: Container(color: Colors.cyan,),
            ),
            Text("Test"),
            Positioned(
              top: 20.0,
              left: 20.0,
              right: 70.0,
              bottom: 70.0,
              child: Container(color:Colors.green),
            )
          ]
        );
      }

    ここで結構ハマりました。上のソースでPositionedが左上から20、右下から70指定されている(薄緑色の部分)ので、画面ほぼいっぱいに広がるのかと思っていました。1つ目の例だと画面いっぱいに広がって、その端から描画されていたのに...実はStack自体のサイズの決定には以下の規則があります。

  • Stackは全てのnon-positionedを描画してそれを包括できるサイズになる
  • ただし、non-positionedが1つも存在しない時は、可能な限り大きくなる
  • なので1つ目の例ではnon-positionなWidgetが指定されていないため、Stackが可能な限り広がって、その端から計算されています。一方で2つ目の例ではnon-positionedなSizeBox(200x200)が最大領域となるので、それがStackのサイズになっています。

    この例ではalignmentを指定していますが、これはnon-positionedなWidgetの配置位置を指定するものです。今回はbottomRightを指定していますが、これをtopLeftに指定するとTestの文字が左上に来ます。他にもfitを指定する事で、non-positionedなWidgetの領域を可能な限り広めたり、最小限に留めたりする事ができます。

    最後に

    ちゃんとリファレンス読めば書いてあったりするのですが、動かしてみないとなんともイメージがつきにくい事ってありますよね。あと、動かすのって楽しいからついつい時間を忘れてしまう...

    参照元

  • Stack Class - リファレンス
  • RenderStack Class - リファレンス