Flutter-滑动单行日历组件
本文最后更新于 631 天前,其中的信息可能已经有所发展或是发生改变。
import 'package:flutter/material.dart';

class CalendarWidget extends StatefulWidget {
  // 选择器范围
  DateTime startTime;
  DateTime endTime;
  // 日期文本字体大小
  TextStyle? style;
  // 头部 周文本样式
  TextStyle? headerStyle;
  // 日期容器大小
  double? size;
  // 选择器高度
  double? height;
  // 选择器padding
  EdgeInsets? padding;
  // 选中回调
  Function(DateTime dateTime)? onTapTime;

  CalendarWidget({
    Key? key,
    required this.startTime,
    required this.endTime,
    this.style,
    this.size,
    this.height,
    this.padding,
    this.onTapTime,
    this.headerStyle,
  }) : super(key: key);


  @override
  State<CalendarWidget> createState() => _CalendarWidgetState();
}

class _CalendarWidgetState extends State<CalendarWidget> {
  /*
     {
       end:"2024-12-01",  //最后可显示日历的日期
       hot:{"2023-07-05":"2023-08-31","2024-07-05":"2024-08-31"},  //所有暑假段
       code:{"2023-01-15":"2023-02-06","2024-01-15":"2024-02-06"},  //所有寒假段
       extholiday:["2023-05-03","2023-05-04"], //额外假日列表
       extworkday:["2023-06-08","2023-06-09"] //额外工作日列表
     }
   */

  late PageController _pageController;

  List<WeekItem> source = [];

  late DateTime start;
  late DateTime end;

  // 记录之前页码 用于清除之前页选中效果
  int beforeChoosePageIndex = -1;
  @override
  void initState() {
    DateTime currentDate = DateTime.now();
    start = widget.startTime;
    end = widget.endTime;
    // 初始化构建三页数据
    List<ItemConfig> weekDates = getWeekDates(currentDate);
    source.add(WeekItem()..week = weekDates);
    source.insert(0, WeekItem()..week = getPreviousWeek(weekDates[0].dateTime));
    source.add(WeekItem()..week = getNextWeek(weekDates[6].dateTime));

    _pageController = PageController(initialPage: 1);
    beforeChoosePageIndex = 1;
    super.initState();
  }

  @override
  void dispose() {
    super.dispose();
    _pageController.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 15,vertical: 15),
      child: Column(
        children: [
          Row(
            children: List.generate(7, (index) => Expanded(
              child: Text(_getDayText(index),textAlign: TextAlign.center,style: widget.headerStyle??const TextStyle(
                  fontSize: 12,
                  color: Color(0xFF999999)
              ),),
            )),
          ),
          Padding(
            padding: widget.padding??const EdgeInsets.only(top: 12.0),
            child: SizedBox(
              height: widget.height??36,
              child: PageView.builder(
                controller: _pageController,
                onPageChanged: (page) {
                  if(page == 0) {
                    setState(() {
                      source.insert(0,WeekItem()..week = getPreviousWeek(source[0].week[0].dateTime));
                      _pageController.jumpTo(_pageController.position.pixels + _pageController.position.viewportDimension);
                    });
                  } else if(page == source.length - 1) {
                    setState(() {
                      source.add(WeekItem()..week = getNextWeek(source[source.length - 1].week[6].dateTime));
                    });
                  }
                },
                itemBuilder: (BuildContext context, int index) {
                  WeekItem weekItem = source[index];
                  return Row(
                    children: List.generate(7, (index) {
                      DateTime now = weekItem.week[index].dateTime;
                      // 是否超出startTime 是否超出endTime
                      bool isEnable = now.isBefore(start) || end.isBefore(now);

                      bool isToday = _isSameDate(now,DateTime.now());

                      bool isChoose = weekItem.chooseIndex == index;

                      String extraTip = weekItem.week[index].extraTip;

                      Widget dateTimeWidget = Text("${now.day}",style: widget.style??TextStyle(
                          fontWeight: FontWeight.w600,
                          fontSize: 18,
                          color: isChoose ? Colors.white : const Color(0xFF666666)
                      ));

                      return Expanded(
                        child: UnconstrainedBox(
                          child: GestureDetector(
                            onTap: () {
                              // 重置之前选中状态
                              if(beforeChoosePageIndex != _pageController.page && beforeChoosePageIndex >= 0 && beforeChoosePageIndex < source.length) {
                                source[beforeChoosePageIndex].chooseIndex = -1;
                                beforeChoosePageIndex = toInt(_pageController.page);
                              }

                              widget.onTapTime?.call(weekItem.week[index].dateTime);
                              if(mounted) {
                                setState(() {
                                  weekItem.chooseIndex = index;
                                });
                              }
                            },
                            child: Container(
                              width: widget.size??36,
                              height: widget.size??36,
                              decoration: BoxDecoration(
                                  color: isEnable ? weekItem.disableColor : (
                                      isChoose ? weekItem.chooseColor : (isToday ? weekItem.todayColor : Colors.transparent)),
                                  borderRadius: BorderRadius.circular(widget.size == null ? 18 : widget.size! / 2)
                              ),
                              child: extraTip.isEmpty || isChoose ? Center(
                                child: dateTimeWidget,
                              ) : Row(
                                mainAxisAlignment: MainAxisAlignment.center,
                                children: [
                                  dateTimeWidget,
                                  Text(extraTip,style: const TextStyle(
                                      fontWeight: FontWeight.w600,
                                      fontSize: 11,
                                      color: Color(0xFFFF893F)
                                  )),
                                ],
                              ),
                            ),
                          ),
                        ),
                      );
                    }),
                  );
                },
                itemCount: source.length,
              ),
            ),
          )
        ],
      ),
    );
  }

  int toInt(double? page) {
    if(page == null) {
      return -1;
    }
    try {
      return page.toInt();
    } catch(e) {
      return -1;
    }
  }

  bool _isSameDate(DateTime date1, DateTime date2) {
    return date1.year == date2.year && date1.month == date2.month && date1.day == date2.day;
  }

  String _getDayText(int index) {
    switch(index) {
      case 0 :
        return "一";
      case 1 :
        return "二";
      case 2 :
        return "三";
      case 3 :
        return "四";
      case 4 :
        return "五";
      case 5 :
        return "六";
      case 6 :
        return "七";
    }
    return "一";
  }

  List<ItemConfig> getWeekDates(DateTime dateTime) {
    List<ItemConfig> weekDates = [];

    DateTime firstDayOfWeek = dateTime.subtract(Duration(days: dateTime.weekday - 1));

    for (int i = 0; i < 7; i++) {
      DateTime now = firstDayOfWeek.add(Duration(days: i));
      // 测试使用
      String extraTip = "";
      if(7 <= now.month && now.month <= 9) {
        extraTip = "暑";
      }

      if(1 <= now.month && now.month <= 2) {
        extraTip = "寒";
      }
      weekDates.add(ItemConfig()..dateTime = now..extraTip = extraTip);
    }

    return weekDates;
  }

  List<ItemConfig> getPreviousWeek(DateTime dateTime) {
    DateTime startDate = dateTime.subtract(const Duration(days: 7));
    return getWeekDates(startDate);
  }

  List<ItemConfig> getNextWeek(DateTime dateTime) {
    DateTime endDate = dateTime.add(const Duration(days: 1));
    return getWeekDates(endDate);
  }
}

class WeekItem {
  Color todayColor = const Color(0xFF0BB9B9).withOpacity(0.1);
  Color disableColor = const Color(0xFFE5ECF3);
  Color chooseColor = const Color(0xFF0BB9B9);
  List<ItemConfig> week = [];

  // 点击索引
  int chooseIndex = -1;
}

class ItemConfig {
  DateTime dateTime = DateTime.now();
  String extraTip = "";

  @override
  String toString() {
    return 'ItemConfig{dateTime: $dateTime, extraTip: $extraTip}';
  }
}
本文链接:https://iichen.cn/?p=714
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇