阅读 1092

Flutter仿钉钉考勤日历

效果

在这里插入图片描述

原型

在这里插入图片描述

开发

  1. 使用
          // 考勤日历
            DatePickerDialog(
              initialDate: DateTime.now(),
              firstDate: DateTime(2020),
              lastDate: DateTime(2030),
              onDateChanged: onDateChanged,
              // 0:无状态,1:正常考勤  2:异常考情,迟到,早退,
              // 若不满一个月,日历会自动用0补满一个月
              checking: [
                0,
                0,
                1,
                2,
              ],
            ),
复制代码

DatePickerDialog是在存在与Flutter的material包中,Flutter自带的日历是以dialog形式存在的,本文将dialog改成StatefulWidget直接在页面中,将多余东西去掉,直接在material/calendar_date_picker.dart中_DayPicker上进行修改。 2. 修改日历中日期样式:

 Widget dayWidget = Container(
          margin: EdgeInsets.all(4.0),
          decoration: decoration,
          alignment: Alignment.center,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(localizations.formatDecimal(day),
                  style: TextStyle(
                      fontSize: 14.0,
                      color: dayColor,
                      fontWeight: FontWeight.bold)),
              Visibility(
                visible: checking[day - 1] == 1 || checking[day - 1] == 2,
                child: Container(
                  height: 6.0,
                  width: 6.0,
                  decoration: BoxDecoration(
                    shape: BoxShape.circle,
                    color: isSelectedDay ? Colors.white :
                     (checking[day - 1] == 1 ?  Color(0xFF1376EE): Color(0xFFFF8A21)),
                  ),
                ),
              ),
            ],
          ),
        );
复制代码

Visibility原来没有,是修改加上去的,主要是显示当天打卡状态,若打卡正常则在日期显示下方显示蓝色小点,若有异常则显示橙色的点,若没有状态就不显示,checking则是使用DatePickerDialog传入的,由于日历从1开始,数组是从索引0开始的,所以使用checking[day - 1]才能准确获取某一日的打卡状态,day 则是日历中某一月中所有日期。

2.设置星期标题 修改后:

  List<Widget> _dayHeaders() {
    final List<Widget> result = <Widget>[];
    final List<String> weekdays = ["日", "一", "二", "三", "四", "五", "六"];
    for (int i = 1; true; i = (i + 1) % 7) {
      final String weekday = weekdays[i];
      result.add(ExcludeSemantics(
        child: Center(
            child: Text(weekday,
                style: TextStyle(fontSize: 14.0, color: Color(0xFF999999)))),
      ));
      if (i == (1 - 1) % 7) break;
    }
    return result;
  }
复制代码

原文:

  List<Widget> _dayHeaders(TextStyle? headerStyle, MaterialLocalizations localizations) {
    final List<Widget> result = <Widget>[];
    for (int i = localizations.firstDayOfWeekIndex; true; i = (i + 1) % 7) {
      final String weekday = localizations.narrowWeekdays[i];
      result.add(ExcludeSemantics(
        child: Center(child: Text(weekday, style: headerStyle)),
      ));
      if (i == (localizations.firstDayOfWeekIndex - 1) % 7)
        break;
    }
    return result;
  }
复制代码

localizations.firstDayOfWeekIndex返回值为0或者1,若返回0,则星期日为每周的第一天;若返回1,则星期一为每周的第一天。本文中没有从localizations.firstDayOfWeekIndex获取,直接赋值为1,则每周从星期一开始。

3.补全每个月空白日期:

  • 获取指定月份有多少天
  static int getDaysInMonth(int year, int month) {
    if(month < 1){
      year = year - 1;
      month = month + 12;
    }

    if(month > 12){
      year = year + 1;
      month = month - 12;
    }
    if (month == DateTime.february) {
      final bool isLeapYear = (year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0);
      return isLeapYear ? 29 : 28;
    }
    const List<int> daysInMonth = <int>[31, -1, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
    return daysInMonth[month - 1];
  }
复制代码
  • 获取指定月份1日的偏移量,即每个月第一天是星期几,若1号是星期3,因为日历每周是从第一天开始的,所以第一周的星期的星期一,星期二为空白,需要补全上个月的倒数后两天,所以还需要获取上个月的最后两天是哪两天。
// 获取日期偏移
  static int firstDayOffsets(int year, int month) {
    final int weekdayFromMonday = DateTime(year, month).weekday - 1;
    int firstDayOfWeekIndex = 1;
    firstDayOfWeekIndex = (firstDayOfWeekIndex - 1) % 7;
    return (weekdayFromMonday - firstDayOfWeekIndex) % 7;
  }
  ...
  // 补全开始日期
     int day = -dayOffset;
    while (day < daysInMonth) {
      day++;
      if (day < 1) {
        dayItems.add(Container(
          margin: EdgeInsets.all(4.0),
          alignment: Alignment.center,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
            // daysInPreMonth上个月的天数-本月日期偏移则可把本月开始缺失的日期补全
              Text(localizations.formatDecimal(daysInPreMonth - day.abs()),
                  style: TextStyle(
                      fontSize: 14.0,
                      color: Color(0xFF888888),
                      fontWeight: FontWeight.bold)),
              Visibility(
                visible: false,
                child: Container(
                  height: 6.0,
                  width: 6.0,
                  decoration: BoxDecoration(
                    shape: BoxShape.circle,
                    color: Color(0xFFFF8A21),
                  ),
                ),
              ),
            ],
          ),
        ));
      } else {
        ...
      }
        ...
      
      }
    }
复制代码
  • 补全日历结束日期,每个月最后一天若为星期日,则无需补全,若为星期五则把下个月的前两天补在本月的星期六和星期日:
    if ((daysInMonth + dayOffset) % 7 > 0) {
    // 计算需要补多少天,直接从1开始就好
      int addNum = 7 - ((daysInMonth + dayOffset) % 7);
      for (int i = 1; i <= addNum; i++) {
        dayItems.add(Container(
          margin: EdgeInsets.all(4.0),
          alignment: Alignment.center,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(localizations.formatDecimal(i),
                  style: TextStyle(
                      fontSize: 14.0,
                      color: Color(0xFF888888),
                      fontWeight: FontWeight.bold)),
              Visibility(
                visible: false,
                child: Container(
                  height: 6.0,
                  width: 6.0,
                  decoration: BoxDecoration(
                    shape: BoxShape.circle,
                    color: Color(0xFFFF8A21),
                  ),
                ),
              ),
            ],
          ),
        ));
      }
    }
复制代码
  1. 计算每个月从星期一开始展示可以展示几行,即计算日历高度,月份不同,高度不同,最少4行,最多6行,根据内容动态显示不至于留大量空白
    // 计算一个月有多少天 
    int daysInMonth = ChinaDateUtils.getDaysInMonth(
        widget.initialDate.year, widget.initialDate.month);
     // 计算每个月第一天的星期
    int dayOffset = ChinaDateUtils.firstDayOffsets(
        widget.initialDate.year, widget.initialDate.month);
    // 计算用多少行展示所有日期
    int row = ((daysInMonth + dayOffset) / 7).ceil();
复制代码

修改这些内容大概可以实现上述效果。 Demo地址:gitee.com/masshub/sig…

文章分类
Android
文章标签