├── .DS_Store
├── .github
└── workflows
│ └── dart.yml
├── README.md
├── r_calendar
├── .github
│ └── workflows
│ │ └── publish.yaml
├── .gitignore
├── .metadata
├── CHANGELOG.md
├── LICENSE
├── README.md
├── README_ZH.md
├── example
│ ├── .gitignore
│ ├── .metadata
│ ├── README.md
│ ├── lib
│ │ ├── en.arb
│ │ ├── l10n
│ │ │ ├── en.dart
│ │ │ ├── s.dart
│ │ │ └── zh.dart
│ │ ├── main.dart
│ │ └── zh.arb
│ ├── pubspec.lock
│ ├── pubspec.yaml
│ └── test
│ │ └── widget_test.dart
├── lib
│ ├── r_calendar.dart
│ └── src
│ │ ├── r_calendar_controller.dart
│ │ ├── r_calendar_custom_widget.dart
│ │ ├── r_calendar_extension.dart
│ │ ├── r_calendar_utils.dart
│ │ └── r_calendar_widget_item.dart
├── pubspec.lock
├── pubspec.yaml
└── test
│ └── r_calendar_test.dart
└── screen
├── .DS_Store
├── s1.png
├── s2.png
├── s3.png
└── s4.png
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rhymelph/r_calendar/a2a33d772336025552a6205252049aeec71cb94d/.DS_Store
--------------------------------------------------------------------------------
/.github/workflows/dart.yml:
--------------------------------------------------------------------------------
1 |
2 | name: Publish to Pub.dev
3 |
4 | # 流程触发时机,当有标签创建时触发,如 v1.0.0。当然也可以选择别的触发时机,如 push,release 等
5 | on: create
6 |
7 | jobs:
8 | publishing:
9 | runs-on: ubuntu-latest
10 | steps:
11 | # 拉取仓库代码
12 | - name: "Checkout"
13 | uses: actions/checkout@v2
14 | - name: "cd rcalendar"
15 | run: cd r_calendar
16 | # 发布插件
17 | - name: Dart and Flutter Package Publisher
18 | uses: k-paxian/dart-package-publisher@v1.2
19 | with:
20 | # 设置发布插件需要的 Token
21 | accessToken: ${{ secrets.OAUTH_ACCESS_TOKEN }}
22 | refreshToken: ${{ secrets.OAUTH_REFRESH_TOKEN }}
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # r_calendar
2 | [](https://pub.dartlang.org/packages/r_calendar)
3 |
4 | 📅📆Flutter日历插件,支持自定义日历,月视图/周视图切花、点击拦截、单选(切换月自动选)、多选(散选/聚选)
5 |
6 | - [✔] 月视图/周视图切换
7 | - [✔] 自定义日历
8 | - [✔] 点击拦截
9 | - [✔] 单选,切换月/周自动选
10 | - [✔] 多选,散选/聚选
11 |
12 | | 
单选 | 
周视图 | 
多选 | 
多选 |
13 | | :---: | :---: | :---: | :---: |
14 |
15 |
--------------------------------------------------------------------------------
/r_calendar/.github/workflows/publish.yaml:
--------------------------------------------------------------------------------
1 |
2 | name: Publish to Pub.dev
3 |
4 | # 流程触发时机,当有标签创建时触发,如 v1.0.0。当然也可以选择别的触发时机,如 push,release 等
5 | on: create
6 |
7 | jobs:
8 | publishing:
9 | runs-on: ubuntu-latest
10 | steps:
11 | # 拉取仓库代码
12 | - name: "Checkout"
13 | uses: actions/checkout@v2
14 | # 发布插件
15 | - name: Dart and Flutter Package Publisher
16 | uses: k-paxian/dart-package-publisher@v1.2
17 | with:
18 | # 设置发布插件需要的 Token
19 | accessToken: ${{ secrets.OAUTH_ACCESS_TOKEN }}
20 | refreshToken: ${{ secrets.OAUTH_REFRESH_TOKEN }}
--------------------------------------------------------------------------------
/r_calendar/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | lib/generated/
13 | res/
14 | # IntelliJ related
15 | *.iml
16 | *.ipr
17 | *.iws
18 | .idea/
19 |
20 | # The .vscode folder contains launch configuration and tasks you configure in
21 | # VS Code which you may wish to be included in version control, so this line
22 | # is commented out by default.
23 | #.vscode/
24 |
25 | # Flutter/Dart/Pub related
26 | **/doc/api/
27 | .dart_tool/
28 | .flutter-plugins
29 | .flutter-plugins-dependencies
30 | .packages
31 | .pub-cache/
32 | .pub/
33 | build/
34 |
35 | # Android related
36 | **/android/**/gradle-wrapper.jar
37 | **/android/.gradle
38 | **/android/captures/
39 | **/android/gradlew
40 | **/android/gradlew.bat
41 | **/android/local.properties
42 | **/android/**/GeneratedPluginRegistrant.java
43 |
44 | # iOS/XCode related
45 | **/ios/**/*.mode1v3
46 | **/ios/**/*.mode2v3
47 | **/ios/**/*.moved-aside
48 | **/ios/**/*.pbxuser
49 | **/ios/**/*.perspectivev3
50 | **/ios/**/*sync/
51 | **/ios/**/.sconsign.dblite
52 | **/ios/**/.tags*
53 | **/ios/**/.vagrant/
54 | **/ios/**/DerivedData/
55 | **/ios/**/Icon?
56 | **/ios/**/Pods/
57 | **/ios/**/.symlinks/
58 | **/ios/**/profile
59 | **/ios/**/xcuserdata
60 | **/ios/.generated/
61 | **/ios/Flutter/App.framework
62 | **/ios/Flutter/Flutter.framework
63 | **/ios/Flutter/Flutter.podspec
64 | **/ios/Flutter/Generated.xcconfig
65 | **/ios/Flutter/app.flx
66 | **/ios/Flutter/app.zip
67 | **/ios/Flutter/flutter_assets/
68 | **/ios/Flutter/flutter_export_environment.sh
69 | **/ios/ServiceDefinitions.json
70 | **/ios/Runner/GeneratedPluginRegistrant.*
71 |
72 | # Exceptions to above rules.
73 | !**/ios/**/default.mode1v3
74 | !**/ios/**/default.mode2v3
75 | !**/ios/**/default.pbxuser
76 | !**/ios/**/default.perspectivev3
77 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
78 |
--------------------------------------------------------------------------------
/r_calendar/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: 27321ebbad34b0a3fafe99fac037102196d655ff
8 | channel: stable
9 |
10 | project_type: package
11 |
--------------------------------------------------------------------------------
/r_calendar/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [0.1.12] - update intl package.
2 |
3 | ## [0.1.11] - null safety.
4 |
5 | ## [0.1.10-nullsafety] - chinese lunar year.
6 |
7 | ## [0.1.9-nullsafety] - null safety
8 |
9 | ## [0.1.8] - Fix the bugs in week select bug.
10 |
11 | ## [0.1.7] - Fix the bugs in aggregate selection
12 |
13 | ## [0.1.6] - add expand data in controller
14 |
15 | ## [0.1.5] - format
16 |
17 | ## [0.1.4] - add week view .
18 |
19 | ## [0.1.3] - remove buildMonthYear 、left and right , add build top widget.
20 |
21 | ## [0.1.2] - add single and multiple selected to packages.
22 |
23 | ## [0.1.1] - fix listener data not change and add monthDate to controller.
24 |
25 | ## [0.1.0] - release
26 |
27 |
--------------------------------------------------------------------------------
/r_calendar/LICENSE:
--------------------------------------------------------------------------------
1 | // Copyright 2019 The rhyme_lph Authors. All rights reserved.
2 | //
3 | // Redistribution and use in source and binary forms, with or without
4 | // modification, are permitted provided that the following conditions are
5 | // met:
6 | //
7 | // * Redistributions of source code must retain the above copyright
8 | // notice, this list of conditions and the following disclaimer.
9 | // * Redistributions in binary form must reproduce the above
10 | // copyright notice, this list of conditions and the following disclaimer
11 | // in the documentation and/or other materials provided with the
12 | // distribution.
13 | // * Neither the name of rhyme_lph authors. nor the names of its
14 | // contributors may be used to endorse or promote products derived from
15 | // this software without specific prior written permission.
16 | //
17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/r_calendar/README.md:
--------------------------------------------------------------------------------
1 | # r_calendar
2 |
3 | [](https://pub.dartlang.org/packages/r_calendar)
4 |
5 | A new Flutter package about calendar,you can use this design the calendar and support single or multiple selected.
6 |
7 |
8 | ## [中文点此](README_ZH.md)
9 |
10 | ## 1.Getting Started.
11 |
12 | - use plugin:
13 | add this code in `pubspec.yaml`
14 | ```yaml
15 | dependencies:
16 | r_calendar: last version
17 | ```
18 | - add the packages to your file.
19 | ```dart
20 | import 'package:r_calendar/r_calendar.dart';
21 |
22 | ```
23 | - radio select
24 | ```dart
25 | ///
26 | /// [selectedDate] will default selected one
27 | /// [isAutoSelect] when the page view change will change month to auto select same day.
28 | RCalendarController controller= RCalendarController.single(
29 | selectedDate: DateTime.now(),
30 | isAutoSelect: true,);
31 | ```
32 | - check select
33 | ```dart
34 | ///
35 | /// [selectedDates] will default selected more.
36 | /// [isDispersion] will selected continuity dateTime.
37 | RCalendarController controller = RCalendarController.multiple(
38 | selectedDates: [
39 | DateTime(2019, 12, 1),
40 | DateTime(2019, 12, 2),
41 | DateTime(2019, 12, 3),
42 | ],
43 | isDispersion: true);
44 | ```
45 | - value change listener
46 | ```dart
47 | ///
48 | /// addListener to observe value change
49 | RCalendarController controller = RCalendarController.multiple(...)
50 | addListener((){
51 | // controller.isMultiple
52 |
53 | // single selected
54 | // controller.isAutoSelect
55 | // controller.selectedDate;
56 |
57 | // multiple selected
58 | // controller.selectedDates;
59 | // controller.isDispersion;
60 |
61 | // mode month/week
62 | // controller.mode
63 | });
64 | ```
65 | - custom your widget
66 | ```dart
67 |
68 | class MyRCalendarCustomWidget extends RCalendarCustomWidget {
69 |
70 | // If you want to change first day is Monday ,you can change [MaterialLocalizations.firstDayOfWeekIndex]
71 | // SUM MON TUE WED THU FRI SUT
72 | //build your week header
73 | @override
74 | List buildWeekListWidget(BuildContext context,MaterialLocalizations localizations){...};
75 |
76 | // 1 2 3 4 5 6 7
77 | //build normal dateTime
78 | @override
79 | Widget buildDateTime(BuildContext context, DateTime time, List types){...};
80 |
81 | // < 2019 year 11 month >
82 | //build year and month and left 、 right Indicator
83 | @override
84 | Widget buildTopWidget(BuildContext context, RCalendarController controller){...};
85 |
86 |
87 | //is unable will not have tap events
88 | @override
89 | bool isUnable(DateTime time, bool isSameMonth){...};
90 |
91 | //Click intercept. When it returns true to intercept, the selected date will not be changed
92 | @override
93 | FutureOr clickInterceptor(BuildContext context, DateTime dateTime){...};
94 |
95 | //Height of child view
96 | @override
97 | double get childHeight=>{...};
98 | }
99 | ```
100 |
101 | ## 2.Use this
102 | ```dart
103 | import 'package:flutter/material.dart';
104 | import 'package:r_calendar/r_calendar.dart';
105 |
106 | void main() => runApp(MyApp());
107 |
108 | class MyApp extends StatelessWidget {
109 | @override
110 | Widget build(BuildContext context) {
111 | return MaterialApp(
112 | title: 'Flutter Demo',
113 | home: MyHomePage(),
114 | );
115 | }
116 | }
117 |
118 | class MyHomePage extends StatefulWidget {
119 | @override
120 | _MyHomePageState createState() => _MyHomePageState();
121 | }
122 | class _MyHomePageState extends State {
123 | RCalendarController controller;
124 |
125 | @override
126 | void initState() {
127 | super.initState();
128 | controller = RCalendarController.multiple(selectedDates: [
129 | DateTime(2019, 12, 1),
130 | DateTime(2019, 12, 2),
131 | DateTime(2019, 12, 3),
132 | ]);
133 | // controller = RCalendarController.single(selectedDate: DateTime.now(),isAutoSelect: true);
134 | }
135 |
136 | @override
137 | void dispose() {
138 | controller.dispose();
139 | super.dispose();
140 | }
141 |
142 | @override
143 | Widget build(BuildContext context) {
144 | return Scaffold(
145 | body: RCalendarWidget(
146 | controller: controller,
147 | customWidget: DefaultRCalendarCustomWidget(),
148 | firstDate: DateTime(1970, 1, 1),
149 | lastDate: DateTime(2055, 12, 31),
150 | ),
151 | );
152 | }
153 | }
154 | ```
155 | ## 3.Expand Data(update by v0.1.6 )
156 | ```dart
157 | /// initial
158 | RCalendarController> controller = RCalendarController.single(
159 | initialData: [
160 | DateTime.now(),
161 | DateTime.now().add(Duration(days: 1)),
162 | DateTime.now().add(Duration(days: 2)),
163 | ]
164 | );
165 |
166 | /// if you want to update expand data ,use it.
167 | controller.data = [....];
168 |
169 | /// you can use it get the controller in RCalendarCustomWidget
170 |
171 | class MyRCalendarCustomWidget extends RCalendarCustomWidget {
172 | ///...
173 | @override
174 | Widget buildDateTime(
175 | BuildContext context, DateTime time, List types) {
176 |
177 | // new
178 | RCalendarController> controller =RCalendarMarker.of(context).notifier;
179 | // new
180 |
181 | //...
182 | }
183 | ///...
184 | ```
--------------------------------------------------------------------------------
/r_calendar/README_ZH.md:
--------------------------------------------------------------------------------
1 | ## r_calendar
2 | [](https://pub.dartlang.org/packages/r_calendar)
3 |
4 | 一个日历插件,支持自定义日历,月视图/周视图切花、点击拦截、单选(切换月自动选)、多选(散选/聚选)
5 |
6 | ## 1.如何使用.
7 |
8 | - `pubspec.yaml`文件添加依赖
9 |
10 | ```yaml
11 | dependencies:
12 | r_calendar: last version
13 | ```
14 | - 导入包
15 |
16 | ```dart
17 | import 'package:r_calendar/r_calendar.dart';
18 |
19 | ```
20 | - 单选控制器初始化
21 | ```dart
22 | ///
23 | /// [selectedDate] 默认选中的那天
24 | /// [isAutoSelect] 当月份改变时,是否自动选中对应的月份的同一天
25 |
26 | RCalendarController controller= RCalendarController.single(
27 | selectedDate: DateTime.now(),
28 | isAutoSelect: true,);
29 | ```
30 | - 多选控制器初始化
31 | ```dart
32 | ///
33 | /// [selectedDates] 默认选中的日期数组
34 | /// [isDispersion] 是否散选,否则为连续选中
35 |
36 | RCalendarController controller = RCalendarController.multiple(
37 | selectedDates: [
38 | DateTime(2019, 12, 1),
39 | DateTime(2019, 12, 2),
40 | DateTime(2019, 12, 3),
41 | ],
42 | isDispersion: true);
43 | ```
44 | - 周视图/月视图(默认月视图)
45 | ```dart
46 | ///
47 | /// [mode] 模式
48 | /// - RCalendarMode.week 周视图模式
49 | /// - RCalendarMode.month 月视图模式
50 |
51 | RCalendarController controller = RCalendarController.single(
52 | mode:RCalendarMode.week);
53 | ```
54 | - 数据变化监听
55 | ```dart
56 | /// 添加监听器观察值的变化
57 | RCalendarController controller = RCalendarController.multiple(...)
58 | ..addListener((){
59 | // 是否为多选
60 | // controller.isMultiple
61 |
62 | // 单选下
63 | // 当月份改变时,是否自动选中对应的月份的同一天
64 | // controller.isAutoSelect
65 | // 当前选中的日期
66 | // controller.selectedDate;
67 |
68 | // 多选
69 | // 是否散选,否则为连续选中
70 | // controller.isDispersion;
71 | // 当前选中的日期列表
72 | // controller.selectedDates;
73 |
74 | // 周视图/月视图
75 | // controller.mode
76 | });
77 | ```
78 | - 自定义日历
79 | ```dart
80 |
81 | class MyRCalendarCustomWidget extends RCalendarCustomWidget {
82 | // 如果你想设置第一天是星期一,请更改MaterialLocalizations 的firstDayOfWeekIndex
83 | // 日 一 二 三 四 五 六
84 | //构建头部
85 | @override
86 | List buildWeekListWidget(BuildContext context,MaterialLocalizations localizations){...};
87 |
88 | // 1 2 3 4 5 6 7
89 | //构建普通的日期
90 | @override
91 | Widget buildDateTime(BuildContext context,DateTime time, List types){...};
92 |
93 | // < 2019年 11月 >
94 | //构建年份和月份 左指示器、右指示器,返回null就没有
95 | @override
96 | Widget buildTopWidget(BuildContext context,RCalendarController controller){...};
97 |
98 | //是否不可用,不可用时,无点击事件
99 | @override
100 | bool isUnable(DateTime time, bool isSameMonth){...};
101 |
102 | //点击拦截,当返回true时进行拦截,就不会改变选中日期
103 | @override
104 | FutureOr clickInterceptor(BuildContext context,DateTime dateTime){...};
105 |
106 | //子view的高度
107 | @override
108 | double get childHeight=>{...};
109 | }
110 | ```
111 |
112 | ## 2.使用它.
113 |
114 | ```dart
115 | import 'package:flutter/material.dart';
116 | import 'package:r_calendar/r_calendar.dart';
117 |
118 | void main() => runApp(MyApp());
119 | class MyApp extends StatelessWidget {
120 | @override
121 | Widget build(BuildContext context) {
122 | return MaterialApp(
123 | title: 'Flutter Demo',
124 | theme: ThemeData(
125 | primarySwatch: Colors.blue,
126 | ),
127 | home: MyHomePage(title: 'Flutter Demo Home Page'),
128 | );
129 | }
130 | }
131 |
132 | class MyHomePage extends StatefulWidget {
133 | @override
134 | _MyHomePageState createState() => _MyHomePageState();
135 | }
136 | class _MyHomePageState extends State {
137 | RCalendarController controller;
138 |
139 | @override
140 | void initState() {
141 | super.initState();
142 | controller = RCalendarController.multiple(selectedDates: [
143 | DateTime(2019, 12, 1),
144 | DateTime(2019, 12, 2),
145 | DateTime(2019, 12, 3),
146 | ]);
147 | // controller = RCalendarController.single(selectedDate: DateTime.now(),isAutoSelect: true);
148 | }
149 |
150 | @override
151 | void dispose() {
152 | controller.dispose();
153 | super.dispose();
154 | }
155 |
156 | @override
157 | Widget build(BuildContext context) {
158 | return Scaffold(
159 | body: RCalendarWidget(
160 | controller: controller,
161 | customWidget: DefaultRCalendarCustomWidget(),
162 | firstDate: DateTime(1970, 1, 1), //当前日历的最小日期
163 | lastDate: DateTime(2055, 12, 31),//当前日历的最大日期
164 | ),
165 | );
166 | }
167 | }
168 | ```
169 | ## 3.自定义数据(请更新版本到 v0.1.6)
170 | 用户可以通过将数据设置到controller里面,然后再从`RCalendarCustomWidget`里获取
171 | ```dart
172 | /// 初始化控制器,我这里是设置自定义数据的类型为List,当然,你可以设置成自己需要的类型
173 | /// 在构造方法中新增了一个`initialData`参数,用于初始化你的自定义数据
174 | RCalendarController> controller = RCalendarController.single(
175 | initialData: [
176 | DateTime.now(),
177 | DateTime.now().add(Duration(days: 1)),
178 | DateTime.now().add(Duration(days: 2)),
179 | ]
180 | );
181 |
182 | /// 如果你想更改自定义数据,请使用下面的例子,无需setState
183 | controller.data = [....];
184 |
185 | /// 你可以在RCalendarCustomWidget自定义类中通过context获取对应的controller.然后根据自定义数据进行显示判断
186 |
187 | class MyRCalendarCustomWidget extends RCalendarCustomWidget {
188 | ///...
189 | @override
190 | Widget buildDateTime(
191 | BuildContext context, DateTime time, List types) {
192 |
193 | // new
194 | RCalendarController> controller =RCalendarMarker.of(context).notifier;
195 | // new
196 |
197 | //...
198 | }
199 | ///...
200 | ```
--------------------------------------------------------------------------------
/r_calendar/example/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | .dart_tool/
26 | .flutter-plugins
27 | .flutter-plugins-dependencies
28 | .packages
29 | .pub-cache/
30 | .pub/
31 | /build/
32 |
33 | # Web related
34 | lib/generated_plugin_registrant.dart
35 |
36 | # Exceptions to above rules.
37 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
38 |
--------------------------------------------------------------------------------
/r_calendar/example/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: 27321ebbad34b0a3fafe99fac037102196d655ff
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/r_calendar/example/README.md:
--------------------------------------------------------------------------------
1 | # r_calendar_example
2 |
3 | ## English Language
4 |
5 | ### 1.RCalendarController
6 |
7 | value controller
8 |
9 | ```dart
10 | import 'package:r_calendar/r_calendar.dart';
11 |
12 | /// radio select
13 | ///
14 | /// [selectedDate] will default selected one
15 | /// [isAutoSelect] when the page view change will change month to auto select same day.
16 |
17 | RCalendarController controller= RCalendarController.single(
18 | selectedDate: DateTime.now(),
19 | isAutoSelect: true,);
20 |
21 |
22 | /// check select
23 | ///
24 | /// [selectedDates] will default selected more.
25 | /// [isDispersion] will selected continuity dateTime.
26 | RCalendarController controller = RCalendarController.multiple(
27 | selectedDates: [
28 | DateTime(2019, 12, 1),
29 | DateTime(2019, 12, 2),
30 | DateTime(2019, 12, 3),
31 | ],
32 | isDispersion: true);
33 |
34 | /// addListener to observe value change
35 | RCalendarController controller = RCalendarController.multiple(...)
36 | addListener((){
37 | // controller.isMultiple
38 |
39 | // single selected
40 | // controller.isAutoSelect
41 | // controller.selectedDate;
42 |
43 | // multiple selected
44 | // controller.selectedDates;
45 | // controller.isDispersion;
46 | });
47 | ```
48 |
49 | ### 2.RCalendarCustomWidget
50 |
51 | custom your widget
52 |
53 | ```dart
54 |
55 | class MyRCalendarCustomWidget extends RCalendarCustomWidget {
56 |
57 | // If you want to change first day is Monday ,you can change [MaterialLocalizations.firstDayOfWeekIndex]
58 | // SUM MON TUE WED THU FRI SUT
59 | //build your week header
60 | @override
61 | List buildWeekListWidget(MaterialLocalizations localizations){...};
62 |
63 | // 1 2 3 4 5 6 7
64 | //build normal dateTime
65 | @override
66 | Widget buildDateTime(DateTime time, List types){...};
67 |
68 | // < 2019 year 11 month >
69 | //build year and month and left 、 right Indicator
70 | @override
71 | Widget buildTopWidget(RCalendarController controller){...};
72 |
73 | //is unable will not have tap events
74 | @override
75 | bool isUnable(DateTime time, bool isSameMonth){...};
76 |
77 | //Click intercept. When it returns true to intercept, the selected date will not be changed
78 | @override
79 | FutureOr clickInterceptor(DateTime dateTime){...};
80 |
81 | //Height of child view
82 | @override
83 | double get childHeight=>{...};
84 | }
85 |
86 | ```
87 | ### 3.Use the widget.
88 |
89 | ```dart
90 | import 'package:flutter/material.dart';
91 | import 'package:r_calendar/r_calendar.dart';
92 |
93 | void main() => runApp(MyApp());
94 | class MyApp extends StatelessWidget {
95 | @override
96 | Widget build(BuildContext context) {
97 | return MaterialApp(
98 | title: 'Flutter Demo',
99 | theme: ThemeData(
100 | primarySwatch: Colors.blue,
101 | ),
102 | home: MyHomePage(title: 'Flutter Demo Home Page'),
103 | );
104 | }
105 | }
106 |
107 | class MyHomePage extends StatefulWidget {
108 | @override
109 | _MyHomePageState createState() => _MyHomePageState();
110 | }
111 | class _MyHomePageState extends State {
112 | RCalendarController controller;
113 |
114 | @override
115 | void initState() {
116 | super.initState();
117 | controller = RCalendarController.multiple(selectedDates: [
118 | DateTime(2019, 12, 1),
119 | DateTime(2019, 12, 2),
120 | DateTime(2019, 12, 3),
121 | ]);
122 | // controller = RCalendarController.single(selectedDate: DateTime.now(),isAutoSelect: true);
123 | }
124 |
125 | @override
126 | void dispose() {
127 | controller.dispose();
128 | super.dispose();
129 | }
130 |
131 | @override
132 | Widget build(BuildContext context) {
133 | return Scaffold(
134 | body: RCalendarWidget(
135 | controller: controller,
136 | customWidget: DefaultRCalendarCustomWidget(),
137 | firstDate: DateTime(1970, 1, 1),
138 | lastDate: DateTime(2055, 12, 31),
139 | ),
140 | );
141 | }
142 | }
143 |
144 | ```
145 |
146 | ## Chinese Language
147 |
148 | ### 1.RCalendarController
149 |
150 | 值的控制器
151 |
152 | ```dart
153 | import 'package:r_calendar/r_calendar.dart';
154 |
155 | /// 单选
156 | ///
157 | /// [selectedDate] 默认选中的那天
158 | /// [isAutoSelect] 当月份改变时,是否自动选中对应的月份的同一天
159 |
160 | RCalendarController controller= RCalendarController.single(
161 | selectedDate: DateTime.now(),
162 | isAutoSelect: true,);
163 |
164 |
165 | /// 多选
166 | ///
167 | /// [selectedDates] 默认选中的日期数组
168 | /// [isDispersion] 是否散选,否则为连续选中
169 |
170 | RCalendarController controller = RCalendarController.multiple(
171 | selectedDates: [
172 | DateTime(2019, 12, 1),
173 | DateTime(2019, 12, 2),
174 | DateTime(2019, 12, 3),
175 | ],
176 | isDispersion: true);
177 |
178 | /// 添加监听器观察值的变化
179 | RCalendarController controller = RCalendarController.multiple(...)
180 | addListener((){
181 | // 是否为多选
182 | // controller.isMultiple
183 |
184 | // 单选下
185 | // 当月份改变时,是否自动选中对应的月份的同一天
186 | // controller.isAutoSelect
187 | // 当前选中的日期
188 | // controller.selectedDate;
189 |
190 | // 多选
191 | // 是否散选,否则为连续选中
192 | // controller.isDispersion;
193 | // 当前选中的日期列表
194 | // controller.selectedDates;
195 | });
196 | ```
197 |
198 | ### 2.RCalendarCustomWidget
199 |
200 | 自定义 日历小部件
201 |
202 | ```dart
203 |
204 | class MyRCalendarCustomWidget extends RCalendarCustomWidget {
205 | // 如果你想设置第一天是星期一,请更改MaterialLocalizations 的firstDayOfWeekIndex
206 | // 日 一 二 三 四 五 六
207 | //构建头部
208 | @override
209 | List buildWeekListWidget(MaterialLocalizations localizations){...};
210 |
211 | // 1 2 3 4 5 6 7
212 | //构建普通的日期
213 | @override
214 | Widget buildDateTime(DateTime time, List types){...};
215 |
216 | // < 2019年 11月 >
217 | //构建年份和月份 左指示器、右指示器,返回null就没有
218 | @override
219 | Widget buildTopWidget(RCalendarController controller){...};
220 |
221 | //是否不可用,不可用时,无点击事件
222 | @override
223 | bool isUnable(DateTime time, bool isSameMonth){...};
224 |
225 | //点击拦截,当返回true时进行拦截,就不会改变选中日期
226 | @override
227 | FutureOr clickInterceptor(DateTime dateTime){...};
228 |
229 | //子view的高度
230 | @override
231 | double get childHeight=>{...};
232 | }
233 |
234 | ```
235 |
236 | ## 3.使用小部件
237 |
238 | ### 3.Use the widget.
239 |
240 | ```dart
241 | import 'package:flutter/material.dart';
242 | import 'package:r_calendar/r_calendar.dart';
243 |
244 | void main() => runApp(MyApp());
245 | class MyApp extends StatelessWidget {
246 | @override
247 | Widget build(BuildContext context) {
248 | return MaterialApp(
249 | title: 'Flutter Demo',
250 | theme: ThemeData(
251 | primarySwatch: Colors.blue,
252 | ),
253 | home: MyHomePage(title: 'Flutter Demo Home Page'),
254 | );
255 | }
256 | }
257 |
258 | class MyHomePage extends StatefulWidget {
259 | @override
260 | _MyHomePageState createState() => _MyHomePageState();
261 | }
262 | class _MyHomePageState extends State {
263 | RCalendarController controller;
264 |
265 | @override
266 | void initState() {
267 | super.initState();
268 | controller = RCalendarController.multiple(selectedDates: [
269 | DateTime(2019, 12, 1),
270 | DateTime(2019, 12, 2),
271 | DateTime(2019, 12, 3),
272 | ]);
273 | // controller = RCalendarController.single(selectedDate: DateTime.now(),isAutoSelect: true);
274 | }
275 |
276 | @override
277 | void dispose() {
278 | controller.dispose();
279 | super.dispose();
280 | }
281 |
282 | @override
283 | Widget build(BuildContext context) {
284 | return Scaffold(
285 | body: RCalendarWidget(
286 | controller: controller,
287 | customWidget: DefaultRCalendarCustomWidget(),
288 | firstDate: DateTime(1970, 1, 1), //当前日历的最小日期
289 | lastDate: DateTime(2055, 12, 31),//当前日历的最大日期
290 | ),
291 | );
292 | }
293 | }
294 |
295 | ```
--------------------------------------------------------------------------------
/r_calendar/example/lib/en.arb:
--------------------------------------------------------------------------------
1 | {
2 | "more":"more",
3 | "single":"single",
4 | "alone":"alone",
5 | "together":"together",
6 | "add":"add",
7 | "reduce":"reduce",
8 | "automatic":"automatic",
9 | "manual":"manual",
10 | "month":"month",
11 | "week":"week"
12 | }
--------------------------------------------------------------------------------
/r_calendar/example/lib/l10n/en.dart:
--------------------------------------------------------------------------------
1 | part of s;
2 |
3 | // Generated by flutter_l10n. Do not edit it.
4 | class $en extends S {
5 | const $en();
6 |
7 | @override
8 | TextDirection get textDirection => TextDirection.ltr;
9 |
10 | @override
11 | String get more => r'more';
12 |
13 | @override
14 | String get single => r'single';
15 |
16 | @override
17 | String get alone => r'alone';
18 |
19 | @override
20 | String get together => r'together';
21 |
22 | @override
23 | String get add => r'add';
24 |
25 | @override
26 | String get reduce => r'reduce';
27 |
28 | @override
29 | String get automatic => r'automatic';
30 |
31 | @override
32 | String get manual => r'manual';
33 |
34 | @override
35 | String get month => r'month';
36 |
37 | @override
38 | String get week => r'week';
39 | }
40 |
--------------------------------------------------------------------------------
/r_calendar/example/lib/l10n/s.dart:
--------------------------------------------------------------------------------
1 | library s;
2 |
3 | import 'package:flutter/foundation.dart';
4 | import 'package:flutter/widgets.dart';
5 |
6 | part 'zh.dart';
7 | part 'en.dart';
8 |
9 | // Generated by flutter_l10n. Do not edit it.
10 | class S implements WidgetsLocalizations {
11 | const S();
12 |
13 | @override
14 | TextDirection get textDirection => TextDirection.ltr;
15 |
16 | static S of(BuildContext context) => Localizations.of(context, S)!;
17 |
18 | static const LocalizationsDelegate delegate = _Delegate();
19 |
20 | String get more => r'多选';
21 |
22 | String get single => r'单选';
23 |
24 | String get alone => r'单独';
25 |
26 | String get together => r'一起';
27 |
28 | String get add => r'加';
29 |
30 | String get reduce => r'减';
31 |
32 | String get automatic => r'自动';
33 |
34 | String get manual => r'手动';
35 |
36 | String get month => r'月';
37 |
38 | String get week => r'周';
39 |
40 | @override
41 | String get reorderItemDown => 'down';
42 |
43 | @override
44 | String get reorderItemLeft => 'left';
45 |
46 | @override
47 | String get reorderItemRight => 'right';
48 |
49 | @override
50 | String get reorderItemToEnd => 'end';
51 |
52 | @override
53 | String get reorderItemToStart => 'start';
54 |
55 | @override
56 | String get reorderItemUp => 'up';
57 | }
58 |
59 | class _Delegate extends LocalizationsDelegate {
60 | const _Delegate();
61 |
62 | @override
63 | Future load(Locale locale) {
64 | String tag = locale.countryCode == null || locale.countryCode!.isEmpty
65 | ? locale.languageCode
66 | : locale.toString();
67 | switch (tag) {
68 | case 'zh':
69 | return SynchronousFuture(const $zh());
70 | case 'en':
71 | return SynchronousFuture(const $en());
72 | }
73 | return SynchronousFuture(const S());
74 | }
75 |
76 | @override
77 | bool isSupported(Locale locale) => true;
78 |
79 | @override
80 | bool shouldReload(_Delegate old) => false;
81 | }
82 |
--------------------------------------------------------------------------------
/r_calendar/example/lib/l10n/zh.dart:
--------------------------------------------------------------------------------
1 | part of s;
2 |
3 | // Generated by flutter_l10n. Do not edit it.
4 | class $zh extends S {
5 | const $zh();
6 |
7 | @override
8 | TextDirection get textDirection => TextDirection.ltr;
9 |
10 | @override
11 | String get more => r'多选';
12 |
13 | @override
14 | String get single => r'单选';
15 |
16 | @override
17 | String get alone => r'单独';
18 |
19 | @override
20 | String get together => r'一起';
21 |
22 | @override
23 | String get add => r'加';
24 |
25 | @override
26 | String get reduce => r'减';
27 |
28 | @override
29 | String get automatic => r'自动';
30 |
31 | @override
32 | String get manual => r'手动';
33 |
34 | @override
35 | String get month => r'月';
36 |
37 | @override
38 | String get week => r'周';
39 | }
40 |
--------------------------------------------------------------------------------
/r_calendar/example/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_localizations/flutter_localizations.dart';
5 | import 'package:r_calendar/r_calendar.dart';
6 | import 'package:r_calendar_example/l10n/s.dart';
7 | import 'package:intl/intl.dart';
8 |
9 | void main() => runApp(MyApp());
10 |
11 | class MyApp extends StatefulWidget {
12 | @override
13 | _MyAppState createState() => _MyAppState();
14 | }
15 |
16 | class _MyAppState extends State {
17 | @override
18 | void initState() {
19 | super.initState();
20 | }
21 |
22 | @override
23 | Widget build(BuildContext context) {
24 | return MaterialApp(
25 | title: 'Flutter Demo',
26 | theme: ThemeData(
27 | primarySwatch: Colors.blue,
28 | ),
29 | localizationsDelegates: [
30 | S.delegate,
31 | GlobalMaterialLocalizations.delegate,
32 | GlobalWidgetsLocalizations.delegate
33 | ],
34 | supportedLocales: [Locale('zh', ''), Locale('en', '')],
35 | home: MyHomePage(title: 'Flutter Demo Home Page'),
36 | );
37 | }
38 | }
39 |
40 | class MyHomePage extends StatefulWidget {
41 | MyHomePage({super. key, required this.title});
42 |
43 | final String title;
44 |
45 | @override
46 | _MyHomePageState createState() => _MyHomePageState();
47 | }
48 |
49 | class _MyHomePageState extends State {
50 | late RCalendarController> controller;
51 |
52 | @override
53 | void initState() {
54 | super.initState();
55 | controller =
56 | // RCalendarController.multiple(
57 | // selectedDates: [
58 | // DateTime(2019, 12, 1),
59 | // DateTime(2019, 12, 2),
60 | // DateTime(2019, 12, 3),
61 | // ],
62 | // isDispersion: false,
63 | // )
64 | RCalendarController.single(
65 | isAutoSelect: true,
66 | //mark day
67 | initialData: [
68 | // DateTime.now(),
69 | // DateTime.now().add(Duration(days: 1)),
70 | // DateTime.now().add(Duration(days: 2)),
71 | ]
72 | // selectedDate: DateTime.now(),
73 | )
74 | ..addListener(() {
75 | // controller.isMultiple
76 |
77 | // single selected
78 | // controller.isAutoSelect
79 | // controller.selectedDate;
80 |
81 | // multiple selected
82 | // controller.selectedDates;
83 | // controller.isDispersion;
84 | });
85 | }
86 |
87 | @override
88 | void dispose() {
89 | controller.dispose();
90 | super.dispose();
91 | }
92 |
93 | @override
94 | Widget build(BuildContext context) {
95 | return Scaffold(
96 | appBar: AppBar(
97 | title: Text(widget.title),
98 | ),
99 | body: Container(
100 | decoration: BoxDecoration(
101 | color: Colors.grey[200],
102 | ),
103 | margin: EdgeInsets.symmetric(
104 | horizontal: 8,
105 | ),
106 | child: RCalendarWidget(
107 | controller: controller,
108 | customWidget: MyRCalendarCustomWidget(),
109 | firstDate: DateTime(1970, 1, 1),
110 | lastDate: DateTime(2055, 12, 31),
111 | ),
112 | // child: RecordDatePicker(
113 | // selectedDate: DateTime.now(),
114 | // firstDate: DateTime(1970, 1, 1),
115 | // lastDate: DateTime(2055, 12, 31),
116 | // children: List.generate(20, (index)=>ListTile(
117 | // title: Text('data $index'),
118 | // )),
119 | // ),
120 | ),
121 | floatingActionButton: Row(
122 | mainAxisSize: MainAxisSize.min,
123 | children: [
124 | FloatingActionButton(
125 | onPressed: () {
126 | setState(() {
127 | if (controller.isMonthMode) {
128 | controller.mode = RCalendarMode.week;
129 | } else {
130 | controller.mode = RCalendarMode.month;
131 | }
132 | });
133 | },
134 | tooltip: controller.isMonthMode
135 | ? S.of(context).month
136 | : S.of(context).week,
137 | child: Text(
138 | controller.isMonthMode ? S.of(context).month : S.of(context).week,
139 | style: TextStyle(
140 | color: Colors.white,
141 | ),
142 | maxLines: 1,
143 | semanticsLabel: controller.isMonthMode
144 | ? S.of(context).month
145 | : S.of(context).week,
146 | ),
147 | ),
148 | SizedBox(
149 | width: 20,
150 | ),
151 | FloatingActionButton(
152 | onPressed: () {
153 | setState(() {
154 | controller.isMultiple = !controller.isMultiple;
155 | });
156 | },
157 | tooltip: controller.isMultiple
158 | ? S.of(context).more
159 | : S.of(context).single,
160 | child: Text(
161 | controller.isMultiple ? S.of(context).more : S.of(context).single,
162 | style: TextStyle(
163 | color: Colors.white,
164 | ),
165 | maxLines: 1,
166 | semanticsLabel: controller.isMultiple
167 | ? S.of(context).more
168 | : S.of(context).single,
169 | ),
170 | ),
171 | SizedBox(
172 | width: 20,
173 | ),
174 | ...controller.isMultiple
175 | ? [
176 | FloatingActionButton(
177 | onPressed: () {
178 | setState(() {
179 | controller.isDispersion = !controller.isDispersion;
180 | });
181 | },
182 | tooltip: controller.isDispersion
183 | ? S.of(context).alone
184 | : S.of(context).together,
185 | child: Text(
186 | controller.isDispersion
187 | ? S.of(context).alone
188 | : S.of(context).together,
189 | style: TextStyle(
190 | color: Colors.white,
191 | ),
192 | maxLines: 1,
193 | semanticsLabel: controller.isDispersion
194 | ? S.of(context).alone
195 | : S.of(context).together,
196 | ),
197 | ),
198 | ]
199 | : [
200 | FloatingActionButton(
201 | onPressed: () {
202 | setState(() {
203 | controller.isAutoSelect = !controller.isAutoSelect;
204 | });
205 | },
206 | tooltip: controller.isAutoSelect
207 | ? S.of(context).automatic
208 | : S.of(context).manual,
209 | child: Text(
210 | controller.isAutoSelect
211 | ? S.of(context).automatic
212 | : S.of(context).manual,
213 | style: TextStyle(
214 | color: Colors.white,
215 | ),
216 | maxLines: 1,
217 | semanticsLabel: controller.isAutoSelect
218 | ? S.of(context).automatic
219 | : S.of(context).manual,
220 | ),
221 | ),
222 | SizedBox(
223 | width: 20,
224 | ),
225 | FloatingActionButton(
226 | onPressed: () {
227 | setState(() {
228 | controller.selectedDate =
229 | controller.selectedDate?.add(Duration(days: 1));
230 | });
231 | },
232 | tooltip: S.of(context).add,
233 | child: Icon(Icons.add),
234 | ),
235 | SizedBox(
236 | width: 20,
237 | ),
238 | FloatingActionButton(
239 | onPressed: () {
240 | setState(() {
241 | controller.selectedDate = controller.selectedDate
242 | ?.subtract(Duration(days: 1));
243 | });
244 | },
245 | tooltip: S.of(context).reduce,
246 | child: Icon(Icons.remove),
247 | )
248 | ],
249 | ],
250 | ),
251 | );
252 | }
253 | }
254 |
255 | class MyRCalendarCustomWidget extends RCalendarCustomWidget {
256 | @override
257 | Widget buildDateTime(
258 | BuildContext context, DateTime time, List types) {
259 | // new
260 | RCalendarController> controller = RCalendarMarker.of(context)
261 | ?.notifier as RCalendarController>;
262 | // new
263 | TextStyle? childStyle;
264 | BoxDecoration? decoration;
265 | if (types.contains(RCalendarType.disable) ||
266 | types.contains(RCalendarType.differentMonth)) {
267 | childStyle = TextStyle(
268 | color: Colors.grey[400],
269 | fontSize: 18,
270 | );
271 | decoration = BoxDecoration();
272 | }
273 | if (types.contains(RCalendarType.normal)) {
274 | childStyle = TextStyle(
275 | color: Colors.black,
276 | fontSize: 18,
277 | );
278 | decoration = BoxDecoration();
279 | }
280 |
281 | if (types.contains(RCalendarType.today)) {
282 | childStyle = TextStyle(
283 | color: Colors.blue,
284 | fontSize: 18,
285 | );
286 | }
287 |
288 | if (types.contains(RCalendarType.selected)) {
289 | childStyle = TextStyle(
290 | color: Colors.white,
291 | fontSize: 18,
292 | );
293 | decoration = BoxDecoration(
294 | shape: BoxShape.circle,
295 | color: Colors.blue,
296 | );
297 | }
298 | Widget child = Container(
299 | decoration: decoration,
300 | alignment: Alignment.center,
301 | padding: EdgeInsets.only(
302 | bottom: 10,
303 | ),
304 | child: Text(
305 | time.day.toString(),
306 | style: childStyle,
307 | ));
308 | //get mark day
309 | if (controller.data!
310 | .where((m) =>
311 | time.year == m.year && time.month == m.month && time.day == m.day)
312 | .isNotEmpty) {
313 | child = Stack(
314 | children: [
315 | child,
316 | Positioned(
317 | bottom: 0,
318 | left: 0,
319 | right: 0,
320 | child: Icon(
321 | Icons.star,
322 | size: 14,
323 | color: Colors.grey,
324 | )),
325 | ],
326 | );
327 | } else {
328 | String festal = time.specialFestival;
329 | if (festal.isEmpty) {
330 | festal = time.traditionFestival;
331 | }
332 | if (festal.isEmpty) {
333 | festal = time.gregorianFestival;
334 | }
335 | if (festal.isEmpty) {
336 | festal = time.numToChinese;
337 | }
338 | if (festal.isNotEmpty) {
339 | child = Stack(
340 | children: [
341 | child,
342 | Positioned(
343 | bottom: 6,
344 | left: 0,
345 | right: 0,
346 | child: Text(
347 | festal,
348 | textAlign: TextAlign.center,
349 | style: childStyle?.copyWith(
350 | fontSize: 11,
351 | ),
352 | ),
353 | ),
354 | ],
355 | );
356 | }
357 | }
358 |
359 | return Tooltip(
360 | message: MaterialLocalizations.of(context).formatFullDate(time),
361 | child: child,
362 | );
363 | }
364 |
365 | @override
366 | List buildWeekListWidget(
367 | BuildContext context, MaterialLocalizations localizations) {
368 | return localizations.narrowWeekdays
369 | .map(
370 | (d) => Expanded(
371 | child: ExcludeSemantics(
372 | child: Container(
373 | height: 60,
374 | alignment: Alignment.center,
375 | child: Text(
376 | d,
377 | style: TextStyle(
378 | color: Colors.black,
379 | fontSize: 18,
380 | fontWeight: FontWeight.w600),
381 | ),
382 | ),
383 | ),
384 | ),
385 | )
386 | .toList();
387 | }
388 |
389 | @override
390 | double get childHeight => 50;
391 |
392 | @override
393 | FutureOr clickInterceptor(BuildContext context, DateTime dateTime) {
394 | return false;
395 | }
396 |
397 | @override
398 | bool isUnable(BuildContext context, DateTime time, bool isSameMonth) {
399 | return isSameMonth;
400 | }
401 |
402 | @override
403 | Widget buildTopWidget(BuildContext context, RCalendarController? controller) {
404 | return Row(
405 | mainAxisAlignment: MainAxisAlignment.center,
406 | children: [
407 | IconButton(
408 | icon: Icon(Icons.chevron_left),
409 | onPressed: () {
410 | controller?.previousPage();
411 | },
412 | ),
413 | SizedBox(
414 | width: 16,
415 | ),
416 | Text(
417 | DateFormat('yyyy-MM').format(controller?.displayedMonthDate??DateTime.now()),
418 | style: TextStyle(color: Colors.red, fontSize: 18),
419 | ),
420 | SizedBox(
421 | width: 16,
422 | ),
423 | IconButton(
424 | icon: Icon(Icons.chevron_right),
425 | onPressed: () {
426 | controller?.nextPage();
427 | },
428 | ),
429 | ],
430 | );
431 | }
432 | }
433 |
--------------------------------------------------------------------------------
/r_calendar/example/lib/zh.arb:
--------------------------------------------------------------------------------
1 | {
2 | "more":"多选",
3 | "single":"单选",
4 | "alone":"单独",
5 | "together":"一起",
6 | "add":"加",
7 | "reduce":"减",
8 | "automatic":"自动",
9 | "manual":"手动",
10 | "month":"月",
11 | "week":"周"
12 | }
--------------------------------------------------------------------------------
/r_calendar/example/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | _fe_analyzer_shared:
5 | dependency: transitive
6 | description:
7 | name: _fe_analyzer_shared
8 | sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051
9 | url: "https://pub.flutter-io.cn"
10 | source: hosted
11 | version: "64.0.0"
12 | analyzer:
13 | dependency: transitive
14 | description:
15 | name: analyzer
16 | sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893"
17 | url: "https://pub.flutter-io.cn"
18 | source: hosted
19 | version: "6.2.0"
20 | args:
21 | dependency: transitive
22 | description:
23 | name: args
24 | sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
25 | url: "https://pub.flutter-io.cn"
26 | source: hosted
27 | version: "2.4.2"
28 | async:
29 | dependency: transitive
30 | description:
31 | name: async
32 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
33 | url: "https://pub.flutter-io.cn"
34 | source: hosted
35 | version: "2.11.0"
36 | boolean_selector:
37 | dependency: transitive
38 | description:
39 | name: boolean_selector
40 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
41 | url: "https://pub.flutter-io.cn"
42 | source: hosted
43 | version: "2.1.1"
44 | characters:
45 | dependency: transitive
46 | description:
47 | name: characters
48 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
49 | url: "https://pub.flutter-io.cn"
50 | source: hosted
51 | version: "1.3.0"
52 | clock:
53 | dependency: transitive
54 | description:
55 | name: clock
56 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
57 | url: "https://pub.flutter-io.cn"
58 | source: hosted
59 | version: "1.1.1"
60 | collection:
61 | dependency: transitive
62 | description:
63 | name: collection
64 | sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
65 | url: "https://pub.flutter-io.cn"
66 | source: hosted
67 | version: "1.18.0"
68 | convert:
69 | dependency: transitive
70 | description:
71 | name: convert
72 | sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
73 | url: "https://pub.flutter-io.cn"
74 | source: hosted
75 | version: "3.1.1"
76 | crypto:
77 | dependency: transitive
78 | description:
79 | name: crypto
80 | sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
81 | url: "https://pub.flutter-io.cn"
82 | source: hosted
83 | version: "3.0.3"
84 | cupertino_icons:
85 | dependency: "direct main"
86 | description:
87 | name: cupertino_icons
88 | sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d
89 | url: "https://pub.flutter-io.cn"
90 | source: hosted
91 | version: "1.0.6"
92 | dart_style:
93 | dependency: transitive
94 | description:
95 | name: dart_style
96 | sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9"
97 | url: "https://pub.flutter-io.cn"
98 | source: hosted
99 | version: "2.3.6"
100 | fake_async:
101 | dependency: transitive
102 | description:
103 | name: fake_async
104 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
105 | url: "https://pub.flutter-io.cn"
106 | source: hosted
107 | version: "1.3.1"
108 | file:
109 | dependency: transitive
110 | description:
111 | name: file
112 | sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
113 | url: "https://pub.flutter-io.cn"
114 | source: hosted
115 | version: "7.0.0"
116 | flutter:
117 | dependency: "direct main"
118 | description: flutter
119 | source: sdk
120 | version: "0.0.0"
121 | flutter_l10n:
122 | dependency: "direct dev"
123 | description:
124 | name: flutter_l10n
125 | sha256: c756871e2c20dcdc89385254069f232159b1621d93f0e76e0a7af4e486c2617c
126 | url: "https://pub.flutter-io.cn"
127 | source: hosted
128 | version: "1.1.2"
129 | flutter_localizations:
130 | dependency: "direct main"
131 | description: flutter
132 | source: sdk
133 | version: "0.0.0"
134 | flutter_test:
135 | dependency: "direct dev"
136 | description: flutter
137 | source: sdk
138 | version: "0.0.0"
139 | glob:
140 | dependency: transitive
141 | description:
142 | name: glob
143 | sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
144 | url: "https://pub.flutter-io.cn"
145 | source: hosted
146 | version: "2.1.2"
147 | intl:
148 | dependency: "direct main"
149 | description:
150 | name: intl
151 | sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
152 | url: "https://pub.flutter-io.cn"
153 | source: hosted
154 | version: "0.18.1"
155 | matcher:
156 | dependency: transitive
157 | description:
158 | name: matcher
159 | sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
160 | url: "https://pub.flutter-io.cn"
161 | source: hosted
162 | version: "0.12.16"
163 | material_color_utilities:
164 | dependency: transitive
165 | description:
166 | name: material_color_utilities
167 | sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
168 | url: "https://pub.flutter-io.cn"
169 | source: hosted
170 | version: "0.5.0"
171 | meta:
172 | dependency: transitive
173 | description:
174 | name: meta
175 | sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
176 | url: "https://pub.flutter-io.cn"
177 | source: hosted
178 | version: "1.10.0"
179 | package_config:
180 | dependency: transitive
181 | description:
182 | name: package_config
183 | sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd"
184 | url: "https://pub.flutter-io.cn"
185 | source: hosted
186 | version: "2.1.0"
187 | path:
188 | dependency: transitive
189 | description:
190 | name: path
191 | sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
192 | url: "https://pub.flutter-io.cn"
193 | source: hosted
194 | version: "1.8.3"
195 | pub_semver:
196 | dependency: transitive
197 | description:
198 | name: pub_semver
199 | sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c"
200 | url: "https://pub.flutter-io.cn"
201 | source: hosted
202 | version: "2.1.4"
203 | r_calendar:
204 | dependency: "direct main"
205 | description:
206 | path: ".."
207 | relative: true
208 | source: path
209 | version: "0.1.11"
210 | sky_engine:
211 | dependency: transitive
212 | description: flutter
213 | source: sdk
214 | version: "0.0.99"
215 | source_span:
216 | dependency: transitive
217 | description:
218 | name: source_span
219 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
220 | url: "https://pub.flutter-io.cn"
221 | source: hosted
222 | version: "1.10.0"
223 | stack_trace:
224 | dependency: transitive
225 | description:
226 | name: stack_trace
227 | sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
228 | url: "https://pub.flutter-io.cn"
229 | source: hosted
230 | version: "1.11.1"
231 | stream_channel:
232 | dependency: transitive
233 | description:
234 | name: stream_channel
235 | sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
236 | url: "https://pub.flutter-io.cn"
237 | source: hosted
238 | version: "2.1.2"
239 | string_scanner:
240 | dependency: transitive
241 | description:
242 | name: string_scanner
243 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
244 | url: "https://pub.flutter-io.cn"
245 | source: hosted
246 | version: "1.2.0"
247 | term_glyph:
248 | dependency: transitive
249 | description:
250 | name: term_glyph
251 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
252 | url: "https://pub.flutter-io.cn"
253 | source: hosted
254 | version: "1.2.1"
255 | test_api:
256 | dependency: transitive
257 | description:
258 | name: test_api
259 | sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
260 | url: "https://pub.flutter-io.cn"
261 | source: hosted
262 | version: "0.6.1"
263 | typed_data:
264 | dependency: transitive
265 | description:
266 | name: typed_data
267 | sha256: "53bdf7e979cfbf3e28987552fd72f637e63f3c8724c9e56d9246942dc2fa36ee"
268 | url: "https://pub.flutter-io.cn"
269 | source: hosted
270 | version: "1.3.0"
271 | vector_math:
272 | dependency: transitive
273 | description:
274 | name: vector_math
275 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
276 | url: "https://pub.flutter-io.cn"
277 | source: hosted
278 | version: "2.1.4"
279 | watcher:
280 | dependency: transitive
281 | description:
282 | name: watcher
283 | sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
284 | url: "https://pub.flutter-io.cn"
285 | source: hosted
286 | version: "1.1.0"
287 | web:
288 | dependency: transitive
289 | description:
290 | name: web
291 | sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
292 | url: "https://pub.flutter-io.cn"
293 | source: hosted
294 | version: "0.3.0"
295 | yaml:
296 | dependency: transitive
297 | description:
298 | name: yaml
299 | sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
300 | url: "https://pub.flutter-io.cn"
301 | source: hosted
302 | version: "3.1.2"
303 | sdks:
304 | dart: ">=3.2.3 <4.0.0"
305 |
--------------------------------------------------------------------------------
/r_calendar/example/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: r_calendar_example
2 | description: A new Flutter application.
3 |
4 | # The following defines the version and build number for your application.
5 | # A version number is three numbers separated by dots, like 1.2.43
6 | # followed by an optional build number separated by a +.
7 | # Both the version and the builder number may be overridden in flutter
8 | # build by specifying --build-name and --build-number, respectively.
9 | # In Android, build-name is used as versionName while build-number used as versionCode.
10 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning
11 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
12 | # Read more about iOS versioning at
13 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
14 | version: 1.0.0+1
15 |
16 | environment:
17 | sdk: '>=3.2.3 <4.0.0'
18 |
19 | dependencies:
20 | flutter:
21 | sdk: flutter
22 | flutter_localizations:
23 | sdk: flutter
24 | # The following adds the Cupertino Icons font to your application.
25 | # Use with the CupertinoIcons class for iOS style icons.
26 | cupertino_icons: ^1.0.6
27 | r_calendar:
28 | path: ../
29 | intl: ^0.18.1
30 | dev_dependencies:
31 | flutter_test:
32 | sdk: flutter
33 | flutter_l10n: ^1.1.2
34 |
35 | # For information on the generic Dart part of this file, see the
36 | # following page: https://dart.dev/tools/pub/pubspec
37 |
38 | # The following section is specific to Flutter.
39 | flutter:
40 | # The following line ensures that the Material Icons font is
41 | # included with your application, so that you can use the icons in
42 | # the material Icons class.
43 | uses-material-design: true
44 |
45 | # To add assets to your application, add an assets section, like this:
46 | # assets:
47 | # - images/a_dot_burr.jpeg
48 | # - images/a_dot_ham.jpeg
49 |
50 | # An image asset can refer to one or more resolution-specific "variants", see
51 | # https://flutter.dev/assets-and-images/#resolution-aware.
52 |
53 | # For details regarding adding assets from package dependencies, see
54 | # https://flutter.dev/assets-and-images/#from-packages
55 |
56 | # To add custom fonts to your application, add a fonts section here,
57 | # in this "flutter" section. Each entry in this list should have a
58 | # "family" key with the font family name, and a "fonts" key with a
59 | # list giving the asset and other descriptors for the font. For
60 | # example:
61 | # fonts:
62 | # - family: Schyler
63 | # fonts:
64 | # - asset: fonts/Schyler-Regular.ttf
65 | # - asset: fonts/Schyler-Italic.ttf
66 | # style: italic
67 | # - family: Trajan Pro
68 | # fonts:
69 | # - asset: fonts/TrajanPro.ttf
70 | # - asset: fonts/TrajanPro_Bold.ttf
71 | # weight: 700
72 | #
73 | # For details regarding fonts from package dependencies,
74 | # see https://flutter.dev/custom-fonts/#from-packages
75 |
--------------------------------------------------------------------------------
/r_calendar/example/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | //
3 | // To perform an interaction with a widget in your test, use the WidgetTester
4 | // utility that Flutter provides. For example, you can send tap and scroll
5 | // gestures. You can also use WidgetTester to find child widgets in the widget
6 | // tree, read text, and verify that the values of widget properties are correct.
7 |
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_test/flutter_test.dart';
10 |
11 | import 'package:r_calendar_example/main.dart';
12 |
13 | void main() {
14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async {
15 | // Build our app and trigger a frame.
16 | await tester.pumpWidget(MyApp());
17 |
18 | // Verify that our counter starts at 0.
19 | expect(find.text('0'), findsOneWidget);
20 | expect(find.text('1'), findsNothing);
21 |
22 | // Tap the '+' icon and trigger a frame.
23 | await tester.tap(find.byIcon(Icons.add));
24 | await tester.pump();
25 |
26 | // Verify that our counter has incremented.
27 | expect(find.text('0'), findsNothing);
28 | expect(find.text('1'), findsOneWidget);
29 | });
30 | }
31 |
--------------------------------------------------------------------------------
/r_calendar/lib/r_calendar.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2019 The rhyme_lph Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style license that can be
3 | // found in the LICENSE file.
4 |
5 | library r_calendar;
6 |
7 | import 'dart:async';
8 |
9 | import 'package:flutter/material.dart';
10 | import 'package:r_calendar/src/r_calendar_utils.dart';
11 | import 'package:r_calendar/src/r_calendar_widget_item.dart';
12 |
13 | import 'src/r_calendar_controller.dart';
14 | import 'src/r_calendar_custom_widget.dart';
15 | export 'src/r_calendar_controller.dart';
16 | export 'src/r_calendar_custom_widget.dart';
17 | export 'src/r_calendar_widget_item.dart';
18 | export 'src/r_calendar_extension.dart';
19 |
20 | class RCalendarWidget extends StatefulWidget {
21 | // 最小日期
22 | final DateTime? firstDate;
23 |
24 | // 最大日期
25 | final DateTime? lastDate;
26 |
27 | // 控制器
28 | final RCalendarController? controller;
29 |
30 | //自定义部件
31 | final RCalendarCustomWidget? customWidget;
32 |
33 | const RCalendarWidget(
34 | {Key? key,
35 | this.firstDate,
36 | this.lastDate,
37 | this.controller,
38 | this.customWidget})
39 | : super(key: key);
40 |
41 | @override
42 | _RCalendarWidgetState createState() => _RCalendarWidgetState();
43 | }
44 |
45 | class _RCalendarWidgetState extends State {
46 | //今天的日期
47 | DateTime? _toDayDate;
48 |
49 | //用于更新今天
50 | Timer? _timer;
51 |
52 | ///选中日期更改
53 | void _handleDayChanged(DateTime value) {
54 | setState(() {
55 | widget.controller!.updateSelected(value);
56 | });
57 | }
58 |
59 | /// 更新当前日期
60 | void _updateCurrentDate() {
61 | _toDayDate = DateTime.now();
62 | final DateTime tomorrow =
63 | DateTime(_toDayDate!.year, _toDayDate!.month, _toDayDate!.day + 1);
64 | Duration timeUntilTomorrow = tomorrow.difference(_toDayDate!);
65 | timeUntilTomorrow +=
66 | const Duration(seconds: 1); // so we don't miss it by rounding
67 | _timer?.cancel();
68 | _timer = Timer(timeUntilTomorrow, () {
69 | setState(() {
70 | _updateCurrentDate();
71 | });
72 | });
73 | }
74 |
75 | ///处理月份页改变
76 | void _handlePageChanged(int page, RCalendarMode mode) {
77 | setState(() {
78 | widget.controller!.updateDisplayedDate(page, mode);
79 | });
80 | }
81 |
82 | @override
83 | void initState() {
84 | super.initState();
85 | widget.controller!.initial(context, widget.firstDate!, widget.lastDate);
86 | _updateCurrentDate();
87 | }
88 |
89 | @override
90 | void didUpdateWidget(RCalendarWidget oldWidget) {
91 | if (widget.controller!.isMultiple == false) {
92 | DateTime? firstDate = widget.controller!.selectedDate;
93 | if (firstDate != null) {
94 | if (firstDate.month != widget.controller!.displayedMonthDate!.month ||
95 | firstDate.year != widget.controller!.displayedMonthDate!.year) {
96 | if (widget.controller!.isMonthMode) {
97 | widget.controller!.monthController!
98 | .jumpToPage(widget.controller!.selectedPage);
99 | } else {
100 | widget.controller!.weekController!
101 | .jumpToPage(widget.controller!.selectedPage);
102 | }
103 | }
104 | }
105 | }
106 |
107 | super.didUpdateWidget(oldWidget);
108 | }
109 |
110 | int _getSelectRowCount() {
111 | if (!widget.controller!.isMonthMode) return 1;
112 | int rowCount = 4;
113 | int maxDay = RCalendarUtils.getDaysInMonth(
114 | widget.controller!.displayedMonthDate!.year,
115 | widget.controller!.displayedMonthDate!.month);
116 | int labelCount = maxDay +
117 | RCalendarUtils.computeFirstDayOffset(
118 | widget.controller!.displayedMonthDate!.year,
119 | widget.controller!.displayedMonthDate!.month,
120 | MaterialLocalizations.of(context));
121 | if (labelCount <= 7 * 4) {
122 | rowCount = 4;
123 | } else if (labelCount <= 7 * 5) {
124 | rowCount = 5;
125 | } else if (labelCount <= 7 * 6) {
126 | rowCount = 6;
127 | }
128 | return rowCount;
129 | }
130 |
131 | @override
132 | Widget build(BuildContext context) {
133 | double maxHeight =
134 | widget.customWidget!.childHeight * _getSelectRowCount() + 1;
135 | //获取星期的第一天
136 | final MaterialLocalizations localizations =
137 | MaterialLocalizations.of(context);
138 | return RCalendarMarker(
139 | customWidget: widget.customWidget,
140 | toDayDate: _toDayDate,
141 | onChanged: _handleDayChanged,
142 | controller: widget.controller!,
143 | child: Column(
144 | mainAxisSize: MainAxisSize.min,
145 | children: [
146 | widget.customWidget?.buildTopWidget(context, widget.controller) ??
147 | Container(),
148 | Row(
149 | children:
150 | widget.customWidget!.buildWeekListWidget(context, localizations),
151 | ),
152 | AnimatedContainer(
153 | duration: Duration(milliseconds: 300),
154 | height: maxHeight,
155 | child: IndexedStack(
156 | index: widget.controller!.isMonthMode ? 1 : 0,
157 | children: [
158 | PageView.builder(
159 | key: ValueKey("week_view"),
160 | controller: widget.controller!.weekController,
161 | itemBuilder: _builderWeekItems,
162 | onPageChanged: (int page) {
163 | _handlePageChanged(page, RCalendarMode.week);
164 | },
165 | itemCount: widget.controller!.maxWeekPage,
166 | allowImplicitScrolling: true,
167 | ),
168 | PageView.builder(
169 | key: ValueKey("month_view"),
170 | controller: widget.controller!.monthController,
171 | itemBuilder: _builderMonthItems,
172 | onPageChanged: (int page) {
173 | _handlePageChanged(page, RCalendarMode.month);
174 | },
175 | itemCount: widget.controller!.maxMonthPage,
176 | allowImplicitScrolling: true,
177 | ),
178 | ],
179 | ),
180 | ),
181 | ],
182 | ),
183 | );
184 | }
185 |
186 | Widget _builderMonthItems(BuildContext context, int index) {
187 | final DateTime month =
188 | RCalendarUtils.addMonthsToMonthDate(widget.firstDate!, index);
189 | return RCalendarMonthItem(
190 | monthDate: month,
191 | );
192 | }
193 |
194 | Widget _builderWeekItems(BuildContext context, int index) {
195 | DateTime week = RCalendarUtils.addWeeksToWeeksDate(
196 | widget.firstDate!, index, MaterialLocalizations.of(context));
197 | return AutoKeepAliveWidget(
198 | child: RCalendarWeekItem(
199 | weekDate: week,
200 | ),
201 | );
202 | }
203 | }
204 |
205 | class AutoKeepAliveWidget extends StatefulWidget {
206 | final Widget? child;
207 |
208 | const AutoKeepAliveWidget({Key? key, this.child}) : super(key: key);
209 |
210 | @override
211 | _AutoKeepAliveWidgetState createState() => _AutoKeepAliveWidgetState();
212 | }
213 |
214 | class _AutoKeepAliveWidgetState extends State
215 | with AutomaticKeepAliveClientMixin {
216 | @override
217 | Widget build(BuildContext context) {
218 | super.build(context);
219 | return widget.child!;
220 | }
221 |
222 | @override
223 | bool get wantKeepAlive => true;
224 | }
225 |
226 | class RCalendarMarker extends InheritedNotifier {
227 | //部件
228 | final RCalendarCustomWidget? customWidget;
229 |
230 | //今天
231 | final DateTime? toDayDate;
232 |
233 | //当前选中的日期事件
234 | final ValueChanged onChanged;
235 |
236 | const RCalendarMarker({
237 | required this.onChanged,
238 | required this.toDayDate,
239 | required this.customWidget,
240 | required RCalendarController controller,
241 | required Widget child,
242 | }) : super(notifier: controller, child: child);
243 |
244 | static RCalendarMarker? of(BuildContext context, {bool nullOk = false}) {
245 | final RCalendarMarker? inherited =
246 | context.dependOnInheritedWidgetOfExactType();
247 | assert(() {
248 | if (nullOk) {
249 | return true;
250 | }
251 | if (inherited == null) {
252 | throw FlutterError(
253 | 'Unable to find a $RCalendarMarker widget in the context.\n'
254 | '$RCalendarMarker.of() was called with a context that does not contain a '
255 | '$RCalendarMarker widget.\n'
256 | 'No $RCalendarMarker ancestor could be found starting from the context that was '
257 | 'passed to $RCalendarMarker.of().\n'
258 | 'The context used was:\n'
259 | ' $context');
260 | }
261 | return true;
262 | }());
263 | return inherited;
264 | }
265 |
266 | @override
267 | bool updateShouldNotify(RCalendarMarker oldWidget) {
268 | return this.notifier != oldWidget.notifier ||
269 | this.toDayDate != toDayDate ||
270 | this.customWidget != customWidget ||
271 | this.onChanged != onChanged;
272 | }
273 | }
274 |
--------------------------------------------------------------------------------
/r_calendar/lib/src/r_calendar_controller.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2019 The rhyme_lph Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style license that can be
3 | // found in the LICENSE file.
4 | import 'dart:collection';
5 |
6 | import 'package:flutter/material.dart';
7 | import 'dart:math' as math;
8 |
9 | import 'package:r_calendar/src/r_calendar_utils.dart';
10 |
11 | enum RCalendarMode {
12 | week,
13 | month,
14 | }
15 |
16 | /// you can use [RCalendarMarker.of(context).notifier] get it.
17 | class RCalendarController extends ChangeNotifier {
18 | RCalendarController.single(
19 | {RCalendarMode? mode,
20 | DateTime? selectedDate,
21 | bool? isAutoSelect,
22 | T? initialData})
23 | : _selectedDates = selectedDate != null
24 | ? SplayTreeSet.of([selectedDate])
25 | : SplayTreeSet(),
26 | _isMultiple = false,
27 | _isDispersion = true,
28 | _isAutoSelect = isAutoSelect ?? true,
29 | _data = initialData,
30 | _mode = mode ?? RCalendarMode.month;
31 |
32 | RCalendarController.multiple(
33 | {RCalendarMode? mode,
34 | required List selectedDates,
35 | bool? isDispersion,
36 | T? initialData})
37 | : _isMultiple = true,
38 | _isDispersion = isDispersion ?? true,
39 | _isAutoSelect = false,
40 | _selectedDates = SplayTreeSet.of(selectedDates),
41 | _data = initialData,
42 | _mode = mode ?? RCalendarMode.month;
43 |
44 | //当前显示的月份
45 | DateTime? displayedMonthDate;
46 |
47 | // 月视图控制器
48 | PageController? monthController;
49 |
50 | // 星期视图控制器
51 | PageController? weekController;
52 |
53 | late DateTime firstDate;
54 |
55 | DateTime? lastDate;
56 |
57 | //多选
58 | SplayTreeSet _selectedDates;
59 |
60 | List get selectedDates {
61 | // _selectedDates.sort(_sortSelectDates);
62 | return _selectedDates.toList();
63 | }
64 |
65 | set selectedDates(List selectedDates) {
66 | _selectedDates = SplayTreeSet.of(selectedDates);
67 | }
68 |
69 | //单选选中的日期
70 | DateTime? get selectedDate =>
71 | _selectedDates.isEmpty ? null : _selectedDates.first;
72 |
73 | set selectedDate(DateTime? selectedDate) {
74 | _selectedDates.remove(_selectedDates.first);
75 | _selectedDates.add(selectedDate);
76 | }
77 |
78 | //单选是否当切换月份时自动选
79 | bool _isAutoSelect;
80 |
81 | bool get isAutoSelect => _isAutoSelect;
82 |
83 | set isAutoSelect(bool isAutoSelect) {
84 | this._isAutoSelect = isAutoSelect;
85 | notifyListeners();
86 | }
87 |
88 | //是否为散选
89 | bool _isDispersion;
90 |
91 | bool get isDispersion => _isDispersion;
92 |
93 | set isDispersion(bool isDispersion) {
94 | _isDispersion = isDispersion;
95 | if (_selectedDates.length > 1 && !isDispersion) {
96 | DateTime first = _selectedDates.first!;
97 | DateTime end = _selectedDates.last!;
98 | Duration duration = end.difference(first);
99 | _selectedDates.clear();
100 | _selectedDates.addAll(List.generate(
101 | duration.inDays, (int index) => first.add(Duration(days: index)))
102 | .toList()
103 | ..add(end));
104 | }
105 | notifyListeners();
106 | }
107 |
108 | //是否为多选
109 | bool _isMultiple;
110 |
111 | bool get isMultiple => _isMultiple;
112 |
113 | set isMultiple(bool isMultiple) {
114 | this._isMultiple = isMultiple;
115 | if (_selectedDates.length > 1 &&
116 | isMultiple == false) {
117 | DateTime? firstDateTime = _selectedDates.first;
118 | _selectedDates.clear();
119 | _selectedDates.add(firstDateTime);
120 | }
121 | notifyListeners();
122 | }
123 |
124 | //日历模式,支持周视图,月视图
125 | RCalendarMode _mode;
126 |
127 | RCalendarMode get mode => _mode;
128 |
129 | set mode(RCalendarMode mode) {
130 | if (_mode != mode) {
131 | _mode = mode;
132 | jumpTo(selectedDate ?? displayedMonthDate);
133 | notifyListeners();
134 | }
135 | }
136 |
137 | bool get isMonthMode => _mode == RCalendarMode.month;
138 |
139 | //拓展数据
140 | T? _data;
141 |
142 | T? get data => _data;
143 |
144 | set data(T? data) {
145 | _data = data;
146 | notifyListeners();
147 | }
148 |
149 | MaterialLocalizations? _localizations;
150 |
151 | void initial(BuildContext context, DateTime firstDate, DateTime? endDate) {
152 | this.firstDate = firstDate;
153 | this.lastDate = endDate;
154 | if (isMultiple) {
155 | // ignore: unnecessary_null_comparison
156 | if(_selectedDates == null){
157 | _selectedDates = SplayTreeSet();
158 | }
159 | } else {
160 | // ignore: unnecessary_null_comparison
161 | if(_selectedDates == null){
162 | _selectedDates = SplayTreeSet.of([
163 | DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day)
164 | ]);
165 | }
166 | }
167 | displayedMonthDate = selectedDate ?? DateTime.now();
168 | final int monthPage =
169 | RCalendarUtils.monthDelta(firstDate, displayedMonthDate!);
170 | monthController = PageController(initialPage: monthPage);
171 | _localizations = DefaultMaterialLocalizations();
172 |
173 | final int weekPage = RCalendarUtils.weekDelta(
174 | firstDate, displayedMonthDate!, _localizations);
175 | weekController = PageController(initialPage: weekPage);
176 | // controller.addListener(() {
177 | // displayMonth = _addMonthsToMonthDate(firstDate, controller.page ~/ 1);
178 | // notifyListeners();
179 | // });
180 | }
181 |
182 | //下一页
183 | void nextPage({Duration? duration, Curve? curve}) {
184 | if (isMonthMode) {
185 | monthController?.nextPage(
186 | duration: duration ?? Duration(milliseconds: 300),
187 | curve: curve ?? Curves.linear);
188 | } else {
189 | weekController?.nextPage(
190 | duration: duration ?? Duration(milliseconds: 300),
191 | curve: curve ?? Curves.linear);
192 | }
193 | }
194 |
195 | //上一页
196 | void previousPage({Duration? duration, Curve? curve}) {
197 | if (isMonthMode) {
198 | monthController?.previousPage(
199 | duration: duration ?? Duration(milliseconds: 300),
200 | curve: curve ?? Curves.linear);
201 | } else {
202 | weekController?.previousPage(
203 | duration: duration ?? Duration(milliseconds: 300),
204 | curve: curve ?? Curves.linear);
205 | }
206 | }
207 |
208 | //跳转到DateTime
209 | void jumpTo(DateTime? dateTime) {
210 | if (isMonthMode) {
211 | final int monthPage = RCalendarUtils.monthDelta(firstDate, dateTime!);
212 | if (monthController != null && monthPage != monthController!.page! ~/ 1) {
213 | // print('更新显示的月份-月份(实际):$monthPage , 当前页数:${monthController.page ~/ 1}');
214 | monthController!.jumpToPage(monthPage);
215 | }
216 | } else {
217 | final int weekPage =
218 | RCalendarUtils.weekDelta(firstDate, dateTime!, _localizations);
219 | if (weekController != null && weekPage != weekController!.page! ~/ 1) {
220 | weekController?.jumpToPage(weekPage);
221 | }
222 | }
223 | }
224 |
225 | /// when you select the dateTime ,will use this method
226 | ///
227 | /// [selectedDate] your select dateTime
228 | ///
229 | void updateSelected(DateTime selectedDate) {
230 | // _selectedDates.sort(_sortSelectDates);
231 | if (isMultiple) {
232 | // multiple select
233 | if (_selectedDates.contains(selectedDate)) {
234 | // your select dateTime in your selectedDates
235 | if (_isDispersion) {
236 | // dispersion select only remove
237 | _selectedDates.remove(selectedDate);
238 | } else {
239 | // //进行移除
240 | // selectedDates.sort((a, b) =>
241 | // a.millisecondsSinceEpoch > b.millisecondsSinceEpoch ? 0 : 1);
242 | if (selectedDate != _selectedDates.first &&
243 | selectedDate != _selectedDates.last) {
244 | List dateTimes = _selectedDates.toList();
245 |
246 | final int index = dateTimes.indexOf(selectedDate);
247 | Duration duration1 = selectedDate.difference(_selectedDates.first!);
248 | Duration duration2 = _selectedDates.last!.difference(selectedDate);
249 | if (duration1.inMilliseconds < duration2.inMilliseconds) {
250 | dateTimes.removeRange(index + 1, _selectedDates.length);
251 | } else {
252 | dateTimes.removeRange(0, index);
253 | }
254 | _selectedDates.clear();
255 | _selectedDates.addAll(dateTimes);
256 | }
257 | }
258 | } else {
259 | if (_isDispersion || _selectedDates.length < 1) {
260 | _selectedDates.add(selectedDate);
261 | } else {
262 | DateTime? first;
263 | DateTime? last;
264 |
265 | if (_selectedDates.length == 1) {
266 | first = _selectedDates.first;
267 | if (first!.isBefore(selectedDate)) {
268 | last = selectedDate;
269 | } else {
270 | last = first;
271 | first = last;
272 | }
273 | } else {
274 | first = _selectedDates.first;
275 | last = _selectedDates.last;
276 | }
277 |
278 | if (first!.isAfter(selectedDate) &&
279 | (last!.isAfter(selectedDate) || _selectedDates.length == 1)) {
280 | Duration duration = first.difference(selectedDate);
281 | final List addList = List.generate(duration.inDays,
282 | (int index) => first!.subtract(Duration(days: index)));
283 | addList.add(selectedDate);
284 | addList.forEach((dateTime) {
285 | if (!_selectedDates.contains(dateTime)) {
286 | // _selectedDates.insert(0, dateTime);
287 | _selectedDates..add(dateTime);
288 | }
289 | });
290 | } else if (first.isBefore(selectedDate) &&
291 | (last!.isBefore(selectedDate) || _selectedDates.length == 1)) {
292 | Duration duration = selectedDate
293 | .difference(_selectedDates.length == 1 ? first : last);
294 | final List addList = List.generate(
295 | duration.inDays,
296 | (int index) => _selectedDates.length == 1
297 | ? first!.add(Duration(days: index))
298 | : last!.add(Duration(days: index)));
299 | addList.add(selectedDate);
300 | addList.forEach((dateTIme) {
301 | if (!_selectedDates.contains(dateTIme)) {
302 | _selectedDates.add(dateTIme);
303 | }
304 | });
305 | }
306 | }
307 | }
308 | } else {
309 | if (_selectedDates.isEmpty) {
310 | _selectedDates.add(selectedDate);
311 | } else {
312 | _selectedDates.remove(_selectedDates.first);
313 | _selectedDates.add(selectedDate);
314 | }
315 | }
316 | notifyListeners();
317 | if (!isMonthMode) {
318 | int weekPage =
319 | RCalendarUtils.weekDelta(firstDate, selectedDate, _localizations);
320 | updateDisplayedDate(weekPage, mode);
321 | }
322 | }
323 |
324 | //更新display日期
325 | void updateDisplayedDate(int page, RCalendarMode mode) {
326 | if (mode != _mode) return;
327 | if (isMonthMode) {
328 | // print('更新显示的月份-月份$page');
329 | displayedMonthDate = RCalendarUtils.addMonthsToMonthDate(firstDate, page);
330 | if (isAutoSelect && isMultiple == false && selectedDate != null) {
331 | int daysInMonth = RCalendarUtils.getDaysInMonth(
332 | displayedMonthDate!.year, displayedMonthDate!.month);
333 | int day = math.min(daysInMonth, this.selectedDate!.day);
334 | this.selectedDate =
335 | DateTime(displayedMonthDate!.year, displayedMonthDate!.month, day);
336 | }
337 | } else {
338 | // print('更新显示的月份-周期,页数$page');
339 | displayedMonthDate =
340 | RCalendarUtils.addWeeksToWeeksDate(firstDate, page, _localizations);
341 | if (isAutoSelect && isMultiple == false && selectedDate != null) {
342 | bool isBefore = this.selectedDate!.isBefore(displayedMonthDate!);
343 | bool isAfter = this
344 | .selectedDate!
345 | .isAfter(displayedMonthDate!.add(Duration(days: 7)));
346 | if (isBefore || isAfter) {
347 | // this.selectedDate = displayedMonthDate.add(Duration(days: oldIndex));
348 | this.selectedDate =
349 | this.selectedDate!.add(Duration(days: isBefore ? 7 : -7));
350 | }
351 | int selectDatePage =
352 | RCalendarUtils.weekDelta(firstDate, selectedDate!, _localizations);
353 | if (displayedMonthDate!.month != selectedDate!.month &&
354 | selectDatePage == page) {
355 | //月份不一样
356 | displayedMonthDate = selectedDate;
357 | }
358 | }
359 | }
360 | notifyListeners();
361 | }
362 |
363 | //最大的周期页数
364 | int get maxWeekPage =>
365 | RCalendarUtils.weekDelta(firstDate, lastDate!, _localizations) + 1;
366 |
367 | //最大的月份页数
368 | int get maxMonthPage => RCalendarUtils.monthDelta(firstDate, lastDate!) + 1;
369 |
370 | //选中的页数
371 | int get selectedPage => isMonthMode
372 | ? RCalendarUtils.monthDelta(
373 | firstDate, selectedDate ?? displayedMonthDate!)
374 | : RCalendarUtils.weekDelta(
375 | firstDate, selectedDate ?? displayedMonthDate!, _localizations);
376 |
377 | //当前显示的页面
378 | int get displayedPage => isMonthMode
379 | ? RCalendarUtils.monthDelta(firstDate, displayedMonthDate!)
380 | : RCalendarUtils.weekDelta(
381 | firstDate, displayedMonthDate!, _localizations);
382 |
383 | @override
384 | void dispose() {
385 | monthController?.dispose();
386 | weekController?.dispose();
387 | super.dispose();
388 | }
389 | }
390 |
--------------------------------------------------------------------------------
/r_calendar/lib/src/r_calendar_custom_widget.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2019 The rhyme_lph Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style license that can be
3 | // found in the LICENSE file.
4 | import 'dart:async';
5 |
6 | import 'package:flutter/material.dart';
7 | import 'package:intl/intl.dart';
8 | import 'package:r_calendar/r_calendar.dart';
9 |
10 | enum RCalendarType {
11 | //正常
12 | normal,
13 | //不可用
14 | disable,
15 | //不是同一个月
16 | differentMonth,
17 | //选中的
18 | selected,
19 | //当天
20 | today,
21 | }
22 |
23 | abstract class RCalendarCustomWidget {
24 | // 如果你想设置第一天是星期一,请更改MaterialLocalizations 的firstDayOfWeekIndex
25 | // 日 一 二 三 四 五 六
26 | //构建头部
27 | List buildWeekListWidget(
28 | BuildContext context, MaterialLocalizations localizations);
29 |
30 | // 1 2 3 4 5 6 7
31 | //构建普通的日期
32 | Widget buildDateTime(
33 | BuildContext context, DateTime time, List types);
34 |
35 | // < 2019年 11月 >
36 | //构建年份和月份,指示器
37 | Widget buildTopWidget(BuildContext context, RCalendarController? controller);
38 |
39 | //是否不可用,不可用时,无点击事件
40 | bool isUnable(BuildContext context, DateTime time, bool isSameMonth);
41 |
42 | //点击拦截,当返回true时进行拦截,就不会改变选中日期
43 | FutureOr clickInterceptor(BuildContext context, DateTime dateTime);
44 |
45 | //子view的高度
46 | double get childHeight;
47 | }
48 |
49 | class DefaultRCalendarCustomWidget extends RCalendarCustomWidget {
50 | @override
51 | Widget buildDateTime(
52 | BuildContext context, DateTime time, List types) {
53 | TextStyle? childStyle;
54 | BoxDecoration? decoration;
55 |
56 | if (types.contains(RCalendarType.disable) ||
57 | types.contains(RCalendarType.differentMonth)) {
58 | childStyle = TextStyle(
59 | color: Colors.grey[400],
60 | fontSize: 18,
61 | );
62 | decoration = BoxDecoration();
63 | }
64 | if (types.contains(RCalendarType.normal)) {
65 | childStyle = TextStyle(
66 | color: Colors.black,
67 | fontSize: 18,
68 | );
69 | decoration = BoxDecoration();
70 | }
71 |
72 | if (types.contains(RCalendarType.today)) {
73 | childStyle = TextStyle(
74 | color: Colors.blue,
75 | fontSize: 18,
76 | );
77 | }
78 |
79 | if (types.contains(RCalendarType.selected)) {
80 | childStyle = TextStyle(
81 | color: Colors.white,
82 | fontSize: 18,
83 | );
84 | decoration = BoxDecoration(
85 | shape: BoxShape.circle,
86 | color: Colors.blue,
87 | );
88 | }
89 | return Tooltip(
90 | message: MaterialLocalizations.of(context).formatFullDate(time),
91 | child: Container(
92 | decoration: decoration,
93 | alignment: Alignment.center,
94 | child: Text(
95 | time.day.toString(),
96 | style: childStyle,
97 | )),
98 | );
99 | }
100 |
101 | @override
102 | List buildWeekListWidget(
103 | BuildContext context, MaterialLocalizations localizations) {
104 | return localizations.narrowWeekdays
105 | .map(
106 | (d) => Expanded(
107 | child: ExcludeSemantics(
108 | child: Container(
109 | height: 60,
110 | alignment: Alignment.center,
111 | child: Text(
112 | d,
113 | style: TextStyle(
114 | color: Colors.black,
115 | fontSize: 18,
116 | fontWeight: FontWeight.w600),
117 | ),
118 | ),
119 | ),
120 | ),
121 | )
122 | .toList();
123 | }
124 |
125 | @override
126 | double get childHeight => 50;
127 |
128 | @override
129 | FutureOr clickInterceptor(BuildContext context, DateTime dateTime) {
130 | return false;
131 | }
132 |
133 | @override
134 | bool isUnable(BuildContext context, DateTime time, bool isSameMonth) {
135 | return isSameMonth;
136 | }
137 |
138 | @override
139 | Widget buildTopWidget(BuildContext context, RCalendarController? controller) {
140 | return Row(
141 | mainAxisAlignment: MainAxisAlignment.center,
142 | children: [
143 | IconButton(
144 | icon: Icon(Icons.chevron_left),
145 | onPressed: () {
146 | controller!.previousPage();
147 | },
148 | ),
149 | SizedBox(
150 | width: 16,
151 | ),
152 | Text(
153 | DateFormat('yyyy-MM').format(controller!.displayedMonthDate!),
154 | style: TextStyle(color: Colors.red, fontSize: 18),
155 | ),
156 | SizedBox(
157 | width: 16,
158 | ),
159 | IconButton(
160 | icon: Icon(Icons.chevron_right),
161 | onPressed: () {
162 | controller.nextPage();
163 | },
164 | ),
165 | ],
166 | );
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/r_calendar/lib/src/r_calendar_extension.dart:
--------------------------------------------------------------------------------
1 | extension DateTimeExternal on DateTime {
2 | // 是否为周末
3 | bool get isWeekend =>
4 | weekday == DateTime.saturday || weekday == DateTime.sunday;
5 |
6 | // 是否是闰年
7 | bool get isLeapYear =>
8 | ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
9 |
10 | bool get isCurrentDay => this.difference(DateTime.now()).inDays == 0;
11 |
12 | // 本月有几天
13 | int get daysInMonth {
14 | int count = 0;
15 | //判断大月份
16 | if (month == 1 ||
17 | month == 3 ||
18 | month == 5 ||
19 | month == 7 ||
20 | month == 8 ||
21 | month == 10 ||
22 | month == 12) {
23 | count = 31;
24 | }
25 | //判断小月
26 | if (month == 4 || month == 6 || month == 9 || month == 11) {
27 | count = 30;
28 | }
29 |
30 | //判断平年与闰年
31 | if (month == 2) {
32 | if (isLeapYear) {
33 | count = 29;
34 | } else {
35 | count = 28;
36 | }
37 | }
38 | return count;
39 | }
40 |
41 | // 本月第几周
42 | int get weekInMonth {
43 | DateTime firstDayInMonth = DateTime(year, month, 1);
44 | Duration duration = this.difference(firstDayInMonth);
45 | return duration.inDays ~/ 7 + 1;
46 | }
47 |
48 | // 本周第几天
49 | int get dayInWeek {
50 | DateTime firstDayInMonth = new DateTime(
51 | year,
52 | month,
53 | );
54 | Duration duration = this.difference(firstDayInMonth);
55 | return duration.inDays ~/ 7 + 1;
56 | }
57 | }
58 |
59 | const List LUNAR_MONTH_DAYS = [
60 | 1887,
61 | 0x1694,
62 | 0x16aa,
63 | 0x4ad5,
64 | 0xab6,
65 | 0xc4b7,
66 | 0x4ae,
67 | 0xa56,
68 | 0xb52a,
69 | 0x1d2a,
70 | 0xd54,
71 | 0x75aa,
72 | 0x156a,
73 | 0x1096d,
74 | 0x95c,
75 | 0x14ae,
76 | 0xaa4d,
77 | 0x1a4c,
78 | 0x1b2a,
79 | 0x8d55,
80 | 0xad4,
81 | 0x135a,
82 | 0x495d,
83 | 0x95c,
84 | 0xd49b,
85 | 0x149a,
86 | 0x1a4a,
87 | 0xbaa5,
88 | 0x16a8,
89 | 0x1ad4,
90 | 0x52da,
91 | 0x12b6,
92 | 0xe937,
93 | 0x92e,
94 | 0x1496,
95 | 0xb64b,
96 | 0xd4a,
97 | 0xda8,
98 | 0x95b5,
99 | 0x56c,
100 | 0x12ae,
101 | 0x492f,
102 | 0x92e,
103 | 0xcc96,
104 | 0x1a94,
105 | 0x1d4a,
106 | 0xada9,
107 | 0xb5a,
108 | 0x56c,
109 | 0x726e,
110 | 0x125c,
111 | 0xf92d,
112 | 0x192a,
113 | 0x1a94,
114 | 0xdb4a,
115 | 0x16aa,
116 | 0xad4,
117 | 0x955b,
118 | 0x4ba,
119 | 0x125a,
120 | 0x592b,
121 | 0x152a,
122 | 0xf695,
123 | 0xd94,
124 | 0x16aa,
125 | 0xaab5,
126 | 0x9b4,
127 | 0x14b6,
128 | 0x6a57,
129 | 0xa56,
130 | 0x1152a,
131 | 0x1d2a,
132 | 0xd54,
133 | 0xd5aa,
134 | 0x156a,
135 | 0x96c,
136 | 0x94ae,
137 | 0x14ae,
138 | 0xa4c,
139 | 0x7d26,
140 | 0x1b2a,
141 | 0xeb55,
142 | 0xad4,
143 | 0x12da,
144 | 0xa95d,
145 | 0x95a,
146 | 0x149a,
147 | 0x9a4d,
148 | 0x1a4a,
149 | 0x11aa5,
150 | 0x16a8,
151 | 0x16d4,
152 | 0xd2da,
153 | 0x12b6,
154 | 0x936,
155 | 0x9497,
156 | 0x1496,
157 | 0x1564b,
158 | 0xd4a,
159 | 0xda8,
160 | 0xd5b4,
161 | 0x156c,
162 | 0x12ae,
163 | 0xa92f,
164 | 0x92e,
165 | 0xc96,
166 | 0x6d4a,
167 | 0x1d4a,
168 | 0x10d65,
169 | 0xb58,
170 | 0x156c,
171 | 0xb26d,
172 | 0x125c,
173 | 0x192c,
174 | 0x9a95,
175 | 0x1a94,
176 | 0x1b4a,
177 | 0x4b55,
178 | 0xad4,
179 | 0xf55b,
180 | 0x4ba,
181 | 0x125a,
182 | 0xb92b,
183 | 0x152a,
184 | 0x1694,
185 | 0x96aa,
186 | 0x15aa,
187 | 0x12ab5,
188 | 0x974,
189 | 0x14b6,
190 | 0xca57,
191 | 0xa56,
192 | 0x1526,
193 | 0x8e95,
194 | 0xd54,
195 | 0x15aa,
196 | 0x49b5,
197 | 0x96c,
198 | 0xd4ae,
199 | 0x149c,
200 | 0x1a4c,
201 | 0xbd26,
202 | 0x1aa6,
203 | 0xb54,
204 | 0x6d6a,
205 | 0x12da,
206 | 0x1695d,
207 | 0x95a,
208 | 0x149a,
209 | 0xda4b,
210 | 0x1a4a,
211 | 0x1aa4,
212 | 0xbb54,
213 | 0x16b4,
214 | 0xada,
215 | 0x495b,
216 | 0x936,
217 | 0xf497,
218 | 0x1496,
219 | 0x154a,
220 | 0xb6a5,
221 | 0xda4,
222 | 0x15b4,
223 | 0x6ab6,
224 | 0x126e,
225 | 0x1092f,
226 | 0x92e,
227 | 0xc96,
228 | 0xcd4a,
229 | 0x1d4a,
230 | 0xd64,
231 | 0x956c,
232 | 0x155c,
233 | 0x125c,
234 | 0x792e,
235 | 0x192c,
236 | 0xfa95,
237 | 0x1a94,
238 | 0x1b4a,
239 | 0xab55,
240 | 0xad4,
241 | 0x14da,
242 | 0x8a5d,
243 | 0xa5a,
244 | 0x1152b,
245 | 0x152a,
246 | 0x1694,
247 | 0xd6aa,
248 | 0x15aa,
249 | 0xab4,
250 | 0x94ba,
251 | 0x14b6,
252 | 0xa56,
253 | 0x7527,
254 | 0xd26,
255 | 0xee53,
256 | 0xd54,
257 | 0x15aa,
258 | 0xa9b5,
259 | 0x96c,
260 | 0x14ae,
261 | 0x8a4e,
262 | 0x1a4c,
263 | 0x11d26,
264 | 0x1aa4,
265 | 0x1b54,
266 | 0xcd6a,
267 | 0xada,
268 | 0x95c,
269 | 0x949d,
270 | 0x149a,
271 | 0x1a2a,
272 | 0x5b25,
273 | 0x1aa4,
274 | 0xfb52,
275 | 0x16b4,
276 | 0xaba,
277 | 0xa95b,
278 | 0x936,
279 | 0x1496,
280 | 0x9a4b,
281 | 0x154a,
282 | 0x136a5,
283 | 0xda4,
284 | 0x15ac
285 | ];
286 |
287 | const List SOLAR = [
288 | 1887,
289 | 0xec04c,
290 | 0xec23f,
291 | 0xec435,
292 | 0xec649,
293 | 0xec83e,
294 | 0xeca51,
295 | 0xecc46,
296 | 0xece3a,
297 | 0xed04d,
298 | 0xed242,
299 | 0xed436,
300 | 0xed64a,
301 | 0xed83f,
302 | 0xeda53,
303 | 0xedc48,
304 | 0xede3d,
305 | 0xee050,
306 | 0xee244,
307 | 0xee439,
308 | 0xee64d,
309 | 0xee842,
310 | 0xeea36,
311 | 0xeec4a,
312 | 0xeee3e,
313 | 0xef052,
314 | 0xef246,
315 | 0xef43a,
316 | 0xef64e,
317 | 0xef843,
318 | 0xefa37,
319 | 0xefc4b,
320 | 0xefe41,
321 | 0xf0054,
322 | 0xf0248,
323 | 0xf043c,
324 | 0xf0650,
325 | 0xf0845,
326 | 0xf0a38,
327 | 0xf0c4d,
328 | 0xf0e42,
329 | 0xf1037,
330 | 0xf124a,
331 | 0xf143e,
332 | 0xf1651,
333 | 0xf1846,
334 | 0xf1a3a,
335 | 0xf1c4e,
336 | 0xf1e44,
337 | 0xf2038,
338 | 0xf224b,
339 | 0xf243f,
340 | 0xf2653,
341 | 0xf2848,
342 | 0xf2a3b,
343 | 0xf2c4f,
344 | 0xf2e45,
345 | 0xf3039,
346 | 0xf324d,
347 | 0xf3442,
348 | 0xf3636,
349 | 0xf384a,
350 | 0xf3a3d,
351 | 0xf3c51,
352 | 0xf3e46,
353 | 0xf403b,
354 | 0xf424e,
355 | 0xf4443,
356 | 0xf4638,
357 | 0xf484c,
358 | 0xf4a3f,
359 | 0xf4c52,
360 | 0xf4e48,
361 | 0xf503c,
362 | 0xf524f,
363 | 0xf5445,
364 | 0xf5639,
365 | 0xf584d,
366 | 0xf5a42,
367 | 0xf5c35,
368 | 0xf5e49,
369 | 0xf603e,
370 | 0xf6251,
371 | 0xf6446,
372 | 0xf663b,
373 | 0xf684f,
374 | 0xf6a43,
375 | 0xf6c37,
376 | 0xf6e4b,
377 | 0xf703f,
378 | 0xf7252,
379 | 0xf7447,
380 | 0xf763c,
381 | 0xf7850,
382 | 0xf7a45,
383 | 0xf7c39,
384 | 0xf7e4d,
385 | 0xf8042,
386 | 0xf8254,
387 | 0xf8449,
388 | 0xf863d,
389 | 0xf8851,
390 | 0xf8a46,
391 | 0xf8c3b,
392 | 0xf8e4f,
393 | 0xf9044,
394 | 0xf9237,
395 | 0xf944a,
396 | 0xf963f,
397 | 0xf9853,
398 | 0xf9a47,
399 | 0xf9c3c,
400 | 0xf9e50,
401 | 0xfa045,
402 | 0xfa238,
403 | 0xfa44c,
404 | 0xfa641,
405 | 0xfa836,
406 | 0xfaa49,
407 | 0xfac3d,
408 | 0xfae52,
409 | 0xfb047,
410 | 0xfb23a,
411 | 0xfb44e,
412 | 0xfb643,
413 | 0xfb837,
414 | 0xfba4a,
415 | 0xfbc3f,
416 | 0xfbe53,
417 | 0xfc048,
418 | 0xfc23c,
419 | 0xfc450,
420 | 0xfc645,
421 | 0xfc839,
422 | 0xfca4c,
423 | 0xfcc41,
424 | 0xfce36,
425 | 0xfd04a,
426 | 0xfd23d,
427 | 0xfd451,
428 | 0xfd646,
429 | 0xfd83a,
430 | 0xfda4d,
431 | 0xfdc43,
432 | 0xfde37,
433 | 0xfe04b,
434 | 0xfe23f,
435 | 0xfe453,
436 | 0xfe648,
437 | 0xfe83c,
438 | 0xfea4f,
439 | 0xfec44,
440 | 0xfee38,
441 | 0xff04c,
442 | 0xff241,
443 | 0xff436,
444 | 0xff64a,
445 | 0xff83e,
446 | 0xffa51,
447 | 0xffc46,
448 | 0xffe3a,
449 | 0x10004e,
450 | 0x100242,
451 | 0x100437,
452 | 0x10064b,
453 | 0x100841,
454 | 0x100a53,
455 | 0x100c48,
456 | 0x100e3c,
457 | 0x10104f,
458 | 0x101244,
459 | 0x101438,
460 | 0x10164c,
461 | 0x101842,
462 | 0x101a35,
463 | 0x101c49,
464 | 0x101e3d,
465 | 0x102051,
466 | 0x102245,
467 | 0x10243a,
468 | 0x10264e,
469 | 0x102843,
470 | 0x102a37,
471 | 0x102c4b,
472 | 0x102e3f,
473 | 0x103053,
474 | 0x103247,
475 | 0x10343b,
476 | 0x10364f,
477 | 0x103845,
478 | 0x103a38,
479 | 0x103c4c,
480 | 0x103e42,
481 | 0x104036,
482 | 0x104249,
483 | 0x10443d,
484 | 0x104651,
485 | 0x104846,
486 | 0x104a3a,
487 | 0x104c4e,
488 | 0x104e43,
489 | 0x105038,
490 | 0x10524a,
491 | 0x10543e,
492 | 0x105652,
493 | 0x105847,
494 | 0x105a3b,
495 | 0x105c4f,
496 | 0x105e45,
497 | 0x106039,
498 | 0x10624c,
499 | 0x106441,
500 | 0x106635,
501 | 0x106849,
502 | 0x106a3d,
503 | 0x106c51,
504 | 0x106e47,
505 | 0x10703c,
506 | 0x10724f,
507 | 0x107444,
508 | 0x107638,
509 | 0x10784c,
510 | 0x107a3f,
511 | 0x107c53,
512 | 0x107e48
513 | ];
514 |
515 | /**
516 | * 公历节日
517 | */
518 | const List SOLAR_CALENDAR = [
519 | "0101元旦",
520 | "0214情人节",
521 | "0308妇女节",
522 | "0312植树节",
523 | "0315消权日",
524 | "0401愚人节",
525 | "0422地球日",
526 | "0501劳动节",
527 | "0504青年节",
528 | "0601儿童节",
529 | "0701建党节",
530 | "0801建军节",
531 | "0910教师节",
532 | "1001国庆节",
533 | "1031万圣节",
534 | "1111光棍节",
535 | "1224平安夜",
536 | "1225圣诞节",
537 | ];
538 |
539 | /**
540 | * 传统农历节日
541 | */
542 | const List TRADITION_FESTIVAL_STR = [
543 | "除夕",
544 | "0101春节",
545 | "0115元宵",
546 | "0505端午",
547 | "0707七夕",
548 | "0815中秋",
549 | "0909重阳",
550 | ];
551 |
552 | /**
553 | * 特殊节日、母亲节和父亲节,感恩节等
554 | */
555 | const Map> SPECIAL_FESTIVAL = {};
556 |
557 | /**
558 | * 特殊节日的数组
559 | */
560 | const List SPECIAL_FESTIVAL_STR = [
561 | "母亲节",
562 | "父亲节",
563 | "感恩节",
564 | ];
565 |
566 | /**
567 | * 用来表示1900年到2099年间农历年份的相关信息,共24位bit的16进制表示,其中:
568 | * 1. 前4位表示该年闰哪个月;
569 | * 2. 5-17位表示农历年份13个月的大小月分布,0表示小,1表示大;
570 | * 3. 最后7位表示农历年首(正月初一)对应的公历日期。
571 | *
572 | * 以2014年的数据0x955ABF为例说明:
573 | * 1001 0101 0101 1010 1011 1111
574 | * 闰九月 农历正月初一对应公历1月31号
575 | */
576 | const List LUNAR_INFO = [
577 | 0x84B6BF,
578 | /*1900*/
579 | 0x04AE53,
580 | 0x0A5748,
581 | 0x5526BD,
582 | 0x0D2650,
583 | 0x0D9544,
584 | 0x46AAB9,
585 | 0x056A4D,
586 | 0x09AD42,
587 | 0x24AEB6,
588 | 0x04AE4A,
589 | /*1901-1910*/
590 | 0x6A4DBE,
591 | 0x0A4D52,
592 | 0x0D2546,
593 | 0x5D52BA,
594 | 0x0B544E,
595 | 0x0D6A43,
596 | 0x296D37,
597 | 0x095B4B,
598 | 0x749BC1,
599 | 0x049754,
600 | /*1911-1920*/
601 | 0x0A4B48,
602 | 0x5B25BC,
603 | 0x06A550,
604 | 0x06D445,
605 | 0x4ADAB8,
606 | 0x02B64D,
607 | 0x095742,
608 | 0x2497B7,
609 | 0x04974A,
610 | 0x664B3E,
611 | /*1921-1930*/
612 | 0x0D4A51,
613 | 0x0EA546,
614 | 0x56D4BA,
615 | 0x05AD4E,
616 | 0x02B644,
617 | 0x393738,
618 | 0x092E4B,
619 | 0x7C96BF,
620 | 0x0C9553,
621 | 0x0D4A48,
622 | /*1931-1940*/
623 | 0x6DA53B,
624 | 0x0B554F,
625 | 0x056A45,
626 | 0x4AADB9,
627 | 0x025D4D,
628 | 0x092D42,
629 | 0x2C95B6,
630 | 0x0A954A,
631 | 0x7B4ABD,
632 | 0x06CA51,
633 | /*1941-1950*/
634 | 0x0B5546,
635 | 0x555ABB,
636 | 0x04DA4E,
637 | 0x0A5B43,
638 | 0x352BB8,
639 | 0x052B4C,
640 | 0x8A953F,
641 | 0x0E9552,
642 | 0x06AA48,
643 | 0x6AD53C,
644 | /*1951-1960*/
645 | 0x0AB54F,
646 | 0x04B645,
647 | 0x4A5739,
648 | 0x0A574D,
649 | 0x052642,
650 | 0x3E9335,
651 | 0x0D9549,
652 | 0x75AABE,
653 | 0x056A51,
654 | 0x096D46,
655 | /*1961-1970*/
656 | 0x54AEBB,
657 | 0x04AD4F,
658 | 0x0A4D43,
659 | 0x4D26B7,
660 | 0x0D254B,
661 | 0x8D52BF,
662 | 0x0B5452,
663 | 0x0B6A47,
664 | 0x696D3C,
665 | 0x095B50,
666 | /*1971-1980*/
667 | 0x049B45,
668 | 0x4A4BB9,
669 | 0x0A4B4D,
670 | 0xAB25C2,
671 | 0x06A554,
672 | 0x06D449,
673 | 0x6ADA3D,
674 | 0x0AB651,
675 | 0x095746,
676 | 0x5497BB,
677 | /*1981-1990*/
678 | 0x04974F,
679 | 0x064B44,
680 | 0x36A537,
681 | 0x0EA54A,
682 | 0x86B2BF,
683 | 0x05AC53,
684 | 0x0AB647,
685 | 0x5936BC,
686 | 0x092E50,
687 | 0x0C9645,
688 | /*1991-2000*/
689 | 0x4D4AB8,
690 | 0x0D4A4C,
691 | 0x0DA541,
692 | 0x25AAB6,
693 | 0x056A49,
694 | 0x7AADBD,
695 | 0x025D52,
696 | 0x092D47,
697 | 0x5C95BA,
698 | 0x0A954E,
699 | /*2001-2010*/
700 | 0x0B4A43,
701 | 0x4B5537,
702 | 0x0AD54A,
703 | 0x955ABF,
704 | 0x04BA53,
705 | 0x0A5B48,
706 | 0x652BBC,
707 | 0x052B50,
708 | 0x0A9345,
709 | 0x474AB9,
710 | /*2011-2020*/
711 | 0x06AA4C,
712 | 0x0AD541,
713 | 0x24DAB6,
714 | 0x04B64A,
715 | 0x6a573D,
716 | 0x0A4E51,
717 | 0x0D2646,
718 | 0x5E933A,
719 | 0x0D534D,
720 | 0x05AA43,
721 | /*2021-2030*/
722 | 0x36B537,
723 | 0x096D4B,
724 | 0xB4AEBF,
725 | 0x04AD53,
726 | 0x0A4D48,
727 | 0x6D25BC,
728 | 0x0D254F,
729 | 0x0D5244,
730 | 0x5DAA38,
731 | 0x0B5A4C,
732 | /*2031-2040*/
733 | 0x056D41,
734 | 0x24ADB6,
735 | 0x049B4A,
736 | 0x7A4BBE,
737 | 0x0A4B51,
738 | 0x0AA546,
739 | 0x5B52BA,
740 | 0x06D24E,
741 | 0x0ADA42,
742 | 0x355B37,
743 | /*2041-2050*/
744 | 0x09374B,
745 | 0x8497C1,
746 | 0x049753,
747 | 0x064B48,
748 | 0x66A53C,
749 | 0x0EA54F,
750 | 0x06AA44,
751 | 0x4AB638,
752 | 0x0AAE4C,
753 | 0x092E42,
754 | /*2051-2060*/
755 | 0x3C9735,
756 | 0x0C9649,
757 | 0x7D4ABD,
758 | 0x0D4A51,
759 | 0x0DA545,
760 | 0x55AABA,
761 | 0x056A4E,
762 | 0x0A6D43,
763 | 0x452EB7,
764 | 0x052D4B,
765 | /*2061-2070*/
766 | 0x8A95BF,
767 | 0x0A9553,
768 | 0x0B4A47,
769 | 0x6B553B,
770 | 0x0AD54F,
771 | 0x055A45,
772 | 0x4A5D38,
773 | 0x0A5B4C,
774 | 0x052B42,
775 | 0x3A93B6,
776 | /*2071-2080*/
777 | 0x069349,
778 | 0x7729BD,
779 | 0x06AA51,
780 | 0x0AD546,
781 | 0x54DABA,
782 | 0x04B64E,
783 | 0x0A5743,
784 | 0x452738,
785 | 0x0D264A,
786 | 0x8E933E,
787 | /*2081-2090*/
788 | 0x0D5252,
789 | 0x0DAA47,
790 | 0x66B53B,
791 | 0x056D4F,
792 | 0x04AE45,
793 | 0x4A4EB9,
794 | 0x0A4D4C,
795 | 0x0D1541,
796 | 0x2D92B5 /*2091-2099*/
797 | ];
798 |
799 | /**
800 | * 一周七天
801 | */
802 | const List WEEK_LIST = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"];
803 |
804 | /**
805 | * 农历的月份
806 | */
807 | const List LUNAR_MONTH_TEXT = [
808 | "春节",
809 | "二月",
810 | "三月",
811 | "四月",
812 | "五月",
813 | "六月",
814 | "七月",
815 | "八月",
816 | "九月",
817 | "十月",
818 | "冬月",
819 | "腊月",
820 | ];
821 |
822 | /**
823 | * 农历的日期
824 | */
825 | const List LUNAR_DAY_TEXT = [
826 | "初一",
827 | "初二",
828 | "初三",
829 | "初四",
830 | "初五",
831 | "初六",
832 | "初七",
833 | "初八",
834 | "初九",
835 | "初十",
836 | "十一",
837 | "十二",
838 | "十三",
839 | "十四",
840 | "十五",
841 | "十六",
842 | "十七",
843 | "十八",
844 | "十九",
845 | "二十",
846 | "廿一",
847 | "廿二",
848 | "廿三",
849 | "廿四",
850 | "廿五",
851 | "廿六",
852 | "廿七",
853 | "廿八",
854 | "廿九",
855 | "三十"
856 | ];
857 |
858 | /**
859 | * 24节气
860 | */
861 | const List SOLAR_TERMS = [
862 | "春分",
863 | "清明",
864 | "谷雨",
865 | "立夏",
866 | "小满",
867 | "芒种",
868 | "夏至",
869 | "小暑",
870 | "大暑",
871 | "立秋",
872 | "处暑",
873 | "白露",
874 | "秋分",
875 | "寒露",
876 | "霜降",
877 | "立冬",
878 | "小雪",
879 | "大雪",
880 | "冬至",
881 | "小寒",
882 | "大寒",
883 | "立春",
884 | "雨水",
885 | "惊蛰",
886 | ];
887 |
888 | //农历扩展
889 | extension LunerDateTimeExternal on DateTime {
890 | int get lunarDaysInMonth {
891 | if ((LUNAR_INFO[this.year - 1900] & (0x100000 >> this.month)) == 0)
892 | return 29;
893 | else
894 | return 30;
895 | }
896 |
897 | int get lunarYear => _solarToLunar()[0];
898 |
899 | int get lunarMonth => _solarToLunar()[1];
900 |
901 | int get lunarDay => _solarToLunar()[2];
902 |
903 | int? get leapMonth => _solarToLunar()[3] == 1 ? lunarMonth : null;
904 |
905 | // 公历节日
906 | String get gregorianFestival {
907 | String text = string;
908 | String solar = "";
909 | for (String aMSolarCalendar in SOLAR_CALENDAR) {
910 | if (aMSolarCalendar.contains(text)) {
911 | solar = aMSolarCalendar.replaceAll(text, "");
912 | break;
913 | }
914 | }
915 | return solar;
916 | }
917 |
918 | //农历传统节日
919 | String get traditionFestival {
920 | if (lunarMonth == 12) {
921 | int count = lunarDaysInMonth;
922 | if (lunarDay == count) {
923 | return TRADITION_FESTIVAL_STR[0]; //除夕
924 | }
925 | }
926 | String text = lunarString;
927 | String festivalStr = "";
928 | for (String festival in TRADITION_FESTIVAL_STR) {
929 | if (festival.contains(text)) {
930 | festivalStr = festival.replaceAll(text, "");
931 | break;
932 | }
933 | }
934 | return festivalStr;
935 | }
936 |
937 | List get specialFestivals {
938 | List festivals = List.generate(3, (index) => '');
939 | DateTime dateTime = new DateTime(year, 5, 1);
940 | //母亲节
941 | int week = (dateTime.weekday + 1) % 8;
942 | int startDiff = 7 - week + 1;
943 | if (startDiff == 7) {
944 | festivals[0] =
945 | _dateToString(year, 5, startDiff + 1) + SPECIAL_FESTIVAL_STR[0];
946 | } else {
947 | festivals[0] =
948 | _dateToString(year, 5, startDiff + 7 + 1) + SPECIAL_FESTIVAL_STR[0];
949 | }
950 |
951 | //父亲节
952 | dateTime = new DateTime(year, 6, 1);
953 |
954 | week = (dateTime.weekday + 1) % 8;
955 | startDiff = 7 - week + 1;
956 | if (startDiff == 7) {
957 | festivals[1] =
958 | _dateToString(year, 6, startDiff + 7 + 1) + SPECIAL_FESTIVAL_STR[1];
959 | } else {
960 | festivals[1] = _dateToString(year, 6, startDiff + 7 + 7 + 1) +
961 | SPECIAL_FESTIVAL_STR[1];
962 | }
963 |
964 | //感恩节
965 | dateTime = new DateTime(year, 11, 1);
966 | week = (dateTime.weekday + 1) % 8;
967 |
968 | startDiff = 7 - week + 1;
969 | if (startDiff <= 2) {
970 | festivals[2] =
971 | _dateToString(year, 11, startDiff + 21 + 5) + SPECIAL_FESTIVAL_STR[2];
972 | } else {
973 | festivals[2] =
974 | _dateToString(year, 11, startDiff + 14 + 5) + SPECIAL_FESTIVAL_STR[2];
975 | }
976 | return festivals;
977 | }
978 |
979 | String _dateToString(int year, int month, int day) {
980 | String _getString(int month, int day) {
981 | return (month >= 10 ? month.toString() : "0$month") +
982 | (day >= 10 ? day.toString() : "0$day");
983 | }
984 |
985 | return "$year" + _getString(month, day);
986 | }
987 |
988 | String get specialFestival {
989 | List specialFestival = specialFestivals;
990 | String text = "$year" + string;
991 | String solar = "";
992 | for (String special in specialFestival) {
993 | if (special.contains(text)) {
994 | solar = special.replaceAll(text, "");
995 | break;
996 | }
997 | }
998 | return solar;
999 | }
1000 |
1001 | String get numToChinese {
1002 | if (lunarDay == 1) {
1003 | return numToChineseMonth;
1004 | }
1005 | return LUNAR_DAY_TEXT[lunarDay - 1];
1006 | }
1007 |
1008 | String get numToChineseMonth {
1009 | if (leapMonth == 1) {
1010 | return "闰" + LUNAR_MONTH_TEXT[lunarMonth - 1];
1011 | }
1012 | return LUNAR_MONTH_TEXT[lunarMonth - 1];
1013 | }
1014 |
1015 | String get string {
1016 | return _getString(year, month, day);
1017 | }
1018 |
1019 | String get lunarString {
1020 | return _getString(lunarYear, lunarMonth, lunarDay);
1021 | }
1022 |
1023 | String _getString(int year, int month, int day) {
1024 | return (month >= 10 ? month.toString() : "0$month") +
1025 | (day >= 10 ? day.toString() : "0$day");
1026 | }
1027 |
1028 | List _solarToLunar() {
1029 | List lunarInt = List.generate(4, (index) => 0);
1030 | int index = year - SOLAR[0];
1031 | int data = (year << 9) | (month << 5) | (day);
1032 | int solar11;
1033 | if (SOLAR[index] > data) {
1034 | index--;
1035 | }
1036 | solar11 = SOLAR[index];
1037 | int y = _getBitInt(solar11, 12, 9);
1038 | int m = _getBitInt(solar11, 4, 5);
1039 | int d = _getBitInt(solar11, 5, 0);
1040 | int offset = _solarToInt(year, month, day) - _solarToInt(y, m, d);
1041 |
1042 | int days = LUNAR_MONTH_DAYS[index];
1043 | int leap = _getBitInt(days, 4, 13);
1044 |
1045 | int lunarY = index + SOLAR[0];
1046 | int lunarM = 1;
1047 | int lunarD;
1048 | offset += 1;
1049 |
1050 | for (int i = 0; i < 13; i++) {
1051 | int dm = _getBitInt(days, 1, 12 - i) == 1 ? 30 : 29;
1052 | if (offset > dm) {
1053 | lunarM++;
1054 | offset -= dm;
1055 | } else {
1056 | break;
1057 | }
1058 | }
1059 |
1060 | lunarD = offset;
1061 | lunarInt[0] = lunarY;
1062 | lunarInt[1] = lunarM;
1063 | lunarInt[3] = 0;
1064 |
1065 | if (leap != 0 && lunarM > leap) {
1066 | lunarInt[1] = lunarM - 1;
1067 | if (lunarM == leap + 1) {
1068 | lunarInt[3] = 1;
1069 | }
1070 | }
1071 | lunarInt[2] = lunarD;
1072 | return lunarInt;
1073 | }
1074 |
1075 | int _getBitInt(int data, int length, int shift) {
1076 | return (data & (((1 << length) - 1) << shift)) >> shift;
1077 | }
1078 |
1079 | int _solarToInt(int y, int m, int d) {
1080 | m = (m + 9) % 12;
1081 | y = y - m ~/ 10;
1082 | return (365 * y +
1083 | y ~/ 4 -
1084 | y ~/ 100 +
1085 | y ~/ 400 +
1086 | (m * 306 + 5) ~/ 10 +
1087 | (d - 1));
1088 | }
1089 | }
1090 |
--------------------------------------------------------------------------------
/r_calendar/lib/src/r_calendar_utils.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2019 The rhyme_lph Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style license that can be
3 | // found in the LICENSE file.
4 | import 'package:flutter/material.dart';
5 |
6 | ///一个月有多少天
7 | const List _daysInMonth = [
8 | 31,
9 | -1,
10 | 31,
11 | 30,
12 | 31,
13 | 30,
14 | 31,
15 | 31,
16 | 30,
17 | 31,
18 | 30,
19 | 31
20 | ];
21 |
22 | class RCalendarUtils {
23 | /// 获取一个月有多少天
24 | static int getDaysInMonth(int year, int month) {
25 | if (month == DateTime.february) {
26 | final bool isLeapYear =
27 | (year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0);
28 | if (isLeapYear) return 29;
29 | return 28;
30 | }
31 | return _daysInMonth[month - 1];
32 | }
33 |
34 | /// 获取第一天的偏移量,星期一
35 | static int computeFirstDayOffset(
36 | int year, int month, MaterialLocalizations? localizations) {
37 | // 0-based day of week, with 0 representing Monday.
38 | final int weekdayFromMonday = DateTime(year, month).weekday - 1;
39 | // 0-based day of week, with 0 representing Sunday.
40 | final int firstDayOfWeekFromSunday =
41 | localizations?.firstDayOfWeekIndex ?? 0;
42 | // firstDayOfWeekFromSunday recomputed to be Monday-based
43 | final int firstDayOfWeekFromMonday = (firstDayOfWeekFromSunday - 1) % 7;
44 | // Number of days between the first day of week appearing on the calendar,
45 | // and the day corresponding to the 1-st of the month.
46 | return (weekdayFromMonday - firstDayOfWeekFromMonday) % 7;
47 | }
48 |
49 | /// 月份添加和减少
50 | static DateTime addMonthsToMonthDate(DateTime monthDate, int monthsToAdd) {
51 | return DateTime(
52 | monthDate.year + monthsToAdd ~/ 12, monthDate.month + monthsToAdd % 12);
53 | }
54 |
55 | /// 星期添加和减少
56 | static DateTime addWeeksToWeeksDate(
57 | DateTime weekDate, int weeksToAdd, MaterialLocalizations? localizations) {
58 | final firstDayOffset =
59 | computeFirstDayOffset(weekDate.year, weekDate.month, localizations);
60 | DateTime weekFirstDate = weekDate.subtract(Duration(days: firstDayOffset));
61 | return weekFirstDate.add(Duration(days: weeksToAdd * DateTime.daysPerWeek));
62 | }
63 |
64 | /// 月份总页数
65 | static int monthDelta(DateTime startDate, DateTime endDate) {
66 | return (endDate.year - startDate.year) * 12 +
67 | endDate.month -
68 | startDate.month;
69 | }
70 |
71 | /// 星期总页数
72 | static int weekDelta(DateTime startDate, DateTime endDate,
73 | MaterialLocalizations? localizations) {
74 | final int firstDayOffset =
75 | computeFirstDayOffset(startDate.year, startDate.month, localizations);
76 | Duration diff = DateTime(endDate.year, endDate.month, endDate.day)
77 | .difference(DateTime(startDate.year, startDate.month, startDate.day));
78 | int days = diff.inDays + firstDayOffset + 1;
79 | return (days / 7).ceil() - 1;
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/r_calendar/lib/src/r_calendar_widget_item.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2019 The rhyme_lph Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style license that can be
3 | // found in the LICENSE file.
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter/rendering.dart';
6 | import 'package:r_calendar/r_calendar.dart';
7 | import 'package:r_calendar/src/r_calendar_utils.dart';
8 |
9 | class RCalendarMonthItem extends StatelessWidget {
10 | final DateTime? monthDate;
11 |
12 | const RCalendarMonthItem({Key? key, this.monthDate}) : super(key: key);
13 |
14 | @override
15 | Widget build(BuildContext context) {
16 | RCalendarMarker data = RCalendarMarker.of(context)!;
17 | RCalendarController? controller = data.notifier;
18 | //获取星期的第一天
19 | final MaterialLocalizations localizations =
20 | MaterialLocalizations.of(context);
21 | final int year = monthDate!.year;
22 | final int month = monthDate!.month;
23 | final int dayInMonth = RCalendarUtils.getDaysInMonth(year, month);
24 | //第一天的偏移
25 | final int firstDayOffset =
26 | RCalendarUtils.computeFirstDayOffset(year, month, localizations);
27 |
28 | final List labels = [];
29 |
30 | for (int i = 0; true; i += 1) {
31 | final int day = i - firstDayOffset + 1;
32 |
33 | if (day > dayInMonth && i % 7 == 0) break; // 大于当月最大日期和可以除以7
34 | if (day < 1) {
35 | //小于当月的日期
36 | List types = [RCalendarType.differentMonth];
37 | final DateTime dayToBuild =
38 | DateTime(year, month, 1).subtract(Duration(days: (day * -1) + 1));
39 |
40 | final bool disabled = dayToBuild.isAfter(controller!.lastDate!) ||
41 | dayToBuild.isBefore(controller.firstDate) ||
42 | (data.customWidget != null &&
43 | !data.customWidget!.isUnable(context, dayToBuild, false));
44 | if (disabled) {
45 | types.add(RCalendarType.disable);
46 | }
47 |
48 | if (!disabled) {
49 | labels.add(GestureDetector(
50 | behavior: HitTestBehavior.opaque,
51 | onTap: () async {
52 | if (await data.customWidget!
53 | .clickInterceptor(context, dayToBuild)) {
54 | return;
55 | }
56 | data.onChanged(dayToBuild);
57 | },
58 | child: data.customWidget!.buildDateTime(context, dayToBuild, types),
59 | ));
60 | } else {
61 | labels
62 | .add(data.customWidget!.buildDateTime(context, dayToBuild, types));
63 | }
64 | } else if (day > dayInMonth) {
65 | //大于当月的日期
66 | List types = [RCalendarType.differentMonth];
67 | final DateTime dayToBuild = DateTime(year, month, dayInMonth)
68 | .add(Duration(days: day - dayInMonth));
69 |
70 | final bool disabled = dayToBuild.isAfter(controller!.lastDate!) ||
71 | dayToBuild.isBefore(controller.firstDate) ||
72 | (data.customWidget != null &&
73 | !data.customWidget!.isUnable(context, dayToBuild, false));
74 | if (disabled) {
75 | types.add(RCalendarType.disable);
76 | }
77 |
78 | if (!disabled) {
79 | labels.add(GestureDetector(
80 | behavior: HitTestBehavior.opaque,
81 | onTap: () async {
82 | if (await data.customWidget!
83 | .clickInterceptor(context, dayToBuild)) {
84 | return;
85 | }
86 | data.onChanged(dayToBuild);
87 | },
88 | child: data.customWidget!.buildDateTime(context, dayToBuild, types),
89 | ));
90 | } else {
91 | labels
92 | .add(data.customWidget!.buildDateTime(context, dayToBuild, types));
93 | }
94 | } else {
95 | List types = [RCalendarType.disable];
96 | final DateTime dayToBuild = DateTime(year, month, day);
97 | final bool disabled = dayToBuild.isAfter(controller!.lastDate!) ||
98 | dayToBuild.isBefore(controller.firstDate) ||
99 | (data.customWidget != null &&
100 | !data.customWidget!.isUnable(context, dayToBuild, true));
101 | if (disabled) {
102 | types.add(RCalendarType.disable);
103 | }
104 | bool isSelectedDay = false;
105 | try {
106 | isSelectedDay = controller.selectedDates
107 | .where((selectedDate) =>
108 | selectedDate!.year == year &&
109 | selectedDate.month == month &&
110 | selectedDate.day == day)
111 | .length !=
112 | 0;
113 | } catch (_) {}
114 | if (isSelectedDay) {
115 | types.add(RCalendarType.selected);
116 | }
117 | final bool isToday = data.toDayDate!.year == year &&
118 | data.toDayDate!.month == month &&
119 | data.toDayDate!.day == day;
120 | if (isToday) {
121 | types.add(RCalendarType.today);
122 | }
123 | if (!disabled && !isSelectedDay && !isToday) {
124 | types.add(RCalendarType.normal);
125 | }
126 |
127 | if (!disabled) {
128 | labels.add(GestureDetector(
129 | behavior: HitTestBehavior.opaque,
130 | onTap: () async {
131 | if (await data.customWidget!
132 | .clickInterceptor(context, dayToBuild)) {
133 | return;
134 | }
135 | data.onChanged(dayToBuild);
136 | },
137 | child: Semantics(
138 | label:
139 | '${localizations.formatDecimal(day)}, ${localizations.formatFullDate(dayToBuild)}',
140 | selected: isSelectedDay,
141 | excludeSemantics: true,
142 | child:
143 | data.customWidget!.buildDateTime(context, dayToBuild, types),
144 | ),
145 | ));
146 | } else {
147 | labels.add(ExcludeSemantics(
148 | child: data.customWidget!.buildDateTime(context, dayToBuild, types),
149 | ));
150 | }
151 | }
152 | }
153 |
154 | return GridView.custom(
155 | key: ValueKey(month),
156 | physics: NeverScrollableScrollPhysics(),
157 | padding: EdgeInsets.zero,
158 | gridDelegate: _DayPickerGridDelegate(data.customWidget?.childHeight ?? 42),
159 | childrenDelegate:
160 | SliverChildListDelegate(labels, addRepaintBoundaries: false),
161 | );
162 | }
163 | }
164 |
165 | class RCalendarWeekItem extends StatelessWidget {
166 | final DateTime? weekDate;
167 |
168 | const RCalendarWeekItem({Key? key, this.weekDate}) : super(key: key);
169 |
170 | @override
171 | Widget build(BuildContext context) {
172 | RCalendarMarker data = RCalendarMarker.of(context)!;
173 | RCalendarController? controller = data.notifier;
174 |
175 | final List labels = [];
176 | for (int i = 0; i < 7; i += 1) {
177 | final DateTime dayToBuild =
178 | DateTime(weekDate!.year, weekDate!.month, weekDate!.day + i);
179 | final int year = dayToBuild.year;
180 | final int month = dayToBuild.month;
181 | final int day = dayToBuild.day;
182 | List types = [];
183 | final bool disabled = dayToBuild.isAfter(controller!.lastDate!) ||
184 | dayToBuild.isBefore(controller.firstDate) ||
185 | (data.customWidget != null &&
186 | !data.customWidget!.isUnable(context, dayToBuild, true));
187 | if (disabled) {
188 | types.add(RCalendarType.disable);
189 | }
190 | bool isSelectedDay = false;
191 | try {
192 | isSelectedDay = controller.selectedDates
193 | .where((selectedDate) =>
194 | selectedDate!.year == year &&
195 | selectedDate.month == month &&
196 | selectedDate.day == day)
197 | .length !=
198 | 0;
199 | } catch (_) {}
200 | if (isSelectedDay) {
201 | types.add(RCalendarType.selected);
202 | }
203 | final bool isToday = data.toDayDate!.year == year &&
204 | data.toDayDate!.month == month &&
205 | data.toDayDate!.day == day;
206 | if (isToday) {
207 | types.add(RCalendarType.today);
208 | }
209 | if (!disabled && !isSelectedDay && !isToday) {
210 | types.add(RCalendarType.normal);
211 | }
212 | if (!disabled) {
213 | labels.add(GestureDetector(
214 | behavior: HitTestBehavior.opaque,
215 | onTap: () async {
216 | if (await data.customWidget!.clickInterceptor(context, dayToBuild)) {
217 | return;
218 | }
219 | data.onChanged(dayToBuild);
220 | },
221 | child: data.customWidget!.buildDateTime(context, dayToBuild, types),
222 | ));
223 | } else {
224 | labels.add(data.customWidget!.buildDateTime(context, dayToBuild, types));
225 | }
226 | }
227 | return GridView.custom(
228 | key: ValueKey(weekDate!.toIso8601String()),
229 | physics: NeverScrollableScrollPhysics(),
230 | padding: EdgeInsets.zero,
231 | gridDelegate: _DayPickerGridDelegate(data.customWidget?.childHeight ?? 42),
232 | childrenDelegate:
233 | SliverChildListDelegate(labels, addRepaintBoundaries: true),
234 | );
235 | }
236 | }
237 |
238 | class _DayPickerGridDelegate extends SliverGridDelegate {
239 | final double childHeight;
240 |
241 | const _DayPickerGridDelegate(this.childHeight);
242 |
243 | @override
244 | SliverGridLayout getLayout(SliverConstraints constraints) {
245 | const int columnCount = DateTime.daysPerWeek;
246 | final double tileWidth = constraints.crossAxisExtent / columnCount;
247 | return SliverGridRegularTileLayout(
248 | crossAxisCount: columnCount,
249 | mainAxisStride: childHeight,
250 | crossAxisStride: tileWidth,
251 | childMainAxisExtent: childHeight,
252 | childCrossAxisExtent: tileWidth,
253 | reverseCrossAxis: axisDirectionIsReversed(constraints.crossAxisDirection),
254 | );
255 | }
256 |
257 | @override
258 | bool shouldRelayout(_DayPickerGridDelegate oldDelegate) => false;
259 | }
260 |
--------------------------------------------------------------------------------
/r_calendar/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | async:
5 | dependency: transitive
6 | description:
7 | name: async
8 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
9 | url: "https://pub.dev"
10 | source: hosted
11 | version: "2.11.0"
12 | boolean_selector:
13 | dependency: transitive
14 | description:
15 | name: boolean_selector
16 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
17 | url: "https://pub.dev"
18 | source: hosted
19 | version: "2.1.1"
20 | characters:
21 | dependency: transitive
22 | description:
23 | name: characters
24 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
25 | url: "https://pub.dev"
26 | source: hosted
27 | version: "1.3.0"
28 | clock:
29 | dependency: transitive
30 | description:
31 | name: clock
32 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
33 | url: "https://pub.dev"
34 | source: hosted
35 | version: "1.1.1"
36 | collection:
37 | dependency: transitive
38 | description:
39 | name: collection
40 | sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
41 | url: "https://pub.dev"
42 | source: hosted
43 | version: "1.18.0"
44 | fake_async:
45 | dependency: transitive
46 | description:
47 | name: fake_async
48 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
49 | url: "https://pub.dev"
50 | source: hosted
51 | version: "1.3.1"
52 | flutter:
53 | dependency: "direct main"
54 | description: flutter
55 | source: sdk
56 | version: "0.0.0"
57 | flutter_test:
58 | dependency: "direct dev"
59 | description: flutter
60 | source: sdk
61 | version: "0.0.0"
62 | intl:
63 | dependency: "direct main"
64 | description:
65 | name: intl
66 | sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
67 | url: "https://pub.dev"
68 | source: hosted
69 | version: "0.18.1"
70 | leak_tracker:
71 | dependency: transitive
72 | description:
73 | name: leak_tracker
74 | sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa"
75 | url: "https://pub.dev"
76 | source: hosted
77 | version: "10.0.0"
78 | leak_tracker_flutter_testing:
79 | dependency: transitive
80 | description:
81 | name: leak_tracker_flutter_testing
82 | sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0
83 | url: "https://pub.dev"
84 | source: hosted
85 | version: "2.0.1"
86 | leak_tracker_testing:
87 | dependency: transitive
88 | description:
89 | name: leak_tracker_testing
90 | sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47
91 | url: "https://pub.dev"
92 | source: hosted
93 | version: "2.0.1"
94 | matcher:
95 | dependency: transitive
96 | description:
97 | name: matcher
98 | sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
99 | url: "https://pub.dev"
100 | source: hosted
101 | version: "0.12.16+1"
102 | material_color_utilities:
103 | dependency: transitive
104 | description:
105 | name: material_color_utilities
106 | sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
107 | url: "https://pub.dev"
108 | source: hosted
109 | version: "0.8.0"
110 | meta:
111 | dependency: transitive
112 | description:
113 | name: meta
114 | sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
115 | url: "https://pub.dev"
116 | source: hosted
117 | version: "1.11.0"
118 | path:
119 | dependency: transitive
120 | description:
121 | name: path
122 | sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
123 | url: "https://pub.dev"
124 | source: hosted
125 | version: "1.9.0"
126 | sky_engine:
127 | dependency: transitive
128 | description: flutter
129 | source: sdk
130 | version: "0.0.99"
131 | source_span:
132 | dependency: transitive
133 | description:
134 | name: source_span
135 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
136 | url: "https://pub.dev"
137 | source: hosted
138 | version: "1.10.0"
139 | stack_trace:
140 | dependency: transitive
141 | description:
142 | name: stack_trace
143 | sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
144 | url: "https://pub.dev"
145 | source: hosted
146 | version: "1.11.1"
147 | stream_channel:
148 | dependency: transitive
149 | description:
150 | name: stream_channel
151 | sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
152 | url: "https://pub.dev"
153 | source: hosted
154 | version: "2.1.2"
155 | string_scanner:
156 | dependency: transitive
157 | description:
158 | name: string_scanner
159 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
160 | url: "https://pub.dev"
161 | source: hosted
162 | version: "1.2.0"
163 | term_glyph:
164 | dependency: transitive
165 | description:
166 | name: term_glyph
167 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
168 | url: "https://pub.dev"
169 | source: hosted
170 | version: "1.2.1"
171 | test_api:
172 | dependency: transitive
173 | description:
174 | name: test_api
175 | sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
176 | url: "https://pub.dev"
177 | source: hosted
178 | version: "0.6.1"
179 | vector_math:
180 | dependency: transitive
181 | description:
182 | name: vector_math
183 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
184 | url: "https://pub.dev"
185 | source: hosted
186 | version: "2.1.4"
187 | vm_service:
188 | dependency: transitive
189 | description:
190 | name: vm_service
191 | sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957
192 | url: "https://pub.dev"
193 | source: hosted
194 | version: "13.0.0"
195 | sdks:
196 | dart: ">=3.2.3 <4.0.0"
197 |
--------------------------------------------------------------------------------
/r_calendar/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: r_calendar
2 | description: A new Flutter package about calendar,you can use this design the calendar and support single or multiple selected.
3 | version: 0.1.12
4 | homepage: https://github.com/rhymelph/r_calendar
5 |
6 | environment:
7 | sdk: '>=3.2.3 <4.0.0'
8 |
9 | dependencies:
10 | flutter:
11 | sdk: flutter
12 | intl: ^0.18.1
13 | dev_dependencies:
14 | flutter_test:
15 | sdk: flutter
16 |
17 | # For information on the generic Dart part of this file, see the
18 | # following page: https://dart.dev/tools/pub/pubspec
19 |
20 | # The following section is specific to Flutter.
21 | flutter:
22 |
23 | # To add assets to your package, add an assets section, like this:
24 | # assets:
25 | # - images/a_dot_burr.jpeg
26 | # - images/a_dot_ham.jpeg
27 | #
28 | # For details regarding assets in packages, see
29 | # https://flutter.dev/assets-and-images/#from-packages
30 | #
31 | # An image asset can refer to one or more resolution-specific "variants", see
32 | # https://flutter.dev/assets-and-images/#resolution-aware.
33 |
34 | # To add custom fonts to your package, add a fonts section here,
35 | # in this "flutter" section. Each entry in this list should have a
36 | # "family" key with the font family name, and a "fonts" key with a
37 | # list giving the asset and other descriptors for the font. For
38 | # example:
39 | # fonts:
40 | # - family: Schyler
41 | # fonts:
42 | # - asset: fonts/Schyler-Regular.ttf
43 | # - asset: fonts/Schyler-Italic.ttf
44 | # style: italic
45 | # - family: Trajan Pro
46 | # fonts:
47 | # - asset: fonts/TrajanPro.ttf
48 | # - asset: fonts/TrajanPro_Bold.ttf
49 | # weight: 700
50 | #
51 | # For details regarding fonts in packages, see
52 | # https://flutter.dev/custom-fonts/#from-packages
53 |
--------------------------------------------------------------------------------
/r_calendar/test/r_calendar_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_test/flutter_test.dart';
2 |
3 | void main() {
4 | test('adds one to input values', () {
5 | // final calculator = Calculator();
6 | // expect(calculator.addOne(2), 3);
7 | // expect(calculator.addOne(-7), -6);
8 | // expect(calculator.addOne(0), 1);
9 | // expect(() => calculator.addOne(null), throwsNoSuchMethodError);
10 | print(DateTime.now().weekday);
11 | });
12 | }
13 |
--------------------------------------------------------------------------------
/screen/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rhymelph/r_calendar/a2a33d772336025552a6205252049aeec71cb94d/screen/.DS_Store
--------------------------------------------------------------------------------
/screen/s1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rhymelph/r_calendar/a2a33d772336025552a6205252049aeec71cb94d/screen/s1.png
--------------------------------------------------------------------------------
/screen/s2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rhymelph/r_calendar/a2a33d772336025552a6205252049aeec71cb94d/screen/s2.png
--------------------------------------------------------------------------------
/screen/s3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rhymelph/r_calendar/a2a33d772336025552a6205252049aeec71cb94d/screen/s3.png
--------------------------------------------------------------------------------
/screen/s4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rhymelph/r_calendar/a2a33d772336025552a6205252049aeec71cb94d/screen/s4.png
--------------------------------------------------------------------------------