import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import 'package:intl/intl.dart'; import 'package:star_lock/tools/eventBusEventManage.dart'; import 'package:star_lock/translations/current_locale_tool.dart'; import '../../../app_settings/app_settings.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 = 24, //预加载的月视图 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 int monthPageIndex = widget.preloadMonthViewAmount ~/ 2; _monthViewCurrentPage = ValueNotifier(monthPageIndex); _monthPageController = PageController( initialPage: monthPageIndex, ); final int 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, (int 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 DateTime time = _controller.value.subtract( Duration(days: _controller.value.weekday - widget.startWeekDay!), ); final List list = List.generate( 8, (int index) => time.add(Duration(days: index * 1)), ).toList(); // _weekNames = List.generate(7, (index) { // return DateFormat("EEEE").format(list[index]).split('').first; // } // ); final currentLocaleString = CurrentLocaleTool.getCurrentLocaleString(); if (currentLocaleString == 'zh_CN') { //by DaisyWu 修改源文件为中文环境下 周一到周日 _weekNames = List.generate(7, (int index) { String fullWeekName = DateFormat.E('zh_CN').format(list[index]); // 获取星期的完整形式 return fullWeekName .substring(fullWeekName.length - 1); // 获取最后一个字符,即星期的简称 }); } } } @override Widget build(BuildContext context) { final ThemeData theme = Theme.of(context); return Material( color: Colors.transparent, child: DefaultTextStyle.merge( style: theme.textTheme.bodyMedium, child: GestureDetector( onVerticalDragStart: (DragStartDetails details) { _captureOffset = details.globalPosition; }, onVerticalDragUpdate: (DragUpdateDetails details) { final Offset moveOffset = details.globalPosition; final double diffY = moveOffset.dy - _captureOffset!.dy; _animationController.value = _animationValue + diffY / (widget.weekLineHeight * 5); }, onVerticalDragEnd: (DragEndDetails 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: (_, int 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! : [ '简写周日'.tr, '简写周一'.tr, '简写周二'.tr, '简写周三'.tr, '简写周四'.tr, '简写周五'.tr, '简写周六'.tr ], ), AnimatedBuilder( animation: _animationController, builder: (_, __) { final double height = Tween( begin: widget.weekLineHeight, end: widget.weekLineHeight * widget.weeksInMonthViewAmount, ).transform(_animationController.value); return SizedBox( height: height, child: ValueListenableBuilder( valueListenable: _controller, builder: (_, selectedDate, __) { // AppLog.log('****selectedDate: $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: (int pageIndex) { if (widget.onHorizontalDrag != null) { widget.onHorizontalDrag!( _monthRangeList[pageIndex].firstDay, ); } _monthViewCurrentPage.value = pageIndex; AppLog.log('调用onPageChanged'); }, controller: _monthPageController, physics: _animationController.value == 1.0 ? const AlwaysScrollableScrollPhysics() : const NeverScrollableScrollPhysics(), itemCount: _monthRangeList.length, itemBuilder: (_, int 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: (_, int pageIndex, __) { final int index = selectedDate.findWeekIndex( _monthRangeList[_monthViewCurrentPage.value] .dates, ); final double 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: (int indexPage) { final int pageIndex = _monthRangeList.indexWhere( (ViewRange 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: (BuildContext context, int 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( (ViewRange monthRange) => monthRange.dates.contains(date)); } void _handleDateChanged(DateTime date) { _controller.value = date; AppLog.log('点击日期了'); eventBus.fire(DoorLockLogListRefreshUI(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(); } } }