flutter 自定义日历

在项目中需要实现一个日历展示我发布过的动态,效果如图,同时需要左右滑动翻页

 尝试使用组件发现可自定义的范围比较小,且无法实现需求,看到这篇Flutter自定义日历,实现日期底部不同颜色的日历事件_apro-wang的博客-CSDN博客_flutter自定义日历后进行了一些修改最终实现了需要的效果

//日历组件
import 'package:domain/user/model/publish_project.dart';
import 'package:flutter/material.dart';
import 'package:be_real/config/global_style.dart';
import 'package:domain/user/repository/user_repository.dart';
import 'package:data/repository_module.dart';
import 'package:be_real/view/account/components/delete_memory.dart';
import 'package:be_real/event/manager.dart';
import 'package:event_bus/event_bus.dart';

class Calender extends StatefulWidget {
  const Calender({Key key}) : super(key: key);

  @override
  _CalenderState createState() => _CalenderState();
}

class _CalenderState extends State<Calender> {
  int _year = DateTime.now().year; //当前展示年
  int _month = DateTime.now().month; //当前展示月
  int _day = DateTime.now().day; //当前展示日
  List _datas = []; //日期数组--只展示一个月份
  List _recentPublish = []; //我的当前展示月发布--某一天未发布赋值null
  List<Project> _myPublishAll = []; //我的全部动态--接口获取

  UserRepository _userRepository = userRepository();

  @override
  void initState() {
    //设置默认当前月日期
    _setDatas(year: _year, month: _month);

    //获取我的全部动态
    _getMyPublishAll();

    //刷新我的全部动态
    Manager.refreshMyPublishAll = EventBus();
    Manager.refreshMyPublishAll.on().listen((event) async {
      print('刷新我的全部动态');
      _getMyPublishAll();
    });

    super.initState();
  }

  //获取我发布的所有动态--接口获取
  _getMyPublishAll() async {
    await _userRepository.getMyselfPublishAll().then((value) async {
      await setState((){
        _myPublishAll = value.isSuccess;
      });
      print('我的最近发布:${_myPublishAll}');
      await _loadAttendanceMonthRecord();
    });
  }

  //赋值我的当前展示月发布
  _loadAttendanceMonthRecord() async {
    await setState(() {
      for(int i = 0; i < _datas.length; i++){ //外层循环当前展示月日期数组
        for(int j = 0; j < _myPublishAll.length; j++){ //内层循环我发布的动态数组
          if(_datas[i] == _myPublishAll[j].createDate){
            _recentPublish[i] = _myPublishAll[j];
            break;
          }
        }
      }
    });
    print('本月发布:${_recentPublish}');
  }

  @override
  Widget build(BuildContext context) {
    double movedXStart = 0;
    double movedXEnd = 0;
    double movedYStart = 0;
    double movedYEnd = 0;
    return Scaffold(
        backgroundColor: GlobalStyle.mainThemeColor,
        body: SingleChildScrollView(
            child: Column(
              children: [
                GestureDetector(
                  onHorizontalDragStart: (DragStartDetails details) {
                    movedXStart = details.globalPosition.dx;
                    movedYStart = details.globalPosition.dy;
                  },
                  onHorizontalDragUpdate: (DragUpdateDetails details) {
                    movedXEnd = details.globalPosition.dx;
                    movedYEnd = details.globalPosition.dy;
                  },
                  onHorizontalDragEnd: (DragEndDetails details) {
                    if((movedYEnd - movedYStart).abs() < (movedXEnd - movedXStart).abs()){ //横向移动距离大于纵向移动距离时判定为此时在进行横向滑动
                      if (movedXEnd - movedXStart < 0) { //左滑,执行下一月
                        _nextMonth();
                      }else{ //右滑,执行上一月
                        _lastMonth();
                      }
                    }
                  },
                  child: Container(
                    child: Column(
                      children: [
                        _yearHeader(),
                        _weekHeader(),
                        _everyDay(),
                      ],
                    ),
                  ),
                ),
              ],
            )));
  }

  //头部年
  Widget _yearHeader() {
    return Container(
      margin: EdgeInsets.only(top: 10),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          GestureDetector(
            onTap: () {
              _lastMonth();
            },
            child: Container(
              margin: EdgeInsets.only(left: 20),
              child: Image.asset(
                "assets/commons/left_white.png",
                width: 24,
                height: 24,
              ),
            ),
          ),
          Text(
            "$_year 年$_month 月",
            style: const TextStyle(
                fontSize: 16,
                color: Colors.white,
                fontWeight: FontWeight.w700
            ),
          ),
          GestureDetector(
            onTap: () {
              _nextMonth();
            },
            child: Container(
              margin: EdgeInsets.only(right: 20),
              child: Image.asset(
                "assets/commons/right_white.png",
                width: 24,
                height: 24,
              ),
            ),
          ),
        ],
      ),
    );
  }

  //中部周
  Widget _weekHeader() {
    var array = ["一", "二", "三", "四", "五", "六", "日"];
    return Container(
      height: 50,
      alignment: Alignment.center,
      child: GridView.builder(
        padding: EdgeInsets.only(left: 10, right: 10),
        itemCount: array.length,
        shrinkWrap: true,
        physics: const NeverScrollableScrollPhysics(),
        gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 7, //每行7列
            crossAxisSpacing: 5, //横轴间距
            mainAxisSpacing: 20, //纵轴间距
        ),
        itemBuilder: (context, index) {
          return Container(
              alignment: Alignment.center,
              child: Text(
                array[index],
                style: const TextStyle(
                    color: Colors.white,
                    fontSize: 16,
                    fontWeight: FontWeight.w700
                ),
              ));
        },
      ),
    );
  }

  //底部日
  Widget _everyDay() {
    return Container(
      child: GridView.builder(
        padding: EdgeInsets.only(left: 10, top: 10, right: 10),
        itemCount: _getRowsForMonthYear(year: _year, month: _month) * 7,
        shrinkWrap: true,
        physics: const NeverScrollableScrollPhysics(),
        gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 7, //每行7列
          crossAxisSpacing: 5, //横轴间距
          mainAxisSpacing: 20, //纵轴间距
        ),
        itemBuilder: (context, index) {
          Project project = _recentPublish[index];
          return GestureDetector(
            onTap: () {
              project == null ? null : DeleteMemory(context, project);
            },
            child: project == null
                ? Container(
                    //设置底部背景
                    decoration: const BoxDecoration(
                      borderRadius: BorderRadius.all(Radius.circular(8.0)),
                    ),
                    child: Center(
                      child: Text(
                        //不是当前月不显示值
                        int.parse(_datas[index].substring(5,7)) == _month
                            ? _datas[index].substring(8,10)
                            : "",
                        style: const TextStyle(
                            fontSize: 16,
                            color: Color(0xFF5A5A5C),
                            fontWeight: FontWeight.w700
                        )
                      ),
                    ),
                  )
                : Container(
                    //设置底部背景
                    decoration: BoxDecoration(
                        borderRadius: BorderRadius.all(Radius.circular(8.0)),
                        image: DecorationImage(
                            image: NetworkImage(
                              project.worksPhotoAfter,
                            ),
                            //不是当月不显示图片
                            opacity: int.parse(_datas[index].substring(5,7)) == _month ? 1 : 0,
                            fit: BoxFit.cover
                        )
                    ),
                    child: Center(
                      child: Text(
                        //不是当前月不显示值
                          int.parse(_datas[index].substring(5,7)) == _month
                              ? _datas[index].substring(8,10)
                              : "",
                          style: const TextStyle(
                              fontSize: 16,
                              color: Color(0xFFFFFFFF),
                              fontWeight: FontWeight.w700
                          )
                      ),
                    ),
                  ),
          );
        },
      ),
    );
  }

  // 获取行数
  int _getRowsForMonthYear({int year, int month}) {
    //当前月天数
    var _currentMonthDays = _getCurrentMonthDays(year: year, month: month);
    //补齐空缺需要的天数
    var _placeholderDays = _getPlaceholderDays(year: year, month: month);

    //以2022年11月为例,本月30天,第一天为周二,补齐第一天前面的空缺需要1天,30+1=31为本月天数在日历中的占位数
    int rows = (_currentMonthDays + _placeholderDays) ~/ 7;

    //若不能被7整除,则行数在取整行数上+1
    int remainder = (_currentMonthDays + _placeholderDays) % 7;
    if (remainder > 0) {
      rows = rows + 1;
    }
    return rows;
  }

  // weekday得到这个月的第一天是星期几
  // 因为该日历的每行是从周一开始,因此获取到本月的第一天是周几后需要-1得到还需要多少天补齐前面的空缺
  int _getPlaceholderDays({int year, int month}) {
    return DateTime(year, month).weekday - 1 % 7;
  }

  // 获取当前月份天数
  int _getCurrentMonthDays({int year, int month}) {
    if (month == 2) {
      //判断2月份是闰年月还是平年
      if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) {
        return 29;
      } else {
        return 28;
      }
    } else if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) {
      return 31;
    } else {
      return 30;
    }
  }

  /// 获取展示信息
  _setDatas({int year, int month}) {
    /// 上个月占位--以本月为2022年11月为例,10.31在11月占位
    var lastYear = year;
    var lastMonth = month - 1;
    if (month == 1) {
      lastYear = year - 1;
      lastMonth = 12;
    }

    var placeholderDays = _getPlaceholderDays(year: year, month: month); //补齐本月第一天前的空缺需要的天数
    var lastMonthDays = _getCurrentMonthDays(year: lastYear, month: lastMonth); //上个月天数
    var firstDay = lastMonthDays - placeholderDays;
    for (var i = 0; i < placeholderDays; i++) {
      _datas.add(
        '${_dateFormat(lastYear)}-${_dateFormat(lastMonth)}-${_dateFormat(firstDay+i+1)}'
      );
      _recentPublish.add(null);
    }

    /// 本月显示
    var currentMonthDays = _getCurrentMonthDays(year: year, month: month); //本月天数
    for (var i = 0; i < currentMonthDays; i++) {
      _datas.add(
        '${_dateFormat(_year)}-${_dateFormat(_month)}-${_dateFormat(i+1)}'
      );
      _recentPublish.add(null);
    }

    /// 下个月占位--以本月为2022年11月为例,12月1-4日在11月占位
    var nextYear = year;
    var nextMonth = month + 1;
    if (month == 12) {
      nextYear = year + 1;
      nextMonth = 1;
    }
    var nextPlaceholderDays = _getPlaceholderDays(year: nextYear, month: nextMonth); //下个月补齐空缺需要的天数
    for (var i = 0; i < 7 - nextPlaceholderDays; i++) {
      _datas.add(
        '${_dateFormat(nextYear)}-${_dateFormat(nextMonth)}-${_dateFormat(i+1)}'
      );
      _recentPublish.add(null);
    }

    print('本月日期:${_datas}');
    print('创造当前月发布数组:${_recentPublish}');
  }

  //日期格式化-加0
  _dateFormat(int num){
    if(num < 10){
      return '0$num';
    }else{
      return '$num';
    }
  }

  // 上月
  _lastMonth() {
    setState(() {
      //当前月是1月,上个月就是上一年的12月
      if (_month == 1) {
        _year = _year - 1;
        _month = 12;
      } else {
        _month = _month - 1;
      }
      _day = 1; //查看上一个月时,默认选中的为第一天
      _datas.clear();
      _recentPublish.clear();
      _setDatas(year: _year, month: _month);
      //更新月历事件
      _loadAttendanceMonthRecord();
    });
  }

  // 下月
  _nextMonth() {
    //判断展示年是否小于当前年,若是则操作上下月不受限制
    if(_year < DateTime.now().year){
      _setNextMonthData();
    }
    //判断展示年是否是当前年,若是则需要再判断月份
    else if(_year == DateTime.now().year){
      //当展示月大于等于当前月时,不能点击下个月,反之则需要设置下个月数据
      if(_month < DateTime.now().month){
        _setNextMonthData();
      }
    }
  }

  //设置下个月的数据
  _setNextMonthData(){
    setState(() {
      //当前月是12月,下个月就是下一年的1月
      if (_month == 12) {
        _year = _year + 1;
        _month = 1;
      } else {
        _month = _month + 1;
      }
      if (_month == DateTime.now().month) {
        //如果下个月时当前月,默认选中当天
        _day = DateTime.now().day;
      } else {
        //如果不是当前月,默认选中第一天
        _day = 1;
      }
      _datas.clear();
      _recentPublish.clear();
      _setDatas(year: _year, month: _month);
      //更新月历事件
      _loadAttendanceMonthRecord();
    });
  }
}

猜你喜欢

转载自blog.csdn.net/YML_426/article/details/128267254