import 'dart:collection'; import 'package:flutter/material.dart'; import 'package:flutter_custom_calendar/calendar_provider.dart'; import 'package:flutter_custom_calendar/configuration.dart'; import 'package:flutter_custom_calendar/constants/constants.dart'; import 'package:flutter_custom_calendar/model/date_model.dart'; import 'package:flutter_custom_calendar/utils/LogUtil.dart'; import 'package:flutter_custom_calendar/utils/date_util.dart'; import 'package:flutter_custom_calendar/widget/default_combine_day_view.dart'; import 'package:flutter_custom_calendar/widget/default_custom_day_view.dart'; import 'package:flutter_custom_calendar/widget/default_week_bar.dart'; /** * 利用controller来控制视图 */ class CalendarController { static const Set EMPTY_SET = {}; static const Map EMPTY_MAP = {}; static const Duration DEFAULT_DURATION = const Duration(milliseconds: 500); CalendarConfiguration calendarConfiguration; CalendarProvider calendarProvider = CalendarProvider(); /** * 下面的信息不是配置的 */ List monthList = new List(); //月份list List weekList = new List(); //星期list PageController monthController; //月份的controller PageController weekController; //星期的controller CalendarController( {int selectMode = CalendarConstants.MODE_SINGLE_SELECT, int showMode = CalendarConstants.MODE_SHOW_ONLY_MONTH, int minYear = 1971, int maxYear = 2055, int minYearMonth = 1, int maxYearMonth = 12, int nowYear, int nowMonth, int minSelectYear = 1971, int minSelectMonth = 1, int minSelectDay = 1, int maxSelectYear = 2055, int maxSelectMonth = 12, int maxSelectDay = 30, Set selectedDateTimeList = EMPTY_SET, //多选模式下,默认选中的item列表 DateModel selectDateModel, //单选模式下,默认选中的item int maxMultiSelectCount = 9999, Map extraDataMap = EMPTY_MAP, int offset = 0 // 首日偏移量 }) { assert(offset >= 0 && offset <= 6); LogUtil.log(TAG: this.runtimeType, message: "init CalendarConfiguration"); //如果没有指定当前月份和年份,默认是当年时间 if (nowYear == null) { nowYear = DateTime.now().year; } if (nowMonth == null) { nowMonth = DateTime.now().month; } calendarConfiguration = CalendarConfiguration( selectMode: selectMode, showMode: showMode, minYear: minYear, maxYear: maxYear, maxYearMonth: maxYearMonth, nowYear: nowYear, nowMonth: nowMonth, minSelectYear: minSelectYear, minSelectMonth: minSelectMonth, minYearMonth: minYearMonth, minSelectDay: minSelectDay, maxSelectYear: maxSelectYear, maxSelectMonth: maxSelectMonth, extraDataMap: extraDataMap, maxSelectDay: maxSelectDay, maxMultiSelectCount: maxMultiSelectCount, selectDateModel: selectDateModel, offset: offset); calendarConfiguration.defaultSelectedDateList = new HashSet(); calendarConfiguration.defaultSelectedDateList .addAll(selectedDateTimeList.map((dateTime) { return DateModel.fromDateTime(dateTime); }).toSet()); //将默认选中的数据,放到provider中 calendarProvider.selectDateModel = selectDateModel; calendarProvider.selectedDateList = calendarConfiguration.defaultSelectedDateList; calendarConfiguration.minSelectDate = DateModel.fromDateTime(DateTime( calendarConfiguration.minSelectYear, calendarConfiguration.minSelectMonth, calendarConfiguration.minSelectDay)); calendarConfiguration.maxSelectDate = DateModel.fromDateTime(DateTime( calendarConfiguration.maxSelectYear, calendarConfiguration.maxSelectMonth, calendarConfiguration.maxSelectDay)); LogUtil.log( TAG: this.runtimeType, message: "start:${DateModel.fromDateTime(DateTime( minYear, minYearMonth, ))},end:${DateModel.fromDateTime(DateTime( maxYear, maxYearMonth, ))}"); if (showMode != CalendarConstants.MODE_SHOW_ONLY_WEEK) { //初始化pageController,initialPage默认是当前时间对于的页面 int initialPage = 0; int nowMonthIndex = 0; monthList.clear(); for (int i = minYear; i <= maxYear; i++) { for (int j = 1; j <= 12; j++) { if (i == minYear && j < minYearMonth) { continue; } if (i == maxYear && j > maxYearMonth) { continue; } DateModel dateModel = new DateModel(); dateModel.year = i; dateModel.month = j; if (i == nowYear && j == nowMonth) { initialPage = nowMonthIndex; } monthList.add(dateModel); nowMonthIndex++; } } this.monthController = new PageController(initialPage: initialPage, keepPage: true); LogUtil.log( TAG: this.runtimeType, message: "初始化月份视图的信息:一共有${monthList.length}个月,initialPage为$nowMonthIndex"); } if (showMode != CalendarConstants.MODE_SHOW_ONLY_MONTH) { //计算一共多少周 //计算方法:第一天是周几,最后一天是周几,中间的天数/7后加上2就是结果了 int initialWeekPage = 0; weekList.clear(); //如果没有配置当前时间,设置成当前的时间 if (nowYear == -1 || nowMonth == -1) { nowYear = DateTime.now().year; nowMonth = DateTime.now().month; } DateTime nowTime = new DateTime(nowYear, nowMonth, 15); DateTime firstDayOfMonth = DateTime(minYear, minYearMonth, 1); //计算第一个星期的第一天的日期 DateTime firstWeekDate = firstDayOfMonth.add(Duration(days: -(firstDayOfMonth.weekday - 1))); DateTime lastDay = DateTime(maxYear, maxYearMonth, DateUtil.getMonthDaysCount(maxYear, maxYearMonth)); int temp = -1; for (DateTime dateTime = firstWeekDate; !dateTime.isAfter(lastDay); dateTime = dateTime.add(Duration(days: 7))) { DateModel dateModel = DateModel.fromDateTime(dateTime); weekList.add(dateModel); // print("nowTime.isBefore(dateTime)"); // print("$nowTime,,,,$dateTime"); if (nowTime.isAfter(dateTime)) { temp++; } } initialWeekPage = temp; LogUtil.log( TAG: this.runtimeType, message: "初始化星期视图的信息:一共有${weekList.length}个星期,initialPage为$initialWeekPage"); this.weekController = new PageController(initialPage: initialWeekPage); } calendarConfiguration.monthList = monthList; calendarConfiguration.weekList = weekList; calendarConfiguration.monthController = monthController; calendarConfiguration.weekController = weekController; } //周视图切换 void addWeekChangeListener(OnWeekChange listener){ this.calendarConfiguration.weekChangeListeners.add(listener); } //月份切换监听 void addMonthChangeListener(OnMonthChange listener) { // this.calendarConfiguration.monthChange = listener; this.calendarConfiguration.monthChangeListeners.add(listener); } //点击选择监听 void addOnCalendarSelectListener(OnCalendarSelect listener) { this.calendarConfiguration.calendarSelect = listener; } //多选超出指定范围 void addOnMultiSelectOutOfRangeListener(OnMultiSelectOutOfRange listener) { this.calendarConfiguration.multiSelectOutOfRange = listener; } //多选超出限制个数 void addOnMultiSelectOutOfSizeListener(OnMultiSelectOutOfSize listener) { this.calendarConfiguration.multiSelectOutOfSize = listener; } //切换展开状态 void toggleExpandStatus() { calendarProvider.expandStatus.value = !calendarProvider.expandStatus.value; LogUtil.log( TAG: this.runtimeType, message: "toggleExpandStatus:${calendarProvider.expandStatus.value}"); } //监听展开变化 void addExpandChangeListener(ValueChanged expandChange) { calendarProvider.expandStatus.addListener(() { expandChange(calendarProvider.expandStatus.value); }); } //可以动态修改extraDataMap void changeExtraData(Map newMap) { this.calendarConfiguration.extraDataMap = newMap; this.calendarProvider.generation.value++; } //可以动态修改默认选中的item。 void changeDefaultSelectedDateList(Set defaultSelectedDateList) { this.calendarConfiguration.defaultSelectedDateList = defaultSelectedDateList; this.calendarProvider.generation.value++; } //可以动态修改默认选中的item void changeDefaultSelectedDateModel(DateModel dateModel) { this.calendarProvider.selectDateModel = dateModel; this.calendarProvider.generation.value++; } /** * 月份或者星期的上一页 */ Future previousPage() async { if (calendarProvider.expandStatus.value == true) { //月视图 int currentIndex = calendarProvider.calendarConfiguration.monthController.page.toInt(); if (currentIndex == 0) { return false; } else { calendarProvider.calendarConfiguration.monthController .previousPage(duration: DEFAULT_DURATION, curve: Curves.ease); calendarProvider.calendarConfiguration.monthChangeListeners .forEach((listener) { listener(monthList[currentIndex - 1].year, monthList[currentIndex - 1].month); }); DateModel temp = new DateModel(); temp.year = monthList[currentIndex].year; temp.month = monthList[currentIndex].month; temp.day = monthList[currentIndex].day + 14; calendarProvider.lastClickDateModel = temp; return true; } } else { //周视图 int currentIndex = calendarProvider.calendarConfiguration.weekController.page.toInt(); if (currentIndex == 0) { return false; } else { calendarProvider.calendarConfiguration.weekController .previousPage(duration: DEFAULT_DURATION, curve: Curves.ease); return true; } } } /** * 月份或者星期的下一页 * true:成功 * false:是最后一页 */ Future nextPage() async { if (calendarProvider.expandStatus.value == true) { //月视图 int currentIndex = calendarProvider.calendarConfiguration.monthController.page.toInt(); if (monthList.length - 1 == currentIndex) { return false; } else { calendarProvider.calendarConfiguration.monthController .nextPage(duration: DEFAULT_DURATION, curve: Curves.ease); calendarProvider.calendarConfiguration.monthChangeListeners .forEach((listener) { listener(monthList[currentIndex + 1].year, monthList[currentIndex + 1].month); }); DateModel temp = new DateModel(); temp.year = monthList[currentIndex].year; temp.month = monthList[currentIndex].month; temp.day = monthList[currentIndex].day + 14; calendarProvider.lastClickDateModel = temp; return true; } } else { //周视图 int currentIndex = calendarProvider.calendarConfiguration.weekController.page.toInt(); if (weekList.length - 1 == currentIndex) { return false; } else { calendarProvider.calendarConfiguration.weekController .nextPage(duration: DEFAULT_DURATION, curve: Curves.ease); return true; } } } //跳转到指定日期 void moveToCalendar(int year, int month, int day, {bool needAnimation = false, Duration duration = const Duration(milliseconds: 500), Curve curve = Curves.ease}) { if (calendarProvider.expandStatus.value == true) { DateModel dateModel = DateModel.fromDateTime(DateTime(year, month, 1)); //计算目标索引 int targetPage = monthList.indexOf(dateModel); if (targetPage == -1) { return; } if (calendarProvider.calendarConfiguration.monthController.hasClients == false) { return; } if (needAnimation) { calendarProvider.calendarConfiguration.monthController .animateToPage(targetPage, duration: duration, curve: curve); } else { calendarProvider.calendarConfiguration.monthController .jumpToPage(targetPage); } } else { DateModel dateModel = DateModel.fromDateTime(DateTime(year, month, 1)); //计算目标索引 int targetPage = 0; for (int i = 0; i < weekList.length - 1; i++) { DateModel first = weekList[i]; DateModel next = weekList[i + 1]; if (!first.isAfter(dateModel) && next.isAfter(dateModel)) { targetPage = i; return; } } if (calendarProvider.calendarConfiguration.weekController.hasClients == false) { return; } if (needAnimation) { calendarProvider.calendarConfiguration.weekController .animateToPage(targetPage, duration: duration, curve: curve); } else { calendarProvider.calendarConfiguration.weekController .jumpToPage(targetPage); } } } //切换到下一年 void moveToNextYear( {bool needAnimation = false, Duration duration = const Duration(milliseconds: 500), Curve curve = Curves.ease}) { DateTime targetDateTime = monthList[calendarProvider .calendarConfiguration.monthController.page .toInt() + 12] .getDateTime(); moveToCalendar( targetDateTime.year, targetDateTime.month, targetDateTime.day, needAnimation: needAnimation, duration: duration, curve: curve); } //切换到上一年 void moveToPreviousYear( {bool needAnimation = false, Duration duration = const Duration(milliseconds: 500), Curve curve = Curves.ease}) { DateTime targetDateTime = monthList[calendarProvider .calendarConfiguration.monthController.page .toInt() - 12] .getDateTime(); moveToCalendar( targetDateTime.year, targetDateTime.month, targetDateTime.day, needAnimation: needAnimation, duration: duration, curve: curve); } //切换到下一个月份, void moveToNextMonth( {bool needAnimation = false, Duration duration = const Duration(milliseconds: 500), Curve curve = Curves.ease}) { // 如果当前显示的是周视图的话,需要计算出第一个月的index后,调用weekController if (calendarProvider.expandStatus.value == false) { int currentMonth = weekList[calendarProvider .calendarConfiguration.weekController.page .toInt()] .month; for (int i = calendarProvider.calendarConfiguration.weekController.page .toInt(); i < weekList.length; i++) { if (weekList[i].month != currentMonth) { calendarProvider.calendarConfiguration.weekController.jumpToPage(i); break; } } return; } if ((calendarProvider.calendarConfiguration.monthController.page.toInt() + 1) >= monthList.length) { LogUtil.log(TAG: this.runtimeType, message: "moveToNextMonth:当前是最后一个月份"); return; } DateTime targetDateTime = monthList[calendarProvider .calendarConfiguration.monthController.page .toInt() + 1] .getDateTime(); moveToCalendar( targetDateTime.year, targetDateTime.month, targetDateTime.day, needAnimation: needAnimation, duration: duration, curve: curve); } //切换到上一个月份 void moveToPreviousMonth( {bool needAnimation = false, Duration duration = const Duration(milliseconds: 500), Curve curve = Curves.ease}) { // 如果当前显示的是周视图的话,需要计算出第一个月的index后,调用weekController if (calendarProvider.expandStatus.value == false) { int currentMonth = weekList[weekController.page.toInt()].month; for (int i = calendarProvider.calendarConfiguration.weekController.page .toInt(); i >= 0; i--) { if (weekList[i].month != currentMonth && weekList[i].isAfter(DateModel.fromDateTime(DateTime( calendarConfiguration.minYear, calendarConfiguration.minYearMonth)))) { calendarProvider.calendarConfiguration.weekController.jumpToPage(i); break; } } return; } if ((calendarProvider.calendarConfiguration.monthController.page.toInt()) == 0) { LogUtil.log( TAG: this.runtimeType, message: "moveToPreviousMonth:当前是第一个月份"); return; } DateTime targetDateTime = monthList[calendarProvider .calendarConfiguration.monthController.page .toInt() - 1] .getDateTime(); moveToCalendar( targetDateTime.year, targetDateTime.month, targetDateTime.day, needAnimation: needAnimation, duration: duration, curve: curve); } // 获取当前的月份 DateModel getCurrentMonth() { return monthList[monthController.page.toInt()]; } //获取被选中的日期,多选 Set getMultiSelectCalendar() { return calendarProvider.selectedDateList; } //获取被选中的日期,单选 DateModel getSingleSelectCalendar() { return calendarProvider.selectDateModel; } //清除数据 void clearData() { monthList.clear(); weekList.clear(); calendarProvider.clearData(); calendarConfiguration.weekChangeListeners=null; calendarConfiguration.monthChangeListeners=null; } } /** * 默认的weekBar */ Widget defaultWeekBarWidget() { return const DefaultWeekBar(); } /** * 使用canvas绘制item */ Widget defaultCustomDayWidget(DateModel dateModel) { return DefaultCustomDayWidget( dateModel, ); } /** * 使用组合widget的方式构造item */ Widget defaultCombineDayWidget(DateModel dateModel) { return new DefaultCombineDayWidget( dateModel, ); } /** * 判断是否在范围内,不在范围内的话,可以置灰 */ bool defaultInRange(DateModel dateModel) { return true; } /** * 周视图切换 */ typedef void OnWeekChange(int year, int month); /** * 月份切换事件 */ typedef void OnMonthChange(int year, int month); /** * 日期选择事件 */ typedef void OnCalendarSelect(DateModel dateModel); /** * 多选超出指定范围 */ typedef void OnMultiSelectOutOfRange(); /** * 多选超出限制个数 */ typedef void OnMultiSelectOutOfSize(); /** * 可以创建自定义样式的item */ typedef Widget DayWidgetBuilder(DateModel dateModel); /** * 是否可以点击,外部来进行判断,默认都可以点击 */ typedef bool CanClick(DateModel dateModel); /** * 可以自定义绘制每个Item,这种扩展性好一点,以后可以提供给外部进行自定义绘制 */ typedef void DrawDayWidget(DateModel dateModel, Canvas canvas, Size size); /** * 自定义顶部weekBar */ typedef Widget WeekBarItemWidgetBuilder();