Browse Source

Merge branch 'master' of http://git.ius.plus/gavin.chen/calendar_view_demo

# Conflicts:
#	lib/calendar_page/month_calendar/schedule_list.dart
#	lib/popup_layer/popup_layer.dart
#	lib/popup_layer/popup_layer_controller.dart
bakamaka.guan 2 years ago
parent
commit
a96b415f17

+ 1 - 2
lib/calendar_page/calendar_left_panel.dart

@@ -40,9 +40,8 @@ class _CalendarLeftPanelState extends State<CalendarLeftPanel> {
 
   /// 构建顶部【搜索日程、添加日程】
   Widget _buildPanelTop() {
-    return Container(
+    return SizedBox(
       height: 70,
-      // color: const Color.fromARGB(90, 222, 59, 255),
       child: Container(
         padding: const EdgeInsets.symmetric(horizontal: 10),
         child: Row(

+ 1 - 3
lib/calendar_page/calendar_main_panel.dart

@@ -2,7 +2,6 @@ import 'package:calendar_view/calendar_controller/controller.dart';
 import 'package:calendar_view/calendar_page/calendar_views/calendar_month_view.dart';
 import 'package:calendar_view/calendar_page/my_calendar/my_calendar.dart';
 import 'package:calendar_view/popup_layer/popup_layer.dart';
-import 'package:calendar_view/popup_layer/popup_layer_controller.dart';
 import 'package:flutter/material.dart';
 import 'package:get/get.dart';
 
@@ -25,7 +24,6 @@ class _CalendarMainPanelState extends State<CalendarMainPanel> {
   @override
   void initState() {
     super.initState();
-    Get.put<PopupLayerController>(PopupLayerController());
     _isToday = calendarController.isToday;
     _currYearMonthStr = calendarController.currentYearMonth;
     calendarController.onDaysListChange.addListener(_onDaysListChange);
@@ -86,7 +84,7 @@ class _CalendarMainPanelState extends State<CalendarMainPanel> {
 
   /// 构建顶部【今天、年月、视图切换器、更多】
   Widget _buildMainPanelTop() {
-    return Container(
+    return SizedBox(
       height: 70,
       child: Container(
         padding: const EdgeInsets.symmetric(horizontal: 10),

+ 6 - 2
lib/calendar_page/calendar_page.dart

@@ -1,6 +1,8 @@
 import 'package:calendar_view/calendar_controller/controller.dart';
 import 'package:calendar_view/calendar_page/calendar_left_panel.dart';
 import 'package:calendar_view/calendar_page/calendar_main_panel.dart';
+import 'package:calendar_view/popup_layer/global_click_listener.dart';
+import 'package:calendar_view/popup_layer/popup_layer_controller.dart';
 import 'package:flutter/material.dart';
 import 'package:get/get.dart';
 
@@ -12,6 +14,8 @@ class CalendarPage extends StatefulWidget {
 }
 
 class _CalendarPageState extends State<CalendarPage> {
+  final PopupLayerController _popupLayerController =
+      Get.put<PopupLayerController>(PopupLayerController());
   @override
   void initState() {
     super.initState();
@@ -20,8 +24,8 @@ class _CalendarPageState extends State<CalendarPage> {
 
   @override
   Widget build(BuildContext context) {
-    return Container(
-      // color: Color.fromARGB(90, 255, 235, 59),
+    return GlobalClickListener(
+      controller: _popupLayerController,
       child: Row(
         children: const <Widget>[
           CalendarLeftPanel(),

+ 3 - 3
lib/calendar_page/mini_calendar/mini_calendar.dart

@@ -109,7 +109,7 @@ class _MiniCalendarState extends State<MiniCalendar> {
 
   Widget _buildMiniCalendarBody() {
     return Container(
-      padding: EdgeInsets.symmetric(horizontal: 5),
+      padding: const EdgeInsets.symmetric(horizontal: 5),
       child: Column(
         children: <Widget>[
           _buildMiniCalendarWeekTitle(),
@@ -124,7 +124,7 @@ class _MiniCalendarState extends State<MiniCalendar> {
 
   /// 构建「日、一、二、三、四、五、六」的标题
   Widget _buildMiniCalendarWeekTitle() {
-    return Container(
+    return SizedBox(
       height: 30,
       child: Row(
         mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -170,7 +170,7 @@ class _MiniCalendarState extends State<MiniCalendar> {
 
   /// 构建每一行(周)
   Widget _buildMiniCalendarDayRow(List<MiniViewDayStructure> sevenDays) {
-    return Container(
+    return SizedBox(
       height: 32,
       child: Row(
         mainAxisAlignment: MainAxisAlignment.spaceBetween,

+ 4 - 7
lib/calendar_page/month_calendar/month_calendar.dart

@@ -66,7 +66,7 @@ class _MonthCalendarState extends State<MonthCalendar> {
 
   /// 构建「日、一、二、三、四、五、六」的标题
   Widget _buildMonthCalendarWeekTitle() {
-    return Container(
+    return SizedBox(
       height: 30,
       child: Row(
         mainAxisAlignment: MainAxisAlignment.spaceAround,
@@ -115,11 +115,9 @@ class _MonthCalendarState extends State<MonthCalendar> {
   /// 构建每一行(周)
   Widget _buildMonthCalendarDayRow(List<MonthViewDayStructure> sevenDays) {
     return Expanded(
-      child: Container(
-        child: Row(
-          mainAxisAlignment: MainAxisAlignment.spaceAround,
-          children: sevenDays.map(_buildEachDay).toList(),
-        ),
+      child: Row(
+        mainAxisAlignment: MainAxisAlignment.spaceAround,
+        children: sevenDays.map(_buildEachDay).toList(),
       ),
     );
   }
@@ -153,7 +151,6 @@ class _MonthCalendarState extends State<MonthCalendar> {
     final size = MediaQuery.of(context).size;
     if (size.height == _viewHeight &&
         calendarController.calendarLines == _calendarLines) return;
-    print('计算最大日程行数');
     _viewHeight = size.height;
     _calendarLines = calendarController.calendarLines;
     final gridRows = calendarController.calendarLines;

+ 2 - 2
lib/calendar_page/month_calendar/month_calendar_item.dart

@@ -32,7 +32,7 @@ class _MyWidgetState extends State<MonthDayItem> {
   );
   late TextStyle _dayTextStyle;
   late TextStyle _lunarDayTextStyle;
-  GlobalKey _monthGridKey = GlobalKey();
+  final GlobalKey _monthGridKey = GlobalKey();
 
   /// TODO[Gavin]: i18n
   get displayStr => _dayData.isToday ? '今' : _dayData.date.day.toString();
@@ -160,7 +160,7 @@ class _MyWidgetState extends State<MonthDayItem> {
             );
           },
           onTapShowSchedule: (value) {
-            popupLayerController.handlePopupMoreSchedule(
+            popupLayerController.handlePopupSchedule(
               value,
               widget.dayData,
             );

+ 47 - 75
lib/calendar_page/month_calendar/more_schedule_popup.dart

@@ -1,34 +1,63 @@
+import 'package:calendar_view/calendar_controller/controller.dart';
+import 'package:calendar_view/calendar_page/month_calendar/schedule_item.dart';
 import 'package:calendar_view/utils/calendar_util.dart';
 import 'package:calendar_view/utils/chinese_calendar_utils.dart';
+import 'package:calendar_view/utils/event_type.dart';
 import 'package:flutter/material.dart';
+import 'package:get/get.dart';
 
 class MoreSchedulePopup extends StatefulWidget {
   const MoreSchedulePopup({
     Key? key,
     required this.scheduleData,
+    required this.onTapShowSchedule,
   }) : super(key: key);
 
   /// 日程数据
   final MonthViewDayStructure scheduleData;
 
+  /// 日程框详情
+  final ValueCallback? onTapShowSchedule;
+
   @override
   MoreSchedulePopupState createState() => MoreSchedulePopupState();
 }
 
 class MoreSchedulePopupState extends State<MoreSchedulePopup> {
   static const double _itemHeight = 28;
+  List<Schedule> _scheduleDataList = [];
+  CalendarController calendarController = Get.find<CalendarController>();
+
+  @override
+  void initState() {
+    super.initState();
+    _scheduleDataList =
+        calendarController.scheduleListFilter(widget.scheduleData.scheduleList);
+  }
+
+  @override
+  void didUpdateWidget(covariant MoreSchedulePopup oldWidget) {
+    super.didUpdateWidget(oldWidget);
+    _scheduleDataList =
+        calendarController.scheduleListFilter(widget.scheduleData.scheduleList);
+  }
 
   @override
   Widget build(BuildContext context) {
-    return Container(
-      padding: const EdgeInsets.all(10),
-      child: Column(
-        mainAxisSize: MainAxisSize.min,
-        children: [
-          _buildScheduleHead(),
-          _buildScheduleTypeList(),
-        ],
-      ),
+    return ListView(
+      shrinkWrap: true,
+      children: [
+        Container(
+          padding: const EdgeInsets.all(10),
+          child: Column(
+            mainAxisSize: MainAxisSize.min,
+            children: [
+              _buildScheduleHead(),
+              _buildScheduleList(),
+            ],
+          ),
+        ),
+      ],
     );
   }
 
@@ -56,85 +85,28 @@ class MoreSchedulePopupState extends State<MoreSchedulePopup> {
     );
   }
 
-  Widget _buildScheduleTypeList() {
+  Widget _buildScheduleList() {
     return ListView.builder(
       shrinkWrap: true,
       addAutomaticKeepAlives: false,
       addRepaintBoundaries: true,
       itemExtent: _itemHeight,
-      itemCount: widget.scheduleData.scheduleList.length,
+      itemCount: _scheduleDataList.length,
       itemBuilder: (context, index) {
-        return _buildScheduleTypeItem(index);
+        return _buildScheduleItem(index);
       },
     );
   }
 
-  Widget _buildScheduleTypeItem(int scheduleIndex) {
-    ScheduleType scheduleType =
-        widget.scheduleData.scheduleList[scheduleIndex].type;
-    Schedule schedule = widget.scheduleData.scheduleList[scheduleIndex];
-    return Material(
-      color: Colors.transparent,
-      child: InkWell(
-        customBorder: RoundedRectangleBorder(
-          borderRadius: BorderRadius.circular(5),
-        ),
-        onTap: () {
-          ScaffoldMessenger.of(context).showSnackBar(
-            SnackBar(
-              backgroundColor: scheduleType.color,
-              duration: const Duration(milliseconds: 500),
-              content: Text('点击了${scheduleType.typeName}'),
-            ),
-          );
-        },
-        hoverColor: const Color.fromARGB(255, 227, 228, 228),
-        child: Container(
-          height: _itemHeight,
-          padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 0),
-          decoration: BoxDecoration(
-            // color: scheduleType.isSelected
-            //     ? scheduleType.color.withOpacity(0.2)
-            //     : Colors.transparent,
-            borderRadius: BorderRadius.circular(5),
-          ),
-          child: Row(
-            children: [
-              Container(
-                width: 6,
-                height: 6,
-                decoration: BoxDecoration(
-                  color: scheduleType.color,
-                  borderRadius: BorderRadius.circular(5),
-                ),
-              ),
-              const SizedBox(width: 5),
-              Expanded(
-                child: Text(
-                  breakWord(schedule.title),
-                  style: const TextStyle(fontSize: 12),
-                  overflow: TextOverflow.ellipsis,
-                ),
-              ),
-            ],
-          ),
-        ),
-      ),
+  Widget _buildScheduleItem(int scheduleIndex) {
+    Schedule schedule = _scheduleDataList[scheduleIndex];
+    return SchedualItem(
+      scheduleData: schedule,
+      itemHeight: _itemHeight,
+      onTapShowSchedule: widget.onTapShowSchedule,
     );
   }
 
-  static String breakWord(String text) {
-    if (text.isEmpty) {
-      return text;
-    }
-    String breakWord = ' ';
-    for (var element in text.runes) {
-      breakWord += String.fromCharCode(element);
-      breakWord += '\u200B';
-    }
-    return breakWord;
-  }
-
   String _getLunarDay(DateTime date) {
     CalendarInfo lunarDate = CalendarUtils.getInfo(date);
     return lunarDate.term ?? lunarDate.lunarDayName ?? '';

+ 132 - 0
lib/calendar_page/month_calendar/schedule_item.dart

@@ -0,0 +1,132 @@
+import 'package:calendar_view/popup_layer/popup_layer_controller.dart';
+import 'package:calendar_view/utils/calendar_util.dart';
+import 'package:calendar_view/utils/event_type.dart';
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+
+class SchedualItem extends StatefulWidget {
+  const SchedualItem({
+    super.key,
+    required this.scheduleData,
+    required this.itemHeight,
+    this.onTapShowSchedule,
+  });
+  final Schedule scheduleData;
+  final double itemHeight;
+  final ValueCallback? onTapShowSchedule;
+
+  @override
+  State<SchedualItem> createState() => _SchedualItemState();
+}
+
+class _SchedualItemState extends State<SchedualItem> {
+  ScheduleType get scheduleType => widget.scheduleData.type;
+  Schedule get schedule => widget.scheduleData;
+  bool _isSelected = false; // 是否选中状态
+  GlobalKey _scheduleListKey = GlobalKey();
+
+  final PopupLayerController _popupLayerController =
+      Get.find<PopupLayerController>();
+
+  @override
+  void initState() {
+    super.initState();
+
+    _popupLayerController.onCloseSchedulePopup.addListener(_onGlobalClick);
+  }
+
+  @override
+  void dispose() {
+    super.dispose();
+    _popupLayerController.onCloseSchedulePopup.removeListener(_onGlobalClick);
+  }
+
+  void _onGlobalClick(e, bool isClose) {
+    if (!mounted) return;
+    print(isClose);
+    setState(() {
+      _isSelected = isClose;
+    });
+  }
+
+  ////TODO:[Gavin] 监听一个popupController的关闭事件,关闭时将选中状态置为false
+  @override
+  Widget build(BuildContext context) {
+    return Material(
+      color: Colors.transparent,
+      child: InkWell(
+        key: _scheduleListKey,
+        customBorder: RoundedRectangleBorder(
+          borderRadius: BorderRadius.circular(5),
+        ),
+        onTap: () {
+          setState(() {
+            _isSelected = !_isSelected;
+          });
+          widget.onTapShowSchedule!(_scheduleListKey);
+        },
+        hoverColor: const Color.fromARGB(255, 227, 228, 228),
+        child: Container(
+          height: widget.itemHeight,
+          padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 0),
+          decoration: BoxDecoration(
+            color: _isSelected ? scheduleType.color : Colors.transparent,
+            borderRadius: BorderRadius.circular(5),
+          ),
+          child: Row(
+            children: [
+              Container(
+                width: 6,
+                height: 6,
+                decoration: BoxDecoration(
+                  color: scheduleType.color,
+                  borderRadius: BorderRadius.circular(5),
+                ),
+              ),
+              const SizedBox(width: 5),
+              Text(
+                _getStartTime(schedule.startTime),
+                style: TextStyle(
+                  fontSize: 12,
+                  color: _isSelected
+                      ? Colors.white
+                      : const Color.fromARGB(167, 0, 0, 0),
+                ),
+                overflow: TextOverflow.ellipsis,
+              ),
+              Expanded(
+                child: Text(
+                  breakWord(schedule.title),
+                  style: TextStyle(
+                      fontSize: 12,
+                      color: _isSelected ? Colors.white : Colors.black),
+                  overflow: TextOverflow.ellipsis,
+                ),
+              ),
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+
+  static String breakWord(String text) {
+    if (text.isEmpty) {
+      return text;
+    }
+    String breakWord = ' ';
+    for (var element in text.runes) {
+      breakWord += String.fromCharCode(element);
+      breakWord += '\u200B';
+    }
+    return breakWord;
+  }
+
+  static String _getStartTime(DateTime? startTime) {
+    if (startTime == null) {
+      // TODO: i18n
+      return '全天';
+    }
+    return '${startTime.hour}:${startTime.minute}';
+  }
+}

+ 10 - 59
lib/calendar_page/month_calendar/schedule_list.dart

@@ -1,8 +1,7 @@
-import 'package:calendar_view/popup_layer/popup_layer_controller.dart';
+import 'package:calendar_view/calendar_page/month_calendar/schedule_item.dart';
 import 'package:calendar_view/utils/calendar_util.dart';
 import 'package:calendar_view/utils/event_type.dart';
 import 'package:flutter/material.dart';
-import 'package:get/get.dart';
 
 class ScheduleList extends StatefulWidget {
   const ScheduleList({
@@ -35,7 +34,6 @@ class ScheduleList extends StatefulWidget {
 class ScheduleListState extends State<ScheduleList> {
   /// 容纳的数量
   int _itemCount = 0;
-  PopupLayerController popupLayerController = Get.find<PopupLayerController>();
   @override
   void initState() {
     super.initState();
@@ -73,69 +71,22 @@ class ScheduleListState extends State<ScheduleList> {
   }
 
   Widget _buildScheduleTypeItem(int scheduleIndex) {
-    /// TODO: 判断如果构建的是展示的最后一项,但不是列表的最后一项,则需要添加点击事件,并显示“还有${widget.scheduleDataList.length - _itemCount + 1}项...”
     if (scheduleIndex == _itemCount - 1) {
       if (_itemCount < widget.scheduleDataList.length) {
         return _buildMoreItemButton(
-            widget.scheduleDataList.length - _itemCount + 1);
+          widget.scheduleDataList.length - _itemCount + 1,
+        );
       }
     }
-    GlobalKey _scheduleListKey = GlobalKey();
-
-    ScheduleType scheduleType = widget.scheduleDataList[scheduleIndex].type;
     Schedule schedule = widget.scheduleDataList[scheduleIndex];
-    return Material(
-      color: Colors.transparent,
-      child: InkWell(
-        key: _scheduleListKey,
-        customBorder: RoundedRectangleBorder(
-          borderRadius: BorderRadius.circular(5),
-        ),
-        onTap: () {
-          // ScaffoldMessenger.of(context).showSnackBar(
-          //   SnackBar(
-          //     backgroundColor: scheduleType.color,
-          //     duration: const Duration(milliseconds: 500),
-          //     content: Text('点击了${scheduleType.typeName}'),
-          //   ),
-          // );
-          widget.onTapShowSchedule.call(_scheduleListKey);
-        },
-        hoverColor: const Color.fromARGB(255, 227, 228, 228),
-        child: Container(
-          height: widget.itemHeight,
-          padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 0),
-          decoration: BoxDecoration(
-            // color: scheduleType.isSelected
-            //     ? scheduleType.color.withOpacity(0.2)
-            //     : Colors.transparent,
-            borderRadius: BorderRadius.circular(5),
-          ),
-          child: Row(
-            children: [
-              Container(
-                width: 6,
-                height: 6,
-                decoration: BoxDecoration(
-                  color: scheduleType.color,
-                  borderRadius: BorderRadius.circular(5),
-                ),
-              ),
-              const SizedBox(width: 5),
-              Expanded(
-                child: Text(
-                  breakWord(schedule.title),
-                  style: const TextStyle(fontSize: 12),
-                  overflow: TextOverflow.ellipsis,
-                ),
-              ),
-            ],
-          ),
-        ),
-      ),
+    return SchedualItem(
+      scheduleData: schedule,
+      itemHeight: widget.itemHeight,
+      onTapShowSchedule: widget.onTapShowSchedule,
     );
   }
 
+  /// 构建【显示更多】按钮
   Widget _buildMoreItemButton(int moreNum) {
     return Material(
       color: Colors.transparent,
@@ -184,10 +135,10 @@ class ScheduleListState extends State<ScheduleList> {
       return text;
     }
     String breakWord = ' ';
-    text.runes.forEach((element) {
+    for (var element in text.runes) {
       breakWord += String.fromCharCode(element);
       breakWord += '\u200B';
-    });
+    }
     return breakWord;
   }
 }

+ 273 - 0
lib/calendar_page/month_calendar/schedule_popup.dart

@@ -0,0 +1,273 @@
+import 'package:calendar_view/calendar_controller/controller.dart';
+import 'package:calendar_view/utils/calendar_util.dart';
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+
+class SchedulePopup extends StatefulWidget {
+  const SchedulePopup({
+    Key? key,
+    required this.scheduleData,
+  }) : super(key: key);
+
+  /// 日程数据
+  final MonthViewDayStructure scheduleData;
+
+  @override
+  SchedulePopupState createState() => SchedulePopupState();
+}
+
+class SchedulePopupState extends State<SchedulePopup> {
+  static const double _itemHeight = 28;
+  List<Schedule> _scheduleDataList = [];
+  CalendarController calendarController = Get.find<CalendarController>();
+  @override
+  void initState() {
+    super.initState();
+    _scheduleDataList =
+        calendarController.scheduleListFilter(widget.scheduleData.scheduleList);
+  }
+
+  @override
+  void didUpdateWidget(covariant SchedulePopup oldWidget) {
+    super.didUpdateWidget(oldWidget);
+    _scheduleDataList =
+        calendarController.scheduleListFilter(widget.scheduleData.scheduleList);
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return ListView(
+      shrinkWrap: true,
+      children: [
+        Container(
+          padding: const EdgeInsets.all(10),
+          child: _buildScheduleList(),
+        ),
+      ],
+    );
+  }
+
+  /// 构建icon
+  Widget _buildIcon({
+    required IconData icon,
+    required double size,
+  }) {
+    return Icon(
+      icon,
+      size: size,
+      color: const Color.fromRGBO(116, 118, 119, 1),
+    );
+  }
+
+  /// 构建icon按钮
+  Widget _buildIconButton({
+    required VoidCallback onPressed,
+    required IconData icon,
+  }) {
+    return IconButton(
+      onPressed: () => onPressed,
+      icon: _buildIcon(
+        icon: icon,
+        size: 20,
+      ),
+    );
+  }
+
+  Map<int, String> weekMap = {
+    1: '星期一',
+    2: '星期二',
+    3: '星期三',
+    4: '星期四',
+    5: '星期五',
+    6: '星期六',
+    7: '星期天',
+  };
+
+  /// 日程卡片视图列表
+  Widget _buildScheduleList() {
+    final currentDate = widget.scheduleData.date;
+    return Column(
+      mainAxisSize: MainAxisSize.min,
+      children: [
+        _buildScheduleHeadActionBar(),
+        _buildScheduleLayoutItem(
+          iconWidget: const SizedBox(
+            width: 18,
+            child: CircleAvatar(
+              radius: 6,
+              backgroundColor: Colors.red,
+            ),
+          ),
+          scheduleItemWidget: const Text(
+            '杏聆荟每日站会',
+          ),
+        ),
+        _buildScheduleLayoutItem(
+          iconWidget: _buildIcon(
+            icon: Icons.av_timer_rounded,
+            size: 18,
+          ),
+          scheduleItemWidget: Text(
+            currentDate.month.toString() +
+                '月' +
+                currentDate.day.toString() +
+                '日 周 ' +
+                weekMap[currentDate.weekday]! +
+                ' 10:00-10:30',
+          ),
+        ),
+        _buildScheduleLayoutItem(
+          iconWidget: _buildIcon(
+            icon: Icons.personal_injury_rounded,
+            size: 18,
+          ),
+          scheduleItemWidget: const Text('组织人'),
+        ),
+        _buildScheduleLayoutItem(
+          iconWidget: _buildIcon(
+            icon: Icons.people_alt_outlined,
+            size: 18,
+          ),
+          scheduleItemWidget: Row(
+            mainAxisSize: MainAxisSize.max,
+            children: const [
+              Text(
+                '邀请7人,5人接受',
+              ),
+              Expanded(child: SizedBox()),
+              Text(
+                '查看全部',
+                style: TextStyle(
+                  fontSize: 12,
+                  color: Colors.grey,
+                ),
+              ),
+            ],
+          ),
+        ),
+        Row(
+          mainAxisAlignment: MainAxisAlignment.start,
+          children: [
+            const SizedBox(
+              width: 30,
+            ),
+            _buildHeadPortraitItem(),
+            _buildHeadPortraitItem(),
+            _buildHeadPortraitItem(),
+            _buildHeadPortraitItem(),
+            _buildHeadPortraitItem(),
+          ],
+        ),
+        SizedBox(
+          height: 15,
+        ),
+        Divider(),
+        _buildSchedulebottomActionBar(),
+      ],
+    );
+  }
+
+  /// 日程卡片顶部操作栏
+  Widget _buildScheduleHeadActionBar() {
+    return SizedBox(
+      height: 35,
+      child: Row(
+        mainAxisAlignment: MainAxisAlignment.end,
+        children: [
+          _buildIconButton(
+            onPressed: () {},
+            icon: Icons.delete_outlined,
+          ),
+          _buildIconButton(
+            onPressed: () {},
+            icon: Icons.launch_rounded,
+          ),
+          _buildIconButton(
+            onPressed: () {},
+            icon: Icons.close,
+          ),
+        ],
+      ),
+    );
+  }
+
+  /// 日程卡片底部操作栏
+  Widget _buildSchedulebottomActionBar() {
+    return SizedBox(
+      height: 35,
+      child: Row(
+        children: [
+          Container(
+            margin: EdgeInsets.only(
+              left: 5,
+            ),
+            child: Text('查看详情'),
+          ),
+          Expanded(
+            child: Row(
+              mainAxisAlignment: MainAxisAlignment.end,
+              children: [
+                _buildIconButton(
+                  onPressed: () {},
+                  icon: Icons.check_circle_outlined,
+                ),
+              ],
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+
+  /// 日程卡片单行布局
+  Widget _buildScheduleLayoutItem({
+    required Widget iconWidget,
+    required Widget scheduleItemWidget,
+  }) {
+    return Container(
+      padding: const EdgeInsets.only(
+        left: 10,
+        bottom: 10,
+        right: 10,
+      ),
+      child: Row(
+        children: [
+          Container(
+            margin: const EdgeInsets.only(
+              left: 5,
+              right: 10,
+            ),
+            child: iconWidget,
+          ),
+          Expanded(child: scheduleItemWidget),
+        ],
+      ),
+    );
+  }
+
+  Widget _buildHeadPortraitItem() {
+    return Container(
+      width: 55,
+      height: 55,
+      child: Column(
+        mainAxisAlignment: MainAxisAlignment.center,
+        children: [
+          Container(
+            width: 30,
+            height: 30,
+            decoration: BoxDecoration(
+              borderRadius: BorderRadius.circular(
+                4,
+              ),
+              color: Colors.red,
+            ),
+          ),
+          Text(
+            '11222qwwqqwww2',
+            overflow: TextOverflow.ellipsis,
+          ),
+        ],
+      ),
+    );
+  }
+}

+ 6 - 8
lib/calendar_page/my_calendar/my_calendar.dart

@@ -39,14 +39,12 @@ class _MyCalendarState extends State<MyCalendar> {
 
   @override
   Widget build(BuildContext context) {
-    return Container(
-      child: ListView(
-        shrinkWrap: true,
-        children: <Widget>[
-          _buildMyCalendarTitle(),
-          if (_isExpanded) ..._scheduleTypeList.map(_buildMyCalendarItem),
-        ],
-      ),
+    return ListView(
+      shrinkWrap: true,
+      children: <Widget>[
+        _buildMyCalendarTitle(),
+        if (_isExpanded) ..._scheduleTypeList.map(_buildMyCalendarItem),
+      ],
     );
   }
 

+ 27 - 0
lib/popup_layer/global_click_listener.dart

@@ -0,0 +1,27 @@
+import 'package:calendar_view/popup_layer/popup_layer_controller.dart';
+import 'package:flutter/material.dart';
+
+/// 全局事件监听器,用于搭配popup_layer使用
+/// 用于监听全局点击事件,当点击到popup_layer以外的区域时,关闭popup_layer
+class GlobalClickListener extends StatelessWidget {
+  const GlobalClickListener(
+      {Key? key, required this.child, required this.controller})
+      : super(key: key);
+
+  final Widget child;
+  final PopupLayerController controller;
+
+  @override
+  Widget build(BuildContext context) {
+    return Listener(
+      behavior: HitTestBehavior.translucent,
+      onPointerDown: (event) {
+        controller.onGlobalClick.emit(this, event);
+      },
+      onPointerUp: (event) {
+        controller.onGlobalClick.emit(this, event);
+      },
+      child: child,
+    );
+  }
+}

+ 89 - 6
lib/popup_layer/popup_layer.dart

@@ -1,6 +1,7 @@
 import 'dart:math';
 
 import 'package:calendar_view/calendar_page/month_calendar/more_schedule_popup.dart';
+import 'package:calendar_view/calendar_page/month_calendar/schedule_popup.dart';
 import 'package:calendar_view/popup_layer/popup_layer_controller.dart';
 import 'package:flutter/material.dart';
 import 'package:get/get.dart';
@@ -14,7 +15,8 @@ class PopupLayer extends StatefulWidget {
 
 class _PopupLayerState extends State<PopupLayer> {
   PopupLayerController popupLayerController = Get.find<PopupLayerController>();
-  bool _isShowPopup = false;
+  bool _isShowMoreSchedulePopup = false;
+  bool _isShowSchedulePopup = false;
   GlobalKey moreSchedulePopupKey = GlobalKey();
   @override
   void initState() {
@@ -34,7 +36,7 @@ class _PopupLayerState extends State<PopupLayer> {
   /// 收到显示更多日程的事件
   void _onPopupMoreSchedule(e, GlobalKey key) {
     setState(() {
-      _isShowPopup = true;
+      _isShowMoreSchedulePopup = true;
       moreSchedulePopupKey = key;
     });
   }
@@ -42,23 +44,38 @@ class _PopupLayerState extends State<PopupLayer> {
   /// 收到显示日程的事件
   void _onPopupSchedule(e, GlobalKey key) {
     setState(() {
+      _isShowSchedulePopup = true;
       moreSchedulePopupKey = key;
     });
   }
 
   @override
   Widget build(BuildContext context) {
-    return _isShowPopup
-        ? Listener(
+    return Stack(
+      children: [
+        if (_isShowMoreSchedulePopup)
+          Listener(
             behavior: HitTestBehavior.translucent,
             onPointerUp: (event) {
               setState(() {
-                _isShowPopup = false;
+                _isShowMoreSchedulePopup = false;
               });
             },
             child: _buildMoreSchedulePopup(moreSchedulePopupKey),
+          ),
+        if (_isShowSchedulePopup)
+          Listener(
+            behavior: HitTestBehavior.translucent,
+            onPointerUp: (event) {
+              setState(() {
+                _isShowSchedulePopup = false;
+              });
+              popupLayerController.onCloseSchedulePopup.emit(this, false);
+            },
+            child: _buildSchedulePopup(moreSchedulePopupKey),
           )
-        : Container();
+      ],
+    );
   }
 
   /// 只做容器而无需负责内容
@@ -85,6 +102,42 @@ class _PopupLayerState extends State<PopupLayer> {
           width: max(triggerSize.width, 220),
           child: MoreSchedulePopup(
             scheduleData: popupLayerController.currMoreScheduleData,
+            onTapShowSchedule: (value) {
+              popupLayerController.handlePopupSchedule(
+                value,
+                popupLayerController.currMoreScheduleData,
+              );
+            },
+          ),
+        ),
+      ],
+    );
+  }
+
+  /// 只做容器而无需负责内容
+  /// [trigger] 触发器的 key
+  Widget _buildSchedulePopup(GlobalKey trigger) {
+    final Size triggerSize = getWidgetSize(trigger);
+    final Offset triggerOffset = getWidgetOffset(trigger);
+    return Flow(
+      delegate: ScheduleAutoAlignFlowDelegate(triggerOffset, triggerSize),
+      children: [
+        Container(
+          decoration: BoxDecoration(
+            color: Colors.white,
+            borderRadius: BorderRadius.circular(8),
+            border: Border.all(color: Colors.black12, width: 1),
+            boxShadow: const [
+              BoxShadow(
+                color: Colors.black12,
+                offset: Offset(3, 3),
+                blurRadius: 25,
+              ),
+            ],
+          ),
+          width: 360,
+          child: SchedulePopup(
+            scheduleData: popupLayerController.currMoreScheduleData,
           ),
         ),
       ],
@@ -149,3 +202,33 @@ class AutoAlignFlowDelegate extends FlowDelegate {
     return oldDelegate != this;
   }
 }
+
+/// 日程框自动对齐委托
+class ScheduleAutoAlignFlowDelegate extends FlowDelegate {
+  final Offset triggerOffset;
+  final Size triggerSize;
+  ScheduleAutoAlignFlowDelegate(this.triggerOffset, this.triggerSize);
+  @override
+  void paintChildren(FlowPaintingContext context) {
+    final Size containerSize = context.size;
+    for (int i = 0; i < context.childCount; i++) {
+      final Size childSize = context.getChildSize(i) ?? Size.zero;
+      double x = triggerOffset.dx;
+      double y = triggerOffset.dy;
+      if (containerSize.height - y < childSize.height) {
+        y = containerSize.height - childSize.height;
+      }
+      if (containerSize.width < x + childSize.width + 370) {
+        x = x - 370;
+      } else {
+        x = x + triggerSize.width + 10;
+      }
+      context.paintChild(i, transform: Matrix4.translationValues(x, y, 0));
+    }
+  }
+
+  @override
+  bool shouldRepaint(FlowDelegate oldDelegate) {
+    return oldDelegate != this;
+  }
+}

+ 6 - 0
lib/popup_layer/popup_layer_controller.dart

@@ -12,6 +12,12 @@ class PopupLayerController extends GetxController {
   /// 弹出层【显示日程详情】事件通知
   FEventHandler<GlobalKey> onPopupSchedule = FEventHandler<GlobalKey>();
 
+  /// 全局点击事件通知
+  FEventHandler<PointerEvent> onGlobalClick = FEventHandler<PointerEvent>();
+
+  /// 关闭日程弹窗 事件通知
+  FEventHandler<bool> onCloseSchedulePopup = FEventHandler<bool>();
+
   /// Popup 弹出层相关的数据
   /// 当前需要显示更多的日程数据
   MonthViewDayStructure currMoreScheduleData = MonthViewDayStructure(