Flutter上的一个日历控件,能够定制成本身想要的样子。Github地址git
以前写了一个Flutter日历的开源库,最近增长了一些功能,而且对代码进行了一下重构,再搞了下性能优化。(以前的代码写得真的是****,没搞状态框架,还各类嵌套代码)github
日历支持web预览:点击此处进入预览web
在pubspec.yaml添加依赖:canvas
flutter_custom_calendar:
git:
url: https://github.com/LXD312569496/flutter_custom_calendar.git
复制代码
引入flutter_custom_calendar,就可使用CalendarViewWidget,配置CalendarController就能够了。性能优化
import 'package:flutter_custom_calendar/flutter_custom_calendar.dart';
CalendarViewWidget({@required this.calendarController, this.boxDecoration});
复制代码
下面是CalendarController中一些支持自定义配置的属性。不配置的话,会有对应的默认值。(配置如今都是在controller这里进行配置的,内部会将配置的数据抽成Configuration类)bash
配置的含义主要包括了3个方面的配置。框架
//构造函数
CalendarController(
{int selectMode = Constants.MODE_SINGLE_SELECT,
int showMode = Constants.MODE_SHOW_ONLY_MONTH,
bool expandStatus = true,
DayWidgetBuilder dayWidgetBuilder = defaultCustomDayWidget,
WeekBarItemWidgetBuilder weekBarItemWidgetBuilder = defaultWeekBarWidget,
int minYear = 1971,
int maxYear = 2055,
int minYearMonth = 1,
int maxYearMonth = 12,
int nowYear = -1,
int nowMonth = -1,
int minSelectYear = 1971,
int minSelectMonth = 1,
int minSelectDay = 1,
int maxSelectYear = 2055,
int maxSelectMonth = 12,
int maxSelectDay = 30,
Set<DateTime> selectedDateTimeList = EMPTY_SET,
DateModel selectDateModel,
int maxMultiSelectCount = 9999,
double verticalSpacing = 10,
bool enableExpand = true,
Map<DateModel, Object> extraDataMap = EMPTY_MAP})
复制代码
属性 | 含义 | 默认值 |
---|---|---|
selectMode | 选择模式,表示单选或者多选 | 默认是单选 static const int MODE_SINGLE_SELECT = 1; static const int MODE_MULTI_SELECT = 2; |
showMode | 展现模式 | 默认是只展现月视图 static const int MODE_SHOW_ONLY_MONTH=1;//仅支持月视图 static const int MODE_SHOW_ONLY_WEEK=2;//仅支持星期视图 static const int MODE_SHOW_WEEK_AND_MONTH=3;//支持月和星期视图切换 |
minYear | 日历显示的最小年份 | 1971 |
maxYear | 日历显示的最大年份 | 2055 |
minYearMonth | 日历显示的最小年份的月份 | 1 |
maxYearMonth | 日历显示的最大年份的月份 | 12 |
nowYear | 日历显示的当前的年份 | -1 |
nowMonth | 日历显示的当前的月份 | -1 |
minSelectYear | 能够选择的最小年份 | 1971 |
minSelectMonth | 能够选择的最小年份的月份 | 1 |
minSelectDay | 能够选择的最小月份的日子 | 1 |
maxSelectYear | 能够选择的最大年份 | 2055 |
maxSelectMonth | 能够选择的最大年份的月份 | 12 |
maxSelectDay | 能够选择的最大月份的日子 | 30,注意:不能超过对应月份的总天数 |
selectedDateList | 被选中的日期,用于多选 | 默认为空Set, Set selectedDateList = new Set() |
selectDateModel | 当前选择项,用于单选 | 默认为空 |
maxMultiSelectCount | 多选,最多选多少个 | hhh |
extraDataMap | 自定义额外的数据 | 默认为空Map,Map<DateTime, Object> extraDataMap = new Map() |
属性 | 含义 | 默认值 |
---|---|---|
weekBarItemWidgetBuilder | 建立顶部的weekbar | 默认样式 |
dayWidgetBuilder | 建立日历item | 默认样式 |
verticalSpacing | 日历item之间的竖直方向间距 | 默认10 |
boxDecoration | 总体的背景设置 | |
itemSize | 每一个item的边长 | 默认是屏幕宽度/7 |
方法 | 含义 | 默认值 |
---|---|---|
void addMonthChangeListener(OnMonthChange listener) | 月份切换事件 | |
void addOnCalendarSelectListener(OnCalendarSelect listener) | 点击选择事件 | |
void addOnMultiSelectOutOfRangeListener(OnMultiSelectOutOfRange listener) | 多选超出指定范围 | |
void addOnMultiSelectOutOfSizeListener(OnMultiSelectOutOfSize listener) | 多选超出限制个数 | |
void addExpandChangeListener(ValueChanged expandChange) | 监听日历的展开收缩状态 |
方法 | 含义 | 默认值 |
---|---|---|
Future previousPage() | 滑动到上一个页面,会自动根据当前的展开状态,滑动到上一个月或者上一个星期。若是已经在第一个页面,没有上一个页面,就会返回false,其余状况返回true | |
Future nextPage() | 滑动到下一个页面,会自动根据当前的展开状态,滑动到下一个月或者下一个星期。若是已经在最后一个页面,没有下一个页面,就会返回false,其余状况返回true | |
void moveToCalendar(int year, int month, int day, {bool needAnimation = false,Duration duration = const Duration(milliseconds: 500),Curve curve = Curves.ease}) | 到指定日期 | |
void moveToNextYear() | 切换到下一年 | |
void moveToPreviousYear() | 切换到上一年 | |
void moveToNextMonth() | 切换到下一个月份 | |
void moveToPreviousMonth() | 切换到上一个月份 | |
void toggleExpandStatus() | 切换展开状态 |
方法 | 含义 | 默认值 |
---|---|---|
DateTime getCurrentMonth() | 获取当前的月份 | |
Set getMultiSelectCalendar() | 获取被选中的日期,多选 | |
DateModel getSingleSelectCalendar() | 获取被选中的日期,单选 |
包括自定义WeekBar、自定义日历Item,默认使用的都是DefaultXXXWidget。ide
只要继承对应的Base类,实现相应的方法,而后只须要在配置Controller的时候,实现相应的Builder方法就能够了。函数
//支持自定义绘制
DayWidgetBuilder dayWidgetBuilder; //建立日历item
WeekBarItemWidgetBuilder weekBarItemWidgetBuilder; //建立顶部的weekbar
复制代码
继承BaseWeekBar,重写getWeekBarItem(index)方法就能够。随便你怎么实现,只须要返回一个Widget就能够了。性能
class DefaultWeekBar extends BaseWeekBar {
const DefaultWeekBar({Key key}) : super(key: key);
@override
Widget getWeekBarItem(int index) {
/**
* 自定义Widget
*/
return new Container(
height: 40,
alignment: Alignment.center,
child: new Text(
Constants.WEEK_LIST[index],
style: topWeekTextStyle,
),
);
}
}
复制代码
提供两种方法,一种是利用组合widget的方式来建立,一种是利用Canvas来自定义绘制Item。最后只须要在CalendarController的构造参数中进行配置就能够了。
class DefaultCombineDayWidget extends BaseCombineDayWidget {
DefaultCombineDayWidget(DateModel dateModel) : super(dateModel);
@override
Widget getNormalWidget(DateModel dateModel) {
//实现默认状态下的UI
}
@override
Widget getSelectedWidget(DateModel dateModel) {
//绘制被选中的UI
}
}
复制代码
class DefaultCustomDayWidget extends BaseCustomDayWidget {
DefaultCustomDayWidget(DateModel dateModel) : super(dateModel);
@override
void drawNormal(DateModel dateModel, Canvas canvas, Size size) {
//实现默认状态下的UI
defaultDrawNormal(dateModel, canvas, size);
}
@override
void drawSelected(DateModel dateModel, Canvas canvas, Size size) {
//绘制被选中的UI
defaultDrawSelected(dateModel, canvas, size);
}
}
复制代码
//外部处理每一个dateModel所对应的进度
Map<DateModel, int> progressMap = {
DateModel.fromDateTime(temp.add(Duration(days: 1))): 0,
DateModel.fromDateTime(temp.add(Duration(days: 2))): 20,
DateModel.fromDateTime(temp.add(Duration(days: 3))): 40,
DateModel.fromDateTime(temp.add(Duration(days: 4))): 60,
DateModel.fromDateTime(temp.add(Duration(days: 5))): 80,
DateModel.fromDateTime(temp.add(Duration(days: 6))): 100,
};
//建立CalendarController对象的时候,将extraDataMap赋值就好了
new CalendarController(
extraDataMap: progressMap)
//绘制DayWidget的时候,能够直接从dateModel的extraData对象中拿到想要的数据
int progress = dateModel.extraData;
复制代码
//外部处理每一个dateModel所对应的标记
Map<DateModel, String> customExtraData = {
DateModel.fromDateTime(DateTime.now().add(Duration(days: -1))): "假",
DateModel.fromDateTime(DateTime.now().add(Duration(days: -2))): "游",
DateModel.fromDateTime(DateTime.now().add(Duration(days: -3))): "事",
DateModel.fromDateTime(DateTime.now().add(Duration(days: -4))): "班",
DateModel.fromDateTime(DateTime.now().add(Duration(days: -5))): "假",
DateModel.fromDateTime(DateTime.now().add(Duration(days: -6))): "游",
DateModel.fromDateTime(DateTime.now().add(Duration(days: 2))): "游",
DateModel.fromDateTime(DateTime.now().add(Duration(days: 3))): "事",
DateModel.fromDateTime(DateTime.now().add(Duration(days: 4))): "班",
DateModel.fromDateTime(DateTime.now().add(Duration(days: 5))): "假",
DateModel.fromDateTime(DateTime.now().add(Duration(days: 6))): "游",
DateModel.fromDateTime(DateTime.now().add(Duration(days: 7))): "事",
DateModel.fromDateTime(DateTime.now().add(Duration(days: 8))): "班",
};
//建立CalendarController对象的时候,将extraDataMap赋值就好了
new CalendarController(
extraDataMap: customExtraData)
//绘制DayWidget的时候,能够直接从dateModel的extraData对象中拿到想要的数据
String data = dateModel.extraData;
复制代码
日历所用的日期的实体类DateModel,有下面这些属性。能够在自定义绘制DayWidget的时候,根据相应的属性,进行判断后,绘制相应的UI。
属性 | 含义 | 类型 | 默认值 |
---|---|---|---|
year | 年份 | int | |
month | 月份 | int | |
day | 日期 | int | 默认为1 |
lunarYear | 农历年份 | int | |
lunarMonth | 农历月份 | int | |
lunarDay | 农历日期 | int | |
lunarString | 农历字符串 | String | |
solarTerm | 24节气 | String | |
gregorianFestival | gregorianFestival | String | |
traditionFestival | 传统农历节日 | String | |
isCurrentDay | 是不是今天 | bool | false |
isLeapYear | 是不是闰年 | bool | false |
isWeekend | 是不是周末 | bool | false |
isInRange | 是否在范围内,好比能够实如今某个范围外,设置置灰的功能 | bool | false |
isSelected | 是否被选中,用来实现一些标记或者选择功能 | bool | false |
extraData | 自定义的额外数据 | Object | 默认为空 |
方法 | 含义 |
---|---|
DateTime getDateTime() | 将DateModel转化成DateTime |
DateModel fromDateTime(DateTime dateTime) | 根据DateTime建立对应的model,并初始化农历和传统节日等信息 |
bool operator ==(Object other) | 重写==方法,能够判断两个dateModel是不是同一天 |