示例:待办列表
原生列表、搜索、稳定 id 和行级按钮。
适合做任务清单、收藏列表、轻量消息列表。这个样板使用 List + Section + ForEach,支持搜索、新增、完成切换和删除。
#预期效果
运行后会出现可搜索的待办列表,支持新增、完成切换和滑动删除,行身份由 id 保持稳定。
#完整代码
已复制
import appui
state = appui.State(
query="",
next_id=4,
items=[
{"id": 1, "title": "Review layout", "done": False},
{"id": 2, "title": "Check interactions", "done": True},
{"id": 3, "title": "Polish examples", "done": False},
],
)
def visible_items():
query = state.query.lower().strip()
if not query:
return list(state.items)
return [item for item in state.items if query in item["title"].lower()]
def item_key(item):
return item["id"]
def add_item():
item = {"id": state.next_id, "title": f"Task {state.next_id}", "done": False}
state.items = [item] + list(state.items)
state.next_id += 1
def set_query(value):
state.query = value
def toggle_by_id(item_id):
updated = []
for item in state.items:
if item["id"] == item_id:
updated.append({**item, "done": not item["done"]})
else:
updated.append(item)
state.items = updated
def delete_by_id(item_id):
state.items = [item for item in state.items if item["id"] != item_id]
def row_view(item):
def toggle_item():
toggle_by_id(item["id"])
def delete_item():
delete_by_id(item["id"])
title = item["title"]
symbol = "checkmark.circle.fill" if item["done"] else "circle"
color = "systemGreen" if item["done"] else "secondaryLabel"
return (
appui.Button(
action=toggle_item,
content=appui.HStack([
appui.Image(system_name=symbol).foreground_color(color),
appui.Text(title).frame(max_width=appui.infinity, alignment="leading"),
], spacing=10),
)
.button_style("plain")
.swipe_actions(actions=[
appui.Button("Delete", action=delete_item, role="destructive")
])
)
def body():
rows = appui.ForEach(visible_items(), row_builder=row_view, key=item_key)
task_list = (
appui.List([
appui.Section(f"{len(visible_items())} tasks", [rows])
])
.navigation_title("Todo")
.searchable(text=state.query, on_change=set_query)
.toolbar([
appui.ToolbarItem(
placement="navigation_bar_trailing",
content=appui.Button(action=add_item, content=appui.Image(system_name="plus")),
)
])
)
return appui.NavigationStack(
task_list
)
appui.run(body, state=state)
#可复用点
- 搜索状态单独保存。
- 行操作按 id 查找,不依赖筛选后的 index。
- 动态列表替换整个
state.items,避免原地修改导致刷新语义不清。 - 新增、切换、删除、搜索都有真实命名回调。