底部动作面板 ActionSheet 组件
| Files | Last commit | Last update |
|---|---|---|
| 1 month ago | ||
| 16 days ago | ||
| 16 days ago | ||
| 1 month ago | ||
| 1 month ago | ||
| 1 month ago | ||
| 1 month ago | ||
| 16 days ago | ||
| 17 days ago | ||
| 16 days ago | ||
| 16 days ago | ||
| 1 month ago | ||
| 1 month ago | ||
| 1 month ago | ||
| 16 days ago | ||
| 1 month ago | ||
| 1 month ago |
react-native-actions-sheet
react-native-actions-sheet 基于上游 react-native-actions-sheet 开发。如果在使用过程中有任何问题,欢迎在GitCode提交Issue,会及时跟进。
项目介绍
react-native-actions-sheet(上游基线版本 10.1.2)是一个跨平台的底部动作面板(ActionSheet / BottomSheet / Bottom Drawer)组件,把「从屏幕底部升起、可拖拽、可停驻在多个高度档位、带遮罩、可承载任意自定义内容」封装为一个 React 组件。
核心能力:
- 面板开合:既可通过
ref(ActionSheetRef)直接控制,也可用命令式管理层SheetManager.show(id)从应用任意位置唤起并await其关闭时回传的数据。 - 多档位停驻:
snapPoints定义百分比档位,支持拖拽换档、命令式snapToIndex/snapToOffset/snapToRelativeOffset停驻。 - 遮罩与背景交互:遮罩颜色/不透明度可定制,支持点击遮罩关闭;
backgroundInteractionEnabled可切换为不渲染遮罩、放开背景交互的非模态形态。 - 键盘避让:内置键盘监听,弹出时整体上移并压缩可视高度。
- 面板内路由:
routes+useSheetRouter提供面板内多步骤流程,无需切换页面。 - 面板内滚动共存:
useScrollHandlers把滚动容器登记到手势边界,使列表滚动与下拉关闭正确共存。
当前适配包基于上游 10.1.2,源码不包含独立原生模块,但运行期依赖 react-native-reanimated、react-native-gesture-handler 与 react-native-safe-area-context。鸿蒙 RN 0.72 旧架构线按客户方案使用三件 @react-native-ohos/* 适配包承载手势、动画与安全区能力;源码未直接 import react-native-worklets,不单独引入 worklets 包。
集成指南
本节用于接入已有 RN 鸿蒙应用;运行本仓库示例请参见「快速验证(运行 Example)」。以下路径按当前仓库布局编写:适配包位于根目录。
1. 安装适配包
当前 npm 适配包为 @hxa-rn/react-native-actions-sheet@10.1.3,基于上游 10.1.2,本次为发布元数据修订版,业务别名为 react-native-actions-sheet。
在 RN 应用根目录安装精确版本:
npm install @hxa-rn/react-native-actions-sheet@10.1.3 --legacy-peer-deps
本包没有安装期构建脚本,安装内容已包含 JS 源码、类型声明、兼容补丁脚本与 libevent 2.1.13 源码(后者仅供以源码模式编译 RNOH 且需替换 libevent 的宿主使用;使用 RNOH 官方预编译包的宿主用不到)。
peerDependencies:{"react-native":"^0.72.0","react-native-reanimated":"^3.6.0","react-native-gesture-handler":"^2.14.0","react-native-safe-area-context":"^4.7.0"}。还需已有可运行的 RNOH 宿主工程。
2. 配置 Metro 并导入
适配包已声明 harmony.alias。宿主 metro.config.js 合并以下配置,并保留原有的自定义设置:
const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
const {createHarmonyMetroConfig} = require('@react-native-oh/react-native-harmony/metro.config');
module.exports = mergeConfig(
getDefaultConfig(__dirname),
createHarmonyMetroConfig({
reactNativeHarmonyPackageName: '@react-native-oh/react-native-harmony',
}),
);
业务代码:
import ActionSheet, {SheetManager, SheetProvider} from 'react-native-actions-sheet';
3. 鸿蒙运行依赖
本库自身为 JS 组件,不提供独立 HAR,也没有本库专属的 C++ / ETS Package 注册。
本库依赖的原生能力仍需接入。与本仓库示例一致的版本为:
npm install @react-native-ohos/react-native-gesture-handler@2.14.18 @react-native-ohos/react-native-reanimated@3.6.7 @react-native-ohos/react-native-safe-area-context@4.7.5 react-native-gesture-handler@2.14.1 react-native-reanimated@3.6.0 react-native-safe-area-context@4.7.4 --legacy-peer-deps
三件适配包均需完成 HAR、CMake 和 C++ / ETS 注册,参照本仓库 example/harmony/entry/oh-package.json5、src/main/cpp/autolinking.cmake 与 src/main/ets/RNOHPackagesFactory.ets,或由宿主 RNOH 自动链接生成。
- Babel 插件列表最后加入
react-native-reanimated/plugin。 - 应用外层接入
GestureHandlerRootView(flex: 1)和SafeAreaProvider;使用 SheetManager 时再接入SheetProvider,并导入面板注册文件,参见example/App.tsx。 - 示例 CMake 使用
scripts/patch-rngh-modal-root.cmake、patch-rngh-modal-lock.cmake处理当前 RNOH 0.72 组合的兼容问题。接入同版本宿主时核对这些补丁及示例 CMake 中的调用位置,路径须改成宿主安装后的node_modules/@hxa-rn/react-native-actions-sheet/scripts/,不能原样复制仓库相对路径。 - 面板打开期间切换
elevation/disableElevation时,RNOH 0.72 不会正确更新或清除阴影;本库已在 JS 内处理,宿主无需再打scripts/patch-rnoh-shadow.cmake(该脚本仍随包提供,只适用于源码编译 RNOH 的宿主)。 - 示例使用 RNOH 预编译包
react_native_openharmony_release.har。宿主同样使用预编译包时,CMake 头文件路径需包含 RNOH 的third-party/hermes/API与third-party/hermes/public(react-native-reanimated 需要),参见示例entry/src/main/cpp/CMakeLists.txt。 - 库自身不要求额外系统权限。完成原生依赖配置后重新构建 HAP。
使用说明
方式一:通过 ref 直接控制(无需注册)
import React, { useRef } from 'react';
import { View, Text, Button } from 'react-native';
import ActionSheet, { ActionSheetRef } from 'react-native-actions-sheet';
export function Demo() {
const ref = useRef<ActionSheetRef>(null);
return (
<View>
<Button title="打开面板" onPress={() => ref.current?.show()} />
<ActionSheet ref={ref} gestureEnabled snapPoints={[50, 100]}>
<View style={{ height: 400, padding: 20 }}>
<Text>面板内容</Text>
<Button title="关闭并回传数据" onPress={() => ref.current?.hide({ ok: true })} />
</View>
</ActionSheet>
</View>
);
}
ref 方式不要求提供 id;管理器方式需要注册面板并配置 Provider。10.1.2 不沿用旧版「ref 加 id 会永久禁用拖拽」的说明,为面板设置 id 不影响手势。
方式二:命令式管理层(注册 + Provider,可 await 返回值)
// sheets.tsx —— 放在单独文件中,并在 App 入口 import,保证被打包
import ActionSheet, { registerSheet, SheetProps } from 'react-native-actions-sheet';
function ConfirmSheet(props: SheetProps) {
return (
<ActionSheet id={props.sheetId}>
<View style={{ height: 260, padding: 20 }}>
<Text>确认执行该操作?</Text>
</View>
</ActionSheet>
);
}
registerSheet('confirm-sheet', ConfirmSheet);
export {};
// App.tsx
import './sheets';
import { SheetProvider, SheetManager } from 'react-native-actions-sheet';
export default function App() {
const onPress = async () => {
const result = await SheetManager.show('confirm-sheet', { payload: { from: 'home' } });
console.log('面板回传:', result);
};
return (
<SheetProvider context="global">
{/* 业务页面 */}
</SheetProvider>
);
}
面板内滚动:优先使用本包导出的 ScrollView / FlatList,它们已接入滚动 Hook 和 NativeViewGestureHandler。自定义滚动容器在 ActionSheet 子组件内调用 Hook,参数是配置对象,不是旧版的 (id, ref):
import {ScrollView as RNScrollView} from 'react-native';
import {NativeViewGestureHandler} from 'react-native-gesture-handler';
import {useScrollHandlers} from 'react-native-actions-sheet';
function SheetContent() {
const handlers = useScrollHandlers<RNScrollView>({hasRefreshControl: false});
return <NativeViewGestureHandler simultaneousHandlers={handlers.simultaneousHandlers}>
<RNScrollView {...handlers}>{/* 列表内容 */}</RNScrollView>
</NativeViewGestureHandler>;
}
接口文档
包级导出
| 导出 | 类型 | 说明 |
|---|---|---|
default(ActionSheet) |
React 组件 | 面板主体组件,forwardRef<ActionSheetRef, ActionSheetProps> |
SheetManager |
单例对象 | 命令式管理层,见下表 |
registerSheet |
(id, Sheet, ...contexts) => void |
按 id 注册面板到一个或多个 context;id 或组件为假值时静默返回 |
SheetProvider |
React 组件 | 渲染指定 context 下所有已注册面板,context 默认 'global' |
useProviderContext / useSheetIDContext |
Hook | 分别返回当前 Provider 的 context 名、当前渲染面板的 id |
useScrollHandlers |
(options?) => {...} |
参数可含 hasRefreshControl、refreshControlBoundary;返回 ref、simultaneousHandlers、onScroll、onLayout、scrollEventThrottle |
ScrollView / FlatList |
组件 | 内置滚动及手势协调的包装组件 |
useSheetRef / useSheetPayload |
Hook | 读取当前注册面板的 ref / payload |
useSheetRouter / useSheetRouteParams |
Hook | 面板内路由实例 / 当前路由参数 |
getSheetStack |
() => {id, context}[] |
返回当前已渲染面板栈,空栈返回 [] |
isRenderedOnTop |
(id, context?) => boolean |
判断指定面板是否位于栈顶;不传 context 且栈为空时会抛 TypeError |
setBaseZIndexForActionSheets |
(zIndex: number) => void |
设置非模态面板堆叠基准 zIndex,默认 999 |
| 类型导出 | — | ActionSheetProps / ActionSheetRef / SheetProps / Route / Router / RouteScreenProps |
ActionSheetRef 常用成员
| 成员 | 签名 | 参数 | 说明 |
|---|---|---|---|
show |
(snapIndex?: number) => void |
可选初始档位索引 | 打开面板,调用打开前回调并初始化路由 |
hide |
(data?: any) => void |
关闭时回传的数据 | 关闭面板,绕过 closable 限制 |
setModalVisible |
(visible?: boolean) => void |
目标可见性 | true 仅设置可见,不执行 show() 的打开前回调及路由初始化;false 走常规关闭流程,受 closable 限制 |
snapToIndex |
(index: number) => void |
档位下标 | 停驻到 snapPoints[index];下标越界时静默返回 |
snapToOffset |
(offset: number) => void |
面板自身高度百分比 0~100 | 停驻到绝对位置 |
snapToRelativeOffset |
(offset: number) => void |
相对百分比 | 在当前位置基础上相对位移;0 表示回到当前档位,超出可用高度时退化为 snapToOffset(100) |
isGestureEnabled / isOpen |
() => boolean |
无 | 读取手势开关 / 当前是否可见 |
ev |
EventManager |
— | 面板实例私有事件管理器(onoffsetchange / safeAreaLayout) |
SheetManager 成员
| 成员 | 签名 | 参数 | 说明 |
|---|---|---|---|
show |
show(id, options?): Promise<T> |
options.payload / onClose / context |
唤起面板,Promise 在面板关闭时 resolve 为回传数据;id 未注册时 Promise 保持挂起且不报错 |
hide |
hide(id, options?): Promise<T> |
options.payload / context |
关闭指定面板并回传数据 |
hideAll |
(id?: string) => void |
可选 id 前缀 | 关闭全部(或前缀匹配的)已打开面板 |
context |
(options?) => string 或 undefined |
可选 context | 优先显式值,否则查找最近的自动嵌套/global Provider;无可用 Provider 时可能为 undefined |
get |
(id, context?) => RefObject<ActionSheetRef> 或 undefined |
面板 id | 获取已登记 ref;调用前检查返回值,不改变当前渲染栈 |
getActiveSheets |
(id) => {id, context, ref}[] |
id 前缀 | 获取匹配的已渲染实例 |
常用属性(完整声明见适配包 dist/src/types.d.ts)
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
gestureEnabled |
boolean |
false |
是否启用拖拽手势 |
snapPoints |
number[] |
[100] |
停驻档位百分比;末项非 100 时组件自动补齐 100 档 |
initialSnapIndex |
number |
0 |
打开时停驻的档位下标 |
springOffset |
number |
50 |
触发换档所需最小位移(RN 逻辑单位),不足则回弹 |
closable |
boolean |
true |
false 时手势与遮罩点击都不关闭,仅回弹 |
closeOnTouchBackdrop |
boolean |
true |
点击遮罩是否关闭 |
closeOnPressBack |
boolean |
true |
返回键是否关闭 |
isModal |
boolean |
true |
true 用 Modal 承载,false 用绝对定位视图 |
backgroundInteractionEnabled |
boolean |
— | true 时不用 Modal、不渲染遮罩,放开背景交互 |
overdrawEnabled / overdrawFactor / overdrawSize |
boolean / number / number |
true / 15 / 100 |
顶部越界拖拽的阻尼与填充配置 |
containerStyle / indicatorStyle |
ViewStyle |
— | 面板容器 / 顶部指示条样式 |
useBottomSafeAreaPadding |
boolean |
true |
按安全区为面板底部增加内边距 |
routes / initialRoute / enableRouterBackNavigation |
Route[] / string / boolean |
— | 面板内路由配置 |
onOpen / onClose / onBeforeShow / onBeforeClose / onChange |
回调 | — | 打开完成 / 关闭完成 / 打开前 / 关闭前 / 可见高度百分比变化(10.1.2 的 onChange) |
快速验证(运行 Example)
前置条件
| 依赖 | 版本要求 |
|---|---|
| Node.js | >= 20(示例工程 engines 要求) |
| DevEco Studio | 6.1.1 及以上(示例 targetSdkVersion 为 6.1.1(24)) |
| HarmonyOS SDK | API 18+(example/harmony 的 compatibleSdkVersion 为 5.1.0(18)) |
| 路径 | Windows 下仓库根目录的完整路径不超过 40 个字符,否则原生构建的中间文件路径会超过系统上限 |
运行步骤
1. 克隆仓库
git clone -b br_rnoh0.72 https://gitcode.com/hxa-rn/react-native-actions-sheet.git
cd react-native-actions-sheet
Windows 下可在克隆时指定短目录名,例如 git clone -b br_rnoh0.72 https://gitcode.com/hxa-rn/react-native-actions-sheet.git C:\ras。
2. 进入 example 目录,安装依赖
cd example
npm install --legacy-peer-deps
Example 从 npm 公仓安装 @hxa-rn/react-native-actions-sheet@10.1.3,不需要在仓根执行 npm pack。
3. 生成 JS Bundle
npm run dev
产物:harmony/entry/src/main/resources/rawfile/bundle.harmony.js
4. 用 DevEco Studio 打开鸿蒙工程
- 打开 DevEco Studio,选择
example/harmony目录 - 首次构建请在 File → Project Structure → Signing Configs 勾选 Automatically generate signature,Apply 后 Sync
- 等待 Sync 完成
5. 编译并运行 HAP
在 DevEco Studio 中点击运行按钮,将 HAP 安装到设备。
example_auto(自动化用例工程)步骤相同,把 example 换成 example_auto 即可,两者的依赖与构建输出相互独立。
注意:Example 已预置 Package 注册、Metro alias 与 HAR 依赖,无需手动 Link。编 HAP 前必须先执行
npm run dev;改过 JS 后重新执行再运行。
常见问题
- 找不到 JS 模块:检查适配包安装、Metro 别名及对应入口,修改 Metro 后重启打包进程。
- 找不到 npm 包或原生依赖 HAR:核对
example/node_modules下本包与 gesture-handler、reanimated、safe-area-context 三件适配包是否已安装;不要将example_auto的 node_modules 当成example的依赖。 - 原生模块或组件未注册:检查 HAR、CMake、C++ 和 ETS 注册,排除重复注册并重建 HAP。
- SDK / 签名报错:按当前工程配置安装对应 SDK,并重新配置本机签名。
- Ninja
.rsp创建失败:查看完整日志;确认是路径长度问题后,缩短工程目录、清理旧路径构建缓存再构建。
约束与限制
兼容性
| 项 | 值 |
|---|---|
| 鸿蒙 SDK(打包档位) | compatibleSdkVersion 为 5.1.0(18)、targetSdkVersion 为 6.1.1(24),runtimeOS 为 HarmonyOS |
| 已适配档位 | API 22 / 23 / 24 / 26 |
| 上游库版本 | react-native-actions-sheet 10.1.2 |
| React Native | 本轮构建与验证基线 RN 0.72.5 + @react-native-oh/react-native-harmony 0.72.143,其他版本未验证 |
| 支持设备类型 | phone(module.json5 deviceTypes) |
当前不再降版本。上游 10.1.2 发布物直接声明
react-native-worklets,源码没有直接 import;本仓独立适配包不安装该依赖,在 RN 0.72 基线上使用 Reanimated 3.x 鸿蒙适配版。
权限
库自身为 UI 组件,不需要额外系统权限。示例工程的宿主声明
ohos.permission.INTERNET、ohos.permission.VIBRATE;不能将库自身的权限需求
等同于宿主应用的权限清单。
使用约束
- ref 与注册 Provider 是两个独立的控制入口,使用方式见「使用说明」,不要混用同一 id 两种入口。
- 自定义面板内滚动容器需要
useScrollHandlers与NativeViewGestureHandler协调;本包导出的ScrollView/FlatList已接入,直接使用可省去该步骤。 - 顶部越界阻尼相关配置(
overdrawEnabled/overdrawFactor/overdrawSize)仅在手势拖拽路径上生效,命令式停驻接口不经过该逻辑。 - 面板非滚动内容区的直接拖拽受鸿蒙触摸机制限制,触摸序列可能被系统提前结束;需要稳定换档时请让内容经滚动容器承接,或改用
snapToIndex/snapToOffset等命令式接口。 statusBarTranslucent属性在库内未被读取(Modal的该属性被固定为true),设置该属性不会产生效果。isRenderedOnTop(id)在不传context且当前无任何面板渲染时会抛出TypeError,调用前应确保栈非空或显式传入 context。
开源license
本项目基于 MIT License 开源,与上游协议一致。
问题反馈渠道
使用过程中发现任何问题,欢迎在GitCode 提交 Issue,也欢迎提交 PR 参与共建。