{"id":714,"date":"2023-08-04T14:37:24","date_gmt":"2023-08-04T06:37:24","guid":{"rendered":"https:\/\/iichen.cn\/?p=714"},"modified":"2023-08-04T14:37:24","modified_gmt":"2023-08-04T06:37:24","slug":"flutter-%e6%bb%91%e5%8a%a8%e5%8d%95%e8%a1%8c%e6%97%a5%e5%8e%86%e7%bb%84%e4%bb%b6","status":"publish","type":"post","link":"https:\/\/iichen.cn\/?p=714","title":{"rendered":"Flutter-\u6ed1\u52a8\u5355\u884c\u65e5\u5386\u7ec4\u4ef6"},"content":{"rendered":"<pre><code class=\"language-dart line-numbers\">import 'package:flutter\/material.dart';\n\nclass CalendarWidget extends StatefulWidget {\n  \/\/ \u9009\u62e9\u5668\u8303\u56f4\n  DateTime startTime;\n  DateTime endTime;\n  \/\/ \u65e5\u671f\u6587\u672c\u5b57\u4f53\u5927\u5c0f\n  TextStyle? style;\n  \/\/ \u5934\u90e8 \u5468\u6587\u672c\u6837\u5f0f\n  TextStyle? headerStyle;\n  \/\/ \u65e5\u671f\u5bb9\u5668\u5927\u5c0f\n  double? size;\n  \/\/ \u9009\u62e9\u5668\u9ad8\u5ea6\n  double? height;\n  \/\/ \u9009\u62e9\u5668padding\n  EdgeInsets? padding;\n  \/\/ \u9009\u4e2d\u56de\u8c03\n  Function(DateTime dateTime)? onTapTime;\n\n  CalendarWidget({\n    Key? key,\n    required this.startTime,\n    required this.endTime,\n    this.style,\n    this.size,\n    this.height,\n    this.padding,\n    this.onTapTime,\n    this.headerStyle,\n  }) : super(key: key);\n\n\n  @override\n  State&lt;CalendarWidget&gt; createState() =&gt; _CalendarWidgetState();\n}\n\nclass _CalendarWidgetState extends State&lt;CalendarWidget&gt; {\n  \/*\n     {\n       end:\"2024-12-01\",  \/\/\u6700\u540e\u53ef\u663e\u793a\u65e5\u5386\u7684\u65e5\u671f\n       hot:{\"2023-07-05\":\"2023-08-31\",\"2024-07-05\":\"2024-08-31\"},  \/\/\u6240\u6709\u6691\u5047\u6bb5\n       code:{\"2023-01-15\":\"2023-02-06\",\"2024-01-15\":\"2024-02-06\"},  \/\/\u6240\u6709\u5bd2\u5047\u6bb5\n       extholiday:[\"2023-05-03\",\"2023-05-04\"], \/\/\u989d\u5916\u5047\u65e5\u5217\u8868\n       extworkday:[\"2023-06-08\",\"2023-06-09\"] \/\/\u989d\u5916\u5de5\u4f5c\u65e5\u5217\u8868\n     }\n   *\/\n\n  late PageController _pageController;\n\n  List&lt;WeekItem&gt; source = [];\n\n  late DateTime start;\n  late DateTime end;\n\n  \/\/ \u8bb0\u5f55\u4e4b\u524d\u9875\u7801 \u7528\u4e8e\u6e05\u9664\u4e4b\u524d\u9875\u9009\u4e2d\u6548\u679c\n  int beforeChoosePageIndex = -1;\n  @override\n  void initState() {\n    DateTime currentDate = DateTime.now();\n    start = widget.startTime;\n    end = widget.endTime;\n    \/\/ \u521d\u59cb\u5316\u6784\u5efa\u4e09\u9875\u6570\u636e\n    List&lt;ItemConfig&gt; weekDates = getWeekDates(currentDate);\n    source.add(WeekItem()..week = weekDates);\n    source.insert(0, WeekItem()..week = getPreviousWeek(weekDates[0].dateTime));\n    source.add(WeekItem()..week = getNextWeek(weekDates[6].dateTime));\n\n    _pageController = PageController(initialPage: 1);\n    beforeChoosePageIndex = 1;\n    super.initState();\n  }\n\n  @override\n  void dispose() {\n    super.dispose();\n    _pageController.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Container(\n      padding: const EdgeInsets.symmetric(horizontal: 15,vertical: 15),\n      child: Column(\n        children: [\n          Row(\n            children: List.generate(7, (index) =&gt; Expanded(\n              child: Text(_getDayText(index),textAlign: TextAlign.center,style: widget.headerStyle??const TextStyle(\n                  fontSize: 12,\n                  color: Color(0xFF999999)\n              ),),\n            )),\n          ),\n          Padding(\n            padding: widget.padding??const EdgeInsets.only(top: 12.0),\n            child: SizedBox(\n              height: widget.height??36,\n              child: PageView.builder(\n                controller: _pageController,\n                onPageChanged: (page) {\n                  if(page == 0) {\n                    setState(() {\n                      source.insert(0,WeekItem()..week = getPreviousWeek(source[0].week[0].dateTime));\n                      _pageController.jumpTo(_pageController.position.pixels + _pageController.position.viewportDimension);\n                    });\n                  } else if(page == source.length - 1) {\n                    setState(() {\n                      source.add(WeekItem()..week = getNextWeek(source[source.length - 1].week[6].dateTime));\n                    });\n                  }\n                },\n                itemBuilder: (BuildContext context, int index) {\n                  WeekItem weekItem = source[index];\n                  return Row(\n                    children: List.generate(7, (index) {\n                      DateTime now = weekItem.week[index].dateTime;\n                      \/\/ \u662f\u5426\u8d85\u51fastartTime \u662f\u5426\u8d85\u51faendTime\n                      bool isEnable = now.isBefore(start) || end.isBefore(now);\n\n                      bool isToday = _isSameDate(now,DateTime.now());\n\n                      bool isChoose = weekItem.chooseIndex == index;\n\n                      String extraTip = weekItem.week[index].extraTip;\n\n                      Widget dateTimeWidget = Text(\"${now.day}\",style: widget.style??TextStyle(\n                          fontWeight: FontWeight.w600,\n                          fontSize: 18,\n                          color: isChoose ? Colors.white : const Color(0xFF666666)\n                      ));\n\n                      return Expanded(\n                        child: UnconstrainedBox(\n                          child: GestureDetector(\n                            onTap: () {\n                              \/\/ \u91cd\u7f6e\u4e4b\u524d\u9009\u4e2d\u72b6\u6001\n                              if(beforeChoosePageIndex != _pageController.page &amp;&amp; beforeChoosePageIndex &gt;= 0 &amp;&amp; beforeChoosePageIndex &lt; source.length) {\n                                source[beforeChoosePageIndex].chooseIndex = -1;\n                                beforeChoosePageIndex = toInt(_pageController.page);\n                              }\n\n                              widget.onTapTime?.call(weekItem.week[index].dateTime);\n                              if(mounted) {\n                                setState(() {\n                                  weekItem.chooseIndex = index;\n                                });\n                              }\n                            },\n                            child: Container(\n                              width: widget.size??36,\n                              height: widget.size??36,\n                              decoration: BoxDecoration(\n                                  color: isEnable ? weekItem.disableColor : (\n                                      isChoose ? weekItem.chooseColor : (isToday ? weekItem.todayColor : Colors.transparent)),\n                                  borderRadius: BorderRadius.circular(widget.size == null ? 18 : widget.size! \/ 2)\n                              ),\n                              child: extraTip.isEmpty || isChoose ? Center(\n                                child: dateTimeWidget,\n                              ) : Row(\n                                mainAxisAlignment: MainAxisAlignment.center,\n                                children: [\n                                  dateTimeWidget,\n                                  Text(extraTip,style: const TextStyle(\n                                      fontWeight: FontWeight.w600,\n                                      fontSize: 11,\n                                      color: Color(0xFFFF893F)\n                                  )),\n                                ],\n                              ),\n                            ),\n                          ),\n                        ),\n                      );\n                    }),\n                  );\n                },\n                itemCount: source.length,\n              ),\n            ),\n          )\n        ],\n      ),\n    );\n  }\n\n  int toInt(double? page) {\n    if(page == null) {\n      return -1;\n    }\n    try {\n      return page.toInt();\n    } catch(e) {\n      return -1;\n    }\n  }\n\n  bool _isSameDate(DateTime date1, DateTime date2) {\n    return date1.year == date2.year &amp;&amp; date1.month == date2.month &amp;&amp; date1.day == date2.day;\n  }\n\n  String _getDayText(int index) {\n    switch(index) {\n      case 0 :\n        return \"\u4e00\";\n      case 1 :\n        return \"\u4e8c\";\n      case 2 :\n        return \"\u4e09\";\n      case 3 :\n        return \"\u56db\";\n      case 4 :\n        return \"\u4e94\";\n      case 5 :\n        return \"\u516d\";\n      case 6 :\n        return \"\u4e03\";\n    }\n    return \"\u4e00\";\n  }\n\n  List&lt;ItemConfig&gt; getWeekDates(DateTime dateTime) {\n    List&lt;ItemConfig&gt; weekDates = [];\n\n    DateTime firstDayOfWeek = dateTime.subtract(Duration(days: dateTime.weekday - 1));\n\n    for (int i = 0; i &lt; 7; i++) {\n      DateTime now = firstDayOfWeek.add(Duration(days: i));\n      \/\/ \u6d4b\u8bd5\u4f7f\u7528\n      String extraTip = \"\";\n      if(7 &lt;= now.month &amp;&amp; now.month &lt;= 9) {\n        extraTip = \"\u6691\";\n      }\n\n      if(1 &lt;= now.month &amp;&amp; now.month &lt;= 2) {\n        extraTip = \"\u5bd2\";\n      }\n      weekDates.add(ItemConfig()..dateTime = now..extraTip = extraTip);\n    }\n\n    return weekDates;\n  }\n\n  List&lt;ItemConfig&gt; getPreviousWeek(DateTime dateTime) {\n    DateTime startDate = dateTime.subtract(const Duration(days: 7));\n    return getWeekDates(startDate);\n  }\n\n  List&lt;ItemConfig&gt; getNextWeek(DateTime dateTime) {\n    DateTime endDate = dateTime.add(const Duration(days: 1));\n    return getWeekDates(endDate);\n  }\n}\n\nclass WeekItem {\n  Color todayColor = const Color(0xFF0BB9B9).withOpacity(0.1);\n  Color disableColor = const Color(0xFFE5ECF3);\n  Color chooseColor = const Color(0xFF0BB9B9);\n  List&lt;ItemConfig&gt; week = [];\n\n  \/\/ \u70b9\u51fb\u7d22\u5f15\n  int chooseIndex = -1;\n}\n\nclass ItemConfig {\n  DateTime dateTime = DateTime.now();\n  String extraTip = \"\";\n\n  @override\n  String toString() {\n    return 'ItemConfig{dateTime: $dateTime, extraTip: $extraTip}';\n  }\n}\n<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>import &#8216;package:flutter\/material.dart&#8217;; class CalendarW [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[12,8],"tags":[],"class_list":["post-714","post","type-post","status-publish","format-standard","hentry","category-flutter","category-8"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v24.4 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Flutter-\u6ed1\u52a8\u5355\u884c\u65e5\u5386\u7ec4\u4ef6 - IIchen<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/iichen.cn\/?p=714\" \/>\n<meta property=\"og:locale\" content=\"zh_CN\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Flutter-\u6ed1\u52a8\u5355\u884c\u65e5\u5386\u7ec4\u4ef6 - IIchen\" \/>\n<meta property=\"og:description\" content=\"import &#039;package:flutter\/material.dart&#039;; class CalendarW [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/iichen.cn\/?p=714\" \/>\n<meta property=\"og:site_name\" content=\"IIchen\" \/>\n<meta property=\"article:published_time\" content=\"2023-08-04T06:37:24+00:00\" \/>\n<meta name=\"author\" content=\"iichen\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"\u4f5c\u8005\" \/>\n\t<meta name=\"twitter:data1\" content=\"iichen\" \/>\n\t<meta name=\"twitter:label2\" content=\"\u9884\u8ba1\u9605\u8bfb\u65f6\u95f4\" \/>\n\t<meta name=\"twitter:data2\" content=\"3 \u5206\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/iichen.cn\/?p=714#article\",\"isPartOf\":{\"@id\":\"https:\/\/iichen.cn\/?p=714\"},\"author\":{\"name\":\"iichen\",\"@id\":\"https:\/\/iichen.cn\/#\/schema\/person\/4a47edf85ab49841df9e8f6aee40b77c\"},\"headline\":\"Flutter-\u6ed1\u52a8\u5355\u884c\u65e5\u5386\u7ec4\u4ef6\",\"datePublished\":\"2023-08-04T06:37:24+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/iichen.cn\/?p=714\"},\"wordCount\":1,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/iichen.cn\/#\/schema\/person\/4a47edf85ab49841df9e8f6aee40b77c\"},\"articleSection\":[\"Flutter\",\"\u7b14\u8bb0\"],\"inLanguage\":\"zh-Hans\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/iichen.cn\/?p=714#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/iichen.cn\/?p=714\",\"url\":\"https:\/\/iichen.cn\/?p=714\",\"name\":\"Flutter-\u6ed1\u52a8\u5355\u884c\u65e5\u5386\u7ec4\u4ef6 - IIchen\",\"isPartOf\":{\"@id\":\"https:\/\/iichen.cn\/#website\"},\"datePublished\":\"2023-08-04T06:37:24+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/iichen.cn\/?p=714#breadcrumb\"},\"inLanguage\":\"zh-Hans\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/iichen.cn\/?p=714\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/iichen.cn\/?p=714#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"\u9996\u9875\",\"item\":\"https:\/\/iichen.cn\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Flutter-\u6ed1\u52a8\u5355\u884c\u65e5\u5386\u7ec4\u4ef6\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/iichen.cn\/#website\",\"url\":\"https:\/\/iichen.cn\/\",\"name\":\"IIchen\",\"description\":\"Just do it!\",\"publisher\":{\"@id\":\"https:\/\/iichen.cn\/#\/schema\/person\/4a47edf85ab49841df9e8f6aee40b77c\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/iichen.cn\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"zh-Hans\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\/\/iichen.cn\/#\/schema\/person\/4a47edf85ab49841df9e8f6aee40b77c\",\"name\":\"iichen\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"zh-Hans\",\"@id\":\"https:\/\/iichen.cn\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/iichen.cn\/wp-content\/uploads\/2025\/01\/avatar.jpg\",\"contentUrl\":\"https:\/\/iichen.cn\/wp-content\/uploads\/2025\/01\/avatar.jpg\",\"width\":940,\"height\":940,\"caption\":\"iichen\"},\"logo\":{\"@id\":\"https:\/\/iichen.cn\/#\/schema\/person\/image\/\"},\"sameAs\":[\"https:\/\/www.iichen.cn\"],\"url\":\"https:\/\/iichen.cn\/?author=1\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Flutter-\u6ed1\u52a8\u5355\u884c\u65e5\u5386\u7ec4\u4ef6 - IIchen","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/iichen.cn\/?p=714","og_locale":"zh_CN","og_type":"article","og_title":"Flutter-\u6ed1\u52a8\u5355\u884c\u65e5\u5386\u7ec4\u4ef6 - IIchen","og_description":"import 'package:flutter\/material.dart'; class CalendarW [&hellip;]","og_url":"https:\/\/iichen.cn\/?p=714","og_site_name":"IIchen","article_published_time":"2023-08-04T06:37:24+00:00","author":"iichen","twitter_card":"summary_large_image","twitter_misc":{"\u4f5c\u8005":"iichen","\u9884\u8ba1\u9605\u8bfb\u65f6\u95f4":"3 \u5206"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/iichen.cn\/?p=714#article","isPartOf":{"@id":"https:\/\/iichen.cn\/?p=714"},"author":{"name":"iichen","@id":"https:\/\/iichen.cn\/#\/schema\/person\/4a47edf85ab49841df9e8f6aee40b77c"},"headline":"Flutter-\u6ed1\u52a8\u5355\u884c\u65e5\u5386\u7ec4\u4ef6","datePublished":"2023-08-04T06:37:24+00:00","mainEntityOfPage":{"@id":"https:\/\/iichen.cn\/?p=714"},"wordCount":1,"commentCount":0,"publisher":{"@id":"https:\/\/iichen.cn\/#\/schema\/person\/4a47edf85ab49841df9e8f6aee40b77c"},"articleSection":["Flutter","\u7b14\u8bb0"],"inLanguage":"zh-Hans","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/iichen.cn\/?p=714#respond"]}]},{"@type":"WebPage","@id":"https:\/\/iichen.cn\/?p=714","url":"https:\/\/iichen.cn\/?p=714","name":"Flutter-\u6ed1\u52a8\u5355\u884c\u65e5\u5386\u7ec4\u4ef6 - IIchen","isPartOf":{"@id":"https:\/\/iichen.cn\/#website"},"datePublished":"2023-08-04T06:37:24+00:00","breadcrumb":{"@id":"https:\/\/iichen.cn\/?p=714#breadcrumb"},"inLanguage":"zh-Hans","potentialAction":[{"@type":"ReadAction","target":["https:\/\/iichen.cn\/?p=714"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/iichen.cn\/?p=714#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"\u9996\u9875","item":"https:\/\/iichen.cn\/"},{"@type":"ListItem","position":2,"name":"Flutter-\u6ed1\u52a8\u5355\u884c\u65e5\u5386\u7ec4\u4ef6"}]},{"@type":"WebSite","@id":"https:\/\/iichen.cn\/#website","url":"https:\/\/iichen.cn\/","name":"IIchen","description":"Just do it!","publisher":{"@id":"https:\/\/iichen.cn\/#\/schema\/person\/4a47edf85ab49841df9e8f6aee40b77c"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/iichen.cn\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"zh-Hans"},{"@type":["Person","Organization"],"@id":"https:\/\/iichen.cn\/#\/schema\/person\/4a47edf85ab49841df9e8f6aee40b77c","name":"iichen","image":{"@type":"ImageObject","inLanguage":"zh-Hans","@id":"https:\/\/iichen.cn\/#\/schema\/person\/image\/","url":"https:\/\/iichen.cn\/wp-content\/uploads\/2025\/01\/avatar.jpg","contentUrl":"https:\/\/iichen.cn\/wp-content\/uploads\/2025\/01\/avatar.jpg","width":940,"height":940,"caption":"iichen"},"logo":{"@id":"https:\/\/iichen.cn\/#\/schema\/person\/image\/"},"sameAs":["https:\/\/www.iichen.cn"],"url":"https:\/\/iichen.cn\/?author=1"}]}},"_links":{"self":[{"href":"https:\/\/iichen.cn\/index.php?rest_route=\/wp\/v2\/posts\/714","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/iichen.cn\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/iichen.cn\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/iichen.cn\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/iichen.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=714"}],"version-history":[{"count":0,"href":"https:\/\/iichen.cn\/index.php?rest_route=\/wp\/v2\/posts\/714\/revisions"}],"wp:attachment":[{"href":"https:\/\/iichen.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=714"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/iichen.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=714"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/iichen.cn\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=714"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}