Date component developed with Flutter, plans to support display, swipe left and right, add date mark, radio, display week, etc.
使用Flutter开发的日期组件,计划支持显示,左右滑动,添加日期标记,单选,显示星期等功能。
2019-12-09 记
主要想实现的内容
使用
引入库
dependencies:mini_calendar: ^0.2.1
导包
import 'package:mini_calendar/mini_calendar.dart';
月视图(MonthWidget)
MonthWidget();// 默认当月
- 可通过控制器参数来控制显示的月份以及选择的日期
MonthWidget(controller: MonthController.init(MonthOption<String>(currentDay: DateDay.now().copyWith(month: index + 1, day: Random().nextInt(27) + 1),currentMonth: DateMonth.now().copyWith(month: index + 1),)),)
- 支持显示连选
MonthWidget(controller: MonthController.init(MonthOption(currentMonth: DateMonth.now().copyWith(month: 1),enableContinuous: true,firstSelectDay: DateDay.now().copyWith(month: 1, day: 8),secondSelectDay: DateDay.now().copyWith(month: 1, day: 18),)),)
- 支持添加标记
- ……

滚动日历(MonthPageView)
控制器需要创建后获取
onCreated
MonthPageView(padding: EdgeInsets.all(1),scrollDirection: Axis.horizontal,// 水平滑动或者竖直滑动option: MonthOption(enableContinuous: true,// 单选、连选控制marks: {DateDay.now().copyWith(day: 1): '111',DateDay.now().copyWith(day: 5): '222',DateDay.now().copyWith(day: 13): '333',DateDay.now().copyWith(day: 19): '444',DateDay.now().copyWith(day: 26): '444',},),showWeekHead: true, // 显示星期头部onContinuousSelectListen: (firstDay, endFay) {},// 连选回调onMonthChange: (month) {},// 月份更改回调onDaySelected: (day, data) {},// 日期选中会迪欧啊onCreated: (controller){}, // 控制器回调),
控制器
参数初始化
MonthOption({DateDay currentDay,//选择的日期DateMonth currentMonth,//当前月份int firstWeek = 7,//第一列显示的星期 [1,7]DateDay firstSelectDay,//连选第一个日期DateDay secondSelectDay,//连选第二个日期bool enableContinuous = false,//是否支持连选Map<DateDay, T> marks = const {},//标记DateDay minDay,//可选的最小日期DateDay maxDay,//可选的最大日期});
注销
MonthPageController#dispose();
更新
MonthPageController#reLoad();
下一月
MonthPageController#next();
上一月
MonthPageController#last();
跳转到指定月份
MonthPageController#goto(DateMonth month);
高级功能
自定义
自定义月视图背景
buildMonthBackground: (_, width, height, month) => Image.network('https://ssyerv1.oss-cn-hangzhou.aliyuncs.com/picture/b0c57bd90abd49d59920924010ab66a9.png!sswm',height: height,width: width,fit: BoxFit.cover,),
自定义月视图头部
buildMonthHead: (ctx, width, height, month) => Container(padding: EdgeInsets.all(5),child: Row(mainAxisAlignment: MainAxisAlignment.start,children: <Widget>[Text("${month.year}年",style: TextStyle(fontSize: 40, color: Colors.white),),Container(margin: EdgeInsets.only(left: 5, right: 5),width: 1,color: Colors.yellow,height: 50,),Column(crossAxisAlignment: CrossAxisAlignment.start,children: <Widget>[Text("${month.month}月",style: TextStyle(fontSize: 18, color: Colors.orange),),Text("这是一个自定义的月头部"),],)],),),
- 自定义星期头部
- 自定义日视图
- ……


更多功能请看demo
开发日记
2019-12-10 记
整体上,从简单的显示开始,然后再加入交互和控制功能。第一版显示方面,先考虑实现最常用的月视图。
月视图实现
日历的月视图,是非常规则的表格形式。所以就考虑用GridView来实现。
- 写两个类,来管理月和日,并重构了比较运算符。
/// 月class DateMonth {int year;int month;int maxDays;// ……}/// 日class DateDay extends DateMonth{int day;// ……}
- 在写月视图时,需要考虑开始星期所在位置,非当月日期的显示情况等。这里的关键在于计算位置。
2019-12-12 记
由于GridView在控制高度上面有所不便(可能有好多方法,我没有尝试到),我换成了Warp的方式来实现。
切换月份
常见的月份切换方式,有类似小米的左右滑动切换,也有上下滑动切换。第一个想到的就是用PageView来实现该功能。
问题:如何实现无限切换?
- 构建前,全部加载完成,然后就可以随意切换了。否决,不够灵活,性能开销大。
- 动态分配,初始化3页,然后监听滑动,动态首尾动态添加对应月份。可以考虑,但跨度大了,数据会不断膨胀,可以再优化下。
- 动态分配,动态移除,设定缓存大小,超出缓存范围的将被移除。可以,那就按照这个方案进行!
问题:如何实现动态分配?
原理如下,先动态分配数据,然后跳转到指定页面。若考虑缓存机制,可在remove前加判断即可。

下面是核心代码
List<int> pages = [-1, 0, 1];PageController _controller = PageController(initialPage: 1);_controller.addListener(() {double position = _controller.position.pixels;if (position == 0) {pages.insert(0, pages.first - 1);pages.remove(pages.last);_controller.jumpToPage(1);} else if (position == _controller.position.maxScrollExtent) {pages.add(pages.last + 1);pages.remove(pages.first);_controller.jumpToPage(1);} else {return;}setState((){});});
2019-12-14 记
连选实现
主要是处理日期选中后的赋值问题。
当下的处理逻辑有很多,我这里采用的是以下逻辑:
- 单选和连选只同时支持一种(理论上可以同时支持,后续可以再优化实现)
- 第一次选择日期,给到开始日期上。
- 第二次选择日期,将小日期给到开始日期,大日期给到结束日期上。
- 第三次选择日期,当落在小、大之间时,更换大日期。当不落在之间时,小、大日期全部清空。相当于取消选择。
