From c3d55d225b0d354f2557969b11157d770d72b7c4 Mon Sep 17 00:00:00 2001 From: Daisy <> Date: Fri, 26 Jan 2024 14:28:02 +0800 Subject: [PATCH] =?UTF-8?q?1=EF=BC=8C=E6=96=B0=E5=A2=9E=E9=97=A8=E9=94=81?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E6=97=A5=E5=8E=86=E7=BB=84=E4=BB=B6=E6=BA=90?= =?UTF-8?q?=E6=96=87=E4=BB=B6=202=EF=BC=8C=E4=BC=98=E5=8C=96=E9=97=A8?= =?UTF-8?q?=E9=94=81=E6=97=A5=E5=BF=97UI=E5=8F=82=E8=80=83=E7=B1=B3?= =?UTF-8?q?=E5=AE=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../doorLockLog/doorLockLog_logic.dart | 8 +- .../doorLockLog/doorLockLog_page.dart | 21 +- .../doorLockLog/doorLockLog_state.dart | 2 +- .../flutter_advanced_calendar.dart | 4 + .../advancedCalendar/src/controller.dart | 15 + .../tools/advancedCalendar/src/date_box.dart | 94 ++++ .../advancedCalendar/src/datetime_util.dart | 56 +++ .../tools/advancedCalendar/src/handlebar.dart | 39 ++ .../tools/advancedCalendar/src/header.dart | 63 +++ .../advancedCalendar/src/month_view.dart | 62 +++ .../advancedCalendar/src/month_view_bean.dart | 40 ++ .../tools/advancedCalendar/src/week_days.dart | 36 ++ .../tools/advancedCalendar/src/week_view.dart | 137 ++++++ .../tools/advancedCalendar/src/widget.dart | 420 ++++++++++++++++++ star_lock/pubspec.yaml | 2 +- 15 files changed, 986 insertions(+), 13 deletions(-) create mode 100644 star_lock/lib/tools/advancedCalendar/flutter_advanced_calendar.dart create mode 100644 star_lock/lib/tools/advancedCalendar/src/controller.dart create mode 100644 star_lock/lib/tools/advancedCalendar/src/date_box.dart create mode 100644 star_lock/lib/tools/advancedCalendar/src/datetime_util.dart create mode 100644 star_lock/lib/tools/advancedCalendar/src/handlebar.dart create mode 100644 star_lock/lib/tools/advancedCalendar/src/header.dart create mode 100644 star_lock/lib/tools/advancedCalendar/src/month_view.dart create mode 100644 star_lock/lib/tools/advancedCalendar/src/month_view_bean.dart create mode 100644 star_lock/lib/tools/advancedCalendar/src/week_days.dart create mode 100644 star_lock/lib/tools/advancedCalendar/src/week_view.dart create mode 100644 star_lock/lib/tools/advancedCalendar/src/widget.dart diff --git a/star_lock/lib/main/lockDetail/doorLockLog/doorLockLog_logic.dart b/star_lock/lib/main/lockDetail/doorLockLog/doorLockLog_logic.dart index ceb96444..12910890 100644 --- a/star_lock/lib/main/lockDetail/doorLockLog/doorLockLog_logic.dart +++ b/star_lock/lib/main/lockDetail/doorLockLog/doorLockLog_logic.dart @@ -271,9 +271,9 @@ class DoorLockLogLogic extends BaseGetXController { super.onClose(); // 获取是否是演示模式 演示模式不获取接口 - var isDemoMode = await Storage.getBool(ifIsDemoModeOrNot); - if (isDemoMode == false) { - _replySubscription.cancel(); - } + // var isDemoMode = await Storage.getBool(ifIsDemoModeOrNot); + // if (isDemoMode == false) { + // _replySubscription.cancel(); + // } } } diff --git a/star_lock/lib/main/lockDetail/doorLockLog/doorLockLog_page.dart b/star_lock/lib/main/lockDetail/doorLockLog/doorLockLog_page.dart index 3c166332..6d3ea284 100644 --- a/star_lock/lib/main/lockDetail/doorLockLog/doorLockLog_page.dart +++ b/star_lock/lib/main/lockDetail/doorLockLog/doorLockLog_page.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; -import 'package:flutter_advanced_calendar/flutter_advanced_calendar.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import 'package:star_lock/main/lockDetail/doorLockLog/doorLockLog_logic.dart'; +import 'package:star_lock/tools/advancedCalendar/src/widget.dart'; import 'package:timelines/timelines.dart'; import '../../../app_settings/app_colors.dart'; @@ -64,10 +64,17 @@ class _DoorLockLogPageState extends State { endIndent: 30.w, ), eventDropDownWidget(), - SizedBox( - height: 20.h, - ), - Expanded(child: timeLineView()), + Expanded(child: timeLineView()) + // Expanded( + // // 添加 Expanded 来让 ListView 占据剩余的空间 + // child: ListView.builder( + // itemBuilder: (context, index) { + // // 返回你想要在 ListView 中显示的小部件 + // return timeLineView(); + // }, + // itemCount: 5, // 替换成你的列表项数量 + // ), + // ), ], ), ); @@ -123,7 +130,7 @@ class _DoorLockLogPageState extends State { value: state.dropdownValue.value, icon: const Icon(Icons.arrow_drop_down), iconSize: 40, - // elevation: 12, + elevation: 12, style: TextStyle( fontSize: 26.sp, color: Colors.black, @@ -149,7 +156,7 @@ class _DoorLockLogPageState extends State { //时间轴组件 Widget timeLineView() { return Container( - margin: EdgeInsets.only(left: 20.w, right: 20.w, bottom: 20.h), + margin: EdgeInsets.only(left: 20.w, right: 20.w, bottom: 20.h, top: 20.h), //给contain设置一个10像素的圆角 decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16.w)), diff --git a/star_lock/lib/main/lockDetail/doorLockLog/doorLockLog_state.dart b/star_lock/lib/main/lockDetail/doorLockLog/doorLockLog_state.dart index c37bece3..c89b39ce 100644 --- a/star_lock/lib/main/lockDetail/doorLockLog/doorLockLog_state.dart +++ b/star_lock/lib/main/lockDetail/doorLockLog/doorLockLog_state.dart @@ -1,5 +1,5 @@ -import 'package:flutter_advanced_calendar/flutter_advanced_calendar.dart'; import 'package:get/get.dart'; +import 'package:star_lock/tools/advancedCalendar/src/controller.dart'; import '../../lockMian/entity/lockListInfo_entity.dart'; import '../electronicKey/electronicKeyDetail/keyOperationRecordEntity.dart'; diff --git a/star_lock/lib/tools/advancedCalendar/flutter_advanced_calendar.dart b/star_lock/lib/tools/advancedCalendar/flutter_advanced_calendar.dart new file mode 100644 index 00000000..d7dff31b --- /dev/null +++ b/star_lock/lib/tools/advancedCalendar/flutter_advanced_calendar.dart @@ -0,0 +1,4 @@ +library flutter_advanced_calendar; + +export 'src/controller.dart'; +export 'src/widget.dart'; diff --git a/star_lock/lib/tools/advancedCalendar/src/controller.dart b/star_lock/lib/tools/advancedCalendar/src/controller.dart new file mode 100644 index 00000000..399ec83a --- /dev/null +++ b/star_lock/lib/tools/advancedCalendar/src/controller.dart @@ -0,0 +1,15 @@ +import 'package:flutter/widgets.dart'; + +import 'datetime_util.dart'; + +/// Advanced Calendar controller that manage selection date state. +class AdvancedCalendarController extends ValueNotifier { + /// Generates controller with custom date selected. + AdvancedCalendarController(DateTime value) : super(value); + + /// Generates controller with today date selected. + AdvancedCalendarController.today() : this(DateTime.now().toZeroTime()); + + @override + set value(DateTime newValue) => super.value = newValue.toZeroTime(); +} diff --git a/star_lock/lib/tools/advancedCalendar/src/date_box.dart b/star_lock/lib/tools/advancedCalendar/src/date_box.dart new file mode 100644 index 00000000..185b5d69 --- /dev/null +++ b/star_lock/lib/tools/advancedCalendar/src/date_box.dart @@ -0,0 +1,94 @@ +part of 'widget.dart'; + +/// Unit of calendar. +class DateBox extends StatelessWidget { + const DateBox({ + Key? key, + required this.child, + this.color, + this.width = 24.0, + this.height = 24.0, + this.borderRadius = const BorderRadius.all(Radius.circular(8.0)), + this.onPressed, + this.showDot = false, + this.isSelected = false, + this.isToday = false, + this.hasEvent = false, + }) : super(key: key); + + /// Child widget. + final Widget child; + + /// Background color. + final Color? color; + + /// Widget width. + final double width; + + /// Widget height. + final double height; + + /// Container border radius. + final BorderRadius borderRadius; + + /// Pressed callback function. + final VoidCallback? onPressed; + + /// Show DateBox event in container. + final bool showDot; + + /// DateBox is today. + final bool isToday; + + /// DateBox selection. + final bool isSelected; + + /// Show event in DateBox. + final bool hasEvent; + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + return UnconstrainedBox( + alignment: Alignment.center, + child: InkResponse( + onTap: onPressed, + radius: 16.0, + borderRadius: borderRadius, + highlightShape: BoxShape.rectangle, + child: AnimatedContainer( + duration: const Duration(milliseconds: 150), + width: width, + height: height, + alignment: Alignment.center, + decoration: BoxDecoration( + color: isSelected + ? theme.primaryColor + : isToday + ? theme.highlightColor + : null, + borderRadius: borderRadius, + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + child, + if (showDot && hasEvent) + Container( + margin: const EdgeInsets.all(2.0), + height: 4, + width: 4, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: isSelected + ? theme.colorScheme.onPrimary + : theme.colorScheme.secondary, + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/star_lock/lib/tools/advancedCalendar/src/datetime_util.dart b/star_lock/lib/tools/advancedCalendar/src/datetime_util.dart new file mode 100644 index 00000000..ce4e2cd9 --- /dev/null +++ b/star_lock/lib/tools/advancedCalendar/src/datetime_util.dart @@ -0,0 +1,56 @@ +extension DateTimeUtil on DateTime { + /// Generate a new DateTime instance with a zero time. + DateTime toZeroTime() => DateTime.utc(year, month, day, 12); + + int findWeekIndex(List dates) { + return dates.indexWhere(isAtSameMomentAs) ~/ 7; + } + + /// Calculates first week date (Sunday) from this date. + DateTime firstDayOfWeek({int? startWeekDay}) { + final utcDate = DateTime.utc(year, month, day, 12); + if (startWeekDay != null && startWeekDay < 7) { + return utcDate.subtract(Duration(days: utcDate.weekday - startWeekDay)); + } + return utcDate.subtract(Duration(days: utcDate.weekday % 7)); + } + + /// Generates 7 dates according to this date. + /// (Supposed that this date is result of [firstDayOfWeek]) + List weekDates() { + return List.generate( + 7, + (index) => add(Duration(days: index)), + growable: false, + ); + } + + /// Generates list of list with [DateTime] + /// according to [date] and [weeksAmount]. + /// gives the beginning of the day of the week [startWeekDay] + List> generateWeeks(int weeksAmount, {int? startWeekDay}) { + final firstViewDate = firstDayOfWeek(startWeekDay: startWeekDay).subtract( + Duration( + days: (weeksAmount ~/ 2) * 7, + ), + ); + + return List.generate( + weeksAmount, + (weekIndex) { + final firstDateOfNextWeek = firstViewDate.add( + Duration( + days: weekIndex * 7, + ), + ); + + return firstDateOfNextWeek.weekDates(); + }, + growable: false, + ); + } + + bool isSameDate(DateTime other) { + return year == other.year && month == other.month && day == other.day; + } +} diff --git a/star_lock/lib/tools/advancedCalendar/src/handlebar.dart b/star_lock/lib/tools/advancedCalendar/src/handlebar.dart new file mode 100644 index 00000000..20154beb --- /dev/null +++ b/star_lock/lib/tools/advancedCalendar/src/handlebar.dart @@ -0,0 +1,39 @@ +part of 'widget.dart'; + +class HandleBar extends StatelessWidget { + const HandleBar({ + Key? key, + this.decoration, + this.margin = const EdgeInsets.only( + top: 8.0, + ), + this.onPressed, + }) : super(key: key); + + final BoxDecoration? decoration; + final EdgeInsetsGeometry margin; + final VoidCallback? onPressed; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onPressed, + behavior: HitTestBehavior.translucent, + child: Container( + margin: margin, + alignment: Alignment.center, + child: FractionallySizedBox( + widthFactor: 0.1, + child: Container( + height: 4.0, + decoration: decoration ?? + BoxDecoration( + color: Theme.of(context).dividerColor, + borderRadius: BorderRadius.circular(2.0), + ), + ), + ), + ), + ); + } +} diff --git a/star_lock/lib/tools/advancedCalendar/src/header.dart b/star_lock/lib/tools/advancedCalendar/src/header.dart new file mode 100644 index 00000000..bdc50a97 --- /dev/null +++ b/star_lock/lib/tools/advancedCalendar/src/header.dart @@ -0,0 +1,63 @@ +part of 'widget.dart'; + +class Header extends StatelessWidget { + const Header({ + Key? key, + required this.monthDate, + this.margin = const EdgeInsets.only( + left: 16.0, + right: 8.0, + top: 4.0, + bottom: 4.0, + ), + this.onPressed, + this.dateStyle, + this.todayStyle, + }) : super(key: key); + + static final _dateFormatter = DateFormat().add_yMMMM(); + // static final _dateFormatter = DateFormat('MM月dd日', 'zh_CN'); + final DateTime monthDate; + final EdgeInsetsGeometry margin; + final VoidCallback? onPressed; + final TextStyle? dateStyle; + final TextStyle? todayStyle; + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + // initializeDateFormatting('zh_CN', null); + + return Container( + margin: margin, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + // Text( + // _dateFormatter.format(monthDate), + // style: dateStyle ?? theme.textTheme.titleMedium, + // ), + InkWell( + onTap: onPressed, + borderRadius: const BorderRadius.all( + Radius.circular(4.0), + ), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 4.0, + ), + child: Text( + '今天', + style: TextStyle( + color: Colors.black, + fontSize: 22.sp, + fontWeight: FontWeight.w600), + ), + ), + ), + ], + ), + ); + } +} diff --git a/star_lock/lib/tools/advancedCalendar/src/month_view.dart b/star_lock/lib/tools/advancedCalendar/src/month_view.dart new file mode 100644 index 00000000..fd1a0598 --- /dev/null +++ b/star_lock/lib/tools/advancedCalendar/src/month_view.dart @@ -0,0 +1,62 @@ +part of 'widget.dart'; + +class MonthView extends StatelessWidget { + const MonthView({ + Key? key, + required this.monthView, + required this.todayDate, + required this.selectedDate, + required this.weekLineHeight, + required this.weeksAmount, + required this.innerDot, + this.onChanged, + this.events, + required this.keepLineSize, + this.textStyle, + }) : super(key: key); + + final ViewRange monthView; + final DateTime? todayDate; + final DateTime selectedDate; + final double weekLineHeight; + final int weeksAmount; + final ValueChanged? onChanged; + final List? events; + final bool innerDot; + final bool keepLineSize; + final TextStyle? textStyle; + + @override + Widget build(BuildContext context) { + final index = selectedDate.findWeekIndex(monthView.dates); + final offset = index / (weeksAmount - 1) * 2 - 1.0; + + return OverflowBox( + alignment: Alignment(0, offset), + minHeight: weekLineHeight, + maxHeight: weekLineHeight * weeksAmount, + child: Column( + mainAxisSize: MainAxisSize.min, + children: List.generate( + 6, + (weekIndex) { + final weekStart = weekIndex * 7; + + return WeekView( + innerDot: innerDot, + dates: monthView.dates.sublist(weekStart, weekStart + 7), + selectedDate: selectedDate, + highlightMonth: monthView.firstDay.month, + lineHeight: weekLineHeight, + onChanged: onChanged, + events: events, + keepLineSize: keepLineSize, + textStyle: textStyle, + ); + }, + growable: false, + ), + ), + ); + } +} diff --git a/star_lock/lib/tools/advancedCalendar/src/month_view_bean.dart b/star_lock/lib/tools/advancedCalendar/src/month_view_bean.dart new file mode 100644 index 00000000..fef38b42 --- /dev/null +++ b/star_lock/lib/tools/advancedCalendar/src/month_view_bean.dart @@ -0,0 +1,40 @@ +part of 'widget.dart'; + +class ViewRange { + const ViewRange._(this.firstDay, this.dates); + + /// Creates custom filled [ViewRange] instance. + const ViewRange.custom( + DateTime firstDay, + List dates, + ) : this._(firstDay, dates); + + /// Generates [ViewRange] instance based on [date], + /// number of [month] and [weeksAmount]. + /// gives the beginning of the day of the week [startWeekDay] + factory ViewRange.generateDates( + DateTime date, + int month, + int weeksAmount, { + int? startWeekDay, + }) { + final firstMonthDate = DateTime.utc(date.year, month, 1); + final firstViewDate = + firstMonthDate.firstDayOfWeek(startWeekDay: startWeekDay); + + return ViewRange._( + firstMonthDate, + List.generate( + weeksAmount * 7, + (index) => firstViewDate.add(Duration(days: index)), + growable: false, + ), + ); + } + + /// Month view index. + final DateTime firstDay; + + /// Month view dates. + final List dates; +} diff --git a/star_lock/lib/tools/advancedCalendar/src/week_days.dart b/star_lock/lib/tools/advancedCalendar/src/week_days.dart new file mode 100644 index 00000000..e1f8ea87 --- /dev/null +++ b/star_lock/lib/tools/advancedCalendar/src/week_days.dart @@ -0,0 +1,36 @@ +part of 'widget.dart'; + +/// Week day names line. +class WeekDays extends StatelessWidget { + const WeekDays({ + Key? key, + this.weekNames = const ['日', '一', '二', '三', '四', '五', '六'], + this.style, + required this.keepLineSize, + }) : assert(weekNames.length == 7, '`weekNames` must have length 7'), + super(key: key); + + /// Week day names. + final List weekNames; + + /// Text style. + final TextStyle? style; + + final bool keepLineSize; + + @override + Widget build(BuildContext context) { + return DefaultTextStyle( + style: style!, + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: List.generate(weekNames.length, (index) { + return DateBox( + child: Text(weekNames[index]), + ); + }), + ), + ); + } +} diff --git a/star_lock/lib/tools/advancedCalendar/src/week_view.dart b/star_lock/lib/tools/advancedCalendar/src/week_view.dart new file mode 100644 index 00000000..725d04e3 --- /dev/null +++ b/star_lock/lib/tools/advancedCalendar/src/week_view.dart @@ -0,0 +1,137 @@ +part of 'widget.dart'; + +class WeekView extends StatelessWidget { + WeekView({ + Key? key, + required this.dates, + required this.selectedDate, + required this.lineHeight, + this.highlightMonth, + this.onChanged, + this.events, + required this.innerDot, + required this.keepLineSize, + this.textStyle, + }) : super(key: key); + + final DateTime todayDate = DateTime.now().toZeroTime(); + final List dates; + final double lineHeight; + final int? highlightMonth; + final DateTime selectedDate; + final ValueChanged? onChanged; + final List? events; + final bool innerDot; + final bool keepLineSize; + final TextStyle? textStyle; + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + return SizedBox( + height: lineHeight, + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.center, + children: List.generate( + 7, + (dayIndex) { + final date = dates[dayIndex]; + final isToday = date.isAtSameMomentAs(todayDate); + final isSelected = date.isAtSameMomentAs(selectedDate); + final isHighlight = highlightMonth == date.month; + + final hasEvent = + events!.indexWhere((element) => element.isSameDate(date)); + + if (keepLineSize) { + return InkResponse( + onTap: onChanged != null ? () => onChanged!(date) : null, + child: Container( + height: 36, + width: 36, + decoration: BoxDecoration( + color: isSelected + ? theme.primaryColor + : isToday + ? theme.highlightColor + : null, + borderRadius: BorderRadius.circular(18), + shape: BoxShape.rectangle, + ), + child: Column( + children: [ + Text( + '${date.day}', + style: textStyle?.copyWith( + color: isSelected || isToday + ? theme.colorScheme.onPrimary + : isHighlight || highlightMonth == null + ? null + : theme.disabledColor, + fontWeight: + isSelected && textStyle?.fontWeight != null + ? FontWeight + .values[textStyle!.fontWeight!.index + 2] + : textStyle?.fontWeight, + ), + ), + if (!hasEvent.isNegative) + Container( + height: 4, + width: 4, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(50), + color: isSelected + ? theme.colorScheme.onPrimary + : theme.colorScheme.secondary, + ), + ) + ], + ), + ), + ); + } + + return Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + DateBox( + width: innerDot ? 32 : 24, + height: innerDot ? 32 : 24, + showDot: innerDot, + onPressed: onChanged != null ? () => onChanged!(date) : null, + isSelected: isSelected, + isToday: isToday, + hasEvent: !hasEvent.isNegative, + child: Text( + '${date.day}', + maxLines: 1, + style: TextStyle( + color: isSelected || isToday + ? theme.colorScheme.onPrimary + : isHighlight || highlightMonth == null + ? null + : theme.disabledColor, + ), + ), + ), + if (!innerDot && !hasEvent.isNegative) + Container( + height: 6, + width: 6, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(50), + color: theme.primaryColor, + ), + ) + ], + ); + }, + growable: false, + ), + ), + ); + } +} diff --git a/star_lock/lib/tools/advancedCalendar/src/widget.dart b/star_lock/lib/tools/advancedCalendar/src/widget.dart new file mode 100644 index 00000000..0df0da71 --- /dev/null +++ b/star_lock/lib/tools/advancedCalendar/src/widget.dart @@ -0,0 +1,420 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:intl/date_symbol_data_local.dart'; +import 'package:intl/intl.dart'; + +import 'controller.dart'; +import 'datetime_util.dart'; + +part 'date_box.dart'; +part 'handlebar.dart'; +part 'header.dart'; +part 'month_view.dart'; +part 'month_view_bean.dart'; +part 'week_days.dart'; +part 'week_view.dart'; + +/// Advanced Calendar widget. +class AdvancedCalendar extends StatefulWidget { + const AdvancedCalendar({ + Key? key, + this.controller, + this.startWeekDay, + this.events, + this.weekLineHeight = 32.0, + this.preloadMonthViewAmount = 13, + this.preloadWeekViewAmount = 21, + this.weeksInMonthViewAmount = 6, + this.todayStyle, + this.headerStyle, + this.onHorizontalDrag, + this.innerDot = false, + this.keepLineSize = false, + this.calendarTextStyle, + }) : assert( + keepLineSize && innerDot || + innerDot && !keepLineSize || + !innerDot && !keepLineSize, + 'keepLineSize should be used only when innerDot is true', + ), + super(key: key); + + /// Calendar selection date controller. + final AdvancedCalendarController? controller; + + /// Executes on horizontal calendar swipe. Allows to load additional dates. + final Function(DateTime)? onHorizontalDrag; + + /// Height of week line. + final double weekLineHeight; + + /// Amount of months in month view to preload. + final int preloadMonthViewAmount; + + /// Amount of weeks in week view to preload. + final int preloadWeekViewAmount; + + /// Weeks lines amount in month view. + final int weeksInMonthViewAmount; + + /// List of points for the week and month + final List? events; + + /// The first day of the week starts[0-6] + final int? startWeekDay; + + /// Style of headers date + final TextStyle? headerStyle; + + /// Style of Today button + final TextStyle? todayStyle; + + /// Show DateBox event in container. + final bool innerDot; + + /// Keeps consistent line size for dates + /// Can't be used without innerDot + final bool keepLineSize; + + /// Text style for dates in calendar + final TextStyle? calendarTextStyle; + + @override + _AdvancedCalendarState createState() => _AdvancedCalendarState(); +} + +class _AdvancedCalendarState extends State + with SingleTickerProviderStateMixin { + late ValueNotifier _monthViewCurrentPage; + late AnimationController _animationController; + late AdvancedCalendarController _controller; + late double _animationValue; + late List _monthRangeList; + late List> _weekRangeList; + + PageController? _monthPageController; + PageController? _weekPageController; + Offset? _captureOffset; + DateTime? _todayDate; + List? _weekNames; + + @override + void initState() { + super.initState(); + + final monthPageIndex = widget.preloadMonthViewAmount ~/ 2; + + _monthViewCurrentPage = ValueNotifier(monthPageIndex); + + _monthPageController = PageController( + initialPage: monthPageIndex, + ); + + final weekPageIndex = widget.preloadWeekViewAmount ~/ 2; + + _weekPageController = PageController( + initialPage: weekPageIndex, + ); + + _animationController = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 300), + value: 0, + ); + + _animationValue = _animationController.value; + + _controller = widget.controller ?? AdvancedCalendarController.today(); + _todayDate = _controller.value; + + _monthRangeList = List.generate( + widget.preloadMonthViewAmount, + (index) => ViewRange.generateDates( + _todayDate!, + _todayDate!.month + (index - _monthPageController!.initialPage), + widget.weeksInMonthViewAmount, + startWeekDay: widget.startWeekDay, + ), + ); + + _weekRangeList = _controller.value.generateWeeks( + widget.preloadWeekViewAmount, + startWeekDay: widget.startWeekDay, + ); + _controller.addListener(() { + _weekRangeList = _controller.value.generateWeeks( + widget.preloadWeekViewAmount, + startWeekDay: widget.startWeekDay, + ); + _weekPageController!.jumpToPage(widget.preloadWeekViewAmount ~/ 2); + }); + if (widget.startWeekDay != null && widget.startWeekDay! < 7) { + final time = _controller.value.subtract( + Duration(days: _controller.value.weekday - widget.startWeekDay!), + ); + final list = List.generate( + 8, + (index) => time.add(Duration(days: index * 1)), + ).toList(); + // _weekNames = List.generate(7, (index) { + // return DateFormat("EEEE").format(list[index]).split('').first; + // } + // ); + //by DaisyWu 修改源文件为中文环境下 周一到周日 + _weekNames = List.generate(7, (index) { + String fullWeekName = + DateFormat.E('zh_CN').format(list[index]); // 获取星期的完整形式 + return fullWeekName + .substring(fullWeekName.length - 1); // 获取最后一个字符,即星期的简称 + }); + } + } + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + return Material( + color: Colors.transparent, + child: DefaultTextStyle.merge( + style: theme.textTheme.bodyMedium, + child: GestureDetector( + onVerticalDragStart: (details) { + _captureOffset = details.globalPosition; + }, + onVerticalDragUpdate: (details) { + final moveOffset = details.globalPosition; + final diffY = moveOffset.dy - _captureOffset!.dy; + + _animationController.value = + _animationValue + diffY / (widget.weekLineHeight * 5); + }, + onVerticalDragEnd: (details) => _handleFinishDrag(), + onVerticalDragCancel: _handleFinishDrag, + child: Container( + color: Colors.transparent, + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ValueListenableBuilder( + valueListenable: _monthViewCurrentPage, + builder: (_, value, __) { + return Header( + monthDate: + _monthRangeList[_monthViewCurrentPage.value].firstDay, + onPressed: _handleTodayPressed, + dateStyle: widget.headerStyle, + todayStyle: widget.todayStyle, + ); + }, + ), + WeekDays( + style: theme.textTheme.bodyLarge?.copyWith( + color: Colors.black, + ), + keepLineSize: widget.keepLineSize, + weekNames: _weekNames != null + ? _weekNames! + : const ['日', '一', '二', '三', '四', '五', '六'], + ), + AnimatedBuilder( + animation: _animationController, + builder: (_, __) { + final height = Tween( + begin: widget.weekLineHeight, + end: + widget.weekLineHeight * widget.weeksInMonthViewAmount, + ).transform(_animationController.value); + return SizedBox( + height: height, + child: ValueListenableBuilder( + valueListenable: _controller, + builder: (_, selectedDate, __) { + return Stack( + alignment: Alignment.center, + children: [ + IgnorePointer( + ignoring: _animationController.value == 0.0, + child: Opacity( + opacity: Tween( + begin: 0.0, + end: 1.0, + ).evaluate(_animationController), + child: PageView.builder( + onPageChanged: (pageIndex) { + if (widget.onHorizontalDrag != null) { + widget.onHorizontalDrag!( + _monthRangeList[pageIndex].firstDay, + ); + } + _monthViewCurrentPage.value = pageIndex; + }, + controller: _monthPageController, + physics: _animationController.value == 1.0 + ? const AlwaysScrollableScrollPhysics() + : const NeverScrollableScrollPhysics(), + itemCount: _monthRangeList.length, + itemBuilder: (_, pageIndex) { + return MonthView( + innerDot: widget.innerDot, + monthView: _monthRangeList[pageIndex], + todayDate: _todayDate, + selectedDate: selectedDate, + weekLineHeight: widget.weekLineHeight, + weeksAmount: + widget.weeksInMonthViewAmount, + onChanged: _handleDateChanged, + events: widget.events, + keepLineSize: widget.keepLineSize, + textStyle: widget.calendarTextStyle, + ); + }, + ), + ), + ), + ValueListenableBuilder( + valueListenable: _monthViewCurrentPage, + builder: (_, pageIndex, __) { + final index = selectedDate.findWeekIndex( + _monthRangeList[_monthViewCurrentPage.value] + .dates, + ); + final offset = index / + (widget.weeksInMonthViewAmount - 1) * + 2 - + 1.0; + return Align( + alignment: Alignment(0.0, offset), + child: IgnorePointer( + ignoring: + _animationController.value == 1.0, + child: Opacity( + opacity: Tween( + begin: 1.0, + end: 0.0, + ).evaluate(_animationController), + child: SizedBox( + height: widget.weekLineHeight, + child: PageView.builder( + onPageChanged: (indexPage) { + final pageIndex = + _monthRangeList.indexWhere( + (index) => + index.firstDay.month == + _weekRangeList[indexPage] + .first + .month, + ); + + if (widget.onHorizontalDrag != + null) { + widget.onHorizontalDrag!( + _monthRangeList[pageIndex] + .firstDay, + ); + } + _monthViewCurrentPage.value = + pageIndex; + }, + controller: _weekPageController, + itemCount: _weekRangeList.length, + physics: _closeMonthScroll(), + itemBuilder: (context, index) { + return WeekView( + innerDot: widget.innerDot, + dates: _weekRangeList[index], + selectedDate: selectedDate, + lineHeight: + widget.weekLineHeight, + onChanged: + _handleWeekDateChanged, + events: widget.events, + keepLineSize: + widget.keepLineSize, + textStyle: + widget.calendarTextStyle, + ); + }, + ), + ), + ), + ), + ); + }, + ), + ], + ); + }, + ), + ); + }, + ), + // HandleBar( + // onPressed: () async { + // await _animationController.forward(); + // _animationValue = 1.0; + // }, + // ), + ], + ), + ), + ), + ), + ); + } + + @override + void dispose() { + _animationController.dispose(); + _monthPageController!.dispose(); + _monthViewCurrentPage.dispose(); + + if (widget.controller == null) { + _controller.dispose(); + } + + super.dispose(); + } + + void _handleWeekDateChanged(DateTime date) { + _handleDateChanged(date); + + _monthViewCurrentPage.value = _monthRangeList + .lastIndexWhere((monthRange) => monthRange.dates.contains(date)); + } + + void _handleDateChanged(DateTime date) { + _controller.value = date; + } + + void _handleFinishDrag() async { + _captureOffset = null; + + if (_animationController.value > 0.5) { + await _animationController.forward(); + _animationValue = 1.0; + } else { + await _animationController.reverse(); + _animationValue = 0.0; + } + } + + void _handleTodayPressed() { + _controller.value = DateTime.now().toZeroTime(); + + _monthPageController!.jumpToPage(widget.preloadMonthViewAmount ~/ 2); + _weekPageController!.jumpToPage(widget.preloadWeekViewAmount ~/ 2); + } + + ScrollPhysics _closeMonthScroll() { + if ((_monthViewCurrentPage.value == + (widget.preloadMonthViewAmount ~/ 2) + 3 || + _monthViewCurrentPage.value == + (widget.preloadMonthViewAmount ~/ 2) - 3)) { + return const NeverScrollableScrollPhysics(); + } else { + return const AlwaysScrollableScrollPhysics(); + } + } +} diff --git a/star_lock/pubspec.yaml b/star_lock/pubspec.yaml index c42eeee6..a07283ec 100644 --- a/star_lock/pubspec.yaml +++ b/star_lock/pubspec.yaml @@ -134,7 +134,7 @@ dependencies: flutter_voice_processor: ^1.1.0 #监听网络连接状态 connectivity_plus: ^5.0.2 - flutter_advanced_calendar: ^1.4.1 + #flutter_advanced_calendar: ^1.4.1 timelines: ^0.1.0 #侧滑删除 flutter_slidable: ^3.0.1