控件 API
Button、TextField、Toggle、Picker、Slider 等控件签名。
本页是 Text、输入控件、选择控件、按钮和系统控件的参考。这里的示例都使用命名函数连接 action / on_change / on_submit,这样预览打开后可以确认按钮、输入和选择是否真的改变状态。
#什么时候用
| 目标 | 首选 API | 说明 |
|---|---|---|
| 普通文字 | Text | 静态标签、标题、说明文字。 |
| 一段文字混排 | AttributedText | 同一段内需要不同颜色、字重、斜体或链接。 |
| 执行动作 | Button | 保存、删除、刷新、打开页面等命令。 |
| 顶层关闭 | CloseButton | MiniApp 自定义关闭入口,尤其是 fullscreen_with_close。 |
| 单行输入 | TextField / SecureField | 普通文本或密码输入。 |
| 多行编辑 | TextEditor | 备注、正文、长文本。 |
| 搜索 | .searchable(...) 或 SearchField | 列表页优先用 .searchable;独立搜索框用 SearchField。 |
| 布尔开关 | Toggle | 开启/关闭某个选项。 |
| 数值调节 | Slider / Stepper | 连续值用 Slider,离散整数用 Stepper。 |
| 单选 | Picker / SegmentedControl / WheelPicker | 设置项用 Picker,少量模式切换用 SegmentedControl。 |
| 日期和颜色 | DatePicker / MultiDatePicker / ColorPicker | 使用系统原生选择器。 |
| 菜单和系统按钮 | Menu / PasteButton / RenameButton / EditButton | 复用系统语义和平台样式。 |
#最小正确示例
已复制
import appui
state = appui.State(
name="Ada",
enabled=True,
level=3,
theme="System",
note="",
)
def set_name(value):
state.name = value
def set_enabled(value):
state.enabled = value
def set_level(value):
state.level = value
def set_theme(value):
state.theme = value
def set_note(value):
state.note = value
def reset_controls():
state.batch_update(
name="Ada",
enabled=True,
level=3,
theme="System",
note="",
)
def body():
return appui.NavigationStack(
appui.Form([
appui.Section("输入", [
appui.TextField(
"Name",
text=state.name,
on_change=set_name,
submit_label="done",
),
appui.TextEditor(text=state.note, on_change=set_note)
.frame(height=80),
appui.Toggle("Enabled", is_on=state.enabled, on_change=set_enabled),
]),
appui.Section("选择", [
appui.Slider(
value=state.level,
minimum=0,
maximum=10,
step=1,
label=f"Level {state.level}",
on_change=set_level,
),
appui.Picker(
"Theme",
selection=state.theme,
options=["System", "Light", "Dark"],
on_change=set_theme,
).picker_style("segmented"),
]),
appui.Section("动作", [
appui.Button("Reset", action=reset_controls)
.button_style("bordered"),
]),
]).navigation_title("Controls")
)
appui.run(body, state=state)
#文本和按钮
| API | 签名 | 分类 |
|---|---|---|
Text | Text(content: str = '') | text |
AttributedText | AttributedText(spans: Optional[Sequence[Dict[str, Any]]] = None) | text |
Button | Button(title: Optional[Union[str, View]] = None, action: Optional[Callable] = None, role: Optional[str] = None, content: Optional[ViewChild] = None, system_image: Optional[str] = None, image: Optional[str] = None, systemImage: Optional[str] = None) | control |
CloseButton | CloseButton(title: str = '', system_image: str = 'xmark', systemImage: Optional[str] = None) | control |
已复制
import appui
def close_page():
appui.dismiss()
def body():
return appui.NavigationStack(
appui.VStack([
appui.Text("Plain Text").font("headline"),
appui.AttributedText(spans=[
{"text": "Rich ", "font_size": 18},
{"text": "Text", "font_size": 18, "weight": "bold", "color": "systemBlue"},
{"text": " link", "italic": True, "link": "https://www.python.org"},
]),
appui.Button(
appui.Label("Close", system_image="xmark.circle"),
action=close_page,
).button_style("bordered"),
], alignment="leading", spacing=12)
.padding()
.navigation_title("Text")
)
appui.run(body)
#输入控件
| API | 签名 | 分类 |
|---|---|---|
TextField | TextField(placeholder: str = '', text: str = '', on_change: Optional[Callable] = None, on_submit: Optional[Callable] = None, keyboard_type: Optional[str] = None, autocapitalization: Optional[str] = None, autocorrection_disabled: bool = False, submit_label: Optional[str] = None, onChange: Optional[Callable] = None, onSubmit: Optional[Callable] = None, keyboardType: Optional[str] = None, autoCapitalization: Optional[str] = None, autocorrectionDisabled: Optional[bool] = None, submitLabel: Optional[str] = None, value: Optional[str] = None, **kwargs: Any) | control |
SecureField | SecureField(placeholder: str = '', text: str = '', on_change: Optional[Callable] = None, on_submit: Optional[Callable] = None, onChange: Optional[Callable] = None, onSubmit: Optional[Callable] = None) | control |
TextEditor | TextEditor(text: str = '', on_change: Optional[Callable] = None, onChange: Optional[Callable] = None) | control |
TextFieldLink | TextFieldLink(title: str = '', prompt: str = '', on_submit: Optional[Callable] = None, onSubmit: Optional[Callable] = None) | control |
SearchField | SearchField(text: str = '', placeholder: str = 'Search', on_change: Optional[Callable] = None, onChange: Optional[Callable] = None, on_submit: Optional[Callable] = None, onSubmit: Optional[Callable] = None) | control |
keyboard_type 常用值:"default"、"emailAddress"、"numberPad"、"decimalPad"、"url"、"phonePad"。submit_label 常用值:"done"、"go"、"send"、"search"、"next"、"continue"、"return"。
#选择和调节控件
| API | 签名 | 分类 |
|---|---|---|
Toggle | Toggle(label: str = '', is_on: bool = False, on_change: Optional[Callable] = None, isOn: Optional[bool] = None, onChange: Optional[Callable] = None, value: Optional[bool] = None) | control |
Slider | Slider(value: float = 0.0, minimum: float = 0.0, maximum: float = 1.0, step: Optional[float] = None, label: str = '', on_change: Optional[Callable] = None, onChange: Optional[Callable] = None, min_value: Optional[float] = None, max_value: Optional[float] = None, minValue: Optional[float] = None, maxValue: Optional[float] = None) | control |
Stepper | Stepper(label: str = '', value: int = 0, minimum: int = 0, maximum: int = 100, step: int = 1, on_change: Optional[Callable] = None, onChange: Optional[Callable] = None) | control |
Picker | Picker(label: str = '', selection: Optional[str] = None, options: Optional[Sequence[str]] = None, on_change: Optional[Callable] = None, onChange: Optional[Callable] = None) | control |
SegmentedControl | SegmentedControl(options: Optional[Sequence[str]] = None, selection: Optional[str] = None, on_change: Optional[Callable] = None, onChange: Optional[Callable] = None) | control |
InlinePickerStyle | InlinePickerStyle(options: Optional[Sequence[str]] = None, selection: Optional[str] = None, on_change: Optional[Callable] = None, onChange: Optional[Callable] = None) | control |
WheelPicker | WheelPicker(options: Optional[Sequence[str]] = None, selection: Optional[str] = None, on_change: Optional[Callable] = None, onChange: Optional[Callable] = None) | control |
DatePicker | DatePicker(label: str = '', selection: Optional[str] = None, components: str = 'date', on_change: Optional[Callable] = None, onChange: Optional[Callable] = None) | control |
MultiDatePicker | MultiDatePicker(title: str = '', on_change: Optional[Callable] = None, onChange: Optional[Callable] = None) | control |
ColorPicker | ColorPicker(label: str = '', selection: Optional[str] = None, on_change: Optional[Callable] = None, onChange: Optional[Callable] = None) | control |
#系统控件
| API | 签名 | 分类 |
|---|---|---|
Menu | Menu(title: str = '', content: Optional[Sequence[View]] = None, children: Optional[Sequence[View]] = None, system_image: Optional[str] = None, image: Optional[str] = None, systemImage: Optional[str] = None) | control |
PasteButton | PasteButton(on_paste: Optional[Callable] = None, onPaste: Optional[Callable] = None) | control |
RenameButton | RenameButton(action: Optional[Callable] = None) | control |
EditButton | EditButton() | control |
SignInWithAppleButton | SignInWithAppleButton(type: str = 'signIn', on_complete: Optional[Callable] = None, onComplete: Optional[Callable] = None) | control |
#按钮与菜单的图标
Button 和 Menu 都支持直接传 system_image(SF Symbol 名)来显示图标,无需再包一层 content=appui.Label(...):
- 同时给
title和system_image→ 图标 + 文字;只给system_image→ 纯图标。 Menu用system_image可以做工具栏右上角的「ellipsis.circle溢出菜单」。- 仍可用
content=传任意视图作为完全自定义的标签;content优先级高于system_image。
已复制
import appui
def noop():
pass
def body():
return appui.NavigationStack(
appui.VStack([
appui.Button("收藏", action=noop, system_image="bookmark"),
appui.Button(action=noop, system_image="arrow.clockwise"),
], alignment="leading", spacing=12)
.padding()
.navigation_title("Icons")
.toolbar([
appui.ToolbarItem(
placement="navigation_bar_trailing",
content=appui.Menu(system_image="ellipsis.circle", content=[
appui.Button("全部", action=noop, system_image="checkmark"),
appui.Button("收藏", action=noop),
]),
),
])
)
appui.run(body)
#与相邻 API 的区别
| API | 不同点 |
|---|---|
TextField vs TextEditor | TextField 是单行输入,支持提交键;TextEditor 是多行编辑区域,通常需要明确 .frame(height=...)。 |
SearchField vs .searchable(...) | SearchField 是普通视图;.searchable(...) 会接入导航栏搜索体验,列表页更自然。 |
Picker vs SegmentedControl | Picker 可切换不同样式;SegmentedControl 是固定分段控件,适合少量模式。 |
Slider vs Stepper | Slider 适合连续数值;Stepper 适合精确整数或固定步进。 |
Button vs Menu | Button 立即执行单个动作;Menu 先展开命令列表。 |
Button vs CloseButton | CloseButton 带关闭语义,系统可以识别并避免重复注入关闭按钮。 |
#常见错误
| 错误 | 正确做法 |
|---|---|
| 在示例里写内联匿名回调,预览里很难读也不利于复用。 | 定义命名函数,例如 def save(): ...,再传 action=save。 |
TextField 只传 text=state.name,没有 on_change。 | 在 on_change(value) 中写回 state.name = value。 |
Slider 没有设置 minimum / maximum,默认范围和业务不一致。 | 显式写清范围和 step。 |
TextEditor 不设高度。 | 用 .frame(height=...) 给多行编辑区域稳定尺寸。 |