react-native-actions-sheet:基于鸿蒙 React Native 生态的底部动作面板组件项目

底部动作面板 ActionSheet 组件

Branch3Tags2
FilesLast commitLast 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 参与共建。

Introduction

底部动作面板 ActionSheet 组件

Customize your domain