Flutter仿钉钉考勤日历的示例代码

本文主要介绍了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 则是日历中某一月中所有日期。

3.设置星期标题

修改后:

  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,则每周从星期一开始。

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

获取指定月份有多少天

  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),
                  ),
                ),
              ),
            ],
          ),
        ));
      }
    }

计算每个月从星期一开始展示可以展示几行,即计算日历高度,月份不同,高度不同,最少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地址:https://gitee.com/masshub/sign_in

到此这篇关于Flutter仿钉钉考勤日历的示例代码的文章就介绍到这了,更多相关Flutter仿钉钉考勤日历内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Flutter中ListView 的使用示例

    这个小例子使用的是豆瓣 API 中 正在上映的电影 的开放接口,要实现的主要效果如下: JSON 数据结构 Item 结构 Item 的结构是一个 Card 包含着一个 Row 然后这个 Row 里面左边是一个 Image ,右边是一个 Column 功能实现 material 库 Json 解析 网络请求 加载菊花 要实现上面四个功能,我们首先需要在 .dart 文件中引入如下代码 import 'dart:convert'; import 'package:http/http.dart' a

  • 详解Flutter WebView与JS互相调用简易指南

    本文采用Flutter官方WebView插件:https://pub.dartlang.org/packages/webview_flutter WebView与JS互相调用是一个刚需,但是貌似现在大家写的文章讲的都不是很清楚,我这个简易指南简单粗暴地分为两部分:JS调用Flutter和Flutter调用JS,拒绝花里胡哨,保证一看就懂,一学就会. 开始之前先简单了解一下官方WebView所包含的API: onWebViewCreated:在WebView创建完成后调用,只会被调用一次: ini

  • Flutter持久化存储之数据库存储(sqflite)详解

    前言 数据库存储是我们常用的存储方式之一,对大批量数据有增.删.改.查操作需求时,我们就会想到使用数据库,Flutter中提供了一个sqflite插件供我们用于大量数据执行CRUD操作.本篇我们就来一起学习sqflite的使用. sqflite是一款轻量级的关系型数据库,类似SQLite. 在Flutter平台我们使用sqflite库来同时支持Android 和iOS. sqflite使用 引入插件 在pubspec.yaml文件中添加path_provider插件,最新版本为1.0.0,如下:

  • 你必须掌握在Flutter中添加资源文件的方法

    在 Flutter 中,需要在根目录下的 pubspec.yaml 文件中配置资源的路径,资源才能被打包使用.现在,看看如何配置资源吧. 1. 添加图片资源文件 1.1 添加本地图片资源 flutter: assets: // 表示引入根目录下的 images 文件夹下的所有资源文件 - images/ // 只添加 images/ 下的 pci.png - images/pci.png 注意缩进!本地文件夹内的资源可以选择导入整个文件夹,或者只导入指定文件.使用:Image.asset("im

  • Flutter中如何加载并预览本地的html文件的方法

    直接进入主题,大概步骤如下 在 assets 创建需要访问 html 文件,如下 这里创建一个files文件夹,专门来放这些静态 html 文件. 在 pubspec.yaml 中配置访问位置 assets: - assets/images/ - assets/files/ 在 pubspec.yaml 添加 webview_flutter 插件依赖 webview_flutter: ^0.3.15+1 // 具体版本请查看官网 进入实际的代码操作 import 'dart:convert';

  • flutter InkWell实现水波纹点击效果

    在flutter 开发中用InkWell或者GestureDetector将某个组件包起来,已添加点击事件. GestureDetector 使用点击无水波纹出现,InkWell可以实现水波纹效果. 正常情况下使用 : InkWell( //单击事件响应 onTap: () { }, child: Container( alignment: Alignment(0, 0), height: 28, width: 120, child: Text("InkWell单击事件"), ), )

  • 详解flutter之网络请求dio,请求,拦截器简单示例

    flutter一直很火的网络请求插件dio 直接上代码,写成一个类,可以直接使用 包含请求的封装,拦截器的封装 import 'package:dio/dio.dart'; import 'dart:async'; import 'dart:io'; import './apidomain.dart'; import './httpHeaders.dart'; import 'package:shared_preferences/shared_preferences.dart'; class D

  • flutter日期选择器 flutter时间选择器

    本文实例为大家分享了flutter日期时间选择器的具体代码,供大家参考,具体内容如下 1 日期选择器 //设置默认显示的日期为当前 DateTime initialDate = DateTime.now(); void showDefaultYearPicker(BuildContext context) async { final DateTime dateTime = await showDatePicker( context: context, //定义控件打开时默认选择日期 initia

  • Flutter实现页面切换后保持原页面状态的3种方法

    前言: 在Flutter应用中,导航栏切换页面后默认情况下会丢失原页面状态,即每次进入页面时都会重新初始化状态,如果在initState中打印日志,会发现每次进入时都会输出,显然这样增加了额外的开销,并且带来了不好的用户体验. 在正文之前,先看一些常见的App导航,以喜马拉雅FM为例: 它拥有一个固定的底部导航以及首页的顶部导航,可以看到不管是点击底部导航切换页面还是在首页左右侧滑切换页面,之前的页面状态都是始终维持的,下面就具体介绍下如何在flutter中实现类似喜马拉雅的导航效果 第一步:实

  • Flutter 超实用简单菜单弹出框 PopupMenuButton功能

    相信在实际开发过程当中,肯定少不了这样的功能: 点击 AppBar 右上角的按钮,弹出一个菜单供用户选择. 幸运的是,Flutter 提供给我们了一个 Widget,直接就能实现如上的效果. PopupMenuButton 还是老规矩,先看官方的说明: Displays a menu when pressed and calls onSelected [1] when the menu is dismissed because an item was selected. The value pa

随机推荐