---
title: "Input 组件 — zenit Zig UI"
description: "单行文本、邮件、密码和搜索输入。label、helper、error、required、readonly、disabled、leading icon 与 IME 共用完整编辑…"
url: https://zenit.z.express/zh/components/input
language: zh-CN
alternate_en: https://zenit.z.express/components/input.md
alternate_es: https://zenit.z.express/es/components/input.md
alternate_ja: https://zenit.z.express/ja/components/input.md
alternate_ko: https://zenit.z.express/ko/components/input.md
alternate_fr: https://zenit.z.express/fr/components/input.md
alternate_de: https://zenit.z.express/de/components/input.md
project: zenit v0.1.0-alpha (Zig 0.15.2, macOS)
source: https://github.com/version-next/zenit
---

# Input

`ui.widgets.Input`

单行文本、邮件、密码和搜索输入。

[Video](https://zenit.z.express/media/stories/input.mp4?v=4248d1fa64)

**BEHAVIOR CONTRACT**

label、helper、error、required、readonly、disabled、leading icon 与 IME 共用完整编辑内核。

**RECORDED INTERACTION**

Harness 聚焦 Name 字段并输入真实文本。

`examples/storybook/stories.zig:2888`

```zig
// ── Input ──
pub fn buildInput(scope: *ui.Scope, cx: *ui.Cx) anyerror!*ui.Node {
    const a = cx.allocator;
    const icons = ui.assets.common;
    const c = try col(cx, 12);

    // ── 类型 ──
    try c.appendChild(a, try label(cx, "Types"));
    const n1 = try W.Input(.{ .label_text = "Name", .placeholder = "Your name", .width = 320 }).mount(scope, cx);
    n1.meta.ownership.meta.test_id = "story.input.name";
    try c.appendChild(a, n1);
    try c.appendChild(a, try W.Input(.{ .label_text = "Email", .input_type = .email, .placeholder = "you@example.com", .width = 320 }).mount(scope, cx));
    try c.appendChild(a, try W.Input(.{ .label_text = "Password", .input_type = .password, .placeholder = "••••••••", .width = 320 }).mount(scope, cx));

    // ── 尺寸 ──
    try c.appendChild(a, try label(cx, "Sizes (sm / md / lg)"));
    inline for (.{ W.input.InputSize.sm, .md, .lg }, .{ "Small", "Medium", "Large" }) |sz, ph| {
        try c.appendChild(a, try W.Input(.{ .size = sz, .placeholder = ph, .width = 320 }).mount(scope, cx));
    }

    // ── 带图标 / append ──
    try c.appendChild(a, try label(cx, "With icon / append"));
    try c.appendChild(a, try W.Input(.{ .label_text = "Search", .placeholder = "Type to search…", .leading_icon_asset = icons.search, .width = 320 }).mount(scope, cx));
    try c.appendChild(a, try W.Input(.{ .label_text = "Website", .placeholder = "mysite", .append_text = ".com", .width = 320 }).mount(scope, cx));

    // ── 状态：required / helper / error / readonly / disabled ──
    try c.appendChild(a, try label(cx, "States"));
    try c.appendChild(a, try W.Input(.{ .label_text = "Required", .placeholder = "Mandatory", .required = true, .width = 320 }).mount(scope, cx));
    try c.appendChild(a, try W.Input(.{ .label_text = "With helper", .placeholder = "Username", .helper = "3–20 characters", .width = 320 }).mount(scope, cx));
    try c.appendChild(a, try W.Input(.{ .label_text = "With error", .initial_value = "bad value", .error_msg = "This field is invalid", .width = 320 }).mount(scope, cx));
    try c.appendChild(a, try W.Input(.{ .label_text = "Readonly", .initial_value = "Read only value", .readonly = true, .width = 320 }).mount(scope, cx));
    try c.appendChild(a, try W.Input(.{ .label_text = "Disabled", .placeholder = "Cannot type", .disabled = true, .width = 320 }).mount(scope, cx));
    return c;
}
```

[在 GitHub 查看源码examples/storybook/stories.zig:2888](https://github.com/version-next/zenit/blob/HEAD/examples/storybook/stories.zig#L2888)

这是 Storybook 中该 story 的原样代码（zenit 5f9add5+wip 2026-09-30）。其中 `W` 即 `ui.widgets`，`col` / `row` / `label` 是 Storybook 内的布局小工具。

### InputProps

`ui.widgets.Input`

[src/ui/components/input/mod.zig:56](https://github.com/version-next/zenit/blob/HEAD/src/ui/components/input/mod.zig#L56)

```zig
fn Input(props: InputProps) InputBuilder
```

| 字段 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `initial_value` | `?[]const u8` | `null` | 初始值。\*\*只在 mount 时读取一次\*\* —— 之后改无效。 首帧之后要写入内容，用 \`mountResult()\` 拿 \`result.state\` 再调 \`setText()\`（2026-07-31 新增）。 |
| `growable` | `bool` | `false` | — |
| `placeholder` | `?[]const u8` | `null` | 占位符 |
| `placeholder_color` | `?Color` | `null` | 占位符文本颜色覆盖 |
| `input_type` | `InputType` `.text` `.number` `.email` `.password` `.tel` | `.text` | 输入类型 |
| `size` | `InputSize` | `.md` | 尺寸 (统一 ControlSize，默认 md；外框高度 = padding\_y × 2 + font\_size × line\_height， 默认主题 xs=20 / sm=24 / md=32 / lg=40) |
| `label_text` | `?[]const u8` | `null` | 标签 |
| `helper` | `?[]const u8` | `null` | 辅助文本 |
| `error_msg` | `?[]const u8` | `null` | 错误信息 |
| `required` | `bool` | `false` | 必填 |
| `readonly` | `bool` | `false` | 只读 |
| `disabled` | `bool` | `false` | 禁用 |
| `width` | `?f32` | `null` | 宽度 |
| `leading_icon_asset` | `?svg_assets.Asset` | `null` | 左侧图标 |
| `append_text` | `?[]const u8` | `null` | 右侧附加文本（如计数） |
| `append_icon_asset` | `?svg_assets.Asset` | `null` | 右侧附加图标（如 clear / eye） |
| `embedded` | `bool` | `false` | 嵌入复合控件时仅保留文本编辑能力，由父控件绘制统一 shell。 Select/ComboBox 等组件使用它避免出现双层 border / focus ring。 |
| `state_id` | `?u64` | `null` | 状态 ID (用于 StateStore, 如果为 null 则使用自动生成的 ID) |
| `on_change` | `?core.HandlerRef` | `null` | 值变化回调 2026-07-31 并轨：统一 ?core.HandlerRef（用 cx.strHandlerFrom 构造以拿到文本）。 |

### \*Node

`ui.widgets.Input`

mount 直接返回 `*Node`，把它 append 到父节点即可。
