最近本身寫了個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;
}
複製代碼