Reference version

Expo 小组件

使用 Expo UI 组件构建 iOS 主屏幕小组件和实时活动的库。

iOS

For the complete documentation index, see llms.txt. Use this Use this file to discover all available pages.

重要 此库目前处于 alpha 阶段,并且可能出现破坏性变更。它无法在 Expo Go 应用中使用,请使用 development builds 进行试用。

expo-widgets 可让你在无需编写原生代码的情况下,使用 Expo UI 组件创建 iOS 主屏幕小组件和实时活动。它提供了一个简单的 API,用于创建和更新小组件时间线,以及启动和管理实时活动。布局可以使用 expo/ui 组件和修饰器来构建。

安装

Terminal
npx expo install expo-widgets

If you are installing this in an existing React Native app, make sure to install expo in your project.

在 app config 中配置

如果你的项目中使用了 config plugins(Continuous Native Generation (CNG)),你可以使用 expo-widgets 内置的 config plugin 进行配置。该插件允许你配置一些无法在运行时设置的属性,这些属性需要重新构建新的应用二进制文件后才会生效。

Example app.json with config plugin

app.json
{ "expo": { "plugins": [ [ "expo-widgets", { "widgets": [ { "name": "MyWidget", "displayName": "我的小组件", "description": "一个示例主屏幕小组件", "supportedFamilies": ["systemSmall", "systemMedium", "systemLarge"] } ] } ] ] } }

Configurable properties

NameDefaultDescription
bundleIdentifier"<app bundle identifier>.ExpoWidgetsTarget"

小组件扩展目标的 bundle 标识符。如果未指定,默认为 <main app bundle identifier>.ExpoWidgetsTarget

groupIdentifier"group.<app bundle identifier>"

用于主应用和小组件之间通信及数据共享的 app group 标识符。这是小组件正常工作所必需的。如果未指定,默认为 group.<main app bundle identifier>

enablePushNotificationsfalse

是否为实时活动启用推送通知。启用后,会添加 aps-environment entitlement,并在 Info.plist 中设置 ExpoLiveActivity_EnablePushNotifications

widgets-

小组件配置数组。数组中的每个小组件都会在你的 widget 扩展中生成一个独立的小组件类型。

widgets[].name-

小组件的内部名称(标识符)。它会被用作 Swift 结构体名称,并且应是一个有效的 Swift 标识符(不包含空格或特殊字符)。

widgets[].displayName-

小组件在用户将小组件添加到主屏幕时,于小组件画廊中显示的面向用户的名称。

widgets[].description-

对小组件功能的简短描述。它会显示在小组件画廊中,帮助用户了解小组件的用途。

widgets[].contentMarginsDisabledfalse

当你为小组件禁用内容边距时,系统不会自动在小组件内容周围添加边距,你需要为不同上下文自行指定小组件内容周围的外边距和内边距。

widgets[].supportedFamilies-

该小组件支持的小组件尺寸数组。可用选项:

  • systemSmall - 小号方形小组件(2x2 网格)
  • systemMedium - 中号矩形小组件(4x2 网格)
  • systemLarge - 大号方形小组件(4x4 网格)
  • systemExtraLarge - 超大号小组件(仅 iPad,6x4 网格)
  • accessoryCircular - 锁屏圆形小组件
  • accessoryRectangular - 锁屏矩形小组件
  • accessoryInline - 锁屏行内文本小组件

包含所有选项的完整示例

app.json
{ "expo": { "plugins": [ [ "expo-widgets", { "bundleIdentifier": "com.example.myapp.widgets", "groupIdentifier": "group.com.example.myapp", "enablePushNotifications": true, "widgets": [ { "name": "StatusWidget", "displayName": "状态", "description": "一眼查看当前状态", "contentMarginsDisabled": true, "supportedFamilies": ["systemSmall", "systemMedium"] }, { "name": "DetailWidget", "displayName": "详情", "description": "显示详细信息", "supportedFamilies": ["systemMedium", "systemLarge"] }, { "name": "LockScreenWidget", "displayName": "快速查看", "description": "在锁屏上查看信息", "supportedFamilies": ["accessoryCircular", "accessoryRectangular", "accessoryInline"] } ] } ] ] } }

用法

小组件

前提:创建小组件

先使用 createWidget 函数创建一个 Widget,并传入带有 'widget' 指令标记的组件。该组件会接收你的 widget props 作为第一个参数,以及一个 WidgetEnvironment 对象作为第二个参数。

import { Text, VStack } from '@expo/ui/swift-ui'; import { font, foregroundStyle } from '@expo/ui/swift-ui/modifiers'; import { createWidget, type WidgetEnvironment } from 'expo-widgets'; type MyWidgetProps = { count: number; }; const MyWidget = (props: MyWidgetProps, environment: WidgetEnvironment) => { 'widget'; return ( <VStack> <Text modifiers={[font({ weight: 'bold', size: 16 }), foregroundStyle('#000000')]}> 数量:{props.count} </Text> <Text>家族:{environment.widgetFamily}</Text> </VStack> ); }; export default createWidget('MyWidget', MyWidget);

小组件名称('MyWidget')必须与 app config 中小组件配置里的 name 字段一致。

基础小组件

更新小组件的一种有效方式是使用 updateSnapshot 方法。它会创建一个只包含单个条目的小组件时间线,并立即显示。

下面的示例延续自 创建小组件

import MyWidget from './MyWidget'; // 更新小组件 MyWidget.updateSnapshot({ count: 5 });

时间线小组件

使用 updateTimeline 方法可以在特定时间安排小组件更新。系统会根据时间线自动更新小组件。

下面的示例延续自 创建小组件

import MyWidget from './MyWidget'; MyWidget.updateTimeline([ { date: new Date(), props: { count: 1 } }, { date: new Date(Date.now() + 3600000), props: { count: 2 } }, // 距现在 1 小时 { date: new Date(Date.now() + 7200000), props: { count: 3 } }, // 距现在 2 小时 { date: new Date(Date.now() + 10800000), props: { count: 4 } }, // 距现在 3 小时 ]);

响应式小组件

使用 environment 参数来根据当前小组件尺寸和渲染上下文调整布局。

import { HStack, Text, VStack } from '@expo/ui/swift-ui'; import { createWidget, type WidgetEnvironment } from 'expo-widgets'; type WeatherWidgetProps = { temperature: number; condition: string; }; const WeatherWidget = (props: WeatherWidgetProps, environment: WidgetEnvironment) => { 'widget'; // 根据尺寸渲染不同布局 if (environment.widgetFamily === 'systemSmall') { return ( <VStack> <Text>{props.temperature}°</Text> </VStack> ); } if (environment.widgetFamily === 'systemMedium') { return ( <HStack> <Text>{props.temperature}°</Text> <Text>{props.condition}</Text> </HStack> ); } // systemLarge 及其他 return ( <VStack> <Text>温度:{props.temperature}°</Text> <Text>状况:{props.condition}</Text> <Text>更新于:{environment.date.toLocaleTimeString()}</Text> </VStack> ); }; const Widget = createWidget('WeatherWidget', WeatherWidget); export default Widget; Widget.updateSnapshot({ temperature: 72, condition: '晴朗', });

实时活动

实时活动会在支持的设备上于锁屏和灵动岛中显示实时信息。

前提:创建实时活动

实时活动布局必须先使用 createLiveActivity 创建一次,并标记为 'widget' 指令。该组件会将你的 props 作为第一个参数,并将一个 LiveActivityEnvironment 对象作为第二个参数。

import { Image, Text, VStack } from '@expo/ui/swift-ui'; import { font, foregroundStyle, padding } from '@expo/ui/swift-ui/modifiers'; import { createLiveActivity, type LiveActivityEnvironment } from 'expo-widgets'; type DeliveryActivityProps = { etaMinutes: number; status: string; }; const DeliveryActivity = (props: DeliveryActivityProps, environment: LiveActivityEnvironment) => { 'widget'; const accentColor = environment.colorScheme === 'dark' ? '#FFFFFF' : '#007AFF'; return { banner: ( <VStack modifiers={[padding({ all: 12 })]}> <Text modifiers={[font({ weight: 'bold' }), foregroundStyle(accentColor)]}> {props.status} </Text> <Text>预计到达:{props.etaMinutes} 分钟</Text> </VStack> ), compactLeading: <Image systemName="box.truck.fill" color={accentColor} />, compactTrailing: <Text>{props.etaMinutes} 分钟</Text>, minimal: <Image systemName="box.truck.fill" color={accentColor} />, expandedLeading: ( <VStack modifiers={[padding({ all: 12 })]}> <Image systemName="box.truck.fill" color={accentColor} /> <Text modifiers={[font({ size: 12 })]}>配送中</Text> </VStack> ), expandedTrailing: ( <VStack modifiers={[padding({ all: 12 })]}> <Text modifiers={[font({ weight: 'bold', size: 20 })]}>{props.etaMinutes}</Text> <Text modifiers={[font({ size: 12 })]}>分钟</Text> </VStack> ), expandedBottom: ( <VStack modifiers={[padding({ all: 12 })]}> <Text>司机:John Smith</Text> <Text>订单 #12345</Text> </VStack> ), }; }; export default createLiveActivity('DeliveryActivity', DeliveryActivity);

启动实时活动

下面的示例延续自 创建实时活动

import { Button, View } from 'react-native'; import DeliveryActivity from './DeliveryActivity'; function App() { const startDeliveryTracking = () => { // 启动实时活动 const instance = DeliveryActivity.start( { etaMinutes: 15, status: '你的配送正在路上', }, 'myapp://deliveries/12345' ); // 存储实例 }; return ( <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}> <Button title="开始配送追踪" onPress={startDeliveryTracking} /> </View> ); } export default App;

更新实时活动

下面的示例延续自 启动实时活动

import { LiveActivity } from 'expo-widgets'; function updateDelivery(instance: LiveActivity<DeliveryActivityProps>) { instance.update({ etaMinutes: 2, status: '配送即将送达!', }); }

结束实时活动

使用 end 来结束实时活动。你可以选择取消显示策略,可选地提供最终内容状态,并传入一个 contentDate,这样系统就可以忽略过时更新。

import { after, type LiveActivity } from 'expo-widgets'; async function completeDelivery(instance: LiveActivity<DeliveryActivityProps>) { await instance.end( after(new Date(Date.now() + 15 * 60 * 1000)), { etaMinutes: 0, status: '已送达', }, new Date() ); }

你也可以在取消显示策略中使用 'default''immediate' 来代替 after(date)

用于远程更新的推送令牌

enablePushNotificationstrue 时,使用 addPushToStartTokenListener 接收应用级别的 push-to-start 令牌,并使用 instance.getPushToken()instance.addPushTokenListener() 处理特定实时活动实例。

import { addPushToStartTokenListener } from 'expo-widgets'; import DeliveryActivity from './DeliveryActivity'; const pushToStartSubscription = addPushToStartTokenListener(event => { console.log('Push-to-start token:', event.activityPushToStartToken); }); async function startDeliveryTracking() { const instance = DeliveryActivity.start({ etaMinutes: 15, status: '你的配送正在路上', }); const pushToken = await instance.getPushToken(); console.log('每个活动的令牌:', pushToken); const subscription = instance.addPushTokenListener(event => { console.log('更新后的推送令牌:', event.activityId, event.pushToken); }); // 之后,当你不再需要更新时: subscription.remove(); } // 之后,当你不再需要更新时: pushToStartSubscription.remove();

API

import { createWidget, createLiveActivity } from 'expo-widgets';

Classes

LiveActivity

iOS

Represents a Live Activity instance. Provides methods to update its content and end it.

LiveActivity Methods

addPushTokenListener(listener)

iOS
ParameterTypeDescription
listener(event: PushTokenEvent) => void

Callback invoked when a new push token is available.


Adds a listener for push token update events on this Live Activity instance. The token can be used to send content updates to this specific activity via APNs.

Returns:
EventSubscription

An event subscription that can be used to remove the listener.

end(dismissalPolicy, props, contentDate)

iOS
ParameterTypeDescription
dismissalPolicy(optional)LiveActivityDismissalPolicy

Controls when the Live Activity is removed from the Lock Screen after ending. Can be 'default', 'immediate', or after(date).

props(optional)T

Final content properties to update after the activity ends.

contentDate(optional)Date

The time the data in the payload was generated. If this is older than a previous update or push payload, the system ignores this update.


Ends the Live Activity.

Returns:
Promise<void>

getPushToken()

iOS

Returns the push token for this Live Activity, used to send push notification updates via APNs. Returns null if push notifications are not enabled or the token is not yet available.

Returns:
Promise<string | null>

update(props)

iOS
ParameterTypeDescription
propsT

The updated content properties.


Updates the Live Activity's content. The UI reflects the new properties immediately.

Returns:
Promise<void>

LiveActivityFactory

iOS

Manages Live Activity instances of a specific type. Use it to start new activities and retrieve currently active ones.

LiveActivityFactory Methods

getInstances()

iOS

Returns all currently active instances of this Live Activity type.

start(props, url)

iOS
ParameterTypeDescription
propsT

The initial content properties for the Live Activity.

url(optional)string

An optional URL to associate with the Live Activity, used for deep linking.


Starts a new Live Activity with the given properties.

Returns:
LiveActivity<T>

The new Live Activity instance.

Widget

iOS

Represents a widget instance. Provides methods to manage the widget's timeline.

Widget Methods

getTimeline()

iOS

Returns the current timeline entries for the widget, including past and future entries.

reload()

iOS

Force reloads the widget, causing it to refresh its content and timeline.

Returns:
void

updateSnapshot(props)

iOS
ParameterTypeDescription
propsT

The properties to display in the widget.


Sets the widget's content to the given props immediately, without scheduling a timeline.

Returns:
void

updateTimeline(entries)

iOS
ParameterTypeDescription
entriesWidgetTimelineEntry[]

Timeline entries, each specifying a date and the props to display at that time.


Schedules a series of updates for the widget's content and reloads the widget.

Returns:
void

Methods

createLiveActivity(name, liveActivity)

iOS
ParameterTypeDescription
namestring

The Live Activity name. Must match the 'name' field in your widget configuration in the app config.

liveActivityLiveActivityComponent<T>

The Live Activity component, marked with the 'widget' directive.


Creates a Live Activity Factory for managing Live Activities of a specific type.

createWidget(name, widget)

iOS
ParameterTypeDescription
namestring

The widget name. Must match the 'name' field in your widget configuration in the app config.

widget(props: T, context: WidgetEnvironment) => Element

The widget component, marked with the 'widget' directive.


Creates a Widget instance.

Returns:
Widget<T>

Event Subscriptions

addPushToStartTokenListener(listener)

iOS
ParameterTypeDescription
listener(event: PushToStartTokenEvent) => void

Callback function to handle push-to-start token events.


Adds a listener for push-to-start token events. This token can be used to start live activities remotely via APNs.

Returns:
EventSubscription

An event subscription that can be used to remove the listener.

addUserInteractionListener(listener)

iOS
ParameterTypeDescription
listener(event: UserInteractionEvent) => void

Callback function to handle user interaction events.


Adds a listener for widget interaction events (for example, button taps).

Returns:
EventSubscription

An event subscription that can be used to remove the listener.

Types

ExpoWidgetsEvents

iOS
PropertyTypeDescription
onExpoWidgetsPushToStartTokenReceived(event: PushToStartTokenEvent) => void

Function that is invoked when a push-to-start token is received.

event: PushToStartTokenEvent

Token event details.

onExpoWidgetsUserInteraction(event: UserInteractionEvent) => void

Function that is invoked when user interacts with a widget.

event: UserInteractionEvent

Interaction event details.

LevelOfDetail

iOS 26+

Literal Type: string

The level of detail the view is recommended to have. The system can update the levelOfDetail value based on user proximity or other system specific factors and allow content customization adapting to show different levels of details.

  • simplified — The system recommends showing a simplified view with less details.
  • default — The system has no specific recommendation for the level of detail.

Acceptable values are: 'simplified' | 'default'

LiveActivityComponent(props, environment)

iOS

A function that returns the layout for a Live Activity.

ParameterType
propsT
environmentLiveActivityEnvironment

LiveActivityDismissalPolicy

iOS

Literal Type: union

Dismissal policy for ending a live activity.

  • 'default' - The system’s default dismissal policy for the Live Activity.
  • 'immediate' - The system immediately removes the Live Activity that ended.
  • after(date) - The system removes the Live Activity that ended at the specified time within a four-hour window.

Acceptable values are: 'default' | 'immediate' | ReturnType<after>

LiveActivityEvents

iOS
PropertyTypeDescription
onExpoWidgetsTokenReceived(event: PushTokenEvent) => void

Function that is invoked when a push token is received for a live activity.

event: PushTokenEvent

Token event details.

LiveActivityLayout

iOS

Defines the layout sections for an iOS Live Activity.

PropertyTypeDescription
bannerReactNode

The main banner content displayed in Notifications Center.

bannerSmall(optional)ReactNode

The small banner content displayed in CarPlay and WatchOS. Falls back to banner if not provided.

compactLeading(optional)ReactNode

The leading content in the compact Dynamic Island presentation.

compactTrailing(optional)ReactNode

The trailing content in the compact Dynamic Island presentation.

expandedBottom(optional)ReactNode

The bottom content in the expanded Dynamic Island presentation.

expandedCenter(optional)ReactNode

The center content in the expanded Dynamic Island presentation.

expandedLeading(optional)ReactNode

The leading content in the expanded Dynamic Island presentation.

expandedTrailing(optional)ReactNode

The trailing content in the expanded Dynamic Island presentation.

minimal(optional)ReactNode

The minimal content shown when the Dynamic Island is in its smallest form.

PushTokenEvent

iOS

Event emitted when a push token is received for a live activity.

PropertyTypeDescription
activityIdstring

The ID of the live activity.

pushTokenstring

The push token for the live activity.

PushToStartTokenEvent

iOS

Event emitted when a push-to-start token is received.

PropertyTypeDescription
activityPushToStartTokenstring

The push-to-start token for starting live activities remotely.

UserInteractionEvent

iOS

Event emitted when a user interacts with a widget.

PropertyTypeDescription
sourcestring

Widget that triggered the interaction.

targetstring

Button/toggle that was pressed.

timestampnumber

Timestamp of the event.

type'ExpoWidgetsUserInteraction'

The event type identifier.

WidgetEnvironment

iOS
PropertyTypeDescription
colorScheme(optional)'light' | 'dark'

The color scheme of the widget's environment.

dateDate

The date of this timeline entry.

isLuminanceReduced(optional)boolean
Only for:
iOS 16+

A Boolean value that indicates whether the display or environment currently requires reduced luminance.

When you detect this condition, lower the overall brightness of your view. For example, you can change large, filled shapes to be stroked, and choose less bright colors.

levelOfDetail(optional)LevelOfDetail
Only for:
iOS 26+

The level of detail the view is recommended to have.

showsWidgetLabel(optional)boolean
Only for:
iOS 16+

A Boolean value that indicates whether an accessory family widget can display an accessory label.

widgetContentMargins(optional){ bottom: number, leading: number, top: number, trailing: number }
Only for:
iOS 17+

The content margins for the widget.

widgetFamilyWidgetFamily

The widget family.

widgetRenderingMode(optional)WidgetRenderingMode
Only for:
iOS 16+

The widget's rendering mode, based on where the system is displaying it.

WidgetFamily

iOS

Literal Type: string

The widget family (size).

  • systemSmall - Small square widget (2x2 grid).
  • systemMedium - Medium widget (4x2 grid).
  • systemLarge - Large widget (4x4 grid).
  • systemExtraLarge - Extra large widget (iPad only, 6x4 grid).
  • accessoryCircular - Circular accessory widget for the Lock Screen.
  • accessoryRectangular - Rectangular accessory widget for the Lock Screen.
  • accessoryInline - Inline accessory widget for the Lock Screen.

Acceptable values are: 'systemSmall' | 'systemMedium' | 'systemLarge' | 'systemExtraLarge' | 'accessoryCircular' | 'accessoryRectangular' | 'accessoryInline'

WidgetRenderingMode

iOS

Literal Type: string

The rendering mode of the widget as provided by WidgetKit.

  • fullColor — Home screen widgets (default).
  • accented — Tinted widgets (iOS 18+) and watchOS.
  • vibrant — Lock screen widgets.

Acceptable values are: 'fullColor' | 'accented' | 'vibrant'

WidgetTimelineEntry

iOS
PropertyTypeDescription
dateDate

Date when widget should update.

propsT

Props to be passed to the widget.