Flutter日曆2.0,支持月視圖和周視圖,能夠支持自定義風格

FlutterCalendarWidget

Flutter上的一個日曆控件,能夠定製成本身想要的樣子。Github地址git

介紹

以前寫了一個Flutter日曆的開源庫,最近增長了一些功能,而且對代碼進行了一下重構,再搞了下性能優化。(以前的代碼寫得真的是****,沒搞狀態框架,還各類嵌套代碼)github

示例

日曆支持web預覽:點擊此處進入預覽web

主要功能

  • 支持公曆,農曆,節氣,傳統節日,經常使用節假日
  • 日期範圍設置,默認支持的最大日期範圍爲1971.01-2055.12
  • 禁用日期範圍設置,好比想實現某範圍的日期內能夠點擊,範圍外的日期置灰
  • 支持單選、多選模式,提供多選超過限制個數的回調和多選超過指定範圍的回調。
  • 跳轉到指定日期,默認支持動畫切換
  • 自定義日曆Item,支持組合widget的方式和利用canvas繪製的方式
  • 自定義頂部的WeekBar
  • 根據實際場景,能夠給Item添加自定義的額外數據,實現各類額外的功能。好比實現進度條風格的日曆,實現日曆的各類標記
  • 支持周視圖的展現
  • 支持月份視圖和星期視圖的展現與切換聯動

近期修改

[1.0.0] - 2019/10/10

  • 重構日曆的代碼,進行性能優化
  • 建立configuration類,將配置的信息放到這裏
  • 引入provider狀態管理,避免數據各類嵌套傳遞,以及實現局部刷新
  • 實現周視圖,並實現周視圖和月視圖之間的聯動。使用IndexStack包裝兩個周視圖和月視圖,切換的使用修改IndexStack的index就好了
  • DateModel增長isCurrentMonth,用於繪製月視圖能夠屏蔽一些非當前月份的日子,前面幾天或者後面幾天的isCurrentMonth是爲false的。
  • 點擊item後進行刷新,控制刷新範圍:單選模式下只刷新兩個item,當前item和上一個item。多選模式下只刷新選中的item就好了。
  • DateModel的數據初始化,部分屬性使用get方法進行懶加載

[0.0.1] - 2019/5/19.

  • 支持公曆,農曆,節氣,傳統節日,經常使用節假日
  • 日期範圍設置,默認支持的最大日期範圍爲1971.01-2055.12
  • 禁用日期範圍設置,好比想實現某範圍的日期內能夠點擊,範圍外的日期置灰
  • 支持單選、多選模式,提供多選超過限制個數的回調和多選超過指定範圍的回調。
  • 跳轉到指定日期,默認支持動畫切換
  • 自定義日曆Item,支持組合widget的方式和利用canvas繪製的方式
  • 自定義頂部的WeekBar
  • 能夠給Item添加自定義的額外數據,實現各類額外的功能。好比實現進度條風格的日曆

使用

在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});
複製代碼
  • boxDecoration用來配置總體的背景
  • 利用CalendarController來配置一些數據,而且能夠經過CalendarController進行一些操做或者事件監聽,好比滾動到下一個月,獲取當前被選中的Item等等。

配置CalendarController

下面是CalendarController中一些支持自定義配置的屬性。不配置的話,會有對應的默認值。(配置如今都是在controller這裏進行配置的,內部會將配置的數據抽成Configuration類)bash

配置的含義主要包括了3個方面的配置。框架

  • 一個是顯示日曆所須要的相關數據,
  • 一個是顯示日曆的自定義UI的相關配置,
  • 一個是對日曆的監聽事件進行配置。
//構造函數
      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()

UI繪製相關的配置

屬性 含義 默認值
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) 監聽日曆的展開收縮狀態

利用controller來控制日曆的切換,支持配置動畫

方法 含義 默認值
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() 切換展開狀態

利用controller來獲取日曆的一些數據信息

方法 含義 默認值
DateTime getCurrentMonth() 獲取當前的月份
Set getMultiSelectCalendar() 獲取被選中的日期,多選
DateModel getSingleSelectCalendar() 獲取被選中的日期,單選

如何自定義UI

包括自定義WeekBar、自定義日曆Item,默認使用的都是DefaultXXXWidget。ide

只要繼承對應的Base類,實現相應的方法,而後只須要在配置Controller的時候,實現相應的Builder方法就能夠了。函數

//支持自定義繪製
DayWidgetBuilder dayWidgetBuilder; //建立日曆item
WeekBarItemWidgetBuilder weekBarItemWidgetBuilder; //建立頂部的weekbar
複製代碼

自定義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,
      ),
    );
  }
}
複製代碼

自定義日曆Item:

提供兩種方法,一種是利用組合widget的方式來建立,一種是利用Canvas來自定義繪製Item。最後只須要在CalendarController的構造參數中進行配置就能夠了。

  • 繼承BaseCombineDayWidget,重寫getNormalWidget(DateModel dateModel) 和getSelectedWidget(DateModel dateModel)就能夠了,返回對應的widget就行。
class DefaultCombineDayWidget extends BaseCombineDayWidget {
  DefaultCombineDayWidget(DateModel dateModel) : super(dateModel);

  @override
  Widget getNormalWidget(DateModel dateModel) {
     //實現默認狀態下的UI
  }

  @override
  Widget getSelectedWidget(DateModel dateModel) {
    //繪製被選中的UI
  }
}
複製代碼
  • 繼承BaseCustomDayWidget,重寫drawNormal和drawSelected的兩個方法就能夠了,利用canvas本身繪製Item。
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);
  }
}
複製代碼

根據實際場景,自定義額外的數據extraData

自定義每一個item的進度條數據

//外部處理每一個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實體類

日曆所用的日期的實體類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是不是同一天
相關文章
相關標籤/搜索