diff --git a/apps/expo-go/android/expoview/src/main/java/versioned/host/exp/exponent/ExperiencePackagePicker.kt b/apps/expo-go/android/expoview/src/main/java/versioned/host/exp/exponent/ExperiencePackagePicker.kt
index 4fb6183399b87a..2284e0613e54fc 100644
--- a/apps/expo-go/android/expoview/src/main/java/versioned/host/exp/exponent/ExperiencePackagePicker.kt
+++ b/apps/expo-go/android/expoview/src/main/java/versioned/host/exp/exponent/ExperiencePackagePicker.kt
@@ -80,6 +80,7 @@ import expo.modules.systemui.SystemUIPackage
import expo.modules.taskManager.TaskManagerModule
import expo.modules.taskManager.TaskManagerPackage
import expo.modules.trackingtransparency.TrackingTransparencyModule
+import expo.modules.ui.ExpoUIModule
import expo.modules.updates.UpdatesPackage
import expo.modules.video.VideoModule
import expo.modules.videothumbnails.VideoThumbnailsModule
@@ -156,6 +157,7 @@ object ExperiencePackagePicker : ModulesProvider {
FontUtilsModule::class.java to null,
ExpoLinkingModule::class.java to null,
ExpoRouterModule::class.java to null,
+ ExpoUIModule::class.java to null,
FileSystemModule::class.java to null,
FileSystemLegacyModule::class.java to null,
FontLoaderModule::class.java to null,
diff --git a/apps/expo-go/android/settings.gradle b/apps/expo-go/android/settings.gradle
index e8248465b187e1..971462fe5bc490 100644
--- a/apps/expo-go/android/settings.gradle
+++ b/apps/expo-go/android/settings.gradle
@@ -39,7 +39,6 @@ expoAutolinking.exclude = [
'expo-maps',
'expo-network-addons',
'expo-splash-screen',
- '@expo/ui',
'expo-mesh-gradient',
'@expo/app-integrity',
'@expo/home',
diff --git a/apps/expo-go/ios/Podfile b/apps/expo-go/ios/Podfile
index 774f9b8c7dd7a7..f27e949e88ea6a 100644
--- a/apps/expo-go/ios/Podfile
+++ b/apps/expo-go/ios/Podfile
@@ -56,7 +56,6 @@ target 'Expo Go' do
'expo-network-addons',
'expo-insights',
'expo-splash-screen',
- '@expo/ui',
'@expo/app-integrity',
'expo-brownfield',
'expo-widgets'
diff --git a/apps/expo-go/package.json b/apps/expo-go/package.json
index b47f8eb91b91bb..a769a9ddad0917 100644
--- a/apps/expo-go/package.json
+++ b/apps/expo-go/package.json
@@ -7,6 +7,7 @@
"author": "Expo",
"license": "MIT",
"dependencies": {
+ "@expo/ui": "workspace:*",
"@expo/vector-icons": "^15.0.2",
"@react-native-async-storage/async-storage": "2.2.0",
"@react-native-community/datetimepicker": "^9.1.0",
diff --git a/docs/pages/versions/unversioned/sdk/ui/jetpack-compose/index.mdx b/docs/pages/versions/unversioned/sdk/ui/jetpack-compose/index.mdx
index dfdcbcf33041f7..85d7725027ece4 100644
--- a/docs/pages/versions/unversioned/sdk/ui/jetpack-compose/index.mdx
+++ b/docs/pages/versions/unversioned/sdk/ui/jetpack-compose/index.mdx
@@ -5,14 +5,11 @@ description: Jetpack Compose components for building native Android interfaces w
sourceCodeUrl: 'https://github.com/expo/expo/tree/main/packages/expo-ui'
packageName: '@expo/ui'
platforms: ['android', 'expo-go']
-isAlpha: true
hideTOC: true
---
import { APIInstallSection } from '~/components/plugins/InstallSection';
-> **important** **This library is currently in [alpha](/more/release-statuses/#alpha) and will frequently experience breaking changes.** It is not available in the Expo Go app — use [development builds](/develop/development-builds/introduction/) to try it out.
-
The Jetpack Compose components in `@expo/ui/jetpack-compose` allow you to build fully native Android interfaces using Jetpack Compose from React Native.
## Installation
diff --git a/docs/pages/versions/unversioned/sdk/ui/jetpack-compose/usenativestate.mdx b/docs/pages/versions/unversioned/sdk/ui/jetpack-compose/usenativestate.mdx
new file mode 100644
index 00000000000000..2a83c9c54159ce
--- /dev/null
+++ b/docs/pages/versions/unversioned/sdk/ui/jetpack-compose/usenativestate.mdx
@@ -0,0 +1,81 @@
+---
+title: useNativeState
+description: A React hook that creates observable state shared between JavaScript and native Jetpack Compose views.
+sourceCodeUrl: 'https://github.com/expo/expo/tree/main/packages/expo-ui/src/State/useNativeState.ts'
+packageName: '@expo/ui'
+platforms: ['android', 'expo-go']
+---
+
+import APISection from '~/components/plugins/APISection';
+import { APIInstallSection } from '~/components/plugins/InstallSection';
+
+`useNativeState` returns an [`ObservableState`](#observablestate) that maps to a Compose [`MutableState`](https://developer.android.com/reference/kotlin/androidx/compose/runtime/MutableState) on the native side, so reads and writes to `.value` are tracked directly by Compose without going through the React render cycle. This lets you update the native view synchronously from a worklet on the UI thread.
+
+## Installation
+
+
+
+## Usage
+
+> **Note:** Using worklets requires installing [`react-native-reanimated`](https://docs.swmansion.com/react-native-reanimated/) and [`react-native-worklets`](https://docs.swmansion.com/react-native-worklets/) in your project. `useNativeState` itself works without them, but the synchronous UI-thread updates shown below depend on the worklet runtime.
+
+The example below masks a phone number as the user types. The formatting and the write to `maskedPhone.value` both happen synchronously on the UI thread, so there is no flicker between the typed value and the masked value.
+
+```tsx WorkletPhoneMaskExample.tsx
+import {
+ Host,
+ TextField,
+ TextFieldValue,
+ Text as ComposeText,
+ useNativeState,
+} from '@expo/ui/jetpack-compose';
+import { fillMaxWidth } from '@expo/ui/jetpack-compose/modifiers';
+
+export default function WorkletPhoneMaskExample() {
+ const maskedPhone = useNativeState({
+ text: '',
+ selection: { start: 0, end: 0 },
+ });
+
+ return (
+
+ {
+ 'worklet';
+ const digits = v.text.replace(/\D/g, '').slice(0, 10);
+ let formatted: string;
+ if (digits.length === 0) {
+ formatted = '';
+ } else if (digits.length <= 3) {
+ formatted = digits;
+ } else if (digits.length <= 6) {
+ formatted = `(${digits.slice(0, 3)}) ${digits.slice(3)}`;
+ } else {
+ formatted = `(${digits.slice(0, 3)}) ${digits.slice(3, 6)}-${digits.slice(6)}`;
+ }
+ if (formatted !== v.text) {
+ maskedPhone.value = {
+ text: formatted,
+ selection: { start: formatted.length, end: formatted.length },
+ };
+ }
+ }}>
+
+ (555) 123-4567
+
+
+
+ );
+}
+```
+
+## API
+
+```tsx
+import { useNativeState } from '@expo/ui/jetpack-compose';
+```
+
+
diff --git a/docs/pages/versions/unversioned/sdk/ui/swift-ui/index.mdx b/docs/pages/versions/unversioned/sdk/ui/swift-ui/index.mdx
index 34e297dac82ec8..b1fcece2db72bc 100644
--- a/docs/pages/versions/unversioned/sdk/ui/swift-ui/index.mdx
+++ b/docs/pages/versions/unversioned/sdk/ui/swift-ui/index.mdx
@@ -5,7 +5,6 @@ description: SwiftUI components for building native iOS interfaces with @expo/ui
sourceCodeUrl: 'https://github.com/expo/expo/tree/main/packages/expo-ui'
packageName: '@expo/ui'
platforms: ['ios', 'tvos', 'expo-go']
-isBeta: true
hasVideoLink: true
---
@@ -16,8 +15,6 @@ import { BoxLink } from '~/ui/components/BoxLink';
import { CODE } from '~/ui/components/Text';
import { VideoBoxLink } from '~/ui/components/VideoBoxLink';
-> **important** **This library is currently in [beta](/more/release-statuses/#beta) and subject to breaking changes.** It is not available in the Expo Go app — use [development builds](/develop/development-builds/introduction/) to try it out.
-
The SwiftUI components in `@expo/ui/swift-ui` allow you to build fully native iOS interfaces using SwiftUI from React Native.
## Installation
diff --git a/docs/pages/versions/unversioned/sdk/ui/swift-ui/usenativestate.mdx b/docs/pages/versions/unversioned/sdk/ui/swift-ui/usenativestate.mdx
new file mode 100644
index 00000000000000..b3d13813f6f9bb
--- /dev/null
+++ b/docs/pages/versions/unversioned/sdk/ui/swift-ui/usenativestate.mdx
@@ -0,0 +1,75 @@
+---
+title: useNativeState
+description: A React hook that creates observable state shared between JavaScript and native SwiftUI views.
+sourceCodeUrl: 'https://github.com/expo/expo/tree/main/packages/expo-ui/src/State/useNativeState.ts'
+packageName: '@expo/ui'
+platforms: ['ios', 'tvos', 'expo-go']
+---
+
+import APISection from '~/components/plugins/APISection';
+import { APIInstallSection } from '~/components/plugins/InstallSection';
+
+`useNativeState` returns an [`ObservableState`](#observablestate) that maps to a SwiftUI [`ObservableObject`](https://developer.apple.com/documentation/combine/observableobject) on the native side, so reads and writes to `.value` are observed directly by SwiftUI without going through the React render cycle. This lets you update the native view synchronously from a worklet on the UI thread.
+
+## Installation
+
+
+
+## Usage
+
+> **Note:** Using worklets requires installing [`react-native-reanimated`](https://docs.swmansion.com/react-native-reanimated/) and [`react-native-worklets`](https://docs.swmansion.com/react-native-worklets/) in your project. `useNativeState` itself works without them, but the synchronous UI-thread updates shown below depend on the worklet runtime.
+
+The example below masks a phone number as the user types. The formatting and the write to `maskedPhone.value` both happen synchronously on the UI thread, so there is no flicker between the typed value and the masked value.
+
+```tsx WorkletPhoneMaskExample.tsx
+import { Host, TextField, TextFieldRef, useNativeState } from '@expo/ui/swift-ui';
+import { keyboardType } from '@expo/ui/swift-ui/modifiers';
+import { useCallback, useRef } from 'react';
+import { runOnJS } from 'react-native-worklets';
+
+export default function WorkletPhoneMaskExample() {
+ const phoneRef = useRef(null);
+ const maskedPhone = useNativeState('');
+
+ const setPhoneCursor = useCallback((position: number) => {
+ phoneRef.current?.setSelection(position, position);
+ }, []);
+
+ return (
+
+ {
+ 'worklet';
+ const digits = v.replace(/\D/g, '').slice(0, 10);
+ let formatted: string;
+ if (digits.length === 0) {
+ formatted = '';
+ } else if (digits.length <= 3) {
+ formatted = digits;
+ } else if (digits.length <= 6) {
+ formatted = `(${digits.slice(0, 3)}) ${digits.slice(3)}`;
+ } else {
+ formatted = `(${digits.slice(0, 3)}) ${digits.slice(3, 6)}-${digits.slice(6)}`;
+ }
+ if (formatted !== v) {
+ maskedPhone.value = formatted;
+ runOnJS(setPhoneCursor)(formatted.length);
+ }
+ }}
+ />
+
+ );
+}
+```
+
+## API
+
+```tsx
+import { useNativeState } from '@expo/ui/swift-ui';
+```
+
+
diff --git a/docs/public/static/data/unversioned/expo-ui/jetpack-compose/usenativestate.json b/docs/public/static/data/unversioned/expo-ui/jetpack-compose/usenativestate.json
new file mode 100644
index 00000000000000..e8ffe960976fba
--- /dev/null
+++ b/docs/public/static/data/unversioned/expo-ui/jetpack-compose/usenativestate.json
@@ -0,0 +1 @@
+{"schemaVersion":"2.0","name":"expo-ui/jetpack-compose/usenativestate","variant":"project","kind":1,"children":[{"name":"ObservableState","variant":"declaration","kind":2097152,"comment":{"summary":[{"kind":"text","text":"Observable state shared between JavaScript and native views (SwiftUI on iOS,\nJetpack Compose on Android)."}]},"typeParameters":[{"name":"T","variant":"typeParam","kind":131072}],"type":{"type":"intersection","types":[{"type":"reference","target":{"packageName":"expo-modules-core","packagePath":"src/SharedObject.ts","qualifiedName":"SharedObject"},"name":"SharedObject","package":"expo-modules-core"},{"type":"reflection","declaration":{"name":"__type","variant":"declaration","kind":65536,"children":[{"name":"value","variant":"declaration","kind":1024,"comment":{"summary":[{"kind":"text","text":"The current value. Reads are safe from any thread; prefer writing from a worklet\nso the update runs on the native UI thread. Updating state from the JS thread\nmight show a development warning."}]},"type":{"type":"reference","name":"T","package":"@expo/ui","refersToTypeParameter":true}}]}}]}},{"name":"useNativeState","variant":"declaration","kind":64,"signatures":[{"name":"useNativeState","variant":"signature","kind":4096,"comment":{"summary":[{"kind":"text","text":"Creates an observable native state that is automatically cleaned up when the component unmounts."}]},"typeParameters":[{"name":"T","variant":"typeParam","kind":131072}],"parameters":[{"name":"initialValue","variant":"param","kind":32768,"type":{"type":"reference","name":"T","package":"@expo/ui","refersToTypeParameter":true}}],"type":{"type":"reference","typeArguments":[{"type":"reference","name":"T","package":"@expo/ui","refersToTypeParameter":true}],"name":"ObservableState","package":"@expo/ui"}}]}],"packageName":"@expo/ui"}
\ No newline at end of file
diff --git a/docs/public/static/data/unversioned/expo-ui/swift-ui/usenativestate.json b/docs/public/static/data/unversioned/expo-ui/swift-ui/usenativestate.json
new file mode 100644
index 00000000000000..593a65b7677c17
--- /dev/null
+++ b/docs/public/static/data/unversioned/expo-ui/swift-ui/usenativestate.json
@@ -0,0 +1 @@
+{"schemaVersion":"2.0","name":"expo-ui/swift-ui/usenativestate","variant":"project","kind":1,"children":[{"name":"ObservableState","variant":"declaration","kind":2097152,"comment":{"summary":[{"kind":"text","text":"Observable state shared between JavaScript and native views (SwiftUI on iOS,\nJetpack Compose on Android)."}]},"typeParameters":[{"name":"T","variant":"typeParam","kind":131072}],"type":{"type":"intersection","types":[{"type":"reference","target":{"packageName":"expo-modules-core","packagePath":"src/SharedObject.ts","qualifiedName":"SharedObject"},"name":"SharedObject","package":"expo-modules-core"},{"type":"reflection","declaration":{"name":"__type","variant":"declaration","kind":65536,"children":[{"name":"value","variant":"declaration","kind":1024,"comment":{"summary":[{"kind":"text","text":"The current value. Reads are safe from any thread; prefer writing from a worklet\nso the update runs on the native UI thread. Updating state from the JS thread\nmight show a development warning."}]},"type":{"type":"reference","name":"T","package":"@expo/ui","refersToTypeParameter":true}}]}}]}},{"name":"useNativeState","variant":"declaration","kind":64,"signatures":[{"name":"useNativeState","variant":"signature","kind":4096,"comment":{"summary":[{"kind":"text","text":"Creates an observable native state that is automatically cleaned up when the component unmounts."}]},"typeParameters":[{"name":"T","variant":"typeParam","kind":131072}],"parameters":[{"name":"initialValue","variant":"param","kind":32768,"type":{"type":"reference","name":"T","package":"@expo/ui","refersToTypeParameter":true}}],"type":{"type":"reference","typeArguments":[{"type":"reference","name":"T","package":"@expo/ui","refersToTypeParameter":true}],"name":"ObservableState","package":"@expo/ui"}}]}],"packageName":"@expo/ui"}
\ No newline at end of file
diff --git a/packages/expo-app-metrics/android/src/main/java/expo/modules/appmetrics/AppMetadata.kt b/packages/expo-app-metrics/android/src/main/java/expo/modules/appmetrics/AppMetadata.kt
index 09f8364e1b2e48..ff79680c5f7b40 100644
--- a/packages/expo-app-metrics/android/src/main/java/expo/modules/appmetrics/AppMetadata.kt
+++ b/packages/expo-app-metrics/android/src/main/java/expo/modules/appmetrics/AppMetadata.kt
@@ -14,7 +14,7 @@ data class AppMetadata(
val deviceName: String?,
val expoSdkVersion: String,
val reactNativeVersion: String,
- val clientVersion: String?,
+ val clientVersion: String?
)
data class AppUpdatesInfo(
diff --git a/packages/expo-app-metrics/android/src/main/java/expo/modules/appmetrics/updates/UpdatesStateEvent.kt b/packages/expo-app-metrics/android/src/main/java/expo/modules/appmetrics/updates/UpdatesStateEvent.kt
index 2ddcab63862a87..71fd1a7f3719af 100644
--- a/packages/expo-app-metrics/android/src/main/java/expo/modules/appmetrics/updates/UpdatesStateEvent.kt
+++ b/packages/expo-app-metrics/android/src/main/java/expo/modules/appmetrics/updates/UpdatesStateEvent.kt
@@ -18,7 +18,7 @@ data class UpdatesStateEvent(
DownloadCompleteWithRollback("downloadCompleteWithRollback"),
DownloadError("downloadError"),
DownloadProgress("downloadProgress"),
- Restart("restart");
+ Restart("restart")
}
companion object {
diff --git a/packages/expo-modules-jsi/package.json b/packages/expo-modules-jsi/package.json
index b934a088d38b2c..4246fb5beddc23 100644
--- a/packages/expo-modules-jsi/package.json
+++ b/packages/expo-modules-jsi/package.json
@@ -3,7 +3,6 @@
"version": "55.0.0",
"description": "The JavaScript Interface for Expo Modules",
"main": "index.js",
- "private": true,
"sideEffects": [],
"exports": {
"./package.json": "./package.json",
diff --git a/packages/expo-observe/android/src/main/java/expo/modules/observe/EventDispatcher.kt b/packages/expo-observe/android/src/main/java/expo/modules/observe/EventDispatcher.kt
index b2baa8516cce4c..db4d9f3b6cd1ac 100644
--- a/packages/expo-observe/android/src/main/java/expo/modules/observe/EventDispatcher.kt
+++ b/packages/expo-observe/android/src/main/java/expo/modules/observe/EventDispatcher.kt
@@ -23,8 +23,8 @@ class EventDispatcher(
) {
private fun endpointUrl(): String {
val base = when (baseUrl.endsWith("/")) {
- true -> "${baseUrl}${projectId}"
- else -> "${baseUrl}/${projectId}"
+ true -> "${baseUrl}$projectId"
+ else -> "$baseUrl/$projectId"
}
return if (useOpenTelemetry) "$base/v1/metrics" else base
}
diff --git a/packages/expo-observe/android/src/main/java/expo/modules/observe/OpenTelemetry.kt b/packages/expo-observe/android/src/main/java/expo/modules/observe/OpenTelemetry.kt
index eb3dd87fc3d794..54e32208ceffd7 100644
--- a/packages/expo-observe/android/src/main/java/expo/modules/observe/OpenTelemetry.kt
+++ b/packages/expo-observe/android/src/main/java/expo/modules/observe/OpenTelemetry.kt
@@ -94,7 +94,7 @@ private val metricNameMap = mapOf(
"launchTime" to "expo.app_startup.launch_time",
// Updates
- "updateDownloadTime" to "expo.updates.download_time",
+ "updateDownloadTime" to "expo.updates.download_time"
)
fun EASMetric.toOTMetric(): OTMetric {
@@ -134,7 +134,7 @@ fun Event.toOTMetadata(easClientId: String): OTMetadata {
OTAttribute.of("telemetry.sdk.language", "kotlin"),
OTAttribute.of("expo.sdk.version", metadata.expoSdkVersion),
OTAttribute.of("expo.react_native.version", metadata.reactNativeVersion),
- OTAttribute.of("expo.eas_client.id", easClientId),
+ OTAttribute.of("expo.eas_client.id", easClientId)
)
// Send optional attributes only if they are set.
diff --git a/packages/expo-observe/android/src/test/java/expo/modules/observe/OpenTelemetryTest.kt b/packages/expo-observe/android/src/test/java/expo/modules/observe/OpenTelemetryTest.kt
index edd5d45eb399e4..03ca51a6fd1cea 100644
--- a/packages/expo-observe/android/src/test/java/expo/modules/observe/OpenTelemetryTest.kt
+++ b/packages/expo-observe/android/src/test/java/expo/modules/observe/OpenTelemetryTest.kt
@@ -165,10 +165,12 @@ class OpenTelemetryTest {
category = "appStartup",
name = "bundleLoadTime",
value = 1.0,
- customParams = JsonObject(mapOf(
- "screen" to JsonPrimitive("dashboard"),
- "variant" to JsonPrimitive("A")
- ))
+ customParams = JsonObject(
+ mapOf(
+ "screen" to JsonPrimitive("dashboard"),
+ "variant" to JsonPrimitive("A")
+ )
+ )
)
val otMetric = metric.toOTMetric()
val attrs = otMetric.gauge.dataPoints[0].attributes.associate { it.key to it.value.stringValue }
diff --git a/packages/expo-ui/android/src/main/java/expo/modules/ui/ChipView.kt b/packages/expo-ui/android/src/main/java/expo/modules/ui/ChipView.kt
index c649d67a0d986c..a7b3ff8f303a3e 100644
--- a/packages/expo-ui/android/src/main/java/expo/modules/ui/ChipView.kt
+++ b/packages/expo-ui/android/src/main/java/expo/modules/ui/ChipView.kt
@@ -28,44 +28,63 @@ open class ChipPressedEvent : Record, Serializable
@OptimizedRecord
class AssistChipColors : Record {
@Field val containerColor: Color? = null
+
@Field val labelColor: Color? = null
+
@Field val leadingIconContentColor: Color? = null
+
@Field val trailingIconContentColor: Color? = null
}
@OptimizedRecord
class FilterChipColors : Record {
@Field val containerColor: Color? = null
+
@Field val labelColor: Color? = null
+
@Field val iconColor: Color? = null
+
@Field val selectedContainerColor: Color? = null
+
@Field val selectedLabelColor: Color? = null
+
@Field val selectedLeadingIconColor: Color? = null
+
@Field val selectedTrailingIconColor: Color? = null
}
@OptimizedRecord
class InputChipColors : Record {
@Field val containerColor: Color? = null
+
@Field val labelColor: Color? = null
+
@Field val leadingIconColor: Color? = null
+
@Field val trailingIconColor: Color? = null
+
@Field val selectedContainerColor: Color? = null
+
@Field val selectedLabelColor: Color? = null
+
@Field val selectedLeadingIconColor: Color? = null
+
@Field val selectedTrailingIconColor: Color? = null
}
@OptimizedRecord
class SuggestionChipColors : Record {
@Field val containerColor: Color? = null
+
@Field val labelColor: Color? = null
+
@Field val iconContentColor: Color? = null
}
@OptimizedRecord
class ChipBorder : Record {
@Field val width: Float = 1f
+
@Field val color: Color? = null
}
diff --git a/packages/expo-ui/android/src/main/java/expo/modules/ui/DatePickerView.kt b/packages/expo-ui/android/src/main/java/expo/modules/ui/DatePickerView.kt
index 4d97284d0d9048..0ea798eb30f898 100644
--- a/packages/expo-ui/android/src/main/java/expo/modules/ui/DatePickerView.kt
+++ b/packages/expo-ui/android/src/main/java/expo/modules/ui/DatePickerView.kt
@@ -37,7 +37,6 @@ data class DatePickerResult(
val date: Long?
) : Record
-
enum class DisplayedComponents(val value: String) : Enumerable {
DATE("date"),
HOUR_AND_MINUTE("hourAndMinute"),
@@ -61,49 +60,85 @@ enum class Variant(val value: String) : Enumerable {
class DateTimePickerColorOverrides : Record {
// DatePicker colors
@Field val containerColor: AndroidColor? = null
+
@Field val titleContentColor: AndroidColor? = null
+
@Field val headlineContentColor: AndroidColor? = null
+
@Field val weekdayContentColor: AndroidColor? = null
+
@Field val subheadContentColor: AndroidColor? = null
+
@Field val navigationContentColor: AndroidColor? = null
+
@Field val yearContentColor: AndroidColor? = null
+
@Field val disabledYearContentColor: AndroidColor? = null
+
@Field val currentYearContentColor: AndroidColor? = null
+
@Field val selectedYearContentColor: AndroidColor? = null
+
@Field val disabledSelectedYearContentColor: AndroidColor? = null
+
@Field val selectedYearContainerColor: AndroidColor? = null
+
@Field val disabledSelectedYearContainerColor: AndroidColor? = null
+
@Field val dayContentColor: AndroidColor? = null
+
@Field val disabledDayContentColor: AndroidColor? = null
+
@Field val selectedDayContentColor: AndroidColor? = null
+
@Field val disabledSelectedDayContentColor: AndroidColor? = null
+
@Field val selectedDayContainerColor: AndroidColor? = null
+
@Field val disabledSelectedDayContainerColor: AndroidColor? = null
+
@Field val todayContentColor: AndroidColor? = null
+
@Field val todayDateBorderColor: AndroidColor? = null
+
@Field val dayInSelectionRangeContentColor: AndroidColor? = null
+
@Field val dayInSelectionRangeContainerColor: AndroidColor? = null
+
@Field val dividerColor: AndroidColor? = null
// TimePicker colors
@Field val clockDialColor: AndroidColor? = null
+
@Field val clockDialSelectedContentColor: AndroidColor? = null
+
@Field val clockDialUnselectedContentColor: AndroidColor? = null
+
@Field val selectorColor: AndroidColor? = null
+
@Field val periodSelectorBorderColor: AndroidColor? = null
+
@Field val periodSelectorSelectedContainerColor: AndroidColor? = null
+
@Field val periodSelectorUnselectedContainerColor: AndroidColor? = null
+
@Field val periodSelectorSelectedContentColor: AndroidColor? = null
+
@Field val periodSelectorUnselectedContentColor: AndroidColor? = null
+
@Field val timeSelectorSelectedContainerColor: AndroidColor? = null
+
@Field val timeSelectorUnselectedContainerColor: AndroidColor? = null
+
@Field val timeSelectorSelectedContentColor: AndroidColor? = null
+
@Field val timeSelectorUnselectedContentColor: AndroidColor? = null
}
@OptimizedRecord
class SelectableDatesRecord : Record {
@Field val start: Long? = null
+
@Field val end: Long? = null
}
@@ -129,7 +164,7 @@ data class DatePickerDialogProps(
val dismissButtonLabel: String? = null,
val color: AndroidColor? = null,
val elementColors: DateTimePickerColorOverrides = DateTimePickerColorOverrides(),
- val selectableDates: SelectableDatesRecord? = null,
+ val selectableDates: SelectableDatesRecord? = null
) : ComposeProps
@OptimizedComposeProps
@@ -139,7 +174,7 @@ data class TimePickerDialogProps(
val confirmButtonLabel: String? = null,
val dismissButtonLabel: String? = null,
val color: AndroidColor? = null,
- val elementColors: DateTimePickerColorOverrides = DateTimePickerColorOverrides(),
+ val elementColors: DateTimePickerColorOverrides = DateTimePickerColorOverrides()
) : ComposeProps
private fun toUtcDayMillis(localMillis: Long): Long {
diff --git a/packages/expo-ui/android/src/main/java/expo/modules/ui/HorizontalFloatingToolbarView.kt b/packages/expo-ui/android/src/main/java/expo/modules/ui/HorizontalFloatingToolbarView.kt
index 7cee7528c882ae..989704755ee564 100644
--- a/packages/expo-ui/android/src/main/java/expo/modules/ui/HorizontalFloatingToolbarView.kt
+++ b/packages/expo-ui/android/src/main/java/expo/modules/ui/HorizontalFloatingToolbarView.kt
@@ -24,8 +24,11 @@ enum class HorizontalFloatingToolbarVariant(val value: String) : Enumerable {
@OptimizedRecord
class HorizontalFloatingToolbarColors : Record {
@Field val toolbarContainerColor: Color? = null
+
@Field val toolbarContentColor: Color? = null
+
@Field val fabContainerColor: Color? = null
+
@Field val fabContentColor: Color? = null
}
@@ -89,7 +92,7 @@ fun FunctionalComposableScope.HorizontalFloatingToolbarContent(props: Horizontal
floatingActionButton = floatingActionButton,
colors = colors,
scrollBehavior = scrollBehavior,
- modifier = modifier,
+ modifier = modifier
) {
Children(UIComposableScope(), filter = { !isSlotView(it) })
}
@@ -98,7 +101,7 @@ fun FunctionalComposableScope.HorizontalFloatingToolbarContent(props: Horizontal
expanded = true,
colors = colors,
scrollBehavior = scrollBehavior,
- modifier = modifier,
+ modifier = modifier
) {
Children(UIComposableScope(), filter = { !isSlotView(it) })
}
diff --git a/packages/expo-ui/android/src/main/java/expo/modules/ui/HorizontalPagerView.kt b/packages/expo-ui/android/src/main/java/expo/modules/ui/HorizontalPagerView.kt
index 8c74cb15802a7d..30e5497984e973 100644
--- a/packages/expo-ui/android/src/main/java/expo/modules/ui/HorizontalPagerView.kt
+++ b/packages/expo-ui/android/src/main/java/expo/modules/ui/HorizontalPagerView.kt
@@ -61,8 +61,12 @@ fun FunctionalComposableScope.HorizontalPagerContent(
val pageCountState = remember { mutableIntStateOf(view.size) }
DisposableEffect(view) {
view.setOnHierarchyChangeListener(object : ViewGroup.OnHierarchyChangeListener {
- override fun onChildViewAdded(parent: View?, child: View?) { pageCountState.intValue = view.size }
- override fun onChildViewRemoved(parent: View?, child: View?) { pageCountState.intValue = view.size }
+ override fun onChildViewAdded(parent: View?, child: View?) {
+ pageCountState.intValue = view.size
+ }
+ override fun onChildViewRemoved(parent: View?, child: View?) {
+ pageCountState.intValue = view.size
+ }
})
pageCountState.intValue = view.size
onDispose { view.setOnHierarchyChangeListener(null) }
diff --git a/packages/expo-ui/android/src/main/java/expo/modules/ui/HostView.kt b/packages/expo-ui/android/src/main/java/expo/modules/ui/HostView.kt
index b4069aa1da8816..3b6258da2d39ac 100644
--- a/packages/expo-ui/android/src/main/java/expo/modules/ui/HostView.kt
+++ b/packages/expo-ui/android/src/main/java/expo/modules/ui/HostView.kt
@@ -157,10 +157,10 @@ internal class HostView(context: Context, appContext: AppContext) :
) { measurables, constraints ->
val useViewportSizeMeasurement = props.useViewportSizeMeasurement.value
- // useViewportSizeMeasurement: clamp Infinity/0 maxConstraints to the safe area so the
- // content has a concrete size to fill.
- // matchContents: pass through, so children measure
- // at intrinsic size (the unbounded constraint comes from onMeasure's UNSPECIFIED).
+ // useViewportSizeMeasurement: clamp Infinity/0 maxConstraints to the safe area so the
+ // content has a concrete size to fill.
+ // matchContents: pass through, so children measure
+ // at intrinsic size (the unbounded constraint comes from onMeasure's UNSPECIFIED).
val boundedConstraints = Constraints(
minWidth = constraints.minWidth,
maxWidth = if (useViewportSizeMeasurement &&
diff --git a/packages/expo-ui/android/src/main/java/expo/modules/ui/ModalBottomSheetView.kt b/packages/expo-ui/android/src/main/java/expo/modules/ui/ModalBottomSheetView.kt
index d9040acf979345..610a022b0ca25d 100644
--- a/packages/expo-ui/android/src/main/java/expo/modules/ui/ModalBottomSheetView.kt
+++ b/packages/expo-ui/android/src/main/java/expo/modules/ui/ModalBottomSheetView.kt
@@ -82,7 +82,7 @@ fun FunctionalComposableScope.ModalBottomSheetContent(
// Swallowing the exception avoids an unhandled promise rejection on the JS side.
}
}
-
+
val resolvedContainerColor = props.containerColor.composeOrNull ?: BottomSheetDefaults.ContainerColor
val resolvedContentColor = props.contentColor.composeOrNull ?: contentColorFor(resolvedContainerColor)
val resolvedScrimColor = props.scrimColor.composeOrNull ?: BottomSheetDefaults.ScrimColor
diff --git a/packages/expo-ui/android/src/main/java/expo/modules/ui/ModifierRegistry.kt b/packages/expo-ui/android/src/main/java/expo/modules/ui/ModifierRegistry.kt
index 6876aaef4c7e4f..e01797a27f9d65 100644
--- a/packages/expo-ui/android/src/main/java/expo/modules/ui/ModifierRegistry.kt
+++ b/packages/expo-ui/android/src/main/java/expo/modules/ui/ModifierRegistry.kt
@@ -586,7 +586,7 @@ object ModifierRegistry {
val params = recordFromMap(map)
Modifier.onVisibilityChanged(
minDurationMs = params.minDurationMs,
- minFractionVisible = params.minFractionVisible,
+ minFractionVisible = params.minFractionVisible
) { isVisible ->
eventDispatcher("onVisibilityChanged", mapOf("isVisible" to isVisible))
}
diff --git a/packages/expo-ui/android/src/main/java/expo/modules/ui/PaddingValuesExtension.kt b/packages/expo-ui/android/src/main/java/expo/modules/ui/PaddingValuesExtension.kt
index cfb6c322c7ad59..86810eccde3929 100644
--- a/packages/expo-ui/android/src/main/java/expo/modules/ui/PaddingValuesExtension.kt
+++ b/packages/expo-ui/android/src/main/java/expo/modules/ui/PaddingValuesExtension.kt
@@ -10,8 +10,11 @@ import expo.modules.kotlin.types.OptimizedRecord
@OptimizedRecord
class PaddingValuesRecord : Record {
@Field val start: Float? = null
+
@Field val top: Float? = null
+
@Field val end: Float? = null
+
@Field val bottom: Float? = null
fun toPaddingValues(): PaddingValues {
diff --git a/packages/expo-ui/android/src/main/java/expo/modules/ui/ProgressView.kt b/packages/expo-ui/android/src/main/java/expo/modules/ui/ProgressView.kt
index 61dbf7021ae080..a43631621f5df6 100644
--- a/packages/expo-ui/android/src/main/java/expo/modules/ui/ProgressView.kt
+++ b/packages/expo-ui/android/src/main/java/expo/modules/ui/ProgressView.kt
@@ -26,7 +26,9 @@ import expo.modules.kotlin.views.OptimizedComposeProps
@OptimizedRecord
class DrawStopIndicatorConfig : Record {
@Field val color: Color? = null
+
@Field val strokeCap: String? = null
+
@Field val stopSize: Float? = null
}
diff --git a/packages/expo-ui/android/src/main/java/expo/modules/ui/RNHostView.kt b/packages/expo-ui/android/src/main/java/expo/modules/ui/RNHostView.kt
index 9bb798f4957533..d871185f78d8d8 100644
--- a/packages/expo-ui/android/src/main/java/expo/modules/ui/RNHostView.kt
+++ b/packages/expo-ui/android/src/main/java/expo/modules/ui/RNHostView.kt
@@ -32,7 +32,6 @@ import expo.modules.kotlin.AppContext
import expo.modules.kotlin.views.ComposableScope
import expo.modules.kotlin.views.ComposeProps
import expo.modules.kotlin.views.ExpoComposeView
-import expo.modules.kotlin.views.RNHostViewInterface
import expo.modules.kotlin.views.OptimizedComposeProps
@OptimizedComposeProps
@@ -116,8 +115,11 @@ internal class RNHostView(context: Context, appContext: AppContext) :
val childSize = remember {
mutableStateOf(
- if (childView.width > 0 && childView.height > 0) IntSize(childView.width, childView.height)
- else IntSize.Zero
+ if (childView.width > 0 && childView.height > 0) {
+ IntSize(childView.width, childView.height)
+ } else {
+ IntSize.Zero
+ }
)
}
diff --git a/packages/expo-ui/android/src/main/java/expo/modules/ui/TextFieldView.kt b/packages/expo-ui/android/src/main/java/expo/modules/ui/TextFieldView.kt
index f865754388d43c..60c19c9a6afc42 100644
--- a/packages/expo-ui/android/src/main/java/expo/modules/ui/TextFieldView.kt
+++ b/packages/expo-ui/android/src/main/java/expo/modules/ui/TextFieldView.kt
@@ -36,7 +36,7 @@ import expo.modules.kotlin.views.OptimizedComposeProps
enum class TextFieldVariant(val value: String) : Enumerable {
FILLED("filled"),
- OUTLINED("outlined"),
+ OUTLINED("outlined")
}
@OptimizedRecord
@@ -44,7 +44,7 @@ data class TextFieldKeyboardOptionsRecord(
@Field val capitalization: String? = null,
@Field val autoCorrectEnabled: Boolean? = null,
@Field val keyboardType: String? = null,
- @Field val imeAction: String? = null,
+ @Field val imeAction: String? = null
) : Record
@OptimizedRecord
@@ -101,22 +101,22 @@ data class TextFieldColorsRecord(
@Field val focusedSuffixColor: Color? = null,
@Field val unfocusedSuffixColor: Color? = null,
@Field val disabledSuffixColor: Color? = null,
- @Field val errorSuffixColor: Color? = null,
+ @Field val errorSuffixColor: Color? = null
) : Record
data class KeyboardActionEvent(
@Field val action: String,
- @Field val value: String,
+ @Field val value: String
) : Record
data class TextFieldSelectionPayload(
@Field val start: Int,
- @Field val end: Int,
+ @Field val end: Int
) : Record
data class TextFieldValuePayload(
@Field val text: String,
- @Field val selection: TextFieldSelectionPayload,
+ @Field val selection: TextFieldSelectionPayload
) : Record
// endregion Records
@@ -169,7 +169,7 @@ fun TextFieldColorsRecord.toColors(isOutlined: Boolean): TextFieldColors {
focusedSuffixColor = focusedSuffixColor.composeOrNull ?: defaults.focusedSuffixColor,
unfocusedSuffixColor = unfocusedSuffixColor.composeOrNull ?: defaults.unfocusedSuffixColor,
disabledSuffixColor = disabledSuffixColor.composeOrNull ?: defaults.disabledSuffixColor,
- errorSuffixColor = errorSuffixColor.composeOrNull ?: defaults.errorSuffixColor,
+ errorSuffixColor = errorSuffixColor.composeOrNull ?: defaults.errorSuffixColor
)
}
@@ -192,7 +192,7 @@ data class TextFieldProps(
val shape: ShapeRecord? = null,
val colors: TextFieldColorsRecord? = null,
val onValueChangeSync: WorkletCallback? = null,
- val modifiers: ModifierList = emptyList(),
+ val modifiers: ModifierList = emptyList()
) : ComposeProps
// endregion Props
@@ -317,12 +317,30 @@ fun FunctionalComposableScope.TextFieldContent(
)
val currentText = { state.value.extractText() }
val keyboardActions = KeyboardActions(
- onDone = { defaultKeyboardAction(ImeAction.Done); onKeyboardActionTriggered(KeyboardActionEvent("done", currentText())) },
- onGo = { defaultKeyboardAction(ImeAction.Go); onKeyboardActionTriggered(KeyboardActionEvent("go", currentText())) },
- onNext = { defaultKeyboardAction(ImeAction.Next); onKeyboardActionTriggered(KeyboardActionEvent("next", currentText())) },
- onPrevious = { defaultKeyboardAction(ImeAction.Previous); onKeyboardActionTriggered(KeyboardActionEvent("previous", currentText())) },
- onSearch = { defaultKeyboardAction(ImeAction.Search); onKeyboardActionTriggered(KeyboardActionEvent("search", currentText())) },
- onSend = { defaultKeyboardAction(ImeAction.Send); onKeyboardActionTriggered(KeyboardActionEvent("send", currentText())) },
+ onDone = {
+ defaultKeyboardAction(ImeAction.Done)
+ onKeyboardActionTriggered(KeyboardActionEvent("done", currentText()))
+ },
+ onGo = {
+ defaultKeyboardAction(ImeAction.Go)
+ onKeyboardActionTriggered(KeyboardActionEvent("go", currentText()))
+ },
+ onNext = {
+ defaultKeyboardAction(ImeAction.Next)
+ onKeyboardActionTriggered(KeyboardActionEvent("next", currentText()))
+ },
+ onPrevious = {
+ defaultKeyboardAction(ImeAction.Previous)
+ onKeyboardActionTriggered(KeyboardActionEvent("previous", currentText()))
+ },
+ onSearch = {
+ defaultKeyboardAction(ImeAction.Search)
+ onKeyboardActionTriggered(KeyboardActionEvent("search", currentText()))
+ },
+ onSend = {
+ defaultKeyboardAction(ImeAction.Send)
+ onKeyboardActionTriggered(KeyboardActionEvent("send", currentText()))
+ }
)
// Lines
@@ -384,7 +402,7 @@ fun FunctionalComposableScope.TextFieldContent(
isError = props.isError,
keyboardOptions = keyboardOptions, keyboardActions = keyboardActions,
singleLine = singleLine, maxLines = maxLines, minLines = minLines,
- shape = shape, colors = colors,
+ shape = shape, colors = colors
)
} else {
TextField(
@@ -396,7 +414,7 @@ fun FunctionalComposableScope.TextFieldContent(
isError = props.isError,
keyboardOptions = keyboardOptions, keyboardActions = keyboardActions,
singleLine = singleLine, maxLines = maxLines, minLines = minLines,
- shape = shape, colors = colors,
+ shape = shape, colors = colors
)
}
}
diff --git a/packages/expo-ui/android/src/main/java/expo/modules/ui/button/Button.kt b/packages/expo-ui/android/src/main/java/expo/modules/ui/button/Button.kt
index 9545d97246805d..82220bb6bddb46 100644
--- a/packages/expo-ui/android/src/main/java/expo/modules/ui/button/Button.kt
+++ b/packages/expo-ui/android/src/main/java/expo/modules/ui/button/Button.kt
@@ -29,16 +29,22 @@ open class ButtonPressedEvent() : Record, Serializable
@OptimizedRecord
class ButtonColors : Record {
@Field val containerColor: Color? = null
+
@Field val contentColor: Color? = null
+
@Field val disabledContainerColor: Color? = null
+
@Field val disabledContentColor: Color? = null
}
@OptimizedRecord
class ContentPaddingRecord : Record {
@Field val start: Double? = null
+
@Field val top: Double? = null
+
@Field val end: Double? = null
+
@Field val bottom: Double? = null
}
diff --git a/packages/expo-ui/android/src/main/java/expo/modules/ui/colors/MaterialColors.kt b/packages/expo-ui/android/src/main/java/expo/modules/ui/colors/MaterialColors.kt
index fd2f05a26204b1..2dd359c098e436 100644
--- a/packages/expo-ui/android/src/main/java/expo/modules/ui/colors/MaterialColors.kt
+++ b/packages/expo-ui/android/src/main/java/expo/modules/ui/colors/MaterialColors.kt
@@ -23,6 +23,7 @@ internal val isDynamicColorSupported: Boolean = Build.VERSION.SDK_INT >= Build.V
internal class MaterialColorsOptions : Record {
@Field val scheme: ExpoColorScheme? = null
+
@Field val seedColor: android.graphics.Color? = null
}
diff --git a/packages/expo-ui/android/src/main/java/expo/modules/ui/menu/DropdownMenuItem.kt b/packages/expo-ui/android/src/main/java/expo/modules/ui/menu/DropdownMenuItem.kt
index edaa8551a7ab9e..e4654ea01b1794 100644
--- a/packages/expo-ui/android/src/main/java/expo/modules/ui/menu/DropdownMenuItem.kt
+++ b/packages/expo-ui/android/src/main/java/expo/modules/ui/menu/DropdownMenuItem.kt
@@ -19,10 +19,15 @@ import expo.modules.kotlin.views.OptimizedComposeProps
@OptimizedRecord
class DropdownMenuItemColors : Record {
@Field val textColor: Color? = null
+
@Field val leadingIconColor: Color? = null
+
@Field val trailingIconColor: Color? = null
+
@Field val disabledTextColor: Color? = null
+
@Field val disabledLeadingIconColor: Color? = null
+
@Field val disabledTrailingIconColor: Color? = null
}
diff --git a/packages/expo-ui/android/src/main/java/expo/modules/ui/state/WorkletCallback.kt b/packages/expo-ui/android/src/main/java/expo/modules/ui/state/WorkletCallback.kt
index 09012f13e8b721..83708686e4d589 100644
--- a/packages/expo-ui/android/src/main/java/expo/modules/ui/state/WorkletCallback.kt
+++ b/packages/expo-ui/android/src/main/java/expo/modules/ui/state/WorkletCallback.kt
@@ -14,12 +14,12 @@ class WorkletCallback : SharedObject() {
fun invoke(vararg arguments: Any?) {
val worklet = worklet ?: run {
- Log.w("ExpoUI", "WorkletCallback.invoke: worklet is nil, the callback will not run.")
- return
+ Log.w("ExpoUI", "WorkletCallback.invoke: worklet is nil, the callback will not run.")
+ return
}
val runtime = appContext?.uiRuntime ?: run {
- Log.w("ExpoUI", "WorkletCallback.invoke: UI worklet runtime is not available, the callback will not run.")
- return
+ Log.w("ExpoUI", "WorkletCallback.invoke: UI worklet runtime is not available, the callback will not run.")
+ return
}
worklet.execute(runtime, *arguments)
}
diff --git a/packages/expo-ui/build/State/useNativeState.d.ts b/packages/expo-ui/build/State/useNativeState.d.ts
index 20c69858252867..2862d1af32702f 100644
--- a/packages/expo-ui/build/State/useNativeState.d.ts
+++ b/packages/expo-ui/build/State/useNativeState.d.ts
@@ -1,12 +1,13 @@
import { type SharedObject } from 'expo-modules-core';
/**
- * Observable state shared between JavaScript and native SwiftUI views.
+ * Observable state shared between JavaScript and native views (Jetpack Compose
+ * on Android and SwiftUI on iOS).
*/
export type ObservableState = SharedObject & {
/**
* The current value. Reads are safe from any thread; prefer writing from a worklet
- * so the update runs on SwiftUI's UI thread. Updating state from the JS thread
- * might show a SwiftUI warning.
+ * so the update runs on the native UI thread. Updating state from the JS thread
+ * might show a development warning.
*/
value: T;
};
diff --git a/packages/expo-ui/build/State/useNativeState.d.ts.map b/packages/expo-ui/build/State/useNativeState.d.ts.map
index 009756b934135e..9eb4ad485c5741 100644
--- a/packages/expo-ui/build/State/useNativeState.d.ts.map
+++ b/packages/expo-ui/build/State/useNativeState.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"useNativeState.d.ts","sourceRoot":"","sources":["../../src/State/useNativeState.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,YAAY,EAA4B,MAAM,mBAAmB,CAAC;AAMhF;;GAEG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,IAAI,YAAY,GAAG;IAC9C;;;;OAIG;IACH,KAAK,EAAE,CAAC,CAAC;CACV,CAAC;AAEF;;GAEG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAMrE"}
\ No newline at end of file
+{"version":3,"file":"useNativeState.d.ts","sourceRoot":"","sources":["../../src/State/useNativeState.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,YAAY,EAA4B,MAAM,mBAAmB,CAAC;AAMhF;;;GAGG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,IAAI,YAAY,GAAG;IAC9C;;;;OAIG;IACH,KAAK,EAAE,CAAC,CAAC;CACV,CAAC;AAEF;;GAEG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAMrE"}
\ No newline at end of file
diff --git a/packages/expo-ui/src/State/useNativeState.ts b/packages/expo-ui/src/State/useNativeState.ts
index 85c92c24b7ff98..d4dd47433fe5ac 100644
--- a/packages/expo-ui/src/State/useNativeState.ts
+++ b/packages/expo-ui/src/State/useNativeState.ts
@@ -6,13 +6,14 @@ import { worklets } from './optionalWorklets';
const ExpoUI = requireNativeModule('ExpoUI');
/**
- * Observable state shared between JavaScript and native SwiftUI views.
+ * Observable state shared between JavaScript and native views (Jetpack Compose
+ * on Android and SwiftUI on iOS).
*/
export type ObservableState = SharedObject & {
/**
* The current value. Reads are safe from any thread; prefer writing from a worklet
- * so the update runs on SwiftUI's UI thread. Updating state from the JS thread
- * might show a SwiftUI warning.
+ * so the update runs on the native UI thread. Updating state from the JS thread
+ * might show a development warning.
*/
value: T;
};
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 3436e0e85d056b..eb25da9aae8e1b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -431,6 +431,9 @@ importers:
apps/expo-go:
dependencies:
+ '@expo/ui':
+ specifier: workspace:*
+ version: link:../../packages/expo-ui
'@expo/vector-icons':
specifier: ^15.0.2
version: 15.1.1(expo-font@packages+expo-font)(react-native@0.85.2(@babel/core@7.29.0)(@react-native/jest-preset@0.85.2(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)
diff --git a/templates/expo-template-default/package.json b/templates/expo-template-default/package.json
index 31fe29613fdb4e..b0235bf3c55801 100644
--- a/templates/expo-template-default/package.json
+++ b/templates/expo-template-default/package.json
@@ -12,6 +12,7 @@
"lint": "expo lint"
},
"dependencies": {
+ "@expo/ui": "~55.0.1",
"expo": "~55.0.2",
"expo-constants": "~55.0.7",
"expo-device": "~55.0.9",
diff --git a/tools/src/commands/GenerateDocsAPIData.ts b/tools/src/commands/GenerateDocsAPIData.ts
index d7229a36808194..5328820dbfef27 100644
--- a/tools/src/commands/GenerateDocsAPIData.ts
+++ b/tools/src/commands/GenerateDocsAPIData.ts
@@ -65,6 +65,7 @@ const uiPackagesMapping: Record = {
'expo-ui/swift-ui/text': ['swift-ui/Text/index.tsx', 'expo-ui'],
'expo-ui/swift-ui/textfield': ['swift-ui/TextField/index.tsx', 'expo-ui'],
'expo-ui/swift-ui/toggle': ['swift-ui/Toggle/index.tsx', 'expo-ui'],
+ 'expo-ui/swift-ui/usenativestate': ['State/useNativeState.ts', 'expo-ui'],
'expo-ui/swift-ui/vstack': ['swift-ui/VStack/index.tsx', 'expo-ui'],
'expo-ui/swift-ui/zstack': ['swift-ui/ZStack/index.tsx', 'expo-ui'],
@@ -135,6 +136,7 @@ const uiPackagesMapping: Record = {
'expo-ui/jetpack-compose/textfield': ['jetpack-compose/TextField/index.tsx', 'expo-ui'],
'expo-ui/jetpack-compose/togglebutton': ['jetpack-compose/ToggleButton/index.tsx', 'expo-ui'],
'expo-ui/jetpack-compose/tooltip': ['jetpack-compose/Tooltip/index.tsx', 'expo-ui'],
+ 'expo-ui/jetpack-compose/usenativestate': ['State/useNativeState.ts', 'expo-ui'],
};
const PACKAGES_MAPPING: Record = {