欢迎加入开源鸿蒙跨平台社区 :https://openharmonycrossplatform.csdn.net
📋 前言
下拉选择是移动应用中使用最频繁的交互组件之一,无论是筛选条件、表单输入还是配置选项,都离不开下拉选择器的身影。react-native-dropdown-picker 是一个功能强大的下拉选择器库,支持单选/多选、搜索过滤、分组显示等功能,让开发者能够轻松实现原生般的下拉选择体验。
🎯 库简介
基本信息
- 库名称 :
react-native-dropdown-picker - 版本信息 :
5.4.6+react-native-dropdown-picker: 支持 RN 0.72 / 0.77 版本
- 官方仓库: https://github.com/hossein-zare/react-native-dropdown-picker
- 主要功能 :
- 📋 单选/多选模式
- 🔍 搜索过滤功能
- 📁 分组显示支持
- 🎨 丰富的样式定制
- 📱 跨平台一致体验
为什么需要下拉选择器?
| 特性 | 原生 Picker | react-native-dropdown-picker |
|---|---|---|
| 跨平台一致性 | ❌ 表现各异 | ✅ 统一体验 |
| 搜索功能 | ❌ 无 | ✅ 内置搜索 |
| 多选支持 | ⚠️ 需自定义 | ✅ 原生支持 |
| 分组显示 | ❌ 无 | ✅ 支持分类 |
| 样式定制 | ⚠️ 受限 | ✅ 完全可定制 |
| HarmonyOS 支持 | ❌ 无 | ✅ 完善适配 |
核心功能
| 功能 | 说明 | HarmonyOS 支持 |
|---|---|---|
| 单选模式 | 从列表中选择一项 | ✅ |
| 多选模式 | 同时选择多项 | ✅ |
| 搜索过滤 | 输入关键字过滤选项 | ✅ |
| 分组显示 | 按分类展示选项 | ✅ |
| 禁用状态 | 禁用选择器 | ✅ |
| 加载状态 | 显示加载指示器 | ✅ |
| 自定义图标 | 箭头/勾选图标定制 | ✅ |
兼容性验证
在以下环境验证通过:
- RNOH : 0.72.90; SDK : HarmonyOS 6.0.0; IDE : DevEco Studio 6.0.02; ROM: 6.0.0
📦 安装步骤
1. 安装依赖
bash
npm install react-native-dropdown-picker@5.4.6
# 或者使用 yarn
yarn add react-native-dropdown-picker@5.4.6
2. 验证安装
安装完成后,检查 package.json 文件:
json
{
"dependencies": {
"react-native-dropdown-picker": "^5.4.6"
}
}
📖 API 详解
DropDownPicker 基础用法
tsx
import React, { useState } from 'react';
import { View } from 'react-native';
import DropDownPicker from 'react-native-dropdown-picker';
const App = () => {
const [open, setOpen] = useState(false);
const [value, setValue] = useState(null);
const [items, setItems] = useState([
{ label: '苹果', value: 'apple' },
{ label: '香蕉', value: 'banana' },
{ label: '橙子', value: 'orange' },
]);
return (
<View style={{ flex: 1, justifyContent: 'center', padding: 20 }}>
<DropDownPicker
open={open}
value={value}
items={items}
setOpen={setOpen}
setValue={setValue}
setItems={setItems}
/>
</View>
);
};
基础属性
items - 选项列表
tsx
const [items, setItems] = useState([
{ label: '苹果', value: 'apple' },
{ label: '香蕉', value: 'banana' },
{ label: '橙子', value: 'orange' },
]);
<DropDownPicker items={items} ... />
value - 当前选中值
tsx
// 单选
const [value, setValue] = useState<string | null>(null);
// 多选
const [value, setValue] = useState<string[]>([]);
<DropDownPicker value={value} ... />
open / setOpen - 控制展开状态
tsx
const [open, setOpen] = useState(false);
<DropDownPicker open={open} setOpen={setOpen} ... />
setValue - 值变化回调
tsx
const [value, setValue] = useState(null);
<DropDownPicker
value={value}
setValue={(callback) => {
// callback 可能是值或函数
setValue(prev => callback(prev));
}}
...
/>
multiple - 多选模式
tsx
const [value, setValue] = useState<string[]>([]);
<DropDownPicker
multiple={true}
value={value}
min={1} // 最少选1个
max={3} // 最多选3个
...
/>
disabled - 禁用状态
tsx
<DropDownPicker disabled={true} ... />
显示属性
placeholder - 占位文本
tsx
<DropDownPicker
placeholder="请选择水果"
placeholderStyle={{ color: '#999' }}
...
/>
maxHeight - 下拉框最大高度
tsx
<DropDownPicker maxHeight={200} ... />
zIndex - 堆叠顺序(多个下拉框时需要)
tsx
// 第一个下拉框
<DropDownPicker zIndex={1000} zIndexInverse={3000} ... />
// 第二个下拉框
<DropDownPicker zIndex={2000} zIndexInverse={2000} ... />
// 第三个下拉框
<DropDownPicker zIndex={3000} zIndexInverse={1000} ... />
showArrowIcon / showTickIcon - 图标显示
tsx
<DropDownPicker
showArrowIcon={true} // 显示下拉箭头
showTickIcon={true} // 显示选中勾选
...
/>
listMode - 列表渲染模式(重要)
当 DropDownPicker 嵌套在 ScrollView 中时,必须设置 listMode="SCROLLVIEW" 或 listMode="MODAL",否则会报错 "VirtualizedLists should never be nested inside plain ScrollViews"。
tsx
// 方式一:使用 SCROLLVIEW 模式(推荐)
<DropDownPicker
listMode="SCROLLVIEW"
...
/>
// 方式二:使用 MODAL 模式(弹出模态框)
<DropDownPicker
listMode="MODAL"
...
/>
| listMode 值 | 说明 |
|---|---|
DEFAULT |
默认模式,使用 FlatList(不能嵌套在 ScrollView 中) |
SCROLLVIEW |
使用 ScrollView 渲染,可嵌套在 ScrollView 中 |
MODAL |
弹出模态框显示选项 |
dropDownDirection - 下拉方向
控制下拉框的展开方向,默认为 AUTO(自动判断)。
tsx
<DropDownPicker
dropDownDirection="BOTTOM" // 始终向下展开
...
/>
| dropDownDirection 值 | 说明 |
|---|---|
AUTO |
自动判断(默认),根据空间决定向上或向下 |
TOP |
始终向上展开 |
BOTTOM |
始终向下展开 |
样式属性
tsx
<DropDownPicker
style={{
backgroundColor: '#fff',
borderColor: '#ddd',
borderRadius: 8,
}}
containerStyle={{ marginBottom: 20 }}
textStyle={{ fontSize: 16, color: '#333' }}
labelStyle={{ fontWeight: 'bold' }}
...
/>
搜索属性
searchable - 启用搜索
tsx
<DropDownPicker
searchable={true}
searchPlaceholder="搜索城市..."
searchTextInputProps={{
maxLength: 25,
}}
onChangeSearchText={(text) => {
console.log('搜索:', text);
}}
...
/>
addCustomItem - 添加自定义选项
tsx
<DropDownPicker
searchable={true}
addCustomItem={true} // 搜索无结果时可添加
...
/>
图标属性
自定义图标组件
tsx
import { Text } from 'react-native';
<DropDownPicker
ArrowUpIconComponent={() => <Text>▲</Text>}
ArrowDownIconComponent={() => <Text>▼</Text>}
TickIconComponent={() => <Text>✓</Text>}
...
/>
加载属性
tsx
import { ActivityIndicator } from 'react-native';
<DropDownPicker
loading={isLoading}
activityIndicatorColor="#007AFF"
activityIndicatorSize={20}
ActivityIndicatorComponent={() => (
<ActivityIndicator size="small" color="#007AFF" />
)}
...
/>
事件属性
tsx
<DropDownPicker
onChangeValue={(value) => {
console.log('值变化:', value);
}}
onSelectItem={(item) => {
console.log('选中项:', item);
}}
onOpen={() => {
console.log('下拉框展开');
}}
onClose={() => {
console.log('下拉框关闭');
}}
...
/>
分组功能
tsx
const [items, setItems] = useState([
// 分组标题(不可选)
{ label: '苹果手机', value: 'apple_header', selectable: false },
{ label: 'iPhone 14', value: 'iphone14', parent: 'apple_header' },
{ label: 'iPhone 15', value: 'iphone15', parent: 'apple_header' },
// 另一个分组
{ label: '三星手机', value: 'samsung_header', selectable: false },
{ label: 'Galaxy S23', value: 's23', parent: 'samsung_header' },
{ label: 'Galaxy S24', value: 's24', parent: 'samsung_header' },
]);
<DropDownPicker items={items} ... />
Item 对象结构
typescript
interface Item {
label: string; // 显示文本
value: string; // 值
icon?: any; // 图标组件
disabled?: boolean; // 是否禁用
parent?: string; // 父级值(用于分组)
selectable?: boolean; // 是否可选,false时作为分组标题
}
📋 完整示例
综合示例(单选/多选/搜索/分组/表单)

ts
import React, { useState } from "react";
import {
View,
Text,
TextInput,
TouchableOpacity,
ScrollView,
StyleSheet,
} from "react-native";
import DropDownPicker from "react-native-dropdown-picker";
interface Item {
label: string;
value: string;
parent?: string;
selectable?: boolean;
}
const App: React.FC = () => {
// 基础单选状态
const [fruitOpen, setFruitOpen] = useState(false);
const [fruitValue, setFruitValue] = useState<string | null>(null);
const [fruitItems, setFruitItems] = useState<Item[]>([
{ label: "苹果", value: "apple" },
{ label: "香蕉", value: "banana" },
{ label: "橙子", value: "orange" },
{ label: "葡萄", value: "grape" },
]);
// 多选状态
const [multiOpen, setMultiOpen] = useState(false);
const [multiValue, setMultiValue] = useState<string[]>([]);
const [multiItems, setMultiItems] = useState<Item[]>([
{ label: "阅读", value: "reading" },
{ label: "旅游", value: "traveling" },
{ label: "运动", value: "sports" },
{ label: "音乐", value: "music" },
{ label: "电影", value: "movies" },
{ label: "美食", value: "food" },
]);
// 搜索状态
const [cityOpen, setCityOpen] = useState(false);
const [cityValue, setCityValue] = useState<string | null>(null);
const [cityItems, setCityItems] = useState<Item[]>([
{ label: "北京市", value: "beijing" },
{ label: "上海市", value: "shanghai" },
{ label: "广州市", value: "guangzhou" },
{ label: "深圳市", value: "shenzhen" },
{ label: "杭州市", value: "hangzhou" },
{ label: "南京市", value: "nanjing" },
{ label: "武汉市", value: "wuhan" },
{ label: "成都市", value: "chengdu" },
{ label: "西安市", value: "xian" },
{ label: "重庆市", value: "chongqing" },
]);
// 分组状态
const [productOpen, setProductOpen] = useState(false);
const [productValue, setProductValue] = useState<string | null>(null);
const [productItems, setProductItems] = useState<Item[]>([
{ label: "苹果手机", value: "apple", selectable: false },
{ label: "iPhone 14", value: "iphone14", parent: "apple" },
{ label: "iPhone 14 Pro", value: "iphone14pro", parent: "apple" },
{ label: "三星手机", value: "samsung", selectable: false },
{ label: "Galaxy S23", value: "galaxys23", parent: "samsung" },
{ label: "Galaxy S23 Ultra", value: "galaxys23ultra", parent: "samsung" },
{ label: "小米手机", value: "xiaomi", selectable: false },
{ label: "Mi 13", value: "mi13", parent: "xiaomi" },
{ label: "Mi 13 Pro", value: "mi13pro", parent: "xiaomi" },
]);
// 表单状态 - 独立状态,不与上面示例共享
const [formFruitOpen, setFormFruitOpen] = useState(false);
const [formFruitValue, setFormFruitValue] = useState<string | null>(null);
const [formFruitItems, setFormFruitItems] = useState<Item[]>([
{ label: "苹果", value: "apple" },
{ label: "香蕉", value: "banana" },
{ label: "橙子", value: "orange" },
]);
const [formCityOpen, setFormCityOpen] = useState(false);
const [formCityValue, setFormCityValue] = useState<string | null>(null);
const [formCityItems, setFormCityItems] = useState<Item[]>([
{ label: "北京市", value: "beijing" },
{ label: "上海市", value: "shanghai" },
{ label: "广州市", value: "guangzhou" },
]);
const [formGenderOpen, setFormGenderOpen] = useState(false);
const [formGenderValue, setFormGenderValue] = useState<string | null>(null);
const [formGenderItems, setFormGenderItems] = useState<Item[]>([
{ label: "男", value: "male" },
{ label: "女", value: "female" },
{ label: "保密", value: "secret" },
]);
const getLabel = (items: Item[], val: string | null): string => {
return items.find((i) => i.value === val)?.label || "-";
};
const handleSubmit = () => {
alert(
"表单数据",
`水果: ${getLabel(formFruitItems, formFruitValue)}\n` +
`城市: ${getLabel(formCityItems, formCityValue)}\n` +
`性别: ${getLabel(formGenderItems, formGenderValue)}`
);
};
return (
<ScrollView style={styles.container}>
<Text style={styles.header}>react-native-dropdown-picker 示例</Text>
{/* 基础单选 */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>基础单选</Text>
<DropDownPicker
open={fruitOpen}
value={fruitValue}
items={fruitItems}
setOpen={setFruitOpen}
setValue={setFruitValue}
setItems={setFruitItems}
placeholder="请选择水果"
listMode="SCROLLVIEW"
dropDownDirection="BOTTOM"
style={styles.dropdown}
dropDownContainerStyle={styles.dropdownContainer}
/>
{fruitValue && (
<Text style={styles.resultText}>选择: {getLabel(fruitItems, fruitValue)}</Text>
)}
</View>
{/* 多选模式 */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>多选模式(选择爱好)</Text>
<DropDownPicker
multiple
min={1}
max={4}
open={multiOpen}
value={multiValue}
items={multiItems}
setOpen={setMultiOpen}
setValue={setMultiValue}
setItems={setMultiItems}
placeholder="请选择爱好(1-4项)"
listMode="SCROLLVIEW"
dropDownDirection="BOTTOM"
mode="BADGE"
badgeDotColors={["#5387D8", "#50C7C7", "#FF8C00", "#FF6B6B"]}
style={styles.dropdown}
dropDownContainerStyle={styles.dropdownContainer}
/>
{multiValue.length > 0 && (
<View style={styles.badgeContainer}>
{multiValue.map((v) => (
<View key={v} style={styles.badge}>
<Text style={styles.badgeText}>
{multiItems.find((i) => i.value === v)?.label}
</Text>
</View>
))}
</View>
)}
</View>
{/* 搜索功能 */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>搜索过滤</Text>
<DropDownPicker
open={cityOpen}
value={cityValue}
items={cityItems}
setOpen={setCityOpen}
setValue={setCityValue}
setItems={setCityItems}
searchable
searchPlaceholder="输入城市名称..."
searchTextInputStyle={styles.searchInput}
placeholder="请选择城市"
listMode="SCROLLVIEW"
dropDownDirection="BOTTOM"
style={styles.dropdown}
dropDownContainerStyle={styles.dropdownContainer}
/>
{cityValue && (
<Text style={styles.resultText}>选择: {getLabel(cityItems, cityValue)}</Text>
)}
</View>
{/* 分组显示 */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>分组选择</Text>
<DropDownPicker
open={productOpen}
value={productValue}
items={productItems}
setOpen={setProductOpen}
setValue={setProductValue}
setItems={setProductItems}
placeholder="请选择产品"
categorySelectable
stickyHeader
listMode="SCROLLVIEW"
dropDownDirection="BOTTOM"
style={styles.dropdown}
dropDownContainerStyle={styles.dropdownContainer}
/>
{productValue && (
<Text style={styles.resultText}>选择: {getLabel(productItems, productValue)}</Text>
)}
</View>
{/* 表单集成 */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>表单集成</Text>
<View style={styles.formCard}>
<View style={styles.inputGroup}>
<Text style={styles.label}>水果</Text>
<DropDownPicker
open={formFruitOpen}
value={formFruitValue}
items={formFruitItems}
setOpen={setFormFruitOpen}
setValue={setFormFruitValue}
setItems={setFormFruitItems}
placeholder="请选择"
listMode="SCROLLVIEW"
dropDownDirection="BOTTOM"
style={styles.smallDropdown}
dropDownContainerStyle={styles.smallDropdownContainer}
zIndex={1000}
/>
</View>
<View style={styles.inputGroup}>
<Text style={styles.label}>城市</Text>
<DropDownPicker
open={formCityOpen}
value={formCityValue}
items={formCityItems}
setOpen={setFormCityOpen}
setValue={setFormCityValue}
setItems={setFormCityItems}
searchable
placeholder="请选择"
listMode="SCROLLVIEW"
dropDownDirection="BOTTOM"
style={styles.smallDropdown}
dropDownContainerStyle={styles.smallDropdownContainer}
zIndex={999}
/>
</View>
<View style={styles.inputGroup}>
<Text style={styles.label}>性别</Text>
<DropDownPicker
open={formGenderOpen}
value={formGenderValue}
items={formGenderItems}
setOpen={setFormGenderOpen}
setValue={setFormGenderValue}
setItems={setFormGenderItems}
placeholder="请选择"
listMode="SCROLLVIEW"
dropDownDirection="BOTTOM"
style={styles.smallDropdown}
dropDownContainerStyle={styles.smallDropdownContainer}
zIndex={998}
/>
</View>
<TouchableOpacity style={styles.submitButton} onPress={handleSubmit}>
<Text style={styles.submitButtonText}>提交</Text>
</TouchableOpacity>
</View>
</View>
</ScrollView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
backgroundColor: "#F5F5F5",
},
header: {
fontSize: 24,
fontWeight: "bold",
color: "#333",
textAlign: "center",
marginBottom: 30,
},
section: {
marginBottom: 24,
zIndex: 0,
},
sectionTitle: {
fontSize: 16,
fontWeight: "600",
color: "#333",
marginBottom: 12,
},
dropdown: {
backgroundColor: "#FFF",
borderRadius: 8,
borderWidth: 1,
borderColor: "#E5E5EA",
},
dropdownContainer: {
backgroundColor: "#FFF",
borderRadius: 8,
borderWidth: 1,
borderColor: "#E5E5EA",
},
smallDropdown: {
backgroundColor: "#FFF",
borderRadius: 8,
borderWidth: 1,
borderColor: "#E5E5EA",
minHeight: 44,
},
smallDropdownContainer: {
backgroundColor: "#FFF",
borderRadius: 8,
borderWidth: 1,
borderColor: "#E5E5EA",
},
searchInput: {
borderBottomWidth: 1,
borderBottomColor: "#E5E5EA",
},
resultText: {
marginTop: 12,
fontSize: 14,
color: "#007AFF",
},
badgeContainer: {
flexDirection: "row",
flexWrap: "wrap",
gap: 8,
marginTop: 12,
},
badge: {
backgroundColor: "#007AFF",
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 16,
},
badgeText: {
color: "#FFF",
fontSize: 12,
},
formCard: {
backgroundColor: "#FFF",
borderRadius: 12,
padding: 16,
shadowColor: "#000",
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
inputGroup: {
marginBottom: 16,
zIndex: 10,
},
label: {
fontSize: 14,
color: "#666",
marginBottom: 8,
},
submitButton: {
backgroundColor: "#007AFF",
borderRadius: 8,
paddingVertical: 14,
alignItems: "center",
marginTop: 8,
},
submitButtonText: {
color: "#FFF",
fontSize: 16,
fontWeight: "600",
},
});
export default App;
⚠️ 约束与限制
注意事项
-
Z-Index 层级 :当下拉框在页面底部时,可能会被其他元素遮挡。需要通过
zIndex属性调整层级。 -
FlatList 嵌套 :如果在下拉框内使用 FlatList,确保正确设置
maxHeight避免显示问题。 -
多选模式 Badge :当
multiple={true}时,可以配合mode="BADGE"显示选中数量徽章。
样式建议
-
统一样式:建议在全局定义统一样式常量,确保整个应用的 DropDownPicker 样式一致。
-
安全区域:如果应用使用了安全区域(如刘海屏),需要适当调整下拉框位置。
-
键盘处理:如果下拉框在表单中,建议在展开时自动调整滚动位置避免键盘遮挡。