PythonIDE Docs
中文
简体中文

网络请求列表 MiniApp

加载状态、刷新、错误展示和远端数据列表。

演示 network.get 拉取 JSON、加载/错误状态与可搜索列表展示。

#预期效果

运行后会出现带加载状态的网络列表,刷新、错误展示和空状态都在同一页面闭环。

#适合场景

  • API 列表、远程内容流、搜索结果页。
  • 需要加载中、失败、空结果和下拉刷新状态的页面。

#页面结构

区域结构作用
顶层NavigationStack页面标题和系统搜索栏。
操作区Section + Button用户主动触发网络请求。
结果区Section + ForEach稳定渲染远程数据。
失败/空状态ContentUnavailableView避免请求失败后空白。

#完整示例

python
import appui
import network

state = appui.State(
    loading=False,
    error="",
    query="",
    items=[
        {"id": "sample-1", "title": "示例数据", "subtitle": "点击刷新后替换为接口结果"},
    ],
)


def set_query(value):
    state.query = str(value)


def load_data():
    state.loading = True
    state.error = ""
    try:
        if not network.is_connected():
            state.error = "当前没有网络连接"
            return
        resp = network.get("https://jsonplaceholder.typicode.com/posts", timeout=15)
        if not resp.ok:
            state.error = f"HTTP {resp.status}"
            return
        data = resp.json()
        rows = []
        for item in data[:20]:
            rows.append({
                "id": str(item.get("id", len(rows))),
                "title": str(item.get("title", "Untitled")),
                "subtitle": str(item.get("body", ""))[:120],
            })
        state.items = rows
    except Exception as exc:
        state.error = str(exc)
    finally:
        state.loading = False


def filtered_items():
    q = state.query.strip().lower()
    if not q:
        return state.items
    return [
        item for item in state.items
        if q in item["title"].lower() or q in item["subtitle"].lower()
    ]


def item_key(item):
    return item["id"]


def row(item):
    return appui.VStack(
        [
            appui.Text(item["title"]).font("headline"),
            (
                appui.Text(item["subtitle"])
                .font("subheadline")
                .foreground_color("secondaryLabel")
                .line_limit(2)
            ),
        ],
        alignment="leading",
        spacing=4,
    )


def body():
    result_items = filtered_items()
    result_content = []
    if state.loading:
        result_content.append(appui.ProgressView("正在加载..."))
    elif state.error:
        result_content.append(
            appui.ContentUnavailableView(
                "请求失败",
                system_image="wifi.exclamationmark",
                description=state.error,
            )
        )
    elif not result_items:
        result_content.append(
            appui.ContentUnavailableView(
                "没有结果",
                system_image="doc.text.magnifyingglass",
                description="换个关键词或点击刷新数据。",
            )
        )
    else:
        result_content.append(appui.ForEach(result_items, row_builder=row, key=item_key))

    return appui.NavigationStack(
        appui.List(
            [
                appui.Section(
                    [
                        appui.Button("刷新数据", action=load_data)
                            .button_style("bordered_prominent"),
                    ],
                    header="API",
                    footer="示例接口仅用于演示,请替换为自己的 API。",
                ),
                appui.Section(result_content, header="结果"),
            ]
        )
        .searchable(text=state.query, on_change=set_query)
        .refreshable(load_data)
        .navigation_title("网络列表")
    )


appui.run(body, state=state, presentation="fullscreen_with_close")

#关键技巧

  • 在按钮回调里发起请求,不要在 body() 里直接请求。
  • resp.ok 只表示 HTTP 成功,业务字段仍需自行校验;失败时把 error 文案显示出来避免空白页。
  • 动态远程数据使用 ForEach(..., key=...),不要直接把筛选后的下标当身份。

#可替换点

当前写法可替换为
演示接口你的业务 API
network.get(...)network.request(...) / network.post(...)
state.itemsstorage.get_json(...) 作为离线缓存