ProComponents, templates & AI tooling
2.3k

Select 选择器

通过按钮触发,展示可选列表供用户选择。

导入

import { Select } from 'heroui-native';

结构

<Select>
  <Select.Trigger>
    <Select.Value />
    <Select.TriggerIndicator />
  </Select.Trigger>
  <Select.Portal>
    <Select.Overlay />
    <Select.Content>
      <Select.Close />
      <Select.ListLabel>...</Select.ListLabel>
      <Select.Item>
        <Select.ItemLabel />
        <Select.ItemDescription>...</Select.ItemDescription>
        <Select.ItemIndicator />
      </Select.Item>
    </Select.Content>
  </Select.Portal>
</Select>
  • Select:根容器,管理打开/关闭、选中值,并向子组件提供上下文。
  • Select.Trigger:可点击的触发器,用于切换选择器显示。为任意子元素包裹按压处理,支持 variant'default''unstyled')。
  • Select.Value:显示当前选中值或占位符;选中变化时自动更新,样式随是否有选中值变化。
  • Select.TriggerIndicator:可选的视觉指示器,表示开/关状态;默认渲染带动画的双角标,随打开/关闭旋转。
  • Select.Portal:在Portal层渲染内容,保证正确的层级与定位。
  • Select.Overlay:可选的背景遮罩,可透明或半透明,用于捕获外部点击。
  • Select.Content:内容容器,支持三种呈现:气泡(浮动定位)、底部抽屉或对话框。
  • Select.Close:关闭按钮;可传入自定义子节点,否则使用默认关闭图标。
  • Select.ListLabel:列表标题,使用预设排版样式。
  • Select.Item:可选中的选项,处理选中态与按压。
  • Select.ItemLabel:选项主文案。
  • Select.ItemDescription:可选的说明文字,弱化样式。
  • Select.ItemIndicator:选中项的可选指示器,默认渲染对勾图标。

用法

基础用法

Select 通过复合子组件构建下拉选择界面。

<Select>
  <Select.Trigger>...</Select.Trigger>
  <Select.Portal>
    <Select.Overlay />
    <Select.Content presentation="popover">
      <Select.Item value="1" label="选项 1" />
      <Select.Item value="2" label="选项 2" />
    </Select.Content>
  </Select.Portal>
</Select>

在触发器显示选中值

使用 Value 在触发器区域展示当前选中项。

<Select>
  <Select.Trigger>
    <Select.Value placeholder="请选择一项" />
    <Select.TriggerIndicator />
  </Select.Trigger>
  <Select.Portal>
    <Select.Overlay />
    <Select.Content presentation="popover">
      <Select.Item value="apple" label="苹果" />
      <Select.Item value="orange" label="橙子" />
      <Select.Item value="banana" label="香蕉" />
    </Select.Content>
  </Select.Portal>
</Select>

气泡(Popover)呈现

使用 presentation="popover" 获得带自动定位的浮动内容。

<Select>
  <Select.Trigger>...</Select.Trigger>
  <Select.Portal>
    <Select.Overlay />
    <Select.Content presentation="popover" placement="bottom" align="center">
      <Select.Item value="1" label="项 1" />
      <Select.Item value="2" label="项 2" />
    </Select.Content>
  </Select.Portal>
</Select>

宽度控制

通过 width 控制内容宽度;仅对气泡呈现生效。

{
  /* 固定像素宽度 */
}
<Select>
  <Select.Trigger>...</Select.Trigger>
  <Select.Portal>
    <Select.Overlay />
    <Select.Content presentation="popover" width={280}>
      <Select.Item value="1" label="项 1" />
    </Select.Content>
  </Select.Portal>
</Select>;

{
  /* 与触发器同宽 */
}
<Select>
  <Select.Trigger>...</Select.Trigger>
  <Select.Portal>
    <Select.Overlay />
    <Select.Content presentation="popover" width="trigger">
      <Select.Item value="1" label="项 1" />
    </Select.Content>
  </Select.Portal>
</Select>;

{
  /* 全宽(100%) */
}
<Select>
  <Select.Trigger>...</Select.Trigger>
  <Select.Portal>
    <Select.Overlay />
    <Select.Content presentation="popover" width="full">
      <Select.Item value="1" label="项 1" />
    </Select.Content>
  </Select.Portal>
</Select>;

{
  /* 随内容自适应(默认) */
}
<Select>
  <Select.Trigger>...</Select.Trigger>
  <Select.Portal>
    <Select.Overlay />
    <Select.Content presentation="popover" width="content-fit">
      <Select.Item value="1" label="项 1" />
    </Select.Content>
  </Select.Portal>
</Select>;

底部抽屉呈现

使用底部抽屉以获得更贴近移动端的体验。

<Select presentation="bottom-sheet">
  <Select.Trigger>...</Select.Trigger>
  <Select.Portal>
    <Select.Overlay />
    <Select.Content presentation="bottom-sheet" snapPoints={['35%']}>
      <Select.Item value="1" label="项 1" />
      <Select.Item value="2" label="项 2" />
    </Select.Content>
  </Select.Portal>
</Select>

对话框呈现

使用对话框呈现居中模态式选择。

<Select presentation="dialog">
  <Select.Trigger>...</Select.Trigger>
  <Select.Portal>
    <Select.Overlay />
    <Select.Content presentation="dialog">
      <Select.Close />
      <Select.ListLabel>请选择一项</Select.ListLabel>
      <Select.Item value="1" label="项 1" />
      <Select.Item value="2" label="项 2" />
    </Select.Content>
  </Select.Portal>
</Select>

自定义选项内容

通过自定义子节点与指示器定制选项外观。

<Select>
  <Select.Trigger>...</Select.Trigger>
  <Select.Portal>
    <Select.Overlay />
    <Select.Content presentation="popover">
      <Select.Item value="us" label="美国">
        <View className="flex-row items-center gap-3 flex-1">
          <Text>🇺🇸</Text>
          <Select.ItemLabel />
        </View>
        <Select.ItemIndicator />
      </Select.Item>
      <Select.Item value="uk" label="英国">
        <View className="flex-row items-center gap-3 flex-1">
          <Text>🇬🇧</Text>
          <Select.ItemLabel />
        </View>
        <Select.ItemIndicator />
      </Select.Item>
    </Select.Content>
  </Select.Portal>
</Select>

使用渲染函数

Select.Item 上使用渲染函数,根据选中态等自定义内容。

<Select>
  <Select.Trigger>...</Select.Trigger>
  <Select.Portal>
    <Select.Overlay />
    <Select.Content presentation="popover">
      <Select.Item value="us" label="美国">
        {({ isSelected, value, isDisabled }) => (
          <>
            <View className="flex-row items-center gap-3 flex-1">
              <Text>🇺🇸</Text>
              <Select.ItemLabel
                className={
                  isSelected ? 'text-accent font-medium' : 'text-foreground'
                }
              />
            </View>
            <Select.ItemIndicator />
          </>
        )}
      </Select.Item>
      <Select.Item value="uk" label="英国">
        {({ isSelected }) => (
          <>
            <View className="flex-row items-center gap-3 flex-1">
              <Text>🇬🇧</Text>
              <Select.ItemLabel
                className={
                  isSelected ? 'text-accent font-medium' : 'text-foreground'
                }
              />
            </View>
            <Select.ItemIndicator />
          </>
        )}
      </Select.Item>
    </Select.Content>
  </Select.Portal>
</Select>

带选项说明

为选项添加说明以提供更多上下文。

<Select>
  <Select.Trigger>...</Select.Trigger>
  <Select.Portal>
    <Select.Overlay />
    <Select.Content presentation="popover">
      <Select.Item value="basic" label="基础版">
        <View className="flex-1">
          <Select.ItemLabel />
          <Select.ItemDescription>
            面向个人使用的必备功能
          </Select.ItemDescription>
        </View>
        <Select.ItemIndicator />
      </Select.Item>
    </Select.Content>
  </Select.Portal>
</Select>

带触发器指示器

添加视觉指示器表示开/关状态;打开/关闭时会旋转。

<Select>
  <Select.Trigger>
    <Select.Value placeholder="请选择一项" />
    <Select.TriggerIndicator />
  </Select.Trigger>
  <Select.Portal>
    <Select.Overlay />
    <Select.Content presentation="popover">
      <Select.Item value="1" label="选项 1" />
      <Select.Item value="2" label="选项 2" />
    </Select.Content>
  </Select.Portal>
</Select>

无样式触发器与自定义组合

使用 unstyled 变体,将触发器与 Button 等组件组合。

<Select>
  <Select.Trigger variant="unstyled" asChild>
    <Button variant="secondary">
      <Select.Value placeholder="请选择…" />
      <Select.TriggerIndicator />
    </Button>
  </Select.Trigger>
  <Select.Portal>
    <Select.Overlay />
    <Select.Content presentation="popover">
      <Select.Item value="1" label="选项 1" />
      <Select.Item value="2" label="选项 2" />
    </Select.Content>
  </Select.Portal>
</Select>

受控模式

以编程方式控制打开状态与选中值。

const [value, setValue] = useState();
const [isOpen, setIsOpen] = useState(false);

<Select
  value={value}
  onValueChange={setValue}
  isOpen={isOpen}
  onOpenChange={setIsOpen}
>
  <Select.Trigger>
    <Select.Value placeholder="请选择…" />
    <Select.TriggerIndicator />
  </Select.Trigger>
  <Select.Portal>
    <Select.Overlay />
    <Select.Content presentation="popover">
      <Select.Item value="1" label="选项 1" />
      <Select.Item value="2" label="选项 2" />
    </Select.Content>
  </Select.Portal>
</Select>;

示例

import { Select, Separator } from 'heroui-native';
import React, { useState } from 'react';

type SelectOption = {
  value: string;
  label: string;
};

const US_STATES: SelectOption[] = [
  { value: 'CA', label: '加利福尼亚' },
  { value: 'NY', label: '纽约' },
  { value: 'TX', label: '得克萨斯' },
  { value: 'FL', label: '佛罗里达' },
];

export default function SelectExample() {
  const [value, setValue] = useState<SelectOption | undefined>();

  return (
    <Select value={value} onValueChange={setValue}>
      <Select.Trigger>
        <Select.Value placeholder="请选择一项" />
        <Select.TriggerIndicator />
      </Select.Trigger>
      <Select.Portal>
        <Select.Overlay />
        <Select.Content presentation="popover" width="trigger">
          <Select.ListLabel className="mb-2">选择州/省</Select.ListLabel>
          {US_STATES.map((state, index) => (
            <React.Fragment key={state.value}>
              <Select.Item value={state.value} label={state.label} />
              {index < US_STATES.length - 1 && <Separator />}
            </React.Fragment>
          ))}
        </Select.Content>
      </Select.Portal>
    </Select>
  );
}

更多示例见 GitHub 仓库

API 参考

Select

proptypedefaultdescription
childrenReactNode-选择器子内容
valueSelectOption | SelectOption[]-当前选中值(受控)
onValueChange(value: SelectOption | SelectOption[]) => void-选中值变化时的回调
defaultValueSelectOption | SelectOption[]-默认选中值(非受控)
isOpenboolean-是否打开(受控)
isDefaultOpenboolean-初始是否打开(非受控)
onOpenChange(isOpen: boolean) => void-打开状态变化时的回调
isDisabledbooleanfalse是否禁用
presentation'popover' | 'bottom-sheet' | 'dialog''popover'内容呈现方式
animationSelectRootAnimation-动画配置
asChildbooleanfalse是否将子元素作为实际渲染节点
...ViewPropsViewProps-支持全部标准 React Native View 属性

SelectRootAnimation

Select 根级动画配置,可为:

  • false"disabled":仅禁用根动画
  • "disable-all":禁用根与子级全部动画
  • trueundefined:使用默认动画
  • object:自定义动画配置
proptypedefaultdescription
state'disabled' | 'disable-all' | boolean-在自定义属性时用于禁用动画
entering.valueSpringAnimationConfig | TimingAnimationConfig-打开时的动画配置
exiting.valueSpringAnimationConfig | TimingAnimationConfig-关闭时的动画配置

SpringAnimationConfig

proptypedefaultdescription
type'spring'-动画类型(须为 'spring'
configWithSpringConfig-Reanimated 弹簧动画配置

TimingAnimationConfig

proptypedefaultdescription
type'timing'-动画类型(须为 'timing'
configWithTimingConfig-Reanimated 时长动画配置

Select.Trigger

proptypedefaultdescription
variant'default' | 'unstyled''default'触发器变体:'default' 应用预设容器样式,'unstyled' 移除默认样式
childrenReactNode-触发器内容
classNamestring-触发器额外 class
asChildbooleantrue是否将子元素作为实际渲染节点
isDisabledboolean-是否禁用触发器
...PressablePropsPressableProps-支持全部标准 React Native Pressable 属性

Select.Value

proptypedefaultdescription
placeholderstring-未选中时的占位文案
classNamestring-值区域额外 class
...TextPropsTextProps-支持全部标准 React Native Text 属性

说明: 值组件会根据是否有选中项自动应用不同文字颜色:

  • 已选中:text-foreground
  • 未选中(占位):text-field-placeholder

Select.TriggerIndicator

proptypedefaultdescription
childrenReactNode-自定义指示器内容;默认带动画的双角标
classNamestring-指示器额外 class
styleViewStyle-指示器自定义样式
iconPropsSelectTriggerIndicatorIconProps-双角标图标配置
animationSelectTriggerIndicatorAnimation-动画配置
isAnimatedStyleActivebooleantrue是否启用 Reanimated 动画样式
...ViewPropsViewProps-支持全部标准 React Native View 属性

说明: 以下样式属性由动画占用,不能通过 className 设置:

  • transform(尤其是 rotate)— 用于开/关旋转过渡

若要自定义,请使用 animation。若需完全关闭动画样式并自行用 classNamestyle 控制,请设置 isAnimatedStyleActive={false}

SelectTriggerIndicatorIconProps

proptypedefaultdescription
sizenumber16图标尺寸
colorstring-图标颜色(默认同前景主题色)

SelectTriggerIndicatorAnimation

Select.TriggerIndicator 的动画配置,可为:

  • false"disabled":禁用全部动画
  • trueundefined:使用默认动画(0° 到 -180° 旋转)
  • object:自定义动画配置
proptypedefaultdescription
state'disabled' | boolean-在自定义属性时用于禁用动画
rotation.value[number, number][0, -180]旋转角度 [关闭, 打开],单位度
rotation.springConfigWithSpringConfig{ damping: 140, stiffness: 1000, mass: 4 }旋转弹簧动画配置

Select.Portal

proptypedefaultdescription
childrenReactNode-Portal内容(必填)
disableFullWindowOverlaybooleanfalse在 iOS 为 true 时使用 View 代替 FullWindowOverlay,便于元素检查器;遮罩将无法叠在原生模态之上
unstable_accessibilityContainerViewIsModalbooleanfalse控制 VoiceOver 是否将遮罩窗口视为模态容器。为 true 时,VoiceOver 仅聚焦遮罩内元素。仅 iOS;API 不稳定,可能随 react-native-screens 变更
classNamestring-Portal容器额外 class
hostNamestring-Portal宿主元素的可选名称
forceMountboolean-是否强制挂载到 DOM
...ViewPropsViewProps-支持全部标准 React Native View 属性

Select.Overlay

proptypedefaultdescription
classNamestring-遮罩额外 class
animationSelectOverlayAnimation-动画配置
isAnimatedStyleActivebooleantrue是否启用 Reanimated 动画样式
closeOnPressbooleantrue点击遮罩是否关闭选择器
forceMountboolean-是否强制挂载到 DOM
asChildbooleanfalse是否将子元素作为实际渲染节点
...Animated.ViewPropsAnimated.ViewProps-支持 Reanimated Animated.View 的全部属性

SelectOverlayAnimation

Select.Overlay 的动画配置,可为:

  • false"disabled":禁用全部动画
  • trueundefined:使用默认动画(底部抽屉/对话框为基于进度的透明度;气泡为关键帧动画)
  • object:自定义动画配置
proptypedefaultdescription
state'disabled' | boolean-在自定义属性时用于禁用动画
opacity.value[number, number, number][0, 1, 0]透明度 [空闲, 打开, 关闭](用于底部抽屉/对话框呈现)
enteringEntryOrExitLayoutType-进入过渡自定义关键帧(用于气泡呈现)
exitingEntryOrExitLayoutType-退出过渡自定义关键帧(用于气泡呈现)

Select.Content(气泡呈现)

proptypedefaultdescription
childrenReactNode-选择器内容
widthnumber | 'trigger' | 'content-fit' | 'full''content-fit'内容宽度策略
presentation'popover''popover'呈现模式
placement'top' | 'bottom' | 'left' | 'right''bottom'相对触发器的方位
align'start' | 'center' | 'end''center'沿放置轴的对齐方式
avoidCollisionsbooleantrue靠近视口边缘时是否翻转 placement
offsetnumber8与触发器的间距(像素)
alignOffsetnumber0沿对齐轴的偏移(像素)
classNamestring-内容容器额外 class
animationSelectContentPopoverAnimation-动画配置
forceMountboolean-是否强制挂载到 DOM
insetsInsets-定位时需遵守的屏幕边距
asChildbooleanfalse是否将子元素作为实际渲染节点
...Animated.ViewPropsAnimated.ViewProps-支持 Reanimated Animated.View 的全部属性

SelectContentPopoverAnimation

Select.Content(气泡呈现)的动画配置,可为:

  • false"disabled":禁用全部动画
  • trueundefined:使用默认关键帧(按 placement 的 translateY/translateX、scale、opacity)
  • object:自定义 entering 和/或 exiting 关键帧
proptypedefaultdescription
state'disabled' | boolean-在自定义属性时用于禁用动画
enteringEntryOrExitLayoutType-进入过渡关键帧(默认:按 placement 的 translateY/translateX、scale、opacity,200ms)
exitingEntryOrExitLayoutType-退出过渡关键帧(默认:与进入镜像,150ms)

Select.Content(底部抽屉呈现)

proptypedefaultdescription
childrenReactNode-底部抽屉内容
presentation'bottom-sheet'-呈现模式
contentContainerClassNamestring-内容容器额外 class
...BottomSheetPropsBottomSheetProps-支持 @gorhom/bottom-sheet 的全部属性

Select.Content(对话框呈现)

proptypedefaultdescription
childrenReactNode-对话框内容
presentation'dialog'-呈现模式
classNames{ wrapper?: string; content?: string }-包裹层与内容区额外 class
stylesPartial<Record<DialogContentFallbackSlots, ViewStyle>>-对话框各部分的样式
animationSelectContentAnimation-动画配置
isSwipeablebooleantrue是否允许滑动关闭
forceMountboolean-是否强制挂载到 DOM
asChildbooleanfalse是否将子元素作为实际渲染节点
...ViewPropsViewProps-支持全部标准 React Native View 属性

styles

proptypedescription
wrapperViewStyle外层包裹容器样式
contentViewStyle对话框内容区样式

SelectContentAnimation

Select.Content(对话框呈现)的动画配置,可为:

  • false"disabled":禁用全部动画
  • trueundefined:使用默认关键帧(scale 与 opacity)
  • object:自定义 entering 和/或 exiting 关键帧
proptypedefaultdescription
state'disabled' | boolean-在自定义属性时用于禁用动画
enteringEntryOrExitLayoutType-进入过渡关键帧(默认:scale 与 opacity,200ms)
exitingEntryOrExitLayoutType-退出过渡关键帧(默认:与进入镜像,150ms)

Select.Close

Select.Close 继承 CloseButton,按下时自动关闭选择器。

Select.ListLabel

proptypedefaultdescription
childrenReactNode-列表标题文案
classNamestring-列表标题额外 class
...TextPropsTextProps-支持全部标准 React Native Text 属性

Select.Item

proptypedefaultdescription
childrenReactNode | ((props: SelectItemRenderProps) => ReactNode)-自定义选项内容;默认可为标签+指示器,或渲染函数
valueany-选项关联的值(必填)
labelstring-选项标签文案(必填)
isDisabledbooleanfalse是否禁用该选项
classNamestring-选项额外 class
...PressablePropsPressableProps-支持全部标准 React Native Pressable 属性

SelectItemRenderProps

使用渲染函数作为 children 时,会传入以下属性:

propertytypedescription
isSelectedboolean当前项是否选中
valuestring当前项的值
isDisabledboolean当前项是否禁用

Select.ItemLabel

proptypedefaultdescription
classNamestring-选项标签额外 class
...TextPropsTextProps-支持全部标准 React Native Text 属性

Select.ItemDescription

proptypedefaultdescription
childrenReactNode-说明文案
classNamestring-说明额外 class
...TextPropsTextProps-支持全部标准 React Native Text 属性

Select.ItemIndicator

proptypedefaultdescription
childrenReactNode-自定义指示器;默认对勾图标
classNamestring-指示器额外 class
iconPropsSelectItemIndicatorIconProps-对勾图标配置
...ViewPropsViewProps-支持全部标准 React Native View 属性

SelectItemIndicatorIconProps

proptypedefaultdescription
sizenumber16图标尺寸
colorstring--colors-muted图标颜色

Hooks

useSelect

用于读取 Select 根上下文,返回状态与控制方法。

import { useSelect } from 'heroui-native';

const {
  isOpen,
  onOpenChange,
  isDefaultOpen,
  isDisabled,
  presentation,
  triggerPosition,
  setTriggerPosition,
  contentLayout,
  setContentLayout,
  nativeID,
  value,
  onValueChange,
} = useSelect();

返回值

propertytypedescription
isOpenboolean当前是否打开
onOpenChange(open: boolean) => void修改打开状态的回调
isDefaultOpenboolean | undefined默认是否打开(非受控)
isDisabledboolean | undefined是否禁用
presentation'popover' | 'bottom-sheet' | 'dialog'内容呈现方式
triggerPositionLayoutPosition | null触发器相对视口的位置
setTriggerPosition(position: LayoutPosition | null) => void更新触发器位置
contentLayoutLayoutRectangle | null选择器内容的布局测量
setContentLayout(layout: LayoutRectangle | null) => void更新内容布局测量
nativeIDstring当前实例的唯一标识
valueSelectOption | SelectOption[]当前选中项
onValueChange(option: SelectOption | SelectOption[]) => void选中值变化时的回调

说明: 必须在 Select 内使用;在上下文外调用将抛错。

useSelectAnimation

用于在自定义或复合子组件中读取 Select 动画相关共享值。

import { useSelectAnimation } from 'heroui-native';

const { selectState, progress, isDragging, isGestureReleaseAnimationRunning } =
  useSelectAnimation();

返回值

propertytypedescription
progressSharedValue<number>动画进度(0=空闲,1=打开,2=关闭)
isDraggingSharedValue<boolean>内容是否正在被拖拽
isGestureReleaseAnimationRunningSharedValue<boolean>手势释放后的动画是否正在运行

说明: 必须在 Select 内使用;在动画上下文外调用将抛错。

SelectOption

propertytypedescription
valuestring选项值
labelstring选项显示标签

useSelectItem

用于读取 Select Item 上下文,返回当前项的值与标签。

import { useSelectItem } from 'heroui-native';

const { itemValue, label } = useSelectItem();

返回值

propertytypedescription
itemValuestring当前项的值
labelstring当前项的标签文案

特别说明

元素检查器(iOS)

Select 在 iOS 上使用 FullWindowOverlay。开发时若需启用 React Native 元素检查器,请在 Select.Portal 上设置 disableFullWindowOverlay={true}。代价是下拉层将无法叠在原生模态之上。

原生模态(iOS)

Select 位于以原生模态形式呈现的页面内时(presentation: 'modal' | 'formSheet' | 'pageSheet'),下拉层可能会向上偏移渲染。在新架构(Fabric)中,react-native-screensRNSModalScreen 标记为 Fabric 根节点,因此触发器的坐标是相对于模态原点上报的,而 FullWindowOverlay(下拉层挂载点)锚定在 iOS 应用窗口上。可通过将 safeAreaInsets.top 加到 offset 来补偿:

import { useSafeAreaInsets } from 'react-native-safe-area-context';

const insets = useSafeAreaInsets();

<Select.Content presentation="popover" offset={insets.top + 10}>
  ...
</Select.Content>;

本页目录