Sfoglia il codice sorgente

星期选择与日历选择的初步完成

liukai 4 giorni fa
parent
commit
279da333f7

+ 56 - 9
packages/cpt_facility/lib/modules/booking/facility_booking_page.dart

@@ -1,11 +1,19 @@
+import 'dart:math';
+
+import 'package:cs_resources/generated/assets.dart';
 import 'package:cs_resources/theme/app_colors_theme.dart';
 import 'package:flutter/material.dart';
 import 'package:auto_route/auto_route.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
 import 'package:router/ext/auto_router_extensions.dart';
+import 'package:shared/utils/date_time_utils.dart';
 import 'package:shared/utils/log_utils.dart';
+import 'package:widgets/ext/ex_widget.dart';
 import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_load_image.dart';
 import 'package:widgets/my_text_view.dart';
+import 'package:widgets/shatter/select_calendar/calendar_bottom_sheet.dart';
 import 'package:widgets/widget_export.dart';
 
 import '../../router/page/facility_page_router.dart';
@@ -39,28 +47,67 @@ class FacilityBookingPage extends HookConsumerWidget {
           mainAxisSize: MainAxisSize.max,
           crossAxisAlignment: CrossAxisAlignment.center,
           children: [
-            MyTextView(
-              "Friday,11 October 2024",
-              textColor: context.appColors.textBlack,
-              fontSize: 17,
-              marginTop: 18,
-              marginBottom: 16,
-              marginLeft: 15,
-              isFontMedium: true,
+            Row(
+              mainAxisSize: MainAxisSize.max,
+              children: [
+                MyTextView(
+                  "${DateTimeUtils.getWeekday(state.selectedDate,languageCode: 'en')}, ${DateTimeUtils.formatDate(state.selectedDate,format: 'dd MMM yyyy')}",
+                  textColor: context.appColors.textBlack,
+                  fontSize: 17,
+                  marginTop: 18,
+                  marginBottom: 16,
+                  marginLeft: 15,
+                  isFontMedium: true,
+                ).expanded(),
+                const MyAssetImage(Assets.facilityConfirmDateIcon, width: 22.5, height: 22.5).marginOnly(right: 10).onTap(() {
+                  //日历日期选择器
+                  _datePickerBottomSheet(context, ref);
+                }, padding: 5),
+              ],
             ),
+
+            //二周的日期选择
             WeeklyCalendar(
               isAutoSelect: false,
-              selectedDate: DateTime.now().add(Duration(days: 1)),
+              selectedDate: state.selectedDate,
               onChangedSelectedDate: (dateTime) {
                 Log.d("onChangedSelectedDate选中 - ${dateTime}}");
+                viewModel.changeSelectedDate(dateTime);
               },
               onChangedPage: (dateTime, state) {
                 Log.d("onChangedPage - ${dateTime} state:${state}");
               },
             ),
+
           ],
         ),
       ),
     );
   }
+
+  //日期日历的选择器,底部弹窗选择
+  void _datePickerBottomSheet(BuildContext context, WidgetRef ref) {
+    final viewModel = ref.watch(facilityBookingViewModelProvider.notifier);
+    final state = ref.watch(facilityBookingViewModelProvider);
+
+    showModalBottomSheet<void>(
+      context: context,
+      isScrollControlled: true,
+      shape: const RoundedRectangleBorder(
+        borderRadius: BorderRadius.only(topLeft: Radius.circular(30.0), topRight: Radius.circular(30.0)),
+      ),
+      builder: (BuildContext context) {
+        return CustomCalendarBottomSheet(
+          firstDate: DateTime.now(),
+          lastDate: DateTime.now().add(const Duration(days: 365)),
+          selectedDate: state.selectedDate,
+          locale: "en",
+          onDateChange: (dateTime) {
+            Navigator.pop(context);
+            viewModel.changeSelectedDate(dateTime);
+          },
+        );
+      },
+    );
+  }
 }

+ 15 - 2
packages/cpt_facility/lib/modules/booking/facility_booking_state.dart

@@ -1,3 +1,16 @@
-class FacilityBookingState{
+class FacilityBookingState {
+  //当前选中的时间日期
+  DateTime selectedDate;
 
-}
+  FacilityBookingState({
+    required this.selectedDate,
+  });
+
+  FacilityBookingState copyWith({
+    DateTime? selectedDate,
+  }) {
+    return FacilityBookingState(
+      selectedDate: selectedDate ?? this.selectedDate,
+    );
+  }
+}

+ 25 - 3
packages/cpt_facility/lib/modules/booking/facility_booking_view_model.dart

@@ -1,13 +1,35 @@
 import 'package:riverpod_annotation/riverpod_annotation.dart';
-
+import 'package:shared/utils/log_utils.dart';
 import 'facility_booking_state.dart';
 
 part 'facility_booking_view_model.g.dart';
 
 @riverpod
-class FacilityBookingViewModel extends _$FacilityBookingViewModel{
+class FacilityBookingViewModel extends _$FacilityBookingViewModel {
   @override
   FacilityBookingState build() {
-    return FacilityBookingState();
+
+    final state = FacilityBookingState(selectedDate: DateTime.now());
+    initListener(state);
+    ref.onDispose(() {
+      onDispose(state);
+    });
+
+    return state;
   }
+
+  //修改选中的时间
+  void changeSelectedDate(DateTime dateTime) {
+    state = state.copyWith(selectedDate: dateTime);
+    Log.d("当前选中的日期:$dateTime");
+  }
+
+  void initListener(FacilityBookingState state) {
+
+  }
+
+  void onDispose(FacilityBookingState state) {
+    Log.d("FacilityBookingViewModel - onDispose");
+  }
+
 }

BIN
packages/cs_resources/assets/base_lib/calendar_left_icon.webp


BIN
packages/cs_resources/assets/base_lib/calendar_right_icon.webp


+ 2 - 0
packages/cs_resources/lib/generated/assets.dart

@@ -16,6 +16,8 @@ class Assets {
   static const String authSmsVerifyImg = 'assets/auth/sms_verify_img.webp';
   static const String authYyHomeSuccess = 'assets/auth/yy_home_success.webp';
   static const String baseLibBlackBack = 'assets/base_lib/black_back.webp';
+  static const String baseLibCalendarLeftIcon = 'assets/base_lib/calendar_left_icon.webp';
+  static const String baseLibCalendarRightIcon = 'assets/base_lib/calendar_right_icon.webp';
   static const String baseLibDialogBlueDeleteIcon = 'assets/base_lib/dialog_blue_delete_icon.webp';
   static const String baseLibDialogDeleteIcon = 'assets/base_lib/dialog_delete_icon.webp';
   static const String baseLibImageAddIcon = 'assets/base_lib/image_add_icon.webp';

+ 49 - 0
packages/cs_widgets/lib/shatter/select_calendar/calendar_bottom_sheet.dart

@@ -0,0 +1,49 @@
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'full_calendar.dart';
+
+class CustomCalendarBottomSheet extends StatelessWidget {
+  final DateTime firstDate;
+  final DateTime? lastDate;
+  final String locale;
+  final DateTime selectedDate;
+  final Function(DateTime) onDateChange;
+
+  const CustomCalendarBottomSheet({
+    Key? key,
+    required this.firstDate,
+    this.lastDate,
+    required this.selectedDate,
+    required this.locale,
+    required this.onDateChange,
+  }) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+
+    return Column(
+      mainAxisSize: MainAxisSize.min,
+      children: [
+        const SizedBox(height: 20),
+        Container(
+          width: 60,
+          height: 6,
+          decoration: BoxDecoration(borderRadius: BorderRadius.circular(3.0), color: const Color(0xFFE0E0E0)),
+        ),
+        const SizedBox(height: 13.0),
+        FullCalendar(
+          startDate: firstDate,
+          endDate: lastDate,
+          selectedDate: selectedDate,
+          padding: 25,
+          locale: locale,
+          dateColor: context.appColors.textBlack,
+          dateSelectedBg: context.appColors.btnBgDefault,
+          dateSelectedColor: Colors.white,
+          events: [],
+          onDateChange: onDateChange,
+        ),
+      ],
+    );
+  }
+}

+ 336 - 0
packages/cs_widgets/lib/shatter/select_calendar/full_calendar.dart

@@ -0,0 +1,336 @@
+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,
+              );
+            },
+          ),
+        )
+      ],
+    );
+  }
+
+}

+ 9 - 4
packages/cs_widgets/lib/shatter/weekly_calendar/calendar_utils.dart

@@ -6,11 +6,16 @@ DateTime subtractDay(DateTime date, int days) {
   return date.subtract(Duration(days: days));
 }
 
+//一周的第一天从哪里开始,Sun 还是 Mon
 DateTime firstDayOfWeek(DateTime date) {
-  if (date.weekday == DateTime.sunday) {
-    return date;
-  }
-  return date.subtract(Duration(days: date.weekday));
+  //返回星期天开始
+  // if (date.weekday == DateTime.sunday) {
+  //   return date;
+  // }
+  // return date.subtract(Duration(days: date.weekday));
+
+  // 从星期一开始
+  return date.subtract(Duration(days: date.weekday - 1));
 }
 
 // ===================================  获取一周的数据  ↓  ===================================

+ 1 - 1
packages/cs_widgets/lib/shatter/weekly_calendar/day_cell.dart

@@ -27,7 +27,7 @@ class DayCell extends StatelessWidget {
       height: 40,
       decoration: BoxDecoration(
         color: _isPastDate(display)
-            ? context.appColors.disEnableGray // 小于今天的日期背景颜色,禁用
+            ? context.appColors.disEnableGray.withOpacity(0.6) // 小于今天的日期背景颜色,禁用
             : _isSelected(display)
                 ? _isToday(display)
                     ? context.appColors.btnBgDefault // 今天的日期背景颜色

+ 54 - 2
packages/cs_widgets/lib/shatter/weekly_calendar/week_page.dart

@@ -1,4 +1,5 @@
 import 'package:flutter/material.dart';
+import 'package:shared/utils/log_utils.dart';
 import 'calendar_utils.dart' show get2Weekdays, addDay, subtractDay, firstDayOfWeek;
 import 'day_table_view.dart';
 
@@ -44,11 +45,63 @@ class _WeekPageState extends State<WeekPage> {
 
   DateTime get selectedDate => widget.selectedDate;
 
+  late PageController _pageController;
+
   @override
   void initState() {
     super.initState();
     _currentPageDate = selectedDate;
     _slectedDate = selectedDate;
+    _pageController = PageController(initialPage: initialPage);
+  }
+
+  @override
+  void didUpdateWidget(covariant WeekPage oldWidget) {
+    super.didUpdateWidget(oldWidget);
+
+    if (widget.selectedDate != oldWidget.selectedDate) {
+      setState(() {
+        _currentPageDate = widget.selectedDate;
+        _slectedDate = widget.selectedDate;
+      });
+
+      // 计算要滚动到的页面索引
+      int targetPageIndex = _calculateTargetPageIndex(widget.selectedDate);
+
+      // 使用 WidgetsBinding 以确保在构建完成后再跳转页面
+      WidgetsBinding.instance.addPostFrameCallback((_) {
+        // 检查目标页面索引是否在有效范围内
+        Log.d("targetPageIndex:$targetPageIndex pageCounts.length:${pageCounts.length}");
+        if (targetPageIndex >= 0 && targetPageIndex < pageCounts.length) {
+          _pageController.jumpToPage(targetPageIndex);
+        } else {
+          //超过了索引就刷新数据源?
+          Log.d("超过了索引就刷新数据源");
+        }
+      });
+    }
+  }
+
+  // 计算要滚动到的页面索引
+  int _calculateTargetPageIndex(DateTime date) {
+    // 获取当前周的第一天
+    DateTime currentFirstDayOfWeek = firstDayOfWeek(now);
+    // 获取选中日期的周的第一天
+    DateTime targetFirstDayOfWeek = firstDayOfWeek(date);
+
+    // 计算当前日期与目标日期之间的天数差
+    int differenceInDays = targetFirstDayOfWeek.difference(currentFirstDayOfWeek).inDays + 1;
+
+    // 计算目标页面索引
+    // 每页14天
+    int targetPageIndex = (differenceInDays / 14).floor(); // 用floor确保向下取整
+
+    // 确保目标索引在有效范围内
+    // targetPageIndex = (targetPageIndex + pageCounts.length) % pageCounts.length;
+
+    Log.d("differenceInDays:$differenceInDays targetPageIndex:$targetPageIndex");
+
+    return targetPageIndex;
   }
 
   @override
@@ -68,14 +121,13 @@ class _WeekPageState extends State<WeekPage> {
 
   //PageView的方式展示不同的星期,每周每一个Page
   Widget pageViewBuilder() {
-    final PageController pageController = PageController(initialPage: initialPage);
     return PageView.builder(
       itemBuilder: (context, index) {
         final idx = _getIndex(index);
         return dayTable(idx);
       },
       onPageChanged: onPageChanged,
-      controller: pageController,
+      controller: _pageController,
     );
   }
 

+ 19 - 7
packages/cs_widgets/lib/shatter/weekly_calendar/weekly_calendar.dart

@@ -1,5 +1,6 @@
 import 'package:flutter/material.dart';
 import 'package:intl/date_symbol_data_local.dart';
+import 'package:shared/utils/log_utils.dart';
 import 'package:widgets/shatter/weekly_calendar/week_page.dart';
 
 import 'calendar_utils.dart' show getWeekdays;
@@ -15,7 +16,6 @@ class WeeklyCalendar extends StatefulWidget {
     this.selectedDate,
   });
 
-
   final DateTime? selectedDate;
   final bool isAutoSelect;
   final Function(DateTime)? onChangedSelectedDate;
@@ -27,18 +27,27 @@ class WeeklyCalendar extends StatefulWidget {
 
 class _WeeklyCalendarState extends State<WeeklyCalendar> {
   final DateTime now = DateTime.now();
-  late DateTime selectedDate;
   DateTime currentPageDate = DateTime.now();
-
+  DateTime? selectedDate;
 
   @override
   void initState() {
-    initializeDateFormatting("en");  //日期国际化指定
+    initializeDateFormatting("en"); //日期国际化指定
     super.initState();
     selectedDate = widget.selectedDate ?? DateTime.now();
   }
 
   @override
+  void didUpdateWidget(covariant WeeklyCalendar oldWidget) {
+    super.didUpdateWidget(oldWidget);
+    if (widget.selectedDate != oldWidget.selectedDate) {
+      setState(() {
+        selectedDate = widget.selectedDate;
+      });
+    }
+  }
+
+  @override
   Widget build(BuildContext context) {
     return Column(
       mainAxisSize: MainAxisSize.min,
@@ -50,9 +59,12 @@ class _WeeklyCalendarState extends State<WeeklyCalendar> {
 
         //底部是每个星期的Days的PageView布局
         WeekPage(
-          selectedDate: selectedDate,  //默认选中的日期,如果没有填写默认是今天
-          now: now,                    //今天的日期
-          isAutoSelect: widget.isAutoSelect,  //翻页是否自动选中
+          selectedDate: selectedDate ?? DateTime.now(),
+          //默认选中的日期,如果没有填写默认是今天
+          now: now,
+          //今天的日期
+          isAutoSelect: widget.isAutoSelect,
+          //翻页是否自动选中
           onChangedPage: (date, state) {
             setState(() {
               currentPageDate = date;