最近本身写了个demo,用到了日历方面的东西,而后实现来一下,最后打算封装一下,之后能够直接拿来使用。git
参考了Android的一个开源日历库github.com/huanghaibin…,实现思路其实差很少,都是能够利用canvas将日历给画出来。github
Flutter日历的项目地址:github.com/LXD31256949…canvas
Flutter上的一个日历控件,能够定制成本身想要的样子。bash
在pubspec.yaml添加依赖:ide
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中一些支持自定义配置的属性。不配置的话,会有对应的默认值。优化
//默认是单选,能够配置为MODE_SINGLE_SELECT,MODE_MULTI_SELECT
int selectMode;
//日历显示的最小年份和最大年份
int minYear;
int maxYear;
//日历显示的最小年份的月份,最大年份的月份
int minYearMonth;
int maxYearMonth;
//日历显示的当前的年份和月份
int nowYear;
int nowMonth;
//可操做的范围设置,好比点击选择
int minSelectYear;
int minSelectMonth;
int minSelectDay;
int maxSelectYear;
int maxSelectMonth;
int maxSelectDay; //注意:不能超过对应月份的总天数
Set<DateModel> selectedDateList = new Set(); //被选中的日期,用于多选
DateModel selectDateModel; //当前选择项,用于单选
int maxMultiSelectCount; //多选,最多选多少个
Map<DateTime, Object> extraDataMap = new Map(); //自定义额外的数据
//各类事件回调
OnMonthChange monthChange; //月份切换事件
OnCalendarSelect calendarSelect; //点击选择事件
OnMultiSelectOutOfRange multiSelectOutOfRange; //多选超出指定范围
OnMultiSelectOutOfSize multiSelectOutOfSize; //多选超出限制个数
//支持自定义绘制
DayWidgetBuilder dayWidgetBuilder; //建立日历item
WeekBarItemWidgetBuilder weekBarItemWidgetBuilder; //建立顶部的weekbar
//构造函数
CalendarController(
{int selectMode = Constants.MODE_SINGLE_SELECT,
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,
Map<DateTime, Object> extraDataMap = EMPTY_MAP})
复制代码
好比月份切换事件、点击选择事件。动画
//月份切换监听
void addMonthChangeListener(OnMonthChange listener) {
this.monthChange = listener;
}
//点击选择监听
void addOnCalendarSelectListener(OnCalendarSelect listener) {
this.calendarSelect = listener;
}
//多选超出指定范围
void addOnMultiSelectOutOfRangeListener(OnMultiSelectOutOfRange listener) {
this.multiSelectOutOfRange = listener;
}
//多选超出限制个数
void addOnMultiSelectOutOfSizeListener(OnMultiSelectOutOfSize listener) {
this.multiSelectOutOfSize = listener;
}
复制代码
//跳转到指定日期
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();
复制代码
// 获取当前的月份
DateTime getCurrentMonth();
//获取被选中的日期,多选
Set<DateModel> getMultiSelectCalendar();
//获取被选中的日期,单选
DateModel getSingleSelectCalendar();
复制代码
包括自定义WeekBar、自定义日历Item,默认使用的都是DefaultXXXWidget。ui
只要继承对应的Base类,实现相应的方法,而后只须要在配置Controller的时候,实现相应的Builder方法就能够了。this
//支持自定义绘制
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,有下面这些属性。
/**
* 日期的实体类
*/
class DateModel {
int year;
int month;
int day = 1;
int lunarYear;
int lunarMonth;
int lunarDay;
String lunarString; //农历字符串
String solarTerm; //24节气
String gregorianFestival; //公历节日
String traditionFestival; //传统农历节日
bool isCurrentDay; //是不是今天
bool isLeapYear; //是不是闰年
bool isWeekend; //是不是周末
int leapMonth; //是不是闰月
Object extraData; //自定义的额外数据
bool isInRange = false; //是否在范围内,好比能够实如今某个范围外,设置置灰的功能
bool isSelected; //是否被选中,用来实现一些标记或者选择功能
@override
String toString() {
return 'DateModel{year: $year, month: $month, day: $day}';
} //若是是闰月,则返回闰月
//转化成DateTime格式
DateTime getDateTime() {
return new DateTime(year, month, day);
}
//根据DateTime建立对应的model,并初始化农历和传统节日等信息
static DateModel fromDateTime(DateTime dateTime) {
DateModel dateModel = new DateModel()
..year = dateTime.year
..month = dateTime.month
..day = dateTime.day;
LunarUtil.setupLunarCalendar(dateModel);
return dateModel;
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is DateModel &&
runtimeType == other.runtimeType &&
year == other.year &&
month == other.month &&
day == other.day;
@override
int get hashCode => year.hashCode ^ month.hashCode ^ day.hashCode;
}
复制代码