Skip to content
My custom sidebar

创建小组件

使用 Widget() 函数定义主屏小组件。

小组件声明

typescript
import { Widget, Text, VStack, HStack, Image, Spacer } from "perry/widget";

Widget({
  kind: "WeatherWidget",
  displayName: "Weather",
  description: "Shows current weather",
  entryFields: {
    temperature: "number",
    condition: "string",
    location: "string",
  },
  render: (entry) =>
    VStack([
      HStack([
        Text(entry.location),
        Spacer(),
        Image("cloud.sun.fill"),
      ]),
      Text(`${entry.temperature}°`),
      Text(entry.condition),
    ]),
});

小组件配置项

属性类型描述
kindstring小组件的唯一标识符
displayNamestring小组件库中显示的名称
descriptionstring小组件库中的描述信息
entryFieldsobject带类型的数据字段(支持 "string""number""boolean"、数组、可选类型、对象)
renderfunction接收条目数据的渲染函数,返回小组件树结构。第二个参数为可选参数,用于指定小组件尺寸类型。
configobject用户可编辑的配置参数(见下文)
providerfunction用于为小组件获取动态数据的时间线提供器函数(见下文)
appGroupstring用于与宿主应用共享数据的应用组标识符

条目字段

条目字段用于定义小组件展示的数据,每个字段包含名称和类型:

typescript
entryFields: {
  title: "string",
  count: "number",
  isActive: "boolean",
}

数组、可选及对象字段

条目字段支持除基本类型外更丰富的类型:

typescript
entryFields: {
  items: [{ name: "string", value: "number" }],  // 对象数组
  subtitle: "string?",                             // 可选字符串
  stats: { wins: "number", losses: "number" },     // 嵌套对象
}

这些字段会编译为 Swift 中的 TimelineEntry 结构体:

swift
struct WeatherEntry: TimelineEntry {
    let date: Date
    let temperature: Double
    let condition: String
    let location: String
}

渲染中的条件判断

可使用三元表达式实现条件渲染:

typescript
render: (entry) =>
  VStack([
    Text(entry.isActive ? "Active" : "Inactive"),
    entry.count > 0 ? Text(`${entry.count} items`) : Spacer(),
  ]),

模板字面量

小组件文本中的模板字面量会编译为 Swift 字符串插值:

typescript
Text(`${entry.name}: ${entry.score} points`)
// 编译后:Text("\(entry.name): \(entry.score) points")

配置参数

config 字段用于定义出现在小组件编辑界面中的用户可编辑参数:

typescript
Widget({
  kind: "CityWeather",
  displayName: "City Weather",
  description: "Weather for a chosen city",
  config: {
    city: { type: "string", displayName: "City", default: "New York" },
    units: { type: "enum", displayName: "Units", values: ["Celsius", "Fahrenheit"], default: "Celsius" },
  },
  entryFields: { temperature: "number", condition: "string" },
  render: (entry) => Text(`${entry.temperature}° ${entry.condition}`),
});

提供器函数

provider 字段用于定义为小组件获取数据的时间线提供器:

typescript
Widget({
  kind: "StockWidget",
  displayName: "Stock Price",
  description: "Shows current stock price",
  config: { symbol: { type: "string", displayName: "Symbol", default: "AAPL" } },
  entryFields: { price: "number", change: "string" },
  provider: async (config) => {
    const res = await fetch(`https://api.example.com/stock/${config.symbol}`);
    const data = await res.json();
    return { price: data.price, change: data.change };
  },
  render: (entry) =>
    VStack([
      Text(`$${entry.price}`).font("title"),
      Text(entry.change).color("green"),
    ]),
});

占位数据

当小组件暂未获取到数据时(如首次加载),提供器可通过返回 placeholder 字段来设置占位数据:

typescript
Widget({
  kind: "NewsWidget",
  entryFields: { headline: "string", source: "string" },
  placeholder: { headline: "Loading...", source: "---" },
  // ...
});

按尺寸类型定制渲染

渲染函数的第二个可选参数为小组件尺寸类型,可针对不同尺寸实现差异化布局:

typescript
render: (entry, family) =>
  family === "systemLarge"
    ? VStack([
        Text(entry.title).font("title"),
        ForEach(entry.items, (item) => Text(item.name)),
      ])
    : HStack([
        Image("star.fill"),
        Text(entry.title).font("headline"),
      ]),

支持的尺寸类型:"systemSmall""systemMedium""systemLarge""accessoryCircular""accessoryRectangular""accessoryInline"

应用组

appGroup 字段用于指定宿主应用与小组件之间进行数据交换的共享容器:

typescript
Widget({
  kind: "AppDataWidget",
  appGroup: "group.com.example.myapp",
  // ...
});

多小组件定义

可在单个文件中定义多个小组件,它们会被打包至一个 WidgetBundle 中:

typescript
Widget({
  kind: "SmallWidget",
  // ...
});

Widget({
  kind: "LargeWidget",
  // ...
});

后续参考

MIT License.