From e743ae264737bcdf744b483e9c766730f5bbf901 Mon Sep 17 00:00:00 2001 From: Aman Mittal Date: Thu, 7 May 2026 11:24:45 +0530 Subject: [PATCH 1/5] [docs] Update drawer navigation guide for SDK 56 (#45341) # Why Fix ENG-20811 # How Update drawer navigation guide for SDK 56. # Test Plan See preview. # Checklist - [ ] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting) - [ ] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). - [x] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md) --- docs/pages/router/advanced/drawer.mdx | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/docs/pages/router/advanced/drawer.mdx b/docs/pages/router/advanced/drawer.mdx index 8ff49b6b6d2c2d..61ab7101a9379e 100644 --- a/docs/pages/router/advanced/drawer.mdx +++ b/docs/pages/router/advanced/drawer.mdx @@ -16,21 +16,39 @@ A navigation drawer is a common pattern in mobile apps, it allows users to swipe ## Installation +In **SDK 56 and later**, the drawer navigator is bundled in `expo-router` and uses [`react-native-drawer-layout`](https://www.npmjs.com/package/react-native-drawer-layout) under the hood. + +On Android and iOS, the drawer requires `react-native-reanimated` and `react-native-worklets` to drive its animations. On web, animation is handled by CSS. + - -To use [drawer navigator](https://reactnavigation.org/docs/drawer-navigator) you'll need to install some additional dependencies if you do not have them already. On Android and iOS, the drawer navigator requires `react-native-reanimated` and `react-native-worklets` to drive its animations. On web, this is handled by CSS animations. + + +To use the drawer navigator, install these dependencies if you do not have them already: + + + + + + + +To use [drawer navigator](https://reactnavigation.org/docs/drawer-navigator), install these dependencies if you do not have them already: + -To use [drawer navigator](https://reactnavigation.org/docs/drawer-navigator) you'll need to install some additional dependencies if you do not have them already. On Android and iOS, the drawer navigator requires `react-native-reanimated` and `react-native-gesture-handler` to drive its animations. On web, this is handled by CSS animations. +To use [drawer navigator](https://reactnavigation.org/docs/drawer-navigator), install these dependencies if you do not have them already: + ## Usage From cabb3416608e0d54a97b94ec9be83c4f5fa2f58f Mon Sep 17 00:00:00 2001 From: Aman Mittal Date: Thu, 7 May 2026 12:03:54 +0530 Subject: [PATCH 2/5] [docs] Apply system bars guide changes for SDK 56 (#45165) --- .../develop/user-interface/system-bars.mdx | 28 ++++++++----------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/docs/pages/develop/user-interface/system-bars.mdx b/docs/pages/develop/user-interface/system-bars.mdx index ed2f86edd68ece..e78648fc42083c 100644 --- a/docs/pages/develop/user-interface/system-bars.mdx +++ b/docs/pages/develop/user-interface/system-bars.mdx @@ -17,7 +17,7 @@ System bars are fundamental to the mobile experience, and understanding how to w ## Handling overlaps using safe areas @@ -62,25 +62,21 @@ export default function RootLayout() { To control the `StatusBar` visibility, you can set the [`hidden`](/versions/latest/sdk/status-bar/#hidden) property to `true` or use the [`setStatusBarHidden`](/versions/latest/sdk/status-bar/#statusbarsetstatusbarhiddenhidden-animation) method. -**With edge-to-edge enabled on Android, features from `expo-status-bar` that depend on an opaque status bar [are unavailable](https://developer.android.com/about/versions/15/behavior-changes-15#edge-to-edge)**. It's only possible to customize the style and visibility. Other properties will no-op and warn. - ### Navigation bar configuration (Android only) On Android devices, the Navigation Bar appears at the bottom of the screen. You can customize it using the [`expo-navigation-bar`](/versions/latest/sdk/navigation-bar) library. It provides a `NavigationBar` component that you can use to set the style of the navigation bar using the [`setStyle`](/versions/latest/sdk/navigation-bar/#navigationbarsetstylestyle) method: ```tsx src/app/_layout.tsx -import { Platform } from 'react-native'; -import * as NavigationBar from 'expo-navigation-bar'; -import { useEffect } from 'react'; - -useEffect(() => { - if (Platform.OS === 'android') { - // Set the navigation bar style - NavigationBar.setStyle('dark'); - } -}, []); -``` +import { NavigationBar } from 'expo-navigation-bar'; -To control the `NavigationBar` visibility, you can use the [`setVisibilityAsync`](/versions/latest/sdk/navigation-bar/#navigationbarsetvisibilityasyncvisibility) method. +export default function RootLayout() { + return ( + <> + {/* Set the navigation bar style */} + + + ); +} +``` -**With edge-to-edge enabled on Android, features from `expo-navigation-bar` that depend on an opaque navigation bar [are unavailable](https://developer.android.com/about/versions/15/behavior-changes-15#edge-to-edge)**. It's only possible to customize the style and visibility. Other properties will no-op and warn. +To control the `NavigationBar` visibility, you can use the [`hidden`](/versions/unversioned/sdk/navigation-bar/#hidden) prop or the [`NavigationBar.setHidden`](/versions/unversioned/sdk/navigation-bar/#navigationbarsethiddenhidden) method. From 2e718957182309dbae0631d83b5e2cb2b9aefd0f Mon Sep 17 00:00:00 2001 From: Kadi Kraman Date: Thu, 7 May 2026 09:11:44 +0100 Subject: [PATCH 3/5] [docs] Improve documentation of debug metrics (#45453) # Why Address PR comment from https://github.com/expo/expo/pull/45250#discussion_r3180808455 # How Expand on the distinction between environment and debug builds. # Test Plan Review the updated copy. # Checklist - [ ] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting) - [ ] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). - [ ] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md) --- docs/pages/eas/observe/configuration.mdx | 6 ++++-- docs/pages/eas/observe/reference/metrics.mdx | 10 +++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/docs/pages/eas/observe/configuration.mdx b/docs/pages/eas/observe/configuration.mdx index 93c45cf3d0f2b5..c574a2015ca32e 100644 --- a/docs/pages/eas/observe/configuration.mdx +++ b/docs/pages/eas/observe/configuration.mdx @@ -71,6 +71,8 @@ ExpoObserve.configure({ }); ``` +A build is treated as a debug build if either the native app is a debug build or the JS bundle is a development bundle (`__DEV__` is `true`). This detection is independent of the `environment` value (see [Environments](#environments)). + `dispatchInDebug` has no effect on release builds, which always dispatch (subject to [`dispatchingEnabled`](#configure) and [`sampleRate`](#sampling)). If `dispatchingEnabled` is `false` or this installation is out-of-sample, nothing dispatches regardless of `dispatchInDebug`. > **warning** Enable this only when testing your Expo Observe integration. Development/debug performance differs significantly from production, so collecting development/debug metrics may distort the results shown in your dashboard. @@ -97,6 +99,6 @@ The endpoint URL is baked into the native layer of the app at build time, so cha ## Environments -All metrics are grouped by environment. The environment value is derived from `process.env.NODE_ENV` by default. To override it, use [`configure({ environment })`](#configure). +All metrics are grouped by environment. The environment value is derived from `process.env.NODE_ENV` by default (falling back to `'production'` if unset). To override it, use [`configure({ environment })`](#configure). -Metrics collected from debug builds are dropped before dispatching unless [`dispatchInDebug` is set to `true`](#enable-metrics-in-development) via `configure()`. You can also disable all dispatching globally using `configure({ dispatchingEnabled: false })`. +The environment is a metadata tag attached to each metric and is independent of how the bundle was built. To control whether debug-build metrics are dispatched, see [Enable metrics in development](#enable-metrics-in-development). To disable all dispatching globally, use `configure({ dispatchingEnabled: false })`. diff --git a/docs/pages/eas/observe/reference/metrics.mdx b/docs/pages/eas/observe/reference/metrics.mdx index 41034c4dca1a1e..3fa5078420b178 100644 --- a/docs/pages/eas/observe/reference/metrics.mdx +++ b/docs/pages/eas/observe/reference/metrics.mdx @@ -180,4 +180,12 @@ By default, all installations dispatch their metrics. To dispatch from a fractio ### Environment -All metrics are grouped by environment. The environment value is derived from `process.env.NODE_ENV` by default, or can be overridden via [`configure({ environment })`](/eas/observe/configuration/#configure). Metrics collected from debug builds are dropped before dispatching unless `dispatchInDebug` is set to `true` via [`configure()`](/eas/observe/configuration/#configure) (for information, see [Enable metrics in development](/eas/observe/configuration/#enable-metrics-in-development)). You can also disable all dispatching globally using `configure({ dispatchingEnabled: false })`. +All metrics are grouped by environment. The environment value is derived from `process.env.NODE_ENV` by default (falling back to `'production'` if unset), or can be overridden via [`configure({ environment })`](/eas/observe/configuration/#configure). The environment is a metadata tag attached to each metric and does not affect whether metrics are dispatched. + +### Debug builds + +Metrics collected from debug builds are dropped before dispatching unless `dispatchInDebug` is set to `true` via [`configure()`](/eas/observe/configuration/#configure) (for information, see [Enable metrics in development](/eas/observe/configuration/#enable-metrics-in-development)). A build is treated as a debug build if either the native app is a debug build or the JS bundle is a development bundle (`__DEV__` is `true`). This detection is independent of the `environment` value. + +### Disabling dispatching + +You can disable all dispatching globally using `configure({ dispatchingEnabled: false })`. While disabled, any pending metrics are dropped without being dispatched and no further metrics are dispatched until it is set back to `true`. From 50a96645714d8f2f0369e2fd41538cd6f51ecac3 Mon Sep 17 00:00:00 2001 From: Tomasz Sapeta Date: Thu, 7 May 2026 10:44:23 +0200 Subject: [PATCH 4/5] [app-metrics][ios] Fix NetworkPathMonitor tests (#45472) --- .../ios/Network/NetworkPathMonitor.swift | 30 ++++++++++++------- .../ios/Tests/NetworkPathTests.swift | 22 ++++++-------- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/packages/expo-app-metrics/ios/Network/NetworkPathMonitor.swift b/packages/expo-app-metrics/ios/Network/NetworkPathMonitor.swift index 8f3e50264b2849..f64c64ba1381de 100644 --- a/packages/expo-app-metrics/ios/Network/NetworkPathMonitor.swift +++ b/packages/expo-app-metrics/ios/Network/NetworkPathMonitor.swift @@ -31,7 +31,8 @@ final class NetworkPathMonitor: NetworkPathObserverDelegate, Sendable { */ private var firstPathContinuations: [CheckedContinuation] = [] - private init() {} + /** Internal so tests can construct dedicated instances; production code uses `shared`. */ + init() {} /** Idempotent. Constructs the observer on the first call; later calls are @@ -56,19 +57,28 @@ final class NetworkPathMonitor: NetworkPathObserverDelegate, Sendable { return currentPath } + /** + Caches `path` and resumes any callers awaiting the first path. Tests on + `AppMetricsActor` can call this directly to avoid the async hop in + `onNetworkPathUpdate(_:)`. + */ + func apply(_ path: NetworkPath) { + let wasFirst = currentPath == nil + currentPath = path + if wasFirst { + let pending = firstPathContinuations + firstPathContinuations.removeAll() + for continuation in pending { + continuation.resume() + } + } + } + // MARK: - NetworkPathObserverDelegate nonisolated func onNetworkPathUpdate(_ path: NetworkPath) { AppMetricsActor.isolated { [self] in - let wasFirst = currentPath == nil - currentPath = path - if wasFirst { - let pending = firstPathContinuations - firstPathContinuations.removeAll() - for continuation in pending { - continuation.resume() - } - } + apply(path) } } } diff --git a/packages/expo-app-metrics/ios/Tests/NetworkPathTests.swift b/packages/expo-app-metrics/ios/Tests/NetworkPathTests.swift index 8efa736aa4e33b..02d0d16b489b67 100644 --- a/packages/expo-app-metrics/ios/Tests/NetworkPathTests.swift +++ b/packages/expo-app-metrics/ios/Tests/NetworkPathTests.swift @@ -66,8 +66,8 @@ struct NetworkPathTests { @Suite("NetworkPathMonitor") struct NetworkPathMonitorTests { @Test - func `caches the last received path`() async throws { - let monitor = NetworkPathMonitor.shared + func `caches the last received path`() { + let monitor = NetworkPathMonitor() let path = NetworkPath( status: .satisfied, interfaceType: .wifi, @@ -76,16 +76,13 @@ struct NetworkPathMonitorTests { unsatisfiedReason: nil, timestamp: 1.0 ) - monitor.onNetworkPathUpdate(path) - // `onNetworkPathUpdate` dispatches via `AppMetricsActor.isolated { ... }`, - // so we yield to let the cache update before asserting. - try await Task.sleep(for: .milliseconds(10)) + monitor.apply(path) #expect(monitor.currentPath == path) } @Test - func `overwrites the cached path on subsequent updates`() async throws { - let monitor = NetworkPathMonitor.shared + func `overwrites the cached path on subsequent updates`() { + let monitor = NetworkPathMonitor() let first = NetworkPath( status: .satisfied, interfaceType: .wifi, @@ -102,15 +99,14 @@ struct NetworkPathMonitorTests { unsatisfiedReason: .notAvailable, timestamp: 2.0 ) - monitor.onNetworkPathUpdate(first) - monitor.onNetworkPathUpdate(second) - try await Task.sleep(for: .milliseconds(10)) + monitor.apply(first) + monitor.apply(second) #expect(monitor.currentPath == second) } @Test func `waitForFirstPath returns immediately when a path is already cached`() async { - let monitor = NetworkPathMonitor.shared + let monitor = NetworkPathMonitor() let path = NetworkPath( status: .satisfied, interfaceType: .wifi, @@ -119,7 +115,7 @@ struct NetworkPathMonitorTests { unsatisfiedReason: nil, timestamp: 1.0 ) - monitor.onNetworkPathUpdate(path) + monitor.apply(path) let result = await monitor.waitForFirstPath() #expect(result == path) } From 71f1647a461932f7f6a8711a3b2fb28a47f8f11e Mon Sep 17 00:00:00 2001 From: Vojtech Novak Date: Thu, 7 May 2026 11:42:57 +0200 Subject: [PATCH 5/5] [ui] extend picker item styling support, add docs (#45455) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Why The community Picker drop-in (`@expo/ui/community/picker`) was missing docs and had a few item styling gaps vs `@react-native-picker/picker`. This PR adds the docs pages and brings `Picker.Item` styling closer to the community module's interface. # How - Add docs MDX for `@expo/ui/community/picker` in `unversioned` and `v55.0.0`, plus a top-level `drop-in-replacements/index.mdx` entry. - Wire `Picker.Item` to accept `style` (`color`, `backgroundColor`, `fontFamily`, `fontSize`); top-level `color`/`fontFamily` remain as aliases. iOS uses `foregroundStyle`/`font`/`backgroundOverlay`; Android passes through to the Compose `Text`. - Switch each platform's `Picker` to a top-level function declaration with `Picker.Item` cast to `React.ComponentType` so `Picker.Item` is auto-derived in the API section. # Test Plan 1. Open NCL → UI → Community Picker on iOS and Android. Verify all sections render and selection works. 2. check the docs # Checklist - [ ] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting) - [ ] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). - [ ] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md) --- .../src/screens/UI/CommunityPickerScreen.tsx | 88 +++++------ .../sdk/ui/drop-in-replacements/index.mdx | 1 + .../sdk/ui/drop-in-replacements/picker.mdx | 142 ++++++++++++++++++ .../sdk/ui/drop-in-replacements/index.mdx | 1 + .../sdk/ui/drop-in-replacements/picker.mdx | 142 ++++++++++++++++++ .../unversioned/expo-ui/community/picker.json | 1 + .../v55.0.0/expo-ui/community/picker.json | 1 + .../community/picker/Picker.android.d.ts | 12 +- .../community/picker/Picker.android.d.ts.map | 2 +- .../build/community/picker/Picker.d.ts | 12 +- .../build/community/picker/Picker.d.ts.map | 2 +- .../build/community/picker/Picker.ios.d.ts | 12 +- .../community/picker/Picker.ios.d.ts.map | 2 +- .../expo-ui/build/community/picker/types.d.ts | 22 +-- .../build/community/picker/types.d.ts.map | 2 +- .../src/community/picker/Picker.android.tsx | 17 ++- .../src/community/picker/Picker.ios.tsx | 15 +- .../expo-ui/src/community/picker/Picker.tsx | 7 +- .../expo-ui/src/community/picker/types.tsx | 42 +++--- tools/src/commands/GenerateDocsAPIData.ts | 1 + 20 files changed, 423 insertions(+), 101 deletions(-) create mode 100644 docs/pages/versions/unversioned/sdk/ui/drop-in-replacements/picker.mdx create mode 100644 docs/pages/versions/v55.0.0/sdk/ui/drop-in-replacements/picker.mdx create mode 100644 docs/public/static/data/unversioned/expo-ui/community/picker.json create mode 100644 docs/public/static/data/v55.0.0/expo-ui/community/picker.json diff --git a/apps/native-component-list/src/screens/UI/CommunityPickerScreen.tsx b/apps/native-component-list/src/screens/UI/CommunityPickerScreen.tsx index ed34ba53553cc1..1a20602b0acbfc 100644 --- a/apps/native-component-list/src/screens/UI/CommunityPickerScreen.tsx +++ b/apps/native-component-list/src/screens/UI/CommunityPickerScreen.tsx @@ -1,9 +1,18 @@ import { Picker, type PickerProps, type PickerRef } from '@expo/ui/community/picker'; import React, { useRef, useState } from 'react'; -import { Button, Text } from 'react-native'; +import { Button, Platform, Text } from 'react-native'; import { ScrollPage, Section } from '../../components/Page'; +const monospace = Platform.select({ ios: 'Menlo', android: 'monospace', default: 'monospace' }); +const serif = Platform.select({ ios: 'Georgia', android: 'serif', default: 'serif' }); +const sansSerif = Platform.select({ + ios: 'Helvetica', + android: 'sansSerif', + default: 'sansSerif', +}); +const cursive = Platform.select({ ios: 'Snell Roundhand', android: 'cursive', default: 'cursive' }); + export default function CommunityPickerScreen() { return ( @@ -15,16 +24,8 @@ export default function CommunityPickerScreen() { -
- -
- -
- -
- -
- +
+
@@ -42,48 +43,39 @@ CommunityPickerScreen.navigationOptions = { title: 'Community Picker', }; -function ColoredPicker() { +function StyledPicker() { const [value, setValue] = useState('java'); return ( <> setValue(itemValue)}> - - - - - - Selected: {value} - - ); -} - -function FontFamilyPicker() { - const [value, setValue] = useState('java'); - - return ( - <> - setValue(itemValue)}> - - - - - - Selected: {value} - - ); -} - -function ItemEnabledPicker() { - const [value, setValue] = useState('java'); - - return ( - <> - setValue(itemValue)}> - - - - + + + + Selected: {value} diff --git a/docs/pages/versions/unversioned/sdk/ui/drop-in-replacements/index.mdx b/docs/pages/versions/unversioned/sdk/ui/drop-in-replacements/index.mdx index 3d08bd5ee73781..280d3b73f31119 100644 --- a/docs/pages/versions/unversioned/sdk/ui/drop-in-replacements/index.mdx +++ b/docs/pages/versions/unversioned/sdk/ui/drop-in-replacements/index.mdx @@ -11,4 +11,5 @@ The following components provide API-compatible replacements for popular React N - **[DateTimePicker](datetimepicker)**: Compatible with `@react-native-community/datetimepicker` - **[BottomSheet](bottomsheet)**: Compatible with `@gorhom/bottom-sheet` +- **[Picker](picker)**: Compatible with `@react-native-picker/picker` - **[SegmentedControl](segmentedcontrol)**: Compatible with `@react-native-segmented-control/segmented-control` diff --git a/docs/pages/versions/unversioned/sdk/ui/drop-in-replacements/picker.mdx b/docs/pages/versions/unversioned/sdk/ui/drop-in-replacements/picker.mdx new file mode 100644 index 00000000000000..efc90da13690e0 --- /dev/null +++ b/docs/pages/versions/unversioned/sdk/ui/drop-in-replacements/picker.mdx @@ -0,0 +1,142 @@ +--- +title: Picker +description: A picker compatible with @react-native-picker/picker. +sourceCodeUrl: 'https://github.com/expo/expo/tree/main/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'ios', 'web', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +A `Picker` component with an API compatible with `@react-native-picker/picker`. It uses a SwiftUI wheel `Picker` on iOS, a Material 3 `ExposedDropdownMenuBox` on Android, and a native `` element on web. + +Under the hood this component wraps the platform-specific `@expo/ui` primitives: + +- **Android**: [Jetpack Compose ExposedDropdownMenuBox](../jetpack-compose/exposeddropdownmenubox) +- **iOS**: [SwiftUI Picker](../swift-ui/picker) with `pickerStyle('wheel')` + +If you need lower-level control, use those primitives directly. + +## Installation + + + +## Migrating from `@react-native-picker/picker` + +- Update the import from `import { Picker } from '@react-native-picker/picker'` to `import { Picker } from '@expo/ui/community/picker'`. +- `mode`, `prompt`, `dropdownIconColor`, `dropdownIconRippleColor`, `numberOfLines`, `selectionColor`, `itemStyle`, and `accessibilityLabel` props are not supported. +- On `Picker.Item`, the `style` prop only applies `color`, `backgroundColor`, `fontFamily`, and `fontSize`. The top-level `color` and `fontFamily` props are still supported as aliases for the corresponding `style` values. +- `enabled` on `Picker.Item` only applies on Android. +- The `ref` `focus()` and `blur()` methods only have an effect on Android (open/close the dropdown). On iOS, the wheel picker is always visible. + +## Basic usage + +```tsx PickerExample.tsx +import { useState } from 'react'; +import { Text, View } from 'react-native'; +import { Picker } from '@expo/ui/community/picker'; + +export default function PickerExample() { + const [language, setLanguage] = useState('java'); + + return ( + + setLanguage(value)}> + + + + + + Selected: {language} + + ); +} +``` + +## Per-item styling and state + +Pass a `style` to `Picker.Item` to control `color`, `backgroundColor`, `fontFamily`, and `fontSize` per item, and `enabled={false}` to disable specific items on Android. + +`fontFamily` accepts iOS font names (for example, `'Menlo'`) on iOS, and Compose generic families (`'monospace'`, `'serif'`, `'sansSerif'`, `'cursive'`) or fonts loaded with [`expo-font`](../../font) on Android. + +```tsx StyledPickerExample.tsx +import { useState } from 'react'; +import { Platform } from 'react-native'; +import { Picker } from '@expo/ui/community/picker'; + +const monospace = Platform.select({ ios: 'Menlo', android: 'monospace' }); +const serif = Platform.select({ ios: 'Georgia', android: 'serif' }); + +export default function StyledPickerExample() { + const [language, setLanguage] = useState('java'); + + return ( + setLanguage(value)}> + + + + + + ); +} +``` + +## Imperative focus and blur (Android) + +Use a ref to programmatically open and close the dropdown on Android. On iOS, these methods are no-ops because the wheel picker is always visible. + +```tsx RefPickerExample.tsx +import { useRef, useState } from 'react'; +import { Button } from 'react-native'; +import { Picker, type PickerRef } from '@expo/ui/community/picker'; + +export default function RefPickerExample() { + const [language, setLanguage] = useState('java'); + const pickerRef = useRef(null); + + return ( + <> +