04 - Layout¶
FlowUI separates box styling from child arrangement:
| Owner | Controls |
|---|---|
Style |
Width, height, min/max, padding, margin, overflow, and paint |
| Layout container | Direction, gaps, alignment, flex growth, scrolling, and measurement |
Do not use a style to express sibling spacing or flex growth. Put that policy
on Row, Column, or another container.
Common containers¶
ui.Column(
ui.Text("Title").Size(20),
ui.Row(
ui.Button("cancel", ui.Text("Cancel")),
ui.Button("save", ui.Text("Save")),
).Gap(8),
).Gap(12)
Box gives one child a styled shell. Center centers its child in the
available constraints. Expanded and Flexible distribute remaining space.
ui.Row(
ui.Sidebar("navigation", "home", items),
ui.Expanded(ui.Scroll("content", ui.Column(/* content */))),
)
Scrolling and grids¶
Scroll requires a stable key. Grid uses a fixed column count, while
AutoGrid chooses columns from a minimum column width.
ui.Scroll("content", ui.Column(/* many children */).Gap(8))
ui.Grid(3, cardA, cardB, cardC).Gap(16)
ui.AutoGrid(190, cardA, cardB, cardC).ColumnGap(16).RowGap(12)
Visual outset and clipping¶
Scroll, List, Scrollbar, and SplitPane automatically reserve space
inside their viewport for shadows and outlines declared by Style. Components
with the common Box shell, such as Input and Card, need no extra margin.
The space is applied at viewport edges, not between every pair of list items.
An explicit OverflowHidden box remains a local clip boundary; child paint
already clipped there does not affect an outer viewport.
For custom paint that extends beyond its box, declare a minimum range with
VisualOutset, or use LayoutVisualOutset when there is no Style shell:
ui.Box(customCanvas).Style(
ui.VisualOutset(6, 8, 10, 8),
)
// top, right, bottom, left for direct custom paint.
ui.LayoutVisualOutset(ctx, gtx, customCanvas, 6, 8, 10, 8)
When a new extent is discovered, the viewport requests another frame to settle the safe padding. It never lets that paint escape the scroll viewport or window.
The runnable examples/grid_layout compares
fixed columns, responsive columns, and independent row/column gaps.
Other useful containers include Wrap, Stack, SplitPane, Surface, and
Card. See the component guide for the full list.
Size and constraints¶
Declare dimensions on a style and apply it to a box or component:
ui.Box(ui.Input("name", m.Name)).Style(ui.Width(320))
ui.Box(child).Style(
ui.Width(200).
Height(40).
MinWidth(120).
MaxWidth(400).
Padding(12).
Margin(4),
)
Use AspectRatio for a fixed width-to-height ratio:
Parents provide maximum constraints. Children choose a size inside those constraints, and containers decide how children are placed.
A typical page shell¶
func View(_ *ui.Context, m Model, send ui.Send[Msg]) ui.Widget {
return ui.Column(
ui.WindowTitleBar("title", "FlowUI", menu),
ui.Expanded(ui.Row(
ui.Sidebar("sidebar", m.Selected, items),
ui.Expanded(ui.Scroll("page", content)),
)),
ui.StatusBar(left, right),
)
}
Build widgets every frame, keep keys stable, and use business IDs for list items whose order can change.