资讯专栏INFORMATION COLUMN

Flutter日历,可以自定义风格UI

icyfire / 3334人阅读

摘要:日历的项目地址示例上的一个日历控件,可以定制成自己想要的样子。跳转到指定日期,默认支持动画切换自定义日历,支持组合的方式和利用绘制的方式自定义顶部的可以给添加自定义的额外数据,实现各种额外的功能。

介绍

最近自己写了个demo,用到了日历方面的东西,然后实现来一下,最后打算封装一下,以后可以直接拿来使用。

参考了Android的一个开源日历库github.com/huanghaibin…,实现思路其实差不多,都是可以利用canvas将日历给画出来。

Flutter日历的项目地址:github.com/LXD31256949…

示例

Flutter上的一个日历控件,可以定制成自己想要的样子。

主要功能

支持公历,农历,节气,传统节日,常用节假日

日期范围设置,默认支持的最大日期范围为1971.01-2055.12

禁用日期范围设置,比如想实现某范围的日期内可以点击,范围外的日期置灰

支持单选、多选模式,提供多选超过限制个数的回调和多选超过指定范围的回调。

跳转到指定日期,默认支持动画切换

自定义日历Item,支持组合widget的方式和利用canvas绘制的方式

自定义顶部的WeekBar

可以给Item添加自定义的额外数据,实现各种额外的功能。比如实现进度条风格的日历

使用

在pubspec.yaml添加依赖:

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中一些支持自定义配置的属性。不配置的话,会有对应的默认值。

//默认是单选,可以配置为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 selectedDateList = new Set(); //被选中的日期,用于多选
DateModel selectDateModel; //当前选择项,用于单选
int maxMultiSelectCount; //多选,最多选多少个
Map 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 selectedDateTimeList = EMPTY_SET,
      DateModel selectDateModel,
      int maxMultiSelectCount = 9999,
      Map extraDataMap = EMPTY_MAP})

利用controller添加监听事件

比如月份切换事件、点击选择事件。

//月份切换监听
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;
}
利用controller来控制日历的切换,支持配置动画
//跳转到指定日期
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();
利用controller来获取日历的一些数据信息
// 获取当前的月份
DateTime getCurrentMonth();
//获取被选中的日期,多选
Set getMultiSelectCalendar();
//获取被选中的日期,单选
DateModel getSingleSelectCalendar();
自定义UI

包括自定义WeekBar、自定义日历Item,默认使用的都是DefaultXXXWidget。

只要继承对应的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);
  }
}
DateModel实体类

日历所用的日期的实体类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;
}
TODO LIST

优化代码实现

支持屏蔽指定的某些天

继续写几个不同风格的Demo

支持周视图

支持动画切换周视图和月视图

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/6883.html

相关文章

  • Flutter基础(二)Flutter最新开发环境搭建和Hello World

    摘要:注释处的方法是程序的入口,使用了符号,这是中单行函数或方法的简写,等价于如下代码方法是框架的入口,如果不返回方法,那么执行的是一个控制台应用。 本文首发于微信公众号「刘望舒」 前言 最近的Google I/O大会上,Flutter1.5 开始支持移动、Web、桌面和嵌入式设备,从不温不火的sky一直进化到如今热门的Flutter,Flutter的发展已经超出很多人的想象。我对跨平台技术一...

    tuomao 评论0 收藏0
  • 【开发经验】Flutter避免代码嵌套,写好build方法

    摘要:意味着属性必须在构造函数中就被初始化完成,不接受提前定义,也不接受更改。所以,在生命周期中动态的改变对象的属性是不可能的,必须使用框架的方法来为构造函数动态指定参数,从而达到改变组件属性的功能。 本文适合使用Flutter开发过一段时间的开发者阅读,旨在分享一种避免Flutter的UI代码嵌套太深问题的方法。如果对本文内容或观点有相关疑问,欢迎在评论中指出。 优化效果(缩略图): sh...

    Worktile 评论0 收藏0
  • 深入理解flutter的编译原理与优化

    摘要:本文将以一个简单的为例,介绍下相关原理及定制与优化。模式对应了的模式,又称检查模式或者慢速模式。为快速启动,快速执行,包大小做了优化。并不支持模拟器的原因在于,模拟器上的诊断并不代表真实的性能。因此本文主要讨论因引入的构建,运行等原理。 摘要: 闲鱼技术-正物 问题背景 对于开发者而言,什么是Flutter?它是用什么语言编写的,包含哪几部分,是如何被编译,运行到设备上的呢?Flutt...

    codecook 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<