原生表单设置
Form、Section、输入控件、颜色选择和 storage 持久化。
演示 Form + Section 与各类控件绑定 State,并用 storage 持久化偏好。
#预期效果
运行后会出现完整表单设置页,文本、开关、日期、颜色和保存状态会同步更新。
#完整示例
已复制
import appui
import haptics
import storage
SETTINGS_KEY = "demo.native.settings"
defaults = {
"display_name": "MiniApp 用户",
"api_token": "",
"mode": "自动",
"refresh_minutes": 15,
"volume": 0.55,
"accent": "systemBlue",
"deadline": "2026-05-01 09:00",
"sync_enabled": True,
"wifi_only": True,
}
state = appui.State(**defaults, status="未保存", loaded=False)
def load_saved_settings():
if state.loaded:
return
state.loaded = True
saved = storage.get_json(SETTINGS_KEY, default={}) or {}
for key, fallback in defaults.items():
setattr(state, key, saved.get(key, fallback))
if saved:
state.status = "已加载保存设置"
def update(field):
def setter(value):
setattr(state, field, value)
return setter
def save_settings():
payload = {
"display_name": state.display_name,
"api_token": state.api_token,
"mode": state.mode,
"refresh_minutes": int(state.refresh_minutes),
"volume": float(state.volume),
"accent": state.accent,
"deadline": state.deadline,
"sync_enabled": bool(state.sync_enabled),
"wifi_only": bool(state.wifi_only),
}
storage.set_json(SETTINGS_KEY, payload)
state.status = "已保存"
haptics.notification("success")
def reset_settings():
for key, value in defaults.items():
setattr(state, key, value)
state.status = "已恢复默认"
haptics.notification("warning")
def body():
return appui.NavigationStack(
appui.Form(
[
appui.Section(
[
appui.TextField(
"显示名称",
text=state.display_name,
on_change=update("display_name"),
),
appui.SecureField(
"API Token",
text=state.api_token,
on_change=update("api_token"),
),
],
header="账号",
footer="Token 只用于演示表单输入;生产场景请保存到 keychain。",
),
appui.Section(
[
appui.Toggle(
"启用同步",
is_on=state.sync_enabled,
on_change=update("sync_enabled"),
),
appui.Toggle(
"仅 Wi-Fi 同步",
is_on=state.wifi_only,
on_change=update("wifi_only"),
),
appui.Picker(
"同步模式",
selection=state.mode,
options=["自动", "手动", "低电量"],
on_change=update("mode"),
),
],
header="同步",
),
appui.Section(
[
appui.Stepper(
"刷新间隔",
value=state.refresh_minutes,
minimum=5,
maximum=120,
step=5,
on_change=update("refresh_minutes"),
),
appui.LabeledContent("当前间隔", value=f"{int(state.refresh_minutes)} 分钟"),
appui.Slider(
value=state.volume,
minimum=0,
maximum=1,
step=0.05,
label="提示音量",
on_change=update("volume"),
),
appui.ProgressView(value=state.volume),
],
header="数值",
),
appui.Section(
[
appui.DatePicker(
"提醒时间",
selection=state.deadline,
components="date",
on_change=update("deadline"),
),
appui.ColorPicker(
"强调色",
selection=state.accent,
on_change=update("accent"),
),
],
header="外观与时间",
),
appui.Section(
[
appui.LabeledContent("状态", value=state.status),
(
appui.Button("保存设置", action=save_settings)
.button_style("bordered_prominent")
),
appui.Button("恢复默认", role="destructive", action=reset_settings),
],
header="操作",
),
]
).navigation_title("设置")
).on_appear(load_saved_settings)
appui.run(body, state=state, presentation="sheet")
#关键技巧
- 设置页优先
Form + Section;控件值绑定State并在on_change写回。 - 一般偏好用
storage;真密钥用keychain。 - 持久化读取放进
on_appear,不要在模块加载时访问原生存储,预览和首次渲染会更稳定。 Toggle的on_change只更新状态,避免顺带做 Tab 切换等导航副作用。