@hxa-rn/react-native-actions-sheet
v1.0.1
Published
A Cross Platform(Android & iOS) ActionSheet with a robust and flexible api, native performance and zero dependency code for react native. Create anything you want inside ActionSheet.
Readme
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/[email protected],上游基线为 10.1.2,业务别名为 react-native-actions-sheet。
在 RN 应用根目录安装精确版本:
npm install @hxa-rn/[email protected] --legacy-peer-deps本包没有安装期构建脚本,安装内容已包含 JS 源码、类型声明、兼容补丁脚本与 libevent 2.1.13 源码。
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/[email protected] @react-native-ohos/[email protected] @react-native-ohos/[email protected] [email protected] [email protected] [email protected] --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、patch-rnoh-shadow.cmake处理当前 RNOH 0.72 组合的兼容问题。接入同版本宿主时核对这些补丁及示例 CMake 中的调用位置,路径须改成宿主安装后的node_modules/@hxa-rn/react-native-actions-sheet/scripts/,不能原样复制仓库相对路径。 - 库自身不要求额外系统权限。完成原生依赖配置后重新构建 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 为准),npm,以及 DevEco Studio 配套的 ohpm / SDK。
- 示例版本:React Native
0.72.5、RNOH0.72.143。 - 鸿蒙工程配置:targetSdkVersion=6.1.1(24),compatibleSdkVersion=5.1.0(18)。设备应满足最低版本及本库系统能力要求。
- 在 DevEco Studio 中配置与当前应用包名匹配的签名,不能直接使用其他电脑上的证书路径。
运行步骤
1. 克隆仓库
git clone https://gitcode.com/hxa-rn/react-native-actions-sheet.git
cd react-native-actions-sheet
git checkout br_rnoh0.72以下步骤使用 example 展示工程;example_auto 的依赖与构建输出独立,不要混用。Windows 建议使用较短目录,避免 RNOH 原生构建中间路径过长。
2. 安装示例依赖
示例锁定 npmjs 上的精确 1.0.0 版本。进入示例目录安装:
cd example
npm install --legacy-peer-deps3. 生成 JS Bundle
npm run dev确认生成 example/harmony/entry/src/main/resources/rawfile/bundle.harmony.js。先解决命令报错,再进入原生构建。
4. 同步与运行鸿蒙工程
用 DevEco Studio 打开 example/harmony,完成依赖同步。缺少 ohpm 依赖时,在该目录使用 DevEco 配套工具执行 ohpm install --all,再同步。保留示例已有的 RNOH HAR 路径和版本。
示例已配置本库运行所需的依赖与接线,无需重复添加 Package。选择设备、配置签名并点击运行,生成并安装 HAP。修改 JS 后重新生成 Bundle;修改原生依赖后重新构建安装 HAP。
常见问题
- 找不到 JS 模块:检查适配包安装、Metro 别名及对应入口,修改 Metro 后重启打包进程。
- 找不到本地包或 HAR:核对当前目录、文件名及准备步骤;不要将
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 参与共建。
