コンテンツにスキップ

レイアウトの考え方

大きさは、親の制約と子の希望で決まる

Section titled “大きさは、親の制約と子の希望で決まる”

子の大きさは、子が自分で決めるのではなく、親が渡す制約の範囲で決まります。たとえば SizedBox で幅 100 を指定しても、親が「幅は 400 ちょうど」と決めていれば、子の幅は 400 になります。

uGUI の RectTransform は、アンカーとオフセットで「親のどこに置くか」を子が指定します。FloatSoda は逆向きです。親が「この範囲の大きさなら使ってよい」と伝え、子がその範囲の中で希望の大きさを返します。思った大きさにならないときは、自分の画面の親子関係を上から順にたどり、どの親がどんな制約を渡しているかを見直してください。

制約は上から下へ、サイズは下から上へ流れる

Section titled “制約は上から下へ、サイズは下から上へ流れる”

レイアウトは、制約が親から子へ下り、サイズが子から親へ上る、という 1 つの規則で行われます。親は子に制約を渡します。子はその範囲で大きさを決め、親に返します。親は返ってきた大きさをもとに、子を配置します。

制約は BoxConstraints で表します。最小の幅と高さ、最大の幅と高さの 4 つの値を持ちます。

制約の形 最小と最大 子に起きること
厳密(tight) 最小と最大が同じ 子の大きさが 1 つに決まる
ゆるい(loose) 最小が 0、最大が上限 0 から上限までの好きな大きさを選べる
上限なし 最大が無限大 上限がなく、子が希望の大きさを選ぶ。大きさの決まり方はウィジェットによって異なる

この規則は README.md のレンダリングライフサイクル図の「制約↓ サイズ↑」と同じものです。以降の節は、この規則で説明できる典型の現象です。

SizedBox を指定したのに、大きさが変わらない

Section titled “SizedBox を指定したのに、大きさが変わらない”

SizedBox は、指定した幅や高さを、子の制約に反映するウィジェットです。省略した軸は固定しません。ただし、親の制約が厳しい(tight)と、SizedBox が渡そうとした値は親の制約の範囲に収められます。

たとえばウィンドウに Size を指定すると、ルートの子は、その大きさちょうどの制約を受け取ります。ここに SizedBox を直接置くと、幅と高さの指定は効きません。

app.CreateWindow(new DashboardWindow
{
Title = "Layout",
Size = new SKSize(400, 300),
Child = new SizedBox
{
Width = 100,
Height = 100,
Child = new ColoredBox { Color = SKColors.Tomato },
},
});

この例の四角は 100 x 100 ではなく、400 x 300 で描かれます。直し方は、Align か Center で包んで、制約をゆるめることです。

Child = new Center
{
Child = new SizedBox
{
Width = 100,
Height = 100,
Child = new ColoredBox { Color = SKColors.Tomato },
},
},

Center は子に「0 から 400 x 300 までの好きな大きさでよい」という制約を渡すので、SizedBox の 100 x 100 がそのまま通ります。SizedBox の挙動は、サンプル FloatSoda.Samples.SizedBox でも確かめられます。

Align は、子を Alignment で指定した位置に置くウィジェットです。親から受け取った領域いっぱいに広がり、その中に子を配置します。Center は Alignment.Center を固定した Align です。

前の節で Center を足すと四角が中央に寄ったのは、この動きの結果です。Align が 400 x 300 の領域いっぱいに広がり、100 x 100 の子を中央に置いています。

親の最大の幅や高さが無限大の軸では、Align は広がる先がないので、その軸だけ子の大きさに合わせます。もう一方の軸は、その軸の制約に従います。Size を省略したウィンドウのルートに Align を置くと、この状態になります。位置の指定は Alignment.TopLeft などの 9 つのプリセットか、new Alignment(0.5f, -0.25f) のような任意の値で行います。

Row と Column で、余りの領域を分ける

Section titled “Row と Column で、余りの領域を分ける”

Row と Column(まとめて Flex)は、複数の子を 1 列に並べます。並べる方向を主軸、それと直交する方向を交差軸と呼びます。Row の主軸は水平、Column の主軸は垂直です。

並べ方は次の順で決まります。

  1. Expanded / Flexible で包んでいない子(Spacer を除く)を、先に自分の希望の大きさで置く
  2. 主軸方向に残った領域を、Expanded / Flexible / Spacer の Flex の比率で分ける

Expanded は割り当てられた領域を使い切るように子を引き伸ばし、Flexible は割り当てられた領域を上限として子に大きさを選ばせます。Spacer は何も描かない Expanded で、子の間の空白に使います。

子の大きさの合計が主軸の最大を超えると、領域からはみ出します。はみ出すときは、固定の幅や高さの子を小さくします。主軸の最大が有限であれば、Expanded / Flexible で包んで、残りの領域に収める方法もあります。Expanded の使い方は、サンプル FloatSoda.Samples.Expanded にあります。

Stack は並べずに重ねるウィジェットで、Positioned で、Stack の各辺からの距離として子の位置を指定します。子へ渡す制約を作り替えたいときは、ConstrainedBox、LimitedBox、ConstraintsTransformBox、UnconstrainedBox を使います。

ウィンドウの大きさは、Size を指定するかで変わる

Section titled “ウィンドウの大きさは、Size を指定するかで変わる”

ウィンドウの大きさは、Size を指定するかどうかで決まり方が変わります。

Size ルートの子が受け取る制約 ウィンドウの大きさ
指定する その大きさちょうど(tight) Size のとおり
省略する 上限なし 子のレイアウト結果に追従する

画面の大きさを自分で決めたいときは、Size を指定します。中身に合わせたいときは、省略します。

長さの単位はピクセルです。実際の大きさは Dpm(1 メートルあたりのピクセル数)で決まり、既定は 4000 です。1 ピクセルが 0.25 mm なので、幅 1000 ピクセルのウィジェットは実寸 25 cm になります。Dpm は DashboardWindow では表示サイズに影響しない場合があります。ダッシュボードのオーバーレイの大きさは SteamVR が管理するためです。

Size を省略したウィンドウには、既知の問題があります。起動後に中身の大きさが変わっても、SteamVR 上の表示は最初の大きさのままになります(追跡: #285)。中身の大きさが変わるウィンドウには Size を指定してください。

制約が下り、サイズが上るという基本の規則は Flutter と同じです。個々のウィジェットで Flutter と挙動が違う箇所は、リファレンスの Flutter の読み替え表 にまとめています。

症状が残るときは、ほかのページで切り分けます。エラーが出ずに動かないときは 反応しないときに切り分ける、例外が出るときは エラーを読んで直す を読んでください。どちらでも解けないときの行き先は、ガイドの docs で見つからないとき にあります。