1diff --git a/build/ExpoWidgets.js b/build/ExpoWidgets.js
2index 85f3fbd..05b8dd8 100644
3--- a/build/ExpoWidgets.js
4+++ b/build/ExpoWidgets.js
5@@ -34,6 +34,7 @@ class LiveActivityFactoryStub {
6 getInstances() {
7 return [];
8 }
9+ dismissEnded() {}
10 }
11 class CustomNotificationStub {
12 getId() {
13diff --git a/build/Widgets.d.ts b/build/Widgets.d.ts
14index fb37d5d..47a97af 100644
15--- a/build/Widgets.d.ts
16+++ b/build/Widgets.d.ts
17@@ -100,6 +100,8 @@ export declare class LiveActivityFactory<T extends object = object> {
18 * @platform ios
19 */
20 getInstances(): LiveActivity<T>[];
21+ /** Dismisses ended instances that iOS still shows on the Lock Screen. */
22+ dismissEnded(): void;
23 }
24 /**
25 * Creates a dismissal policy that removes the Live Activity at the specified time within a four-hour window.
26diff --git a/build/Widgets.js b/build/Widgets.js
27index cc1efce..3ce8897 100644
28--- a/build/Widgets.js
29+++ b/build/Widgets.js
30@@ -134,6 +134,9 @@ import ExpoWidgetsModule from './ExpoWidgets';
31 */ getInstances() {
32 return this.nativeLiveActivityFactory.getInstances().map((instance)=>new LiveActivity(instance));
33 }
34+ dismissEnded() {
35+ this.nativeLiveActivityFactory.dismissEnded();
36+ }
37 }
38 /**
39 * Creates a dismissal policy that removes the Live Activity at the specified time within a four-hour window.
40diff --git a/build/Widgets.types.d.ts b/build/Widgets.types.d.ts
41index 4c474bb..dbfb8b1 100644
42--- a/build/Widgets.types.d.ts
43+++ b/build/Widgets.types.d.ts
44@@ -98,6 +98,10 @@ export type LiveActivityEnvironment = {
45 * @platform ios
46 */
47 colorScheme: 'light' | 'dark';
48+ /** The rendering mode supplied by WidgetKit for this presentation. */
49+ widgetRenderingMode?: WidgetRenderingMode;
50+ /** Whether the presentation OS supports Liquid Glass. @platform iOS 26+ */
51+ isLiquidGlassAvailable?: boolean;
52 /**
53 * Whether the activity is displayed in a context with reduced luminance.
54 * @platform iOS 16+
55@@ -307,6 +311,7 @@ export declare class NativeLiveActivityFactory extends SharedObject {
56 constructor(name: string, layout: string);
57 start(props?: string, url?: string, staleDate?: number): NativeLiveActivity;
58 getInstances(): NativeLiveActivity[];
59+ dismissEnded(): void;
60 }
61 export declare class NativeLiveActivity extends SharedObject<LiveActivityEvents> {
62 getId(): string;
63diff --git a/ios/LiveActivityFactory.swift b/ios/LiveActivityFactory.swift
64index 29e4f15..629e547 100644
65--- a/ios/LiveActivityFactory.swift
66+++ b/ios/LiveActivityFactory.swift
67@@ -14,7 +14,16 @@ final class LiveActivityFactory: SharedObject {
68 WidgetsStorage.set(layout, forKey: "__expo_widgets_live_activity_\(name)_layout")
69 }
71+ // iOS ends an activity after 8 hours but leaves it frozen on the Lock Screen for up to
72+ // 4 more. getInstances() can't see it, so dismiss the ended ones directly.
73+ func dismissEnded() {
74+ for activity in Activity<LiveActivityAttributes>.activities
75+ where activity.content.state.name == name && activity.activityState == .ended {
76+ Task { await activity.end(nil, dismissalPolicy: .immediate) }
77+ }
78+ }
79+
80 func start(props: String?, url: URL?, staleDate: Date?) throws -> LiveActivity {
81 guard ActivityAuthorizationInfo().areActivitiesEnabled else {
82 throw LiveActivitiesNotSupportedException()
83 }
84@@ -22,3 +31,6 @@ final class LiveActivityFactory: SharedObject {
85+ // A replacement started beside an ended card would leave both visible.
86+ dismissEnded()
87+
88 do {
89 let initialState = LiveActivityAttributes.ContentState(name: name, props: props)
90 let activity = try Activity.request(
91diff --git a/ios/Widgets/Utils.swift b/ios/Widgets/Utils.swift
92index 76b2199..6cb52c3 100644
93--- a/ios/Widgets/Utils.swift
94+++ b/ios/Widgets/Utils.swift
95@@ -125,6 +125,10 @@ func getLiveActivityEnvironment(for environment: EnvironmentValues, in context:
96 "isStale": context.isStale
97 ]
99+ if #available(iOS 26.0, *) {
100+ env["isLiquidGlassAvailable"] = true
101+ }
102+ env["widgetRenderingMode"] = environment.widgetRenderingMode.description
103 if #available(iOS 18.0, *) {
104 env["isActivityUpdateReduced"] = environment.isActivityUpdateReduced
105 env["activityFamily"] = "\(environment.activityFamily)"
106diff --git a/ios/Widgets/WidgetLiveActivity.swift b/ios/Widgets/WidgetLiveActivity.swift
107index 4d135c4..dae4744 100644
108--- a/ios/Widgets/WidgetLiveActivity.swift
109+++ b/ios/Widgets/WidgetLiveActivity.swift
110@@ -19,53 +19,41 @@ struct LiveActivityAttributes: ActivityAttributes {
111 }
113 public struct WidgetLiveActivity: Widget {
114- @Environment(\.self) var env
115-
116 let widgetContext: AppContext = AppContext()
118 public init() {}
120 public var body: some WidgetConfiguration {
121 ActivityConfiguration(for: LiveActivityAttributes.self) { context in
122- let nodes = getLiveActivityNodes(
123- forName: context.state.name,
124- props: context.state.props,
125- environment: getLiveActivityEnvironment(for: env, in: context)
126- )
127 // Only apply widgetURL when the activity has one: a hierarchy with more than one
128 // widgetURL modifier is undefined behavior, and layouts can set their own through
129 // the widgetURL modifier from @expo/ui.
130- let banner = LiveActivityBannerView(context: context, nodes: nodes)
131+ let banner = LiveActivityBannerView(context: context)
132 if let url = context.attributes.url.flatMap(URL.init(string:)) {
133 banner.widgetURL(url)
134 } else {
135 banner
136 }
137 } dynamicIsland: { context in
138- let nodes = getLiveActivityNodes(
139- forName: context.state.name,
140- props: context.state.props,
141- environment: getLiveActivityEnvironment(for: env, in: context)
142- )
143 let island = DynamicIsland {
144 DynamicIslandExpandedRegion(.center) {
145- LiveActivitySectionView(context: context, nodes: nodes, sectionName: "expandedCenter")
146+ LiveActivitySectionView(context: context, sectionName: "expandedCenter")
147 }
148 DynamicIslandExpandedRegion(.leading) {
149- LiveActivitySectionView(context: context, nodes: nodes, sectionName: "expandedLeading")
150+ LiveActivitySectionView(context: context, sectionName: "expandedLeading")
151 }
152 DynamicIslandExpandedRegion(.trailing) {
153- LiveActivitySectionView(context: context, nodes: nodes, sectionName: "expandedTrailing")
154+ LiveActivitySectionView(context: context, sectionName: "expandedTrailing")
155 }
156 DynamicIslandExpandedRegion(.bottom) {
157- LiveActivitySectionView(context: context, nodes: nodes, sectionName: "expandedBottom")
158+ LiveActivitySectionView(context: context, sectionName: "expandedBottom")
159 }
160 } compactLeading: {
161- LiveActivitySectionView(context: context, nodes: nodes, sectionName: "compactLeading")
162+ LiveActivitySectionView(context: context, sectionName: "compactLeading")
163 } compactTrailing: {
164- LiveActivitySectionView(context: context, nodes: nodes, sectionName: "compactTrailing")
165+ LiveActivitySectionView(context: context, sectionName: "compactTrailing")
166 } minimal: {
167- LiveActivitySectionView(context: context, nodes: nodes, sectionName: "minimal")
168+ LiveActivitySectionView(context: context, sectionName: "minimal")
169 }
170 if let url = context.attributes.url.flatMap(URL.init(string:)) {
171 return island.widgetURL(url)
172@@ -77,11 +65,16 @@ public struct WidgetLiveActivity: Widget {
173 }
175 private struct LiveActivitySectionView: View {
176+ @Environment(\.self) var env
177 let context: ActivityViewContext<LiveActivityAttributes>
178- let nodes: [String: Any]
179 let sectionName: String
181 var body: some View {
182+ let nodes = getLiveActivityNodes(
183+ forName: context.state.name,
184+ props: context.state.props,
185+ environment: getLiveActivityEnvironment(for: env, in: context)
186+ )
187 if let node = nodes[sectionName] as? [String: Any] {
188 WidgetsDynamicView(name: context.activityID, kind: .liveActivity, node: node)
189 } else {
190@@ -91,10 +84,17 @@ private struct LiveActivitySectionView: View {
191 }
193 private struct LiveActivityBannerView: View {
194+ // Read inside the rendered view: Widget-level values do not include the
195+ // presentation's color scheme, rendering mode, or reduced-luminance state.
196+ @Environment(\.self) var env
197 var context: ActivityViewContext<LiveActivityAttributes>
198- let nodes: [String: Any]
200 var body: some View {
201+ let nodes = getLiveActivityNodes(
202+ forName: context.state.name,
203+ props: context.state.props,
204+ environment: getLiveActivityEnvironment(for: env, in: context)
205+ )
206 if #available(iOS 18.0, *) {
207 LiveActivityBanner(context: context, nodes: nodes)
208 } else if let node = nodes["banner"] as? [String: Any] {
209diff --git a/ios/WidgetsModule.swift b/ios/WidgetsModule.swift
210index 285136b..d36eac4 100644
211--- a/ios/WidgetsModule.swift
212+++ b/ios/WidgetsModule.swift
213@@ -97,4 +97,8 @@ public final class WidgetsModule: Module {
214 Function("getInstances") { (liveActivity: LiveActivityFactory) in
215 try liveActivity.getInstances()
216 }
217+
218+ Function("dismissEnded") { (liveActivity: LiveActivityFactory) in
219+ liveActivity.dismissEnded()
220+ }
221 }
222diff --git a/src/ExpoWidgets.ts b/src/ExpoWidgets.ts
223index db15000..6863dc9 100644
224--- a/src/ExpoWidgets.ts
225+++ b/src/ExpoWidgets.ts
226@@ -59,4 +59,5 @@ class LiveActivityFactoryStub {
227 getInstances(): NativeLiveActivity[] {
228 return [];
229 }
230+ dismissEnded(): void {}
231 }
232diff --git a/src/Widgets.ts b/src/Widgets.ts
233index 814b312..9818907 100644
234--- a/src/Widgets.ts
235+++ b/src/Widgets.ts
236@@ -213,4 +213,9 @@ export class LiveActivityFactory<T extends object = object> {
237 .getInstances()
238 .map((instance) => new LiveActivity<T>(instance));
239 }
240+
241+ /** Dismisses ended instances that iOS still shows on the Lock Screen. */
242+ dismissEnded(): void {
243+ this.nativeLiveActivityFactory.dismissEnded();
244+ }
245 }
246diff --git a/src/Widgets.types.ts b/src/Widgets.types.ts
247index 690d069..c60dd79 100644
248--- a/src/Widgets.types.ts
249+++ b/src/Widgets.types.ts
250@@ -112,6 +112,10 @@ export type LiveActivityEnvironment = {
251 * @platform ios
252 */
253 colorScheme: 'light' | 'dark';
254+ /** The rendering mode supplied by WidgetKit for this presentation. */
255+ widgetRenderingMode?: WidgetRenderingMode;
256+ /** Whether the presentation OS supports Liquid Glass. @platform iOS 26+ */
257+ isLiquidGlassAvailable?: boolean;
258 /**
259 * Whether the activity is displayed in a context with reduced luminance.
260 * @platform iOS 16+
261@@ -337,4 +341,5 @@ export declare class NativeLiveActivityFactory extends SharedObject {
262 constructor(name: string, layout: string);
263 start(props?: string, url?: string, staleDate?: number): NativeLiveActivity;
264 getInstances(): NativeLiveActivity[];
265+ dismissEnded(): void;
266 }