123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337 |
- import 'package:cs_resources/generated/assets.dart';
- import 'package:flutter/material.dart';
- import 'package:intl/intl.dart';
- import 'package:shared/utils/log_utils.dart';
- import 'package:widgets/ext/ex_widget.dart';
- import 'package:widgets/my_load_image.dart';
- //日历的具体展示
- class FullCalendar extends StatefulWidget {
- final DateTime startDate;
- final DateTime? endDate;
- final DateTime? selectedDate;
- final Color? dateColor;
- final Color? dateSelectedColor;
- final Color? dateSelectedBg;
- final double? padding;
- final String? locale;
- final Widget? calendarBackground;
- final List<String>? events;
- final Function onDateChange;
- const FullCalendar({
- Key? key,
- this.endDate,
- required this.startDate,
- required this.padding,
- required this.onDateChange,
- this.calendarBackground,
- this.events,
- this.dateColor,
- this.dateSelectedColor,
- this.dateSelectedBg,
- this.locale,
- this.selectedDate,
- }) : super(key: key);
- @override
- State<FullCalendar> createState() => _FullCalendarState();
- }
- class _FullCalendarState extends State<FullCalendar> {
- late DateTime endDate;
- late DateTime startDate;
- late int _initialPage;
- List<String>? _events = [];
- late PageController _horizontalScroll;
- @override
- void initState() {
- setState(() {
- startDate = DateTime.parse("${widget.startDate.toString().split(" ").first} 00:00:00.000");
- endDate = DateTime.parse("${widget.endDate.toString().split(" ").first} 23:00:00.000");
- _events = widget.events;
- });
- super.initState();
- }
- @override
- Widget build(BuildContext context) {
- List<String> partsStart = startDate.toString().split(" ").first.split("-");
- DateTime firstDate = DateTime.parse("${partsStart.first}-${partsStart[1].padLeft(2, '0')}-01 00:00:00.000");
- List<String> partsEnd = endDate.toString().split(" ").first.split("-");
- DateTime lastDate =
- DateTime.parse("${partsEnd.first}-${(int.parse(partsEnd[1]) + 1).toString().padLeft(2, '0')}-01 23:00:00.000").subtract(const Duration(days: 1));
- double width = MediaQuery.of(context).size.width - (2 * widget.padding!);
- List<DateTime?> dates = [];
- DateTime referenceDate = firstDate;
- while (referenceDate.isBefore(lastDate)) {
- List<String> referenceParts = referenceDate.toString().split(" ");
- DateTime newDate = DateTime.parse("${referenceParts.first} 12:00:00.000");
- dates.add(newDate);
- referenceDate = newDate.add(const Duration(days: 1));
- }
- if (firstDate.year == lastDate.year && firstDate.month == lastDate.month) {
- return Padding(
- padding: EdgeInsets.fromLTRB(widget.padding!, 40.0, widget.padding!, 0.0),
- child: month(dates, width, widget.locale),
- );
- } else {
- List<DateTime?> months = [];
- for (int i = 0; i < dates.length; i++) {
- if (i == 0 || (dates[i]!.month != dates[i - 1]!.month)) {
- months.add(dates[i]);
- }
- }
- months.sort((b, a) => a!.compareTo(b!));
- final index = months.indexWhere((element) => element!.month == widget.selectedDate!.month && element.year == widget.selectedDate!.year);
- _initialPage = index;
- _horizontalScroll = PageController(initialPage: _initialPage);
- double monthHeight = 6 * (width / 7) + 16 + 10 + 10 + 80; // 固定高度,6行的高度加上80额外空间
- return Container(
- height: monthHeight, // 使用固定的高度
- padding: const EdgeInsets.fromLTRB(25, 10.0, 25, 20.0),
- //只是PageView
- child: Stack(
- children: [
- //主题
- PageView.builder(
- physics: const BouncingScrollPhysics(),
- controller: _horizontalScroll,
- reverse: true,
- scrollDirection: Axis.horizontal,
- itemCount: months.length,
- itemBuilder: (context, index) {
- DateTime? date = months[index];
- List<DateTime?> daysOfMonth = [];
- for (var item in dates) {
- if (date!.month == item!.month && date.year == item.year) {
- daysOfMonth.add(item);
- }
- }
- bool isLast = index == 0;
- return Container(
- padding: EdgeInsets.only(bottom: isLast ? 0.0 : 10.0),
- child: month(daysOfMonth, width, widget.locale),
- );
- },
- ),
- //返回按钮
- Positioned(
- top: -11,
- width: MediaQuery.of(context).size.width * 0.88,
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- const MyAssetImage(
- Assets.baseLibCalendarLeftIcon,
- width: 44,
- height: 44,
- ).onTap(() {
- _horizontalScroll.nextPage(
- duration: const Duration(milliseconds: 300),
- curve: Curves.ease,
- );
- }),
- const MyAssetImage(
- Assets.baseLibCalendarRightIcon,
- width: 44,
- height: 44,
- ).onTap(() {
- _horizontalScroll.previousPage(
- duration: const Duration(milliseconds: 300),
- curve: Curves.ease,
- );
- }),
- ],
- ),
- )
- ],
- ),
- );
- }
- }
- //顶部星期的文本数据
- Widget daysOfWeek(double width, String? locale) {
- List daysNames = [];
- for (var day = 12; day <= 18; day++) {
- daysNames.add(DateFormat.E(locale.toString()).format(DateTime.parse('1970-01-$day')));
- }
- return Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- dayName(width / 7, daysNames[0]),
- dayName(width / 7, daysNames[1]),
- dayName(width / 7, daysNames[2]),
- dayName(width / 7, daysNames[3]),
- dayName(width / 7, daysNames[4]),
- dayName(width / 7, daysNames[5]),
- dayName(width / 7, daysNames[6]),
- ],
- );
- }
- //顶部星期的文本控件展示
- Widget dayName(double width, String text) {
- return Container(
- width: width,
- alignment: Alignment.center,
- child: Text(
- text,
- style: const TextStyle(
- fontSize: 13.0,
- fontWeight: FontWeight.w500,
- ),
- overflow: TextOverflow.ellipsis,
- ),
- );
- }
- //当前月份,每一天的布局
- Widget dateInCalendar(DateTime date, bool outOfRange, double width, bool event) {
- bool isSelectedDate = date.toString().split(" ").first == widget.selectedDate.toString().split(" ").first;
- return GestureDetector(
- onTap: () => outOfRange ? null : widget.onDateChange(date),
- child: Container(
- width: width / 7,
- height: width / 7,
- decoration: BoxDecoration(
- shape: BoxShape.circle,
- color: isSelectedDate ? widget.dateSelectedBg : Colors.transparent,
- ),
- alignment: Alignment.center,
- child: Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- const SizedBox(
- height: 5.0,
- ),
- Padding(
- padding: const EdgeInsets.symmetric(vertical: 4.0),
- child: Text(
- DateFormat("dd").format(date),
- style: TextStyle(
- color: outOfRange
- ? isSelectedDate
- ? widget.dateSelectedColor!.withOpacity(0.9)
- : widget.dateColor!.withOpacity(0.4)
- : isSelectedDate
- ? widget.dateSelectedColor
- : widget.dateColor,
- fontWeight: FontWeight.w500,
- fontSize: 13),
- ),
- ),
- event
- ? Icon(
- Icons.bookmark,
- size: 8,
- color: isSelectedDate ? widget.dateSelectedColor : widget.dateSelectedBg,
- )
- : const SizedBox(height: 5.0),
- ],
- ),
- ),
- );
- }
- //单独一个月的Page布局
- Widget month(List dates, double width, String? locale) {
- DateTime first = dates.first;
- // 获取这个月的第一天
- DateTime firstDayOfMonth = DateTime(first.year, first.month, 1);
- // 找到这个月的第一天是星期几
- int firstWeekday = firstDayOfMonth.weekday;
- // 计算需要的前导空格数量
- int leadingDaysCount = (firstWeekday - DateTime.monday + 7) % 7;
- // 只保留当前月份的日期
- List<DateTime?> fullDates = List.from(dates);
- // 在视图中添加用于填充的空日期(如果需要,这里就不填充上个月尾的日期了)
- for (int i = 0; i < leadingDaysCount; i++) {
- fullDates.insert(0, null); // 用null填充前导位置
- }
- return Column(
- mainAxisAlignment: MainAxisAlignment.start,
- children: [
- Text(
- DateFormat.yMMMM(Locale(locale!).toString()).format(first),
- style: TextStyle(fontSize: 18.0, color: widget.dateColor, fontWeight: FontWeight.w500),
- ),
- // 周一到周天的星期文本
- Padding(
- padding: const EdgeInsets.only(top: 30.0),
- child: daysOfWeek(width, widget.locale),
- ),
- // 底部的每月的每一天
- Container(
- padding: const EdgeInsets.only(top: 10.0),
- height: (fullDates.length > 28)
- ? (fullDates.length > 35 ? 6.2 * width / 7 : 5.2 * width / 7)
- : 4 * width / 7,
- width: MediaQuery.of(context).size.width - 2 * widget.padding!,
- child: GridView.builder(
- itemCount: fullDates.length,
- physics: const NeverScrollableScrollPhysics(),
- gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 7),
- itemBuilder: (context, index) {
- DateTime? date = fullDates[index]; // 使用 DateTime? 类型以支持 null
- // 如果 date 为 null,表示该位置为空,返回一个透明的容器
- if (date == null) {
- return Container(
- width: width / 7,
- height: width / 7,
- color: Colors.transparent, // 透明或其他样式
- );
- }
- bool outOfRange = date.isBefore(startDate) || date.isAfter(endDate);
- return dateInCalendar(
- date,
- outOfRange,
- width,
- _events!.contains(date.toString().split(" ").first) && !outOfRange,
- );
- },
- ),
- )
- ],
- );
- }
- }
|