mini_calendar - 图1

Date component developed with Flutter, plans to support display, swipe left and right, add date mark, radio, display week, etc.

使用Flutter开发的日期组件,计划支持显示,左右滑动,添加日期标记,单选,显示星期等功能。

2019-12-09 记

主要想实现的内容

mini_calendar - 图2

使用

引入库

  1. dependencies:
  2. mini_calendar: ^0.2.1

导包

  1. import 'package:mini_calendar/mini_calendar.dart';

月视图(MonthWidget)

  1. MonthWidget();// 默认当月
  • 可通过控制器参数来控制显示的月份以及选择的日期
  1. MonthWidget(
  2. controller: MonthController.init(
  3. MonthOption<String>(
  4. currentDay: DateDay.now().copyWith(month: index + 1, day: Random().nextInt(27) + 1),
  5. currentMonth: DateMonth.now().copyWith(month: index + 1),
  6. )
  7. ),
  8. )
  • 支持显示连选
  1. MonthWidget(
  2. controller: MonthController.init(MonthOption(
  3. currentMonth: DateMonth.now().copyWith(month: 1),
  4. enableContinuous: true,
  5. firstSelectDay: DateDay.now().copyWith(month: 1, day: 8),
  6. secondSelectDay: DateDay.now().copyWith(month: 1, day: 18),
  7. )),
  8. )
  • 支持添加标记
  • ……

image.png

滚动日历(MonthPageView)

控制器需要创建后获取 onCreated

  1. MonthPageView(
  2. padding: EdgeInsets.all(1),
  3. scrollDirection: Axis.horizontal,// 水平滑动或者竖直滑动
  4. option: MonthOption(
  5. enableContinuous: true,// 单选、连选控制
  6. marks: {
  7. DateDay.now().copyWith(day: 1): '111',
  8. DateDay.now().copyWith(day: 5): '222',
  9. DateDay.now().copyWith(day: 13): '333',
  10. DateDay.now().copyWith(day: 19): '444',
  11. DateDay.now().copyWith(day: 26): '444',
  12. },
  13. ),
  14. showWeekHead: true, // 显示星期头部
  15. onContinuousSelectListen: (firstDay, endFay) {
  16. },// 连选回调
  17. onMonthChange: (month) {
  18. },// 月份更改回调
  19. onDaySelected: (day, data) {
  20. },// 日期选中会迪欧啊
  21. onCreated: (controller){
  22. }, // 控制器回调
  23. ),

控制器

参数初始化

  1. MonthOption({
  2. DateDay currentDay,//选择的日期
  3. DateMonth currentMonth,//当前月份
  4. int firstWeek = 7,//第一列显示的星期 [1,7]
  5. DateDay firstSelectDay,//连选第一个日期
  6. DateDay secondSelectDay,//连选第二个日期
  7. bool enableContinuous = false,//是否支持连选
  8. Map<DateDay, T> marks = const {},//标记
  9. DateDay minDay,//可选的最小日期
  10. DateDay maxDay,//可选的最大日期
  11. });

注销

  1. MonthPageController#dispose();

更新

  1. MonthPageController#reLoad();

下一月

  1. MonthPageController#next();

上一月

  1. MonthPageController#last();

跳转到指定月份

  1. MonthPageController#goto(DateMonth month);

mouth_page_view2.gif

高级功能

自定义

自定义月视图背景

  1. buildMonthBackground: (_, width, height, month) => Image.network(
  2. 'https://ssyerv1.oss-cn-hangzhou.aliyuncs.com/picture/b0c57bd90abd49d59920924010ab66a9.png!sswm',
  3. height: height,
  4. width: width,
  5. fit: BoxFit.cover,
  6. ),

自定义月视图头部

  1. buildMonthHead: (ctx, width, height, month) => Container(
  2. padding: EdgeInsets.all(5),
  3. child: Row(
  4. mainAxisAlignment: MainAxisAlignment.start,
  5. children: <Widget>[
  6. Text(
  7. "${month.year}年",
  8. style: TextStyle(fontSize: 40, color: Colors.white),
  9. ),
  10. Container(
  11. margin: EdgeInsets.only(left: 5, right: 5),
  12. width: 1,
  13. color: Colors.yellow,
  14. height: 50,
  15. ),
  16. Column(
  17. crossAxisAlignment: CrossAxisAlignment.start,
  18. children: <Widget>[
  19. Text(
  20. "${month.month}月",
  21. style: TextStyle(fontSize: 18, color: Colors.orange),
  22. ),
  23. Text("这是一个自定义的月头部"),
  24. ],
  25. )
  26. ],
  27. ),
  28. ),
  • 自定义星期头部
  • 自定义日视图
  • ……

image.pngimage.png

更多功能请看demo

开发日记

2019-12-10 记

整体上,从简单的显示开始,然后再加入交互和控制功能。第一版显示方面,先考虑实现最常用的月视图。

月视图实现

日历的月视图,是非常规则的表格形式。所以就考虑用GridView来实现。

  1. 写两个类,来管理月和日,并重构了比较运算符。
  1. /// 月
  2. class DateMonth {
  3. int year;
  4. int month;
  5. int maxDays;
  6. // ……
  7. }
  8. /// 日
  9. class DateDay extends DateMonth{
  10. int day;
  11. // ……
  12. }
  1. 在写月视图时,需要考虑开始星期所在位置,非当月日期的显示情况等。这里的关键在于计算位置。

2019-12-12 记

由于GridView在控制高度上面有所不便(可能有好多方法,我没有尝试到),我换成了Warp的方式来实现。

切换月份

常见的月份切换方式,有类似小米的左右滑动切换,也有上下滑动切换。第一个想到的就是用PageView来实现该功能。

问题:如何实现无限切换?

  1. 构建前,全部加载完成,然后就可以随意切换了。否决,不够灵活,性能开销大。
  2. 动态分配,初始化3页,然后监听滑动,动态首尾动态添加对应月份。可以考虑,但跨度大了,数据会不断膨胀,可以再优化下。
  3. 动态分配,动态移除,设定缓存大小,超出缓存范围的将被移除。可以,那就按照这个方案进行!

问题:如何实现动态分配?

原理如下,先动态分配数据,然后跳转到指定页面。若考虑缓存机制,可在remove前加判断即可。

image.png

下面是核心代码

  1. List<int> pages = [-1, 0, 1];
  2. PageController _controller = PageController(initialPage: 1);
  3. _controller.addListener(() {
  4. double position = _controller.position.pixels;
  5. if (position == 0) {
  6. pages.insert(0, pages.first - 1);
  7. pages.remove(pages.last);
  8. _controller.jumpToPage(1);
  9. } else if (position == _controller.position.maxScrollExtent) {
  10. pages.add(pages.last + 1);
  11. pages.remove(pages.first);
  12. _controller.jumpToPage(1);
  13. } else {
  14. return;
  15. }
  16. setState((){});
  17. });

有了上面的基础,切换月份,也就变成一件简单的事情了。

2019-12-14 记

连选实现

主要是处理日期选中后的赋值问题。

当下的处理逻辑有很多,我这里采用的是以下逻辑:

  1. 单选和连选只同时支持一种(理论上可以同时支持,后续可以再优化实现)
  2. 第一次选择日期,给到开始日期上。
  3. 第二次选择日期,将小日期给到开始日期,大日期给到结束日期上。
  4. 第三次选择日期,当落在小、大之间时,更换大日期。当不落在之间时,小、大日期全部清空。相当于取消选择。

2019-12-15 记

实现可单选、连选、左右滑动日历控件

  • 月视图添加背景
  • 可自定义起始星期
  • 可自定义实现周头部控件
  • 可自定义实现标记组件
  • 利用切换月份连选实现完成左右切换的可单选、连选的日期控件

2019-12-17 记

  1. 修复不安全的属性
  2. 支持日视图自定义
  3. 增加月头部视图支持(可自定义)
  4. 可自定义工作日和周末字体颜色
  5. 滑动日历视图自适应高度
  6. 增加上一月、下一月主动控制

    2019-12-18 记

  7. 可选最大日期

  8. 可选最小日期
  9. 跳转到指定日期

    2020-04-13 记

  10. 支持国际化

  11. 支持多选控制