flutter custom calendar

In the project, a calendar needs to be implemented to display the dynamics I have published, the effect is shown in the figure, and at the same time, it is necessary to slide left and right to turn the page

 Try to use components to find that the scope of customization is relatively small, and the requirements cannot be realized. See this Flutter custom calendar to realize calendar events with different colors at the bottom of the date_apro-wang's blog-CSDN blog_flutter custom calendar After making some modifications, the desired effect was finally achieved

//日历组件
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();
    });
  }
}

Guess you like

Origin blog.csdn.net/YML_426/article/details/128267254