omni_calendar_view 1.0.2 copy "omni_calendar_view: ^1.0.2" to clipboard
omni_calendar_view: ^1.0.2 copied to clipboard

A pure Flutter calendar widget with powerful customization, supporting Gregorian and Lunar calendars, single date and date range selection. No third-party dependencies.

Omni Calendar View #

OmniCalendarView 是一个为 Flutter 设计的功能强大、高度可定制且性能卓越的日历视图组件。它无缝集成了单日选择、日期范围选择、农历和节气显示,并通过一个直观的控制器(Controller)模式,让状态管理和外部交互变得前所未有的简单。

✨ 预览 #

核心特性 #

  • 🚀 状态与视图分离: 采用 ChangeNotifier 控制器模式,UI (OmniCalendarView) 负责渲染,逻辑和状态 (OmniCalendarController) 负责管理,代码结构清晰,易于维护和扩展。
  • 📅 灵活的日期选择:
    • 单选: 轻触任意日期即可完成选择。
    • 范围选择: 在已选中的日期上长按,即可开启范围选择模式,拖动到另一日期即可创建范围。再次长按范围端点可取消范围选择。
  • 🏮 完整的农历支持: (仅限中文环境)
    • 显示农历日期、中国传统节日(如春节、中秋节)、以及二十四节气。
    • 支持一键切换公历/农历混合显示,切换过程带有平滑的动画效果。
  • 🌐 国际化 (i18n): 内置中英双语支持,并且可以轻松扩展到其他语言。
  • 🎨 高度可定制: 通过丰富的参数和回调函数,可以自定义日历的行为和外观。
  • 🔄 流畅的交互体验:
    • 使用 PageView 实现月份间的平滑滑动。
    • 当选中非当前月份的日期时,日历会自动翻页到目标月份。
    • UI 元素(如高度变化)均带有平滑的 AnimatedContainer 动画。
  • ⚡️ 性能优化:
    • 月份视图使用 PageView.builder 构建,仅渲染可见的页面。
    • UI 更新由 AnimatedBuilder 驱动,实现局部刷新,避免不必要的 setState 调用,确保应用性能。

设计思路 #

OmniCalendarView 的核心设计哲学是 “关注点分离” (Separation of Concerns)。

状态中心 (OmniCalendarController) #

  • 我没有将所有状态(如当前显示的月份、选中的日期/范围、是否显示农历等)都放在 StatefulWidget 的 State 对象中。'
  • 相反,我创建了一个独立的 OmniCalendarController,它继承自 ChangeNotifier。
  • 这个控制器是日历的“大脑”和“唯一数据源”。任何状态的变更都通过调用控制器的方法来完成(例如 selectDate, jumpToDate)。

响应式UI (OmniCalendarView & AnimatedBuilder) #

  • 日历视图本身 (OmniCalendarView 及其子组件) 是“无状态”的。
  • 它通过 AnimatedBuilder 来监听控制器的变化。
  • 当控制器调用 notifyListeners() 时,只有监听它的 AnimatedBuilder 会被重建,从而以最高效的方式更新UI。
  • 这种模式避免了在顶层组件上调用 setState() 导致的整个组件树的非必要重建。

组合优于继承 #

  • 整个日历是由一系列小而专注的组件组合而成:
    • CalendarHeader: 显示年月、农历开关和当前选择。
    • WeekdayRow: 显示星期标题。
    • CalendarGrid: 负责渲染单个月份的网格。
    • _buildDayCell: 渲染单个日期单元格,通过一个巧妙的 Stack 结构,将范围背景、选中背景和文本内容分层绘制,实现了复杂的视觉效果。
  • 直观的交互设计:
    • 我认为长按是一个非常适合从“单选”模式切换到“范围选择”模式的交互手势,因为它不会与常规的点击事件冲突。
    • handleLongPress 方法中的逻辑清晰地处理了范围的创建、修改和取消,为用户提供了流畅的体验。

开始使用 #

安装 #

在你的 pubspec.yaml 文件中添加依赖:

dependencies:
omni_calendar_view: ^1.0.0 # 请使用最新版本

然后运行 flutter pub get

基本用法 #

import 'package:flutter/material.dart';
import 'package:omni_calendar_view/omni_calendar_view.dart';

class MyCalendarPage extends StatefulWidget {
  const MyCalendarPage({super.key});

  @override
  State<MyCalendarPage> createState() => _MyCalendarPageState();
}

class _MyCalendarPageState extends State<MyCalendarPage> {
  // 1. 创建控制器
  late final OmniCalendarController _controller;

  @override
  void initState() {
    super.initState();
    // 可以传入 initialDate 来指定初始显示的月份和默认选中的日期
    _controller = OmniCalendarController(initialDate: DateTime.now());
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Omni Calendar')),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: OmniCalendarView(
          // 2. 将控制器传递给视图
          controller: _controller,
          // 3. (可选) 设置初始属性和监听回调
          showLunar: true, // 默认显示农历
          locale: const Locale('zh', 'CN'), // 设置为中文
          onDateSelected: (date) {
            print('单选日期: $date');
          },
          onDateRangeSelected: (dateRange) {
            print('范围选择: ${dateRange.start} - ${dateRange.end}');
          },
          onMonthChanged: (newMonth) {
            print('月份改变: $newMonth');
          },
        ),
      ),
    );
  }
}

外部控制 #

你可以通过 OmniCalendarController 从外部控制日历的状态。

// 跳转到指定日期所在的月份
_controller.jumpToDate(DateTime(2025, 10, 1));
// 手动选择一个日期
_controller.selectDate(DateTime(2025, 7, 26));
// 切换农历显示 (这也会被 OmniCalendarView 的 showLunar 属性覆盖)
_controller.toggleLunar(false);

API 概览 #

OmniCalendarView #

属性 类型 描述
controller OmniCalendarController 必需. 控制器,用于状态管理。
showSurroundingDays bool 是否显示非本月的日期。默认为 true。
showLunar bool 是否显示农历。默认为 false。
locale Locale 语言环境,支持 zh 和 en。默认为 zh。
onHeaderTap VoidCallback? 顶部年月被点击时的回调。
onDateSelected ValueChanged 日期被单击时的回调。
onDateRangeSelected ValueChanged 日期范围被选择时的回调。
onMonthChanged ValueChanged 月份因滑动而改变时的回调。

OmniCalendarController #

属性/方法 类型 描述
displayedDate DateTime (Getter) 当前显示的月份。
selectedDate DateTime? (Getter) 当前选中的单个日期。
selectedDateRange DateTimeRange? (Getter) 当前选中的日期范围。
showLunar bool (Getter) 当前是否显示农历。
selectionType SelectionType (Getter) 当前的选择模式 (single 或 range)。
jumpToDate(DateTime) void 跳转到指定日期所在的月份视图。
selectDate(DateTime) void 编程方式选择一个日期。
toggleLunar(bool) void 切换农历显示。

贡献 #

欢迎各种形式的贡献,包括提交问题 (Issues)、请求新功能 (Feature Requests) 和代码合并请求 (Pull Requests)。

许可证 本项目基于 MIT License 开源。

2
likes
0
points
23
downloads

Publisher

unverified uploader

Weekly Downloads

A pure Flutter calendar widget with powerful customization, supporting Gregorian and Lunar calendars, single date and date range selection. No third-party dependencies.

Repository

License

unknown (license)

Dependencies

flutter

More

Packages that depend on omni_calendar_view