跳转至

04 · 布局

分工

负责方 管什么
Style 本盒 width/height、min/max、padding、margin、overflow
布局容器 子节点如何排列:方向、gap、对齐、flex 增长、滚动

不要试图用 Style 表达「兄弟间距」或「flex grow」——那些在 Row / Column 等方法上。

常用容器

Box

单子节点外壳;可带 Key、点击、Style:

ui.Box(
    ui.Text("内容"),
).Style(ui.Padding(16).Background(ui.TokenSurface))

可交互:

ui.Box(ui.Text("点我")).
    Key("tap").
    OnClick(func() { send(Tapped{}) }).
    Style(ui.Padding(8).Cursor(ui.CursorPointer))

Row / Column

ui.Column(
    ui.Text("标题").Size(20),
    ui.Row(
        ui.Button("a", ui.Text("A")).OnClick(...),
        ui.Button("b", ui.Text("B")).OnClick(...),
    ).Gap(8),
).Gap(12)

常用方法:

  • Gap(n) — 子项间距
  • AlignStart / AlignMiddle / AlignEnd 等 — 交叉轴对齐
  • 配合 Flexible / Expanded 分配剩余空间(见示例 examples/layout

Center

在可用空间中居中子树:

ui.Center(ui.Text("居中"))

Scroll

内容超出时滚动:

ui.Scroll(
    "content",
    ui.Column(/* 很多子项 */).Gap(8),
)

视觉外扩与裁剪

ScrollListScrollbarSplitPane 会自动为 Style 的阴影和 轮廓在视口内部预留空间;InputCard 等使用公共 Box 外壳的组件无需额外 添加 margin。安全空间只出现在视口边缘,不会被加到每两个列表项之间。 显式的 OverflowHidden 仍是本地裁剪边界;已被该边界裁掉的子内容不会再影响 外层视口。

自绘超出盒子边界的内容可通过 VisualOutset 声明最小范围,或在没有 Style 外壳时使用 LayoutVisualOutset

ui.Box(customCanvas).Style(
    ui.VisualOutset(6, 8, 10, 8),
)

// 直接自绘时:top, right, bottom, left。
ui.LayoutVisualOutset(ctx, gtx, customCanvas, 6, 8, 10, 8)

首次发现新的外扩范围时,视口会请求下一帧完成安全边距布局。它不会让这类 绘制越过滚动视口或窗口边界。

Grid / Wrap / Stack / SplitPane

容器 用途
Grid 网格
Wrap 自动换行流式排列
Stack 叠放
SplitPane 可拖分割条
Surface / Card 带主题表面/卡片语义的容器

完整演示:examples/layoutexamples/grid_layoutexamples/split_panesexamples/scrollbars

尺寸怎么设

尺寸写在 Style 上,再交给 Box 或控件的 .Style(...)

ui.Box(
    ui.Input("name", m.Name).OnChange(...),
).Style(ui.Width(320))

// 或
ui.Box(child).Style(
    ui.Width(200).
        Height(40).
        MinWidth(120).
        MaxWidth(400).
        Padding(12).
        Margin(4),
)

宽高比:

ui.AspectRatio(1, ui.Box(ui.Text("正方形内容")))

约束直觉

父级给出 最大可用空间;子级在约束内声明自己的尺寸与排列。 Fill / 百分比类能力通过 Style 与容器策略配合(详见规格 03,应用侧记「父约束 → 子声明」即可)。

典型页面骨架

func View(_ *ui.Context, m Model, send ui.Send[Msg]) ui.Widget {
    return ui.Column(
        ui.WindowTitleBar(...),    // 或自定义顶栏
        ui.Row(
            ui.Sidebar(...),
            ui.Expanded(
                ui.Scroll(
                    "page-content",
                    ui.Column(
                        // 主内容
                    ).Gap(16).Style(ui.Padding(24)),
                ),
            ),
        ),
        ui.StatusBar(...),
    )
}

(具体 API 以组件文档与示例为准;骨架思想是「列/行嵌套 + Scroll + Style 内边距」。)

注意

  1. 每帧重建 Widget 树是正常的;不要缓存可变 Widget 状态。
  2. 有状态控件必须有稳定 Key
  3. 列表项 Key 用业务 id,不要用仅与渲染顺序绑定的临时下标(若顺序会变)。

下一步