レイアウトの考え方
大きさは、親の制約と子の希望で決まる
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 で包んだら、中央に寄る
Section titled “Align で包んだら、中央に寄る”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 の主軸は垂直です。
並べ方は次の順で決まります。
Expanded/Flexibleで包んでいない子(Spacerを除く)を、先に自分の希望の大きさで置く- 主軸方向に残った領域を、
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 を知っている人へ
Section titled “Flutter を知っている人へ”制約が下り、サイズが上るという基本の規則は Flutter と同じです。個々のウィジェットで Flutter と挙動が違う箇所は、リファレンスの Flutter の読み替え表 にまとめています。
症状が残るときは、ほかのページで切り分けます。エラーが出ずに動かないときは 反応しないときに切り分ける、例外が出るときは エラーを読んで直す を読んでください。どちらでも解けないときの行き先は、ガイドの docs で見つからないとき にあります。