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/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. 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`. 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 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 ( + <> +