欢迎加入开源鸿蒙跨平台社区https://openharmonycrossplatform.csdn.net

📋 前言

日期选择是移动应用中常见的需求场景,无论是预约服务、填写表单还是记录日程,都离不开直观的日期选择组件。react-native-date-picker 是一个功能丰富的日期选择器库,支持日期、时间、日期时间三种模式,提供模态框和内嵌两种使用方式,让开发者能够轻松实现原生般的日期选择体验。

🎯 库简介

基本信息

  • 库名称: react-native-date-picker
  • 版本信息:
    • 5.0.6 + @react-native-ohos/react-native-date-picker: 支持 RN 0.72 版本(支持 AutoLink)
    • 5.1.0 + @react-native-ohos/react-native-date-picker: 支持 RN 0.77 版本
  • 官方仓库: https://github.com/henninghall/react-native-date-picker
  • 鸿蒙仓库: https://github.com/react-native-oh-library/react-native-date-picker
  • 主要功能:
    • 📅 支持日期、时间、日期时间三种模式
    • 🎛️ 支持模态框和内嵌两种展示方式
    • 🎨 丰富的自定义主题和样式
    • 🌐 支持多语言和区域设置
    • 📱 跨平台一致的用户体验

为什么需要日期选择器?

特性 原生 DatePicker react-native-date-picker
跨平台一致性 ❌ Android/iOS 表现不同 ✅ 统一体验
交互模式 ❌ 单一模式 ✅ 模态框/内嵌任选
样式定制 ⚠️ 需要自定义 ✅ 丰富的配置选项
状态管理 ⚠️ 需要手动处理 ✅ 完善的状态回调
HarmonyOS 支持 ❌ 无 ✅ 完善适配

核心功能

功能 说明 HarmonyOS 支持
date 日期模式 选择年月日
time 时间模式 选择时分秒
datetime 日期时间模式 同时选择日期和时间 ⚠️ 仅模态框模式支持
模态框展示 弹窗式选择器
内嵌展示 页面内直接展示 ⚠️ datetime 模式不支持
最大/最小日期 日期范围限制
分钟间隔 分钟选择粒度

兼容性验证

在以下环境验证通过:

  • RNOH: 0.72.90; SDK: HarmonyOS 6.0.0 ; IDE: DevEco Studio 6.0.01; ROM: 6.0.0

📦 安装步骤

1. 安装依赖

请到三方库的 Releases 发布地址查看配套的版本信息:@react-native-ohos/react-native-date-picker Releases

# RN 0.72 版本
npm install @react-native-ohos/react-native-date-picker@5.0.6-rc.1

# 或者使用 yarn
yarn add @react-native-ohos/react-native-date-picker@5.0.6-rc.1

2. 验证安装

安装完成后,检查 package.json 文件:

{
  "dependencies": {
    "@react-native-ohos/react-native-date-picker": "^5.0.6-rc.1"
  }
}

🔧 HarmonyOS 平台配置 ⭐

本库支持 AutoLink(RN 0.72 的 5.0.6 版本及以上,RN 0.77 版本),如果您的工程已接入 AutoLink,可跳过手动配置。否则请按以下步骤配置。

1. 在工程根目录的 oh-package.json5 添加 overrides 字段

在这里插入图片描述

打开 harmony/oh-package.json5,添加以下配置:

{
  // ... 其他配置
  "overrides": {
    "@rnoh/react-native-openharmony": "0.72.90"
  }
}

方式一:AutoLink

如果您的工程已接入 AutoLink,只需安装依赖即可,无需手动配置。

验证 AutoLink 是否生效

安装依赖后,执行以下命令同步:

cd harmony/entry
ohpm install

然后编译运行即可。


方式二:HAR 包引入(手动配置,用这个)

2.1 在 entry/oh-package.json5 添加依赖

打开 harmony/entry/oh-package.json5,添加以下依赖:

"dependencies": {
  "@react-native-ohos/react-native-date-picker": "file:../../node_modules/@react-native-ohos/react-native-date-picker/harmony/date_picker.har"
}

2.2 同步依赖

点击 DevEco Studio 右上角的 sync 按钮,或者在终端执行:

cd harmony/entry
ohpm install

2.3 配置 CMakeLists.txt

打开 harmony/entry/src/main/cpp/CMakeLists.txt,添加以下配置:

project(rnapp)
cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_SKIP_BUILD_RPATH TRUE)
set(RNOH_APP_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
set(NODE_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../node_modules")
+ set(OH_MODULE "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules")
set(RNOH_CPP_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../react-native-harmony/harmony/cpp")
set(LOG_VERBOSITY_LEVEL 1)
set(CMAKE_ASM_FLAGS "-Wno-error=unused-command-line-argument -Qunused-arguments")
set(CMAKE_CXX_FLAGS "-fstack-protector-strong -Wl,-z,relro,-z,now,-z,noexecstack -s -fPIE -pie")
set(WITH_HITRACE_SYSTRACE 1)
add_compile_definitions(WITH_HITRACE_SYSTRACE)

add_subdirectory("${RNOH_CPP_DIR}" ./rn)

# 添加 DatePicker 模块
+ add_subdirectory("${OH_MODULE}/@react-native-ohos/react-native-date-picker/src/main/cpp" ./date_picker)

file(GLOB GENERATED_CPP_FILES "./generated/*.cpp")

add_library(rnoh_app SHARED
    ${GENERATED_CPP_FILES}
    "./PackageProvider.cpp"
    "${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp"
)

target_link_libraries(rnoh_app PUBLIC rnoh)

# 链接 DatePicker 库
+ target_link_libraries(rnoh_app PUBLIC rnoh_date_picker)

2.4 修改 PackageProvider.cpp

打开 harmony/entry/src/main/cpp/PackageProvider.cpp,添加:

#include "RNOH/PackageProvider.h"
#include "generated/RNOHGeneratedPackage.h"
+ #include "DatePickerPackage.h"

using namespace rnoh;

std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
    return {
        std::make_shared<RNOHGeneratedPackage>(ctx),
        + std::make_shared<DatePickerPackage>(ctx),
    };
}

2.5 在 ArkTs 侧引入 RNDatePicker 组件

找到 entry/src/main/ets/pages/index.etsentry/src/main/ets/rn/LoadBundle.ets,在 buildCustomRNComponent 函数中添加:

+ import { RNDatePicker } from "@react-native-ohos/react-native-date-picker"

@Builder
export function buildCustomRNComponent(ctx: ComponentBuilderContext) {
  // ... 其他组件
  if (ctx.componentName === RNDatePicker.NAME) {
    RNDatePicker({
      ctx: ctx.rnComponentContext,
      tag: ctx.tag
    })
  }
  // ...
}

2.6 添加组件名到 arkTsComponentNames

在相同文件中找到 arkTsComponentNames 数组,添加组件名:

const arkTsComponentNames: Array<string> = [
  // ... 其他组件名
  RNDatePicker.NAME,
];

2.7 在 ArkTs 侧引入 RNDatePickerPackage

打开 entry/src/main/ets/RNPackagesFactory.ts,添加:

import { RNDatePickerPackage } from "@react-native-ohos/react-native-date-picker/ts";

export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
  return [
    // ... 其他包
    new RNDatePickerPackage(ctx)
  ];
}

同步并运行 🚀

点击 DevEco Studio 右上角的 sync 按钮,或者在终端执行:

cd harmony/entry
ohpm install

然后编译、运行即可。

📖 API 详解

DatePicker Props

基础属性
属性 类型 必填 说明 HarmonyOS 支持
mode “date”| “time” | “datetime” 选择器模式
date Date 当前选中的日期
onDateChange function 日期变化回调(仅内联模式)(date: Date) => void
maximumDate Date 最大可选日期
minimumDate Date 最小可选日期
模态框属性
属性 类型 必填 说明 HarmonyOS 支持
modal boolean 是否启用模态框模式,默认 false
open boolean 控制模态框是否打开
onConfirm function 确认按钮回调 (date: Date) => void
onCancel function 取消按钮回调 () => void
样式属性
属性 类型 必填 说明 HarmonyOS 支持
title string 模态框标题(设为 null 可隐藏)
confirmText string 确认按钮文本
cancelText string 取消按钮文本
theme “light”| “dark” | “auto” 模态框主题
buttonColor string 按钮颜色(仅 Android)
dividerColor string 分隔线颜色(仅 Android)
其他属性
属性 类型 必填 说明 HarmonyOS 支持
locale string 区域设置(如 “zh-CN”)
is24hourSource “locale”| “device” 24小时制来源
minuteInterval 1| 2 | 3 | 4 | 5 | 6 | 10 | 12 | 15 | 20 | 30 分钟间隔
timeZoneOffsetInMinutes number 时区偏移量

📋 完整示例

在这里插入图片描述

综合示例(日期/时间/日期时间/预约场景)

import React, { useState } from 'react';
import {
  View,
  Text,
  Button,
  StyleSheet,
  TouchableOpacity,
  ScrollView,
  Alert,
} from 'react-native';
import DatePicker from 'react-native-date-picker';

type PickerMode = 'date' | 'time' | 'datetime';

interface PickerConfig {
  mode: PickerMode;
  title: string;
  minDate?: Date;
  maxDate?: Date;
}

const App: React.FC = () => {
  const [date, setDate] = useState(new Date());
  const [pickerOpen, setPickerOpen] = useState(false);
  const [pickerConfig, setPickerConfig] = useState<PickerConfig>({
    mode: 'date',
    title: '选择日期',
  });

  const openPicker = (mode: PickerMode, title: string, min?: Date, max?: Date) => {
    setPickerConfig({ mode, title, minDate: min, maxDate: max });
    setPickerOpen(true);
  };

  const handleConfirm = (selectedDate: Date) => {
    setDate(selectedDate);
    setPickerOpen(false);
  };

  const handleCancel = () => {
    setPickerOpen(false);
  };

  const formatDate = (d: Date): string => {
    return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
  };

  const formatTime = (d: Date): string => {
    return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;
  };

  const formatDateTime = (d: Date): string => {
    return `${formatDate(d)} ${formatTime(d)}`;
  };

  const calculateAge = (birthDate: Date): number => {
    const today = new Date();
    let age = today.getFullYear() - birthDate.getFullYear();
    const monthDiff = today.getMonth() - birthDate.getMonth();
    if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
      age--;
    }
    return age;
  };

  const getDayName = (d: Date): string => {
    const days = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
    return days[d.getDay()];
  };

  return (
    <ScrollView style={styles.container}>
      <Text style={styles.header}>react-native-date-picker 示例</Text>

      {/* 当前选中值展示 */}
      <View style={styles.currentValue}>
        <Text style={styles.currentLabel}>当前选中的值:</Text>
        <Text style={styles.currentDate}>{formatDate(date)}</Text>
        <Text style={styles.currentTime}>{formatTime(date)}</Text>
        <Text style={styles.currentWeekday}>{getDayName(date)}</Text>
      </View>

      {/* 基础模式选择 */}
      <View style={styles.section}>
        <Text style={styles.sectionTitle}>基础模式</Text>
        <View style={styles.buttonRow}>
          <Button
            title="日期选择"
            onPress={() => openPicker('date', '选择日期', new Date(2020, 0, 1), new Date(2030, 11, 31))}
          />
          <Button
            title="时间选择"
            onPress={() => openPicker('time', '选择时间')}
          />
          <Button
            title="日期时间"
            onPress={() => openPicker('datetime', '选择日期时间', new Date())}
          />
        </View>
      </View>

      {/* 生日选择示例 */}
      <View style={styles.section}>
        <Text style={styles.sectionTitle}>生日选择</Text>
        <TouchableOpacity
          style={styles.birthdayCard}
          onPress={() => openPicker('date', '选择生日', new Date(1900, 0, 1), new Date())}
        >
          <View style={styles.birthdayContent}>
            <Text style={styles.birthdayLabel}>您的生日</Text>
            <Text style={styles.birthdayDate}>
              {date.getFullYear()}年{date.getMonth() + 1}月{date.getDate()}日
            </Text>
            <Text style={styles.ageText}>年龄: {calculateAge(date)} 岁</Text>
          </View>
          <Text style={styles.editHint}>点击修改 ✎</Text>
        </TouchableOpacity>
      </View>

      {/* 预约场景示例 */}
      <View style={styles.section}>
        <Text style={styles.sectionTitle}>服务预约</Text>
        <View style={styles.bookingCard}>
          <Text style={styles.bookingLabel}>选择预约时间</Text>
          <TouchableOpacity
            style={styles.dateButton}
            onPress={() => openPicker('datetime', '选择预约时间', new Date())}
          >
            <Text style={styles.dateButtonText}>{formatDateTime(date)}</Text>
            <Text style={styles.dayText}>{getDayName(date)}</Text>
          </TouchableOpacity>
          <Button
            title="立即预约"
            onPress={() => Alert.alert(`预约成功!\\n预约时间: ${formatDateTime(date)}`)}
          />
        </View>
      </View>

      {/* DatePicker - 使用内置模态框 */}
      <DatePicker
        modal
        mode={pickerConfig.mode}
        open={pickerOpen}
        date={date}
        onConfirm={handleConfirm}
        onCancel={handleCancel}
        minimumDate={pickerConfig.minDate}
        maximumDate={pickerConfig.maxDate}
      />
    </ScrollView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
    backgroundColor: '#F5F5F5',
  },
  header: {
    fontSize: 24,
    fontWeight: 'bold',
    color: '#333',
    textAlign: 'center',
    marginBottom: 30,
  },
  currentValue: {
    backgroundColor: '#FFF',
    borderRadius: 12,
    padding: 20,
    alignItems: 'center',
    marginBottom: 20,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    shadowRadius: 4,
    elevation: 3,
  },
  currentLabel: {
    fontSize: 14,
    color: '#999',
    marginBottom: 8,
  },
  currentDate: {
    fontSize: 28,
    fontWeight: 'bold',
    color: '#333',
  },
  currentTime: {
    fontSize: 18,
    color: '#666',
    marginTop: 4,
  },
  currentWeekday: {
    fontSize: 14,
    color: '#007AFF',
    marginTop: 4,
  },
  section: {
    marginBottom: 24,
  },
  sectionTitle: {
    fontSize: 16,
    fontWeight: '600',
    color: '#333',
    marginBottom: 12,
  },
  buttonRow: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    gap: 10,
  },
  birthdayCard: {
    backgroundColor: '#FFF',
    borderRadius: 12,
    padding: 20,
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    shadowRadius: 4,
    elevation: 3,
  },
  birthdayContent: {
    flex: 1,
  },
  birthdayLabel: {
    fontSize: 14,
    color: '#999',
    marginBottom: 4,
  },
  birthdayDate: {
    fontSize: 20,
    fontWeight: 'bold',
    color: '#333',
    marginBottom: 4,
  },
  ageText: {
    fontSize: 14,
    color: '#666',
  },
  editHint: {
    fontSize: 14,
    color: '#007AFF',
  },
  bookingCard: {
    backgroundColor: '#FFF',
    borderRadius: 12,
    padding: 20,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    shadowRadius: 4,
    elevation: 3,
  },
  bookingLabel: {
    fontSize: 14,
    color: '#999',
    marginBottom: 12,
  },
  dateButton: {
    backgroundColor: '#F5F5F5',
    borderRadius: 8,
    padding: 16,
    marginBottom: 16,
  },
  dateButtonText: {
    fontSize: 18,
    fontWeight: '600',
    color: '#333',
  },
  dayText: {
    fontSize: 14,
    color: '#666',
    marginTop: 4,
  },
});

export default App;


⚠️ 约束与限制

暂不支持的功能

功能 说明 相关 Issue
confirmText 确认按钮文本自定义 issue#11
cancelText 取消按钮文本自定义 issue#12
theme 模态框主题设置 issue#13
locale 区域语言设置 issue#17
timeZoneOffsetInMinutes 时区偏移设置 issue#18
is24hourSource 24小时制来源设置 issue#30
minuteInterval 分钟间隔设置 issue#34

已知问题

  1. 内联 datetime 模式不支持:当 modalfalsemodedatetime 时,HarmonyOS 不支持内联 datetime 组件。请使用模态框模式。
  2. 日期回弹动画:由于本库封装了 ArkUI DatePicker 组件,滑出设置范围的日期需要等待拖尾动画结束后才会回弹,这是预期行为。

建议

  1. 使用模态框模式:建议使用 modal={true} 配合 open 属性控制日期选择器的展示
  2. datetime 模式必须使用模态框:内联模式不支持 datetime,请务必配合 modal={true} 使用
  3. 日期范围限制:使用 minimumDatemaximumDate 限制可选日期范围,提升用户体验

📚 更多资源

  • 官方文档: https://github.com/henninghall/react-native-date-picker
  • HarmonyOS 适配库: https://github.com/react-native-oh-library/react-native-date-picker
  • 问题反馈: https://github.com/react-native-oh-library/react-native-date-picker/issues
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐