├── .gitignore
├── .metadata
├── API.md
├── API_en.md
├── CHANGELOG.md
├── LICENSE
├── README.md
├── README_en.md
├── example
├── .idea
│ ├── codeStyles
│ │ └── Project.xml
│ ├── example.iml
│ ├── libraries
│ │ ├── Dart_Packages.xml
│ │ ├── Dart_SDK.xml
│ │ └── Flutter_Plugins.xml
│ ├── modules.xml
│ └── workspace.xml
├── README.md
├── android
│ ├── .gitignore
│ ├── app
│ │ ├── build.gradle
│ │ └── src
│ │ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java
│ │ │ │ └── com
│ │ │ │ │ └── example
│ │ │ │ │ └── example
│ │ │ │ │ └── MainActivity.java
│ │ │ └── res
│ │ │ │ ├── drawable
│ │ │ │ └── launch_background.xml
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ └── values
│ │ │ │ └── styles.xml
│ │ │ └── profile
│ │ │ └── AndroidManifest.xml
│ ├── build.gradle
│ ├── example_android.iml
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ └── gradle-wrapper.properties
│ └── settings.gradle
├── example.iml
├── example
│ └── example.iml
├── ios
│ ├── .gitignore
│ ├── Flutter
│ │ ├── AppFrameworkInfo.plist
│ │ ├── Debug.xcconfig
│ │ └── Release.xcconfig
│ ├── Runner.xcodeproj
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcshareddata
│ │ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ │ └── WorkspaceSettings.xcsettings
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── Runner.xcscheme
│ ├── Runner.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ └── Runner
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ ├── Icon-App-20x20@1x.png
│ │ │ ├── Icon-App-20x20@2x.png
│ │ │ ├── Icon-App-20x20@3x.png
│ │ │ ├── Icon-App-29x29@1x.png
│ │ │ ├── Icon-App-29x29@2x.png
│ │ │ ├── Icon-App-29x29@3x.png
│ │ │ ├── Icon-App-40x40@1x.png
│ │ │ ├── Icon-App-40x40@2x.png
│ │ │ ├── Icon-App-40x40@3x.png
│ │ │ ├── Icon-App-60x60@2x.png
│ │ │ ├── Icon-App-60x60@3x.png
│ │ │ ├── Icon-App-76x76@1x.png
│ │ │ ├── Icon-App-76x76@2x.png
│ │ │ └── Icon-App-83.5x83.5@2x.png
│ │ └── LaunchImage.imageset
│ │ │ ├── Contents.json
│ │ │ ├── LaunchImage.png
│ │ │ ├── LaunchImage@2x.png
│ │ │ ├── LaunchImage@3x.png
│ │ │ └── README.md
│ │ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ └── Runner-Bridging-Header.h
├── lib
│ └── main.dart
├── pubspec.lock
├── pubspec.yaml
└── test
│ └── widget_test.dart
├── flutter_custom_calendar.iml
├── fluttercustomcalendar.iml
├── img.gif
├── lib
├── cache_data.dart
├── calendar_provider.dart
├── configuration.dart
├── constants
│ └── constants.dart
├── controller.dart
├── flutter_custom_calendar.dart
├── model
│ └── date_model.dart
├── style
│ └── style.dart
├── utils
│ ├── LogUtil.dart
│ ├── date_util.dart
│ ├── lunar_util.dart
│ ├── math_util.dart
│ └── solar_term_util.dart
└── widget
│ ├── base_day_view.dart
│ ├── base_week_bar.dart
│ ├── calendar_view.dart
│ ├── default_combine_day_view.dart
│ ├── default_custom_day_view.dart
│ ├── default_week_bar.dart
│ ├── month_view.dart
│ ├── month_view_pager.dart
│ ├── only_one_pointer_widget.dart
│ ├── week_view.dart
│ └── week_view_pager.dart
├── pubspec.lock
├── pubspec.yaml
└── test
└── fluttercustomcalendar_test.dart
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .idea/
3 | .dart_tool/
4 |
5 | .packages
6 | .pub/
7 |
8 | build/
9 | ios/.generated/
10 | ios/Flutter/Generated.xcconfig
11 | ios/Runner/GeneratedPluginRegistrant.*
12 |
--------------------------------------------------------------------------------
/.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: d42db56b52657d7557664393ac1fe9a734a6b6e7
8 | channel: master
9 |
10 | project_type: package
11 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [1.0.4+0.4]
2 | - 新增三个模式相互且含数据不影响
3 |
4 | ## [1.0.1]
5 | - 新增多选范围功能
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 2-Clause License
2 |
3 | Copyright (c) 2019, LXD312569496
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | > The code owner of the repo has not been actively building Flutter projects, thus the repo has been archived.
2 | > 该项目的作者已经长时间未开发 Flutter 项目,故该项目已被归档。
3 |
4 | ## flutter_custom_calendar
5 | > 本插件是基于[flutter_custom_calendar](https://github.com/fluttercandies/flutter_custom_calendar)做了稍微的修改进行上传的。
6 |
7 | 具体使用方法见[flutter_custom_calendar](https://github.com/ifgyong/flutter_custom_calendar)
8 |
9 | 新增一个选择`mode`
10 |
11 | 支持选择开始和结束,选择范围内的日期,使用方法
12 |
13 | ```
14 | controller = new CalendarController(
15 | minYear: 2019,
16 | minYearMonth: 1,
17 | maxYear: 2021,
18 | maxYearMonth: 12,
19 | showMode: CalendarConstants.MODE_SHOW_MONTH_AND_WEEK,
20 | selectedDateTimeList: _selectedDate,
21 | selectMode: CalendarSelectedMode.mutltiStartToEndSelect)
22 | ..addOnCalendarSelectListener((dateModel) {
23 | _selectedModels.add(dateModel);
24 | })
25 | ..addOnCalendarUnSelectListener((dateModel) {
26 | if (_selectedModels.contains(dateModel)) {
27 | _selectedModels.remove(dateModel);
28 | }
29 | });
30 |
31 | ```
32 | `CalendarSelectedMode.mutltiStartToEndSelect`这个选择模式会选择开始和结束中间的 默认选择的。
33 |
34 |
35 | ### 安装和使用
36 |
37 | Use this package as a library
38 | 1. Depend on it
39 | Add this to your package's pubspec.yaml file:
40 |
41 | ```
42 | dependencies:
43 | flutter_custom_calendar: ^1.0.4+0.5
44 | ```
45 |
46 | 2. Install it
47 | You can install packages from the command line:
48 |
49 | with Flutter:
50 |
51 | ```
52 | $ flutter pub get
53 | ```
54 |
55 | Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.
56 |
57 | 3. Import it
58 | Now in your Dart code, you can use:
59 |
60 | ```
61 | import 'package:flutter_custom_calendar/flutter_custom_calendar.dart';
62 | ```
63 | ### 监听月视图和周视图状态
64 |
65 | ```dart WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
66 | controller.addExpandChangeListener((value) {
67 | /// 添加改变 月视图和 周视图的监听
68 | _isMonthSelected = value;
69 | setState(() {});
70 | });
71 | });
72 | ```
73 | ### 变更月视图和周视图
74 | > 前提条件是`showModel`是`CalendarConstants.MODE_SHOW_MONTH_AND_WEEK`或者`CalendarConstants.MODE_SHOW_WEEK_AND_MONTH`.
75 |
76 | #### 变更到周视图
77 | ```dart
78 | setState(() {
79 | controller.weekAndMonthViewChange(CalendarConstants.MODE_SHOW_ONLY_WEEK);
80 | });
81 | ```
82 |
83 | #### 变更到月视图
84 | ```dart
85 | setState(() {controller.weekAndMonthViewChange(CalendarConstants.MODE_SHOW_ONLY_MONTH);
86 | });
87 | ```
88 |
89 | ### 动画演示
90 | 
91 | ### [查看API](https://github.com/ifgyong/flutter_custom_calendar/blob/master/API.md)
92 |
93 | ### [查看一个例子 如何使用](https://github.com/ifgyong/flutter_custom_calendar/blob/master/example/lib/main.dart)
94 |
--------------------------------------------------------------------------------
/README_en.md:
--------------------------------------------------------------------------------
1 |
2 | ## FlutterCalendarWidget
3 |
4 | A calendar widget in flutter,you can design what you want to show!
5 |
6 | Language:English|[中文简体](README.md)
7 |
8 | - [FlutterCalendarWidget](#fluttercalendarwidget)
9 | - [Overview](#overview)
10 | - [Online Demo](#online-demo)
11 | - [Example](#example)
12 | - [Getting Started](#getting-started)
13 | - [2.0 version](#20-version)
14 | - [matters needing attention](#matters-needing-attention)
15 | - [API Documentation](#api-documentation)
16 |
17 | ### Overview
18 |
19 | * Support the Gregorian calendar, lunar calendar, solar terms, traditional festivals and common holidays
20 | * Date range setting, the maximum date range supported by default is 1971.01-2055.12
21 | * Disable date range settings. For example, you can click within a range of dates and gray the dates outside the range
22 | * Support single selection and multiple selection modes, and provide multiple selection of callbacks exceeding the limit and multiple selection of callbacks exceeding the specified range.
23 | * Jump to the specified date. Animation switching is supported by default
24 | * Custom Calendar items, support the way of combining widgets and drawing with canvas
25 | * Customize the weekbar at the top
26 | * According to the actual scene, you can add custom additional data to the item to realize various additional functions. For example, to realize the calendar of progress bar style and various marks of calendar
27 | * Support weekly view display, monthly view and weekly view display and switching linkage
28 |
29 | ### Online Demo
30 |
31 | Calendar supports web Preview:[Click here for preview](https://lxd312569496.github.io/flutter_custom_calendar/#/)
32 |
33 |
34 | ### Example
35 |
36 |
37 |
38 |
39 |
40 |
41 | |
42 |
43 |
44 |
45 | |
46 |
47 |
48 |
49 |
50 |
51 | |
52 |
53 |
54 | |
55 |
56 |
57 | |
58 |
59 |
60 |
61 |
62 |
63 | |
64 |
65 |
66 | |
67 |
68 |
69 | |
70 |
71 |
72 |
73 |
74 |
75 |
76 | ## Getting Started
77 |
78 | 1.add dependencies into you project pubspec.yaml file:
79 | ```
80 | flutter_custom_calendar:
81 | git:
82 | url: https://github.com/LXD312569496/flutter_custom_calendar.git
83 | ```
84 |
85 | 2.import flutter_custom_calendar lib
86 | ```
87 | import 'package:flutter_custom_calendar/flutter_custom_calendar.dart';
88 | ```
89 |
90 | 3.create CalendarViewWidget object,profile CalendarController
91 | ```
92 | CalendarController controller= new CalendarController(
93 | minYear: 2018,
94 | minYearMonth: 1,
95 | maxYear: 2020,
96 | maxYearMonth: 12,
97 | showMode: CalendarConstants.MODE_SHOW_MONTH_AND_WEEK);
98 | CalendarViewWidget calendar= CalendarViewWidget(
99 | calendarController: controller,
100 | ),
101 | ```
102 |
103 | 4.operate controller
104 | ```
105 | controller.toggleExpandStatus();//Switch between month view and week view
106 | ```
107 |
108 | ```
109 | controller.previousPage();//Action calendar switch to previous page
110 | ```
111 |
112 | ```
113 | controller.nextPage();//Action calendar switch to next page
114 |
115 | ```
116 |
117 |
118 | ## 2.0 version
119 | Major changes:
120 | * The UI configuration related parameters are moved to the calendar view construction method (the old version is configured in the controller)
121 | * Calendar supports padding and margin attributes, and item size calculation and modification.
122 | * Realize the overall adaptive height of calendar
123 | * The controller provides the method of changing extradatamap, which can dynamically modify the customized data extradatamap at any time.
124 | * It supports the display of monthly view and weekly view, with weekly view as the priority, and mode "show" week "and" month "
125 | * Supports verticalspacing and itemsize properties
126 |
127 |
128 | ## matters needing attention
129 |
130 | * If you use the version before 2.0, you need to move the UI configuration related parameters to the calendar view construction method (the old version is configured in the controller).
131 | * I haven't found any other problems for the time being. If you have any, please let me know.
132 | * If you use this library to make a calendar, you can share the display results with me, and I will paste them on the document for display.
133 |
134 |
135 |
136 | ## API Documentation
137 |
138 | [API Documentation](API.md)
139 |
140 |
141 |
--------------------------------------------------------------------------------
/example/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | xmlns:android
14 |
15 | ^$
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | xmlns:.*
25 |
26 | ^$
27 |
28 |
29 | BY_NAME
30 |
31 |
32 |
33 |
34 |
35 |
36 | .*:id
37 |
38 | http://schemas.android.com/apk/res/android
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | .*:name
48 |
49 | http://schemas.android.com/apk/res/android
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | name
59 |
60 | ^$
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | style
70 |
71 | ^$
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | .*
81 |
82 | ^$
83 |
84 |
85 | BY_NAME
86 |
87 |
88 |
89 |
90 |
91 |
92 | .*
93 |
94 | http://schemas.android.com/apk/res/android
95 |
96 |
97 | ANDROID_ATTRIBUTE_ORDER
98 |
99 |
100 |
101 |
102 |
103 |
104 | .*
105 |
106 | .*
107 |
108 |
109 | BY_NAME
110 |
111 |
112 |
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/example/.idea/example.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/example/.idea/libraries/Dart_Packages.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
--------------------------------------------------------------------------------
/example/.idea/libraries/Dart_SDK.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/example/.idea/libraries/Flutter_Plugins.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/example/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/.idea/workspace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | 1592272073156
43 |
44 |
45 | 1592272073156
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # example
2 |
3 | flutter_custom_calendar/example
4 |
5 | ## Getting Started
6 |
7 | This project is a starting point for a Flutter application.
8 |
9 | A few resources to get you started if this is your first Flutter project:
10 |
11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
13 |
14 | For help getting started with Flutter, view our
15 | [online documentation](https://flutter.dev/docs), which offers tutorials,
16 | samples, guidance on mobile development, and a full API reference.
17 |
--------------------------------------------------------------------------------
/example/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
--------------------------------------------------------------------------------
/example/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
26 |
27 | android {
28 | compileSdkVersion 28
29 |
30 | lintOptions {
31 | disable 'InvalidPackage'
32 | }
33 |
34 | defaultConfig {
35 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
36 | applicationId "com.example.example"
37 | minSdkVersion 16
38 | targetSdkVersion 28
39 | versionCode flutterVersionCode.toInteger()
40 | versionName flutterVersionName
41 | }
42 |
43 | buildTypes {
44 | release {
45 | // TODO: Add your own signing config for the release build.
46 | // Signing with the debug keys for now, so `flutter run --release` works.
47 | signingConfig signingConfigs.debug
48 | }
49 | }
50 | }
51 |
52 | flutter {
53 | source '../..'
54 | }
55 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
8 |
12 |
19 |
23 |
27 |
32 |
36 |
37 |
38 |
39 |
40 |
41 |
43 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/com/example/example/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.example;
2 |
3 | import io.flutter.embedding.android.FlutterActivity;
4 |
5 | public class MainActivity extends FlutterActivity {
6 | }
7 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_custom_calendar/f5f822f5a73a55264f382545ca368ff3b7ca98ac/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_custom_calendar/f5f822f5a73a55264f382545ca368ff3b7ca98ac/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_custom_calendar/f5f822f5a73a55264f382545ca368ff3b7ca98ac/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_custom_calendar/f5f822f5a73a55264f382545ca368ff3b7ca98ac/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_custom_calendar/f5f822f5a73a55264f382545ca368ff3b7ca98ac/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/example/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | google()
4 | jcenter()
5 | }
6 |
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:3.5.0'
9 | }
10 | }
11 |
12 | allprojects {
13 | repositories {
14 | google()
15 | jcenter()
16 | }
17 | }
18 |
19 | rootProject.buildDir = '../build'
20 | subprojects {
21 | project.buildDir = "${rootProject.buildDir}/${project.name}"
22 | }
23 | subprojects {
24 | project.evaluationDependsOn(':app')
25 | }
26 |
27 | task clean(type: Delete) {
28 | delete rootProject.buildDir
29 | }
30 |
--------------------------------------------------------------------------------
/example/android/example_android.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.enableR8=true
3 | android.useAndroidX=true
4 | android.enableJetifier=true
5 |
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
7 |
--------------------------------------------------------------------------------
/example/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
4 |
5 | def plugins = new Properties()
6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
7 | if (pluginsFile.exists()) {
8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
9 | }
10 |
11 | plugins.each { name, path ->
12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
13 | include ":$name"
14 | project(":$name").projectDir = pluginDirectory
15 | }
16 |
--------------------------------------------------------------------------------
/example/example.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/example/example/example.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/example/ios/.gitignore:
--------------------------------------------------------------------------------
1 | *.mode1v3
2 | *.mode2v3
3 | *.moved-aside
4 | *.pbxuser
5 | *.perspectivev3
6 | **/*sync/
7 | .sconsign.dblite
8 | .tags*
9 | **/.vagrant/
10 | **/DerivedData/
11 | Icon?
12 | **/Pods/
13 | **/.symlinks/
14 | profile
15 | xcuserdata
16 | **/.generated/
17 | Flutter/App.framework
18 | Flutter/Flutter.framework
19 | Flutter/Flutter.podspec
20 | Flutter/Generated.xcconfig
21 | Flutter/app.flx
22 | Flutter/app.zip
23 | Flutter/flutter_assets/
24 | Flutter/flutter_export_environment.sh
25 | ServiceDefinitions.json
26 | Runner/GeneratedPluginRegistrant.*
27 |
28 | # Exceptions to above rules.
29 | !default.mode1v3
30 | !default.mode2v3
31 | !default.pbxuser
32 | !default.perspectivev3
33 |
--------------------------------------------------------------------------------
/example/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 8.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
12 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
13 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
14 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
15 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
16 | /* End PBXBuildFile section */
17 |
18 | /* Begin PBXCopyFilesBuildPhase section */
19 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
20 | isa = PBXCopyFilesBuildPhase;
21 | buildActionMask = 2147483647;
22 | dstPath = "";
23 | dstSubfolderSpec = 10;
24 | files = (
25 | );
26 | name = "Embed Frameworks";
27 | runOnlyForDeploymentPostprocessing = 0;
28 | };
29 | /* End PBXCopyFilesBuildPhase section */
30 |
31 | /* Begin PBXFileReference section */
32 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
33 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
34 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
35 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
36 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
37 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
38 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
39 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
40 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
41 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
42 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
43 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
44 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
45 | /* End PBXFileReference section */
46 |
47 | /* Begin PBXFrameworksBuildPhase section */
48 | 97C146EB1CF9000F007C117D /* Frameworks */ = {
49 | isa = PBXFrameworksBuildPhase;
50 | buildActionMask = 2147483647;
51 | files = (
52 | );
53 | runOnlyForDeploymentPostprocessing = 0;
54 | };
55 | /* End PBXFrameworksBuildPhase section */
56 |
57 | /* Begin PBXGroup section */
58 | 9740EEB11CF90186004384FC /* Flutter */ = {
59 | isa = PBXGroup;
60 | children = (
61 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
62 | 9740EEB21CF90195004384FC /* Debug.xcconfig */,
63 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
64 | 9740EEB31CF90195004384FC /* Generated.xcconfig */,
65 | );
66 | name = Flutter;
67 | sourceTree = "";
68 | };
69 | 97C146E51CF9000F007C117D = {
70 | isa = PBXGroup;
71 | children = (
72 | 9740EEB11CF90186004384FC /* Flutter */,
73 | 97C146F01CF9000F007C117D /* Runner */,
74 | 97C146EF1CF9000F007C117D /* Products */,
75 | );
76 | sourceTree = "";
77 | };
78 | 97C146EF1CF9000F007C117D /* Products */ = {
79 | isa = PBXGroup;
80 | children = (
81 | 97C146EE1CF9000F007C117D /* Runner.app */,
82 | );
83 | name = Products;
84 | sourceTree = "";
85 | };
86 | 97C146F01CF9000F007C117D /* Runner */ = {
87 | isa = PBXGroup;
88 | children = (
89 | 97C146FA1CF9000F007C117D /* Main.storyboard */,
90 | 97C146FD1CF9000F007C117D /* Assets.xcassets */,
91 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
92 | 97C147021CF9000F007C117D /* Info.plist */,
93 | 97C146F11CF9000F007C117D /* Supporting Files */,
94 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
95 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
96 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
97 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
98 | );
99 | path = Runner;
100 | sourceTree = "";
101 | };
102 | 97C146F11CF9000F007C117D /* Supporting Files */ = {
103 | isa = PBXGroup;
104 | children = (
105 | );
106 | name = "Supporting Files";
107 | sourceTree = "";
108 | };
109 | /* End PBXGroup section */
110 |
111 | /* Begin PBXNativeTarget section */
112 | 97C146ED1CF9000F007C117D /* Runner */ = {
113 | isa = PBXNativeTarget;
114 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
115 | buildPhases = (
116 | 9740EEB61CF901F6004384FC /* Run Script */,
117 | 97C146EA1CF9000F007C117D /* Sources */,
118 | 97C146EB1CF9000F007C117D /* Frameworks */,
119 | 97C146EC1CF9000F007C117D /* Resources */,
120 | 9705A1C41CF9048500538489 /* Embed Frameworks */,
121 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
122 | );
123 | buildRules = (
124 | );
125 | dependencies = (
126 | );
127 | name = Runner;
128 | productName = Runner;
129 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
130 | productType = "com.apple.product-type.application";
131 | };
132 | /* End PBXNativeTarget section */
133 |
134 | /* Begin PBXProject section */
135 | 97C146E61CF9000F007C117D /* Project object */ = {
136 | isa = PBXProject;
137 | attributes = {
138 | LastUpgradeCheck = 1020;
139 | ORGANIZATIONNAME = "";
140 | TargetAttributes = {
141 | 97C146ED1CF9000F007C117D = {
142 | CreatedOnToolsVersion = 7.3.1;
143 | LastSwiftMigration = 1100;
144 | };
145 | };
146 | };
147 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
148 | compatibilityVersion = "Xcode 9.3";
149 | developmentRegion = en;
150 | hasScannedForEncodings = 0;
151 | knownRegions = (
152 | en,
153 | Base,
154 | );
155 | mainGroup = 97C146E51CF9000F007C117D;
156 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
157 | projectDirPath = "";
158 | projectRoot = "";
159 | targets = (
160 | 97C146ED1CF9000F007C117D /* Runner */,
161 | );
162 | };
163 | /* End PBXProject section */
164 |
165 | /* Begin PBXResourcesBuildPhase section */
166 | 97C146EC1CF9000F007C117D /* Resources */ = {
167 | isa = PBXResourcesBuildPhase;
168 | buildActionMask = 2147483647;
169 | files = (
170 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
171 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
172 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
173 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
174 | );
175 | runOnlyForDeploymentPostprocessing = 0;
176 | };
177 | /* End PBXResourcesBuildPhase section */
178 |
179 | /* Begin PBXShellScriptBuildPhase section */
180 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
181 | isa = PBXShellScriptBuildPhase;
182 | buildActionMask = 2147483647;
183 | files = (
184 | );
185 | inputPaths = (
186 | );
187 | name = "Thin Binary";
188 | outputPaths = (
189 | );
190 | runOnlyForDeploymentPostprocessing = 0;
191 | shellPath = /bin/sh;
192 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
193 | };
194 | 9740EEB61CF901F6004384FC /* Run Script */ = {
195 | isa = PBXShellScriptBuildPhase;
196 | buildActionMask = 2147483647;
197 | files = (
198 | );
199 | inputPaths = (
200 | );
201 | name = "Run Script";
202 | outputPaths = (
203 | );
204 | runOnlyForDeploymentPostprocessing = 0;
205 | shellPath = /bin/sh;
206 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
207 | };
208 | /* End PBXShellScriptBuildPhase section */
209 |
210 | /* Begin PBXSourcesBuildPhase section */
211 | 97C146EA1CF9000F007C117D /* Sources */ = {
212 | isa = PBXSourcesBuildPhase;
213 | buildActionMask = 2147483647;
214 | files = (
215 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
216 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
217 | );
218 | runOnlyForDeploymentPostprocessing = 0;
219 | };
220 | /* End PBXSourcesBuildPhase section */
221 |
222 | /* Begin PBXVariantGroup section */
223 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
224 | isa = PBXVariantGroup;
225 | children = (
226 | 97C146FB1CF9000F007C117D /* Base */,
227 | );
228 | name = Main.storyboard;
229 | sourceTree = "";
230 | };
231 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
232 | isa = PBXVariantGroup;
233 | children = (
234 | 97C147001CF9000F007C117D /* Base */,
235 | );
236 | name = LaunchScreen.storyboard;
237 | sourceTree = "";
238 | };
239 | /* End PBXVariantGroup section */
240 |
241 | /* Begin XCBuildConfiguration section */
242 | 249021D3217E4FDB00AE95B9 /* Profile */ = {
243 | isa = XCBuildConfiguration;
244 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
245 | buildSettings = {
246 | ALWAYS_SEARCH_USER_PATHS = NO;
247 | CLANG_ANALYZER_NONNULL = YES;
248 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
249 | CLANG_CXX_LIBRARY = "libc++";
250 | CLANG_ENABLE_MODULES = YES;
251 | CLANG_ENABLE_OBJC_ARC = YES;
252 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
253 | CLANG_WARN_BOOL_CONVERSION = YES;
254 | CLANG_WARN_COMMA = YES;
255 | CLANG_WARN_CONSTANT_CONVERSION = YES;
256 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
257 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
258 | CLANG_WARN_EMPTY_BODY = YES;
259 | CLANG_WARN_ENUM_CONVERSION = YES;
260 | CLANG_WARN_INFINITE_RECURSION = YES;
261 | CLANG_WARN_INT_CONVERSION = YES;
262 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
263 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
264 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
265 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
266 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
267 | CLANG_WARN_STRICT_PROTOTYPES = YES;
268 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
269 | CLANG_WARN_UNREACHABLE_CODE = YES;
270 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
271 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
272 | COPY_PHASE_STRIP = NO;
273 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
274 | ENABLE_NS_ASSERTIONS = NO;
275 | ENABLE_STRICT_OBJC_MSGSEND = YES;
276 | GCC_C_LANGUAGE_STANDARD = gnu99;
277 | GCC_NO_COMMON_BLOCKS = YES;
278 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
279 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
280 | GCC_WARN_UNDECLARED_SELECTOR = YES;
281 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
282 | GCC_WARN_UNUSED_FUNCTION = YES;
283 | GCC_WARN_UNUSED_VARIABLE = YES;
284 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
285 | MTL_ENABLE_DEBUG_INFO = NO;
286 | SDKROOT = iphoneos;
287 | SUPPORTED_PLATFORMS = iphoneos;
288 | TARGETED_DEVICE_FAMILY = "1,2";
289 | VALIDATE_PRODUCT = YES;
290 | };
291 | name = Profile;
292 | };
293 | 249021D4217E4FDB00AE95B9 /* Profile */ = {
294 | isa = XCBuildConfiguration;
295 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
296 | buildSettings = {
297 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
298 | CLANG_ENABLE_MODULES = YES;
299 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
300 | ENABLE_BITCODE = NO;
301 | FRAMEWORK_SEARCH_PATHS = (
302 | "$(inherited)",
303 | "$(PROJECT_DIR)/Flutter",
304 | );
305 | INFOPLIST_FILE = Runner/Info.plist;
306 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
307 | LIBRARY_SEARCH_PATHS = (
308 | "$(inherited)",
309 | "$(PROJECT_DIR)/Flutter",
310 | );
311 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
312 | PRODUCT_NAME = "$(TARGET_NAME)";
313 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
314 | SWIFT_VERSION = 5.0;
315 | VERSIONING_SYSTEM = "apple-generic";
316 | };
317 | name = Profile;
318 | };
319 | 97C147031CF9000F007C117D /* Debug */ = {
320 | isa = XCBuildConfiguration;
321 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
322 | buildSettings = {
323 | ALWAYS_SEARCH_USER_PATHS = NO;
324 | CLANG_ANALYZER_NONNULL = YES;
325 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
326 | CLANG_CXX_LIBRARY = "libc++";
327 | CLANG_ENABLE_MODULES = YES;
328 | CLANG_ENABLE_OBJC_ARC = YES;
329 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
330 | CLANG_WARN_BOOL_CONVERSION = YES;
331 | CLANG_WARN_COMMA = YES;
332 | CLANG_WARN_CONSTANT_CONVERSION = YES;
333 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
334 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
335 | CLANG_WARN_EMPTY_BODY = YES;
336 | CLANG_WARN_ENUM_CONVERSION = YES;
337 | CLANG_WARN_INFINITE_RECURSION = YES;
338 | CLANG_WARN_INT_CONVERSION = YES;
339 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
340 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
341 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
342 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
343 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
344 | CLANG_WARN_STRICT_PROTOTYPES = YES;
345 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
346 | CLANG_WARN_UNREACHABLE_CODE = YES;
347 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
348 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
349 | COPY_PHASE_STRIP = NO;
350 | DEBUG_INFORMATION_FORMAT = dwarf;
351 | ENABLE_STRICT_OBJC_MSGSEND = YES;
352 | ENABLE_TESTABILITY = YES;
353 | GCC_C_LANGUAGE_STANDARD = gnu99;
354 | GCC_DYNAMIC_NO_PIC = NO;
355 | GCC_NO_COMMON_BLOCKS = YES;
356 | GCC_OPTIMIZATION_LEVEL = 0;
357 | GCC_PREPROCESSOR_DEFINITIONS = (
358 | "DEBUG=1",
359 | "$(inherited)",
360 | );
361 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
362 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
363 | GCC_WARN_UNDECLARED_SELECTOR = YES;
364 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
365 | GCC_WARN_UNUSED_FUNCTION = YES;
366 | GCC_WARN_UNUSED_VARIABLE = YES;
367 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
368 | MTL_ENABLE_DEBUG_INFO = YES;
369 | ONLY_ACTIVE_ARCH = YES;
370 | SDKROOT = iphoneos;
371 | TARGETED_DEVICE_FAMILY = "1,2";
372 | };
373 | name = Debug;
374 | };
375 | 97C147041CF9000F007C117D /* Release */ = {
376 | isa = XCBuildConfiguration;
377 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
378 | buildSettings = {
379 | ALWAYS_SEARCH_USER_PATHS = NO;
380 | CLANG_ANALYZER_NONNULL = YES;
381 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
382 | CLANG_CXX_LIBRARY = "libc++";
383 | CLANG_ENABLE_MODULES = YES;
384 | CLANG_ENABLE_OBJC_ARC = YES;
385 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
386 | CLANG_WARN_BOOL_CONVERSION = YES;
387 | CLANG_WARN_COMMA = YES;
388 | CLANG_WARN_CONSTANT_CONVERSION = YES;
389 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
390 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
391 | CLANG_WARN_EMPTY_BODY = YES;
392 | CLANG_WARN_ENUM_CONVERSION = YES;
393 | CLANG_WARN_INFINITE_RECURSION = YES;
394 | CLANG_WARN_INT_CONVERSION = YES;
395 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
396 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
397 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
398 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
399 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
400 | CLANG_WARN_STRICT_PROTOTYPES = YES;
401 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
402 | CLANG_WARN_UNREACHABLE_CODE = YES;
403 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
404 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
405 | COPY_PHASE_STRIP = NO;
406 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
407 | ENABLE_NS_ASSERTIONS = NO;
408 | ENABLE_STRICT_OBJC_MSGSEND = YES;
409 | GCC_C_LANGUAGE_STANDARD = gnu99;
410 | GCC_NO_COMMON_BLOCKS = YES;
411 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
412 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
413 | GCC_WARN_UNDECLARED_SELECTOR = YES;
414 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
415 | GCC_WARN_UNUSED_FUNCTION = YES;
416 | GCC_WARN_UNUSED_VARIABLE = YES;
417 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
418 | MTL_ENABLE_DEBUG_INFO = NO;
419 | SDKROOT = iphoneos;
420 | SUPPORTED_PLATFORMS = iphoneos;
421 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
422 | TARGETED_DEVICE_FAMILY = "1,2";
423 | VALIDATE_PRODUCT = YES;
424 | };
425 | name = Release;
426 | };
427 | 97C147061CF9000F007C117D /* Debug */ = {
428 | isa = XCBuildConfiguration;
429 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
430 | buildSettings = {
431 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
432 | CLANG_ENABLE_MODULES = YES;
433 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
434 | ENABLE_BITCODE = NO;
435 | FRAMEWORK_SEARCH_PATHS = (
436 | "$(inherited)",
437 | "$(PROJECT_DIR)/Flutter",
438 | );
439 | INFOPLIST_FILE = Runner/Info.plist;
440 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
441 | LIBRARY_SEARCH_PATHS = (
442 | "$(inherited)",
443 | "$(PROJECT_DIR)/Flutter",
444 | );
445 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
446 | PRODUCT_NAME = "$(TARGET_NAME)";
447 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
448 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
449 | SWIFT_VERSION = 5.0;
450 | VERSIONING_SYSTEM = "apple-generic";
451 | };
452 | name = Debug;
453 | };
454 | 97C147071CF9000F007C117D /* Release */ = {
455 | isa = XCBuildConfiguration;
456 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
457 | buildSettings = {
458 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
459 | CLANG_ENABLE_MODULES = YES;
460 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
461 | ENABLE_BITCODE = NO;
462 | FRAMEWORK_SEARCH_PATHS = (
463 | "$(inherited)",
464 | "$(PROJECT_DIR)/Flutter",
465 | );
466 | INFOPLIST_FILE = Runner/Info.plist;
467 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
468 | LIBRARY_SEARCH_PATHS = (
469 | "$(inherited)",
470 | "$(PROJECT_DIR)/Flutter",
471 | );
472 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
473 | PRODUCT_NAME = "$(TARGET_NAME)";
474 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
475 | SWIFT_VERSION = 5.0;
476 | VERSIONING_SYSTEM = "apple-generic";
477 | };
478 | name = Release;
479 | };
480 | /* End XCBuildConfiguration section */
481 |
482 | /* Begin XCConfigurationList section */
483 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
484 | isa = XCConfigurationList;
485 | buildConfigurations = (
486 | 97C147031CF9000F007C117D /* Debug */,
487 | 97C147041CF9000F007C117D /* Release */,
488 | 249021D3217E4FDB00AE95B9 /* Profile */,
489 | );
490 | defaultConfigurationIsVisible = 0;
491 | defaultConfigurationName = Release;
492 | };
493 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
494 | isa = XCConfigurationList;
495 | buildConfigurations = (
496 | 97C147061CF9000F007C117D /* Debug */,
497 | 97C147071CF9000F007C117D /* Release */,
498 | 249021D4217E4FDB00AE95B9 /* Profile */,
499 | );
500 | defaultConfigurationIsVisible = 0;
501 | defaultConfigurationName = Release;
502 | };
503 | /* End XCConfigurationList section */
504 | };
505 | rootObject = 97C146E61CF9000F007C117D /* Project object */;
506 | }
507 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 |
4 | @UIApplicationMain
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | GeneratedPluginRegistrant.register(with: self)
11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_custom_calendar/f5f822f5a73a55264f382545ca368ff3b7ca98ac/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_custom_calendar/f5f822f5a73a55264f382545ca368ff3b7ca98ac/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_custom_calendar/f5f822f5a73a55264f382545ca368ff3b7ca98ac/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_custom_calendar/f5f822f5a73a55264f382545ca368ff3b7ca98ac/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_custom_calendar/f5f822f5a73a55264f382545ca368ff3b7ca98ac/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_custom_calendar/f5f822f5a73a55264f382545ca368ff3b7ca98ac/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_custom_calendar/f5f822f5a73a55264f382545ca368ff3b7ca98ac/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_custom_calendar/f5f822f5a73a55264f382545ca368ff3b7ca98ac/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_custom_calendar/f5f822f5a73a55264f382545ca368ff3b7ca98ac/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_custom_calendar/f5f822f5a73a55264f382545ca368ff3b7ca98ac/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_custom_calendar/f5f822f5a73a55264f382545ca368ff3b7ca98ac/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_custom_calendar/f5f822f5a73a55264f382545ca368ff3b7ca98ac/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_custom_calendar/f5f822f5a73a55264f382545ca368ff3b7ca98ac/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_custom_calendar/f5f822f5a73a55264f382545ca368ff3b7ca98ac/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_custom_calendar/f5f822f5a73a55264f382545ca368ff3b7ca98ac/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_custom_calendar/f5f822f5a73a55264f382545ca368ff3b7ca98ac/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_custom_calendar/f5f822f5a73a55264f382545ca368ff3b7ca98ac/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_custom_calendar/f5f822f5a73a55264f382545ca368ff3b7ca98ac/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/example/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/example/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | example
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(FLUTTER_BUILD_NAME)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UIViewControllerBasedStatusBarAppearance
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/example/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/example/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'dart:collection';
2 |
3 | import 'package:flutter/cupertino.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter_custom_calendar/constants/constants.dart';
6 | import 'package:flutter_custom_calendar/controller.dart';
7 | import 'package:flutter_custom_calendar/flutter_custom_calendar.dart';
8 | import 'package:flutter_custom_calendar/utils/LogUtil.dart';
9 |
10 | void main() {
11 | runApp(MyApp());
12 | }
13 |
14 | class MyApp extends StatelessWidget {
15 | // This widget is the root of your application.
16 | @override
17 | Widget build(BuildContext context) {
18 | return MaterialApp(
19 | title: 'Flutter Demo',
20 | theme: ThemeData(
21 | primarySwatch: Colors.blue,
22 | visualDensity: VisualDensity.adaptivePlatformDensity,
23 | focusColor: Colors.teal),
24 | home: MyHomePage(title: 'Flutter Demo Home Page'),
25 | );
26 | }
27 | }
28 |
29 | class MyHomePage extends StatefulWidget {
30 | MyHomePage({Key key, this.title}) : super(key: key);
31 |
32 | final String title;
33 |
34 | @override
35 | _MyHomePageState createState() => _MyHomePageState();
36 | }
37 |
38 | class _MyHomePageState extends State {
39 | CalendarController controller;
40 | CalendarViewWidget calendar;
41 | HashSet _selectedDate = new HashSet();
42 | HashSet _selectedModels = new HashSet();
43 |
44 | GlobalKey _globalKey = new GlobalKey();
45 | @override
46 | void initState() {
47 | _selectedDate.add(DateTime.now());
48 | controller = new CalendarController(
49 | minYear: 2019,
50 | minYearMonth: 1,
51 | maxYear: 2021,
52 | maxYearMonth: 12,
53 | showMode: CalendarConstants.MODE_SHOW_WEEK_AND_MONTH,
54 | selectedDateTimeList: _selectedDate,
55 | selectMode: CalendarSelectedMode.singleSelect)
56 | ..addOnCalendarSelectListener((dateModel) {
57 | _selectedModels.add(dateModel);
58 | setState(() {
59 | _selectDate = _selectedModels.toString();
60 | });
61 | })
62 | ..addOnCalendarUnSelectListener((dateModel) {
63 | LogUtil.log(TAG: '_selectedModels', message: _selectedModels.toString());
64 | LogUtil.log(TAG: 'dateModel', message: dateModel.toString());
65 | if (_selectedModels.contains(dateModel)) {
66 | _selectedModels.remove(dateModel);
67 | }
68 | setState(() {
69 | _selectDate = '';
70 | });
71 | });
72 | calendar = new CalendarViewWidget(
73 | key: _globalKey,
74 | calendarController: controller,
75 | dayWidgetBuilder: (DateModel model) {
76 | double wd = (MediaQuery.of(context).size.width - 20) / 7;
77 | bool _isSelected = model.isSelected;
78 | if (_isSelected &&
79 | CalendarSelectedMode.singleSelect ==
80 | controller.calendarConfiguration.selectMode) {
81 | _selectDate = model.toString();
82 | }
83 | return ClipRRect(
84 | borderRadius: BorderRadius.all(Radius.circular(wd / 2)),
85 | child: Container(
86 | color: _isSelected ? Theme.of(context).focusColor : Colors.white,
87 | alignment: Alignment.center,
88 | child: Column(
89 | crossAxisAlignment: CrossAxisAlignment.center,
90 | mainAxisAlignment: MainAxisAlignment.center,
91 | children: [
92 | Text(
93 | model.day.toString(),
94 | style: TextStyle(
95 | color: model.isCurrentMonth
96 | ? (_isSelected == false
97 | ? (model.isWeekend
98 | ? Colors.black38
99 | : Colors.black87)
100 | : Colors.white)
101 | : Colors.black38),
102 | ),
103 | // Text(model.lunarDay.toString()),
104 | ],
105 | ),
106 | ),
107 | );
108 | },
109 | );
110 | WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
111 | controller.addExpandChangeListener((value) {
112 | /// 添加改变 月视图和 周视图的监听
113 | _isMonthSelected = value;
114 | setState(() {});
115 | });
116 | });
117 |
118 | super.initState();
119 | }
120 |
121 | bool _isMonthSelected = false;
122 |
123 | String _selectDate = '';
124 | @override
125 | Widget build(BuildContext context) {
126 | return Scaffold(
127 | appBar: AppBar(
128 | title: Text(widget.title),
129 | ),
130 | body: CupertinoScrollbar(
131 | child: CustomScrollView(
132 | slivers: [
133 | _topButtons(),
134 | _topMonths(),
135 | SliverToBoxAdapter(
136 | child: calendar,
137 | ),
138 | SliverToBoxAdapter(
139 | child: Container(
140 | child: Text(
141 | ' $_selectDate ',
142 | style: TextStyle(color: Theme.of(context).focusColor),
143 | ),
144 | ),
145 | )
146 | ],
147 | ),
148 | ),
149 | );
150 | }
151 |
152 | Widget _topButtons() {
153 | return SliverToBoxAdapter(
154 | child: Wrap(
155 | direction: Axis.vertical,
156 | crossAxisAlignment: WrapCrossAlignment.start,
157 | children: [
158 | Text('请选择mode'),
159 | Wrap(
160 | spacing: 10,
161 | runSpacing: 10,
162 | children: [
163 | FlatButton(
164 | child: Text(
165 | '单选',
166 | style: TextStyle(color: Colors.white),
167 | ),
168 | onPressed: () {
169 | setState(() {
170 | controller.calendarConfiguration.selectMode =
171 | CalendarSelectedMode.singleSelect;
172 | });
173 | },
174 | color: controller.calendarConfiguration.selectMode ==
175 | CalendarSelectedMode.singleSelect
176 | ? Colors.teal
177 | : Colors.black38,
178 | ),
179 | FlatButton(
180 | child: Text(
181 | '多选',
182 | style: TextStyle(color: Colors.white),
183 | ),
184 | onPressed: () {
185 | setState(() {
186 | controller.calendarConfiguration.selectMode =
187 | CalendarSelectedMode.multiSelect;
188 | });
189 | },
190 | color: controller.calendarConfiguration.selectMode ==
191 | CalendarSelectedMode.multiSelect
192 | ? Colors.teal
193 | : Colors.black38,
194 | ),
195 | FlatButton(
196 | child: Text(
197 | '多选 选择开始和结束',
198 | style: TextStyle(color: Colors.white),
199 | ),
200 | onPressed: () {
201 | setState(() {
202 | controller.calendarConfiguration.selectMode =
203 | CalendarSelectedMode.mutltiStartToEndSelect;
204 | });
205 | },
206 | color: controller.calendarConfiguration.selectMode ==
207 | CalendarSelectedMode.mutltiStartToEndSelect
208 | ? Colors.teal
209 | : Colors.black38,
210 | ),
211 | ],
212 | ),
213 | ],
214 | ),
215 | );
216 | }
217 |
218 | Widget _topMonths() {
219 | return SliverToBoxAdapter(
220 | child: Wrap(
221 | direction: Axis.vertical,
222 | crossAxisAlignment: WrapCrossAlignment.start,
223 | children: [
224 | Text('月视图和周视图'),
225 | Wrap(
226 | spacing: 10,
227 | runSpacing: 10,
228 | children: [
229 | FlatButton(
230 | child: Text(
231 | '月视图',
232 | style: TextStyle(color: Colors.white),
233 | ),
234 | onPressed: () {
235 | setState(() {
236 | controller.weekAndMonthViewChange(
237 | CalendarConstants.MODE_SHOW_ONLY_WEEK);
238 | });
239 | },
240 | color: _isMonthSelected ? Colors.teal : Colors.black38,
241 | ),
242 | FlatButton(
243 | child: Text(
244 | '周视图',
245 | style: TextStyle(color: Colors.white),
246 | ),
247 | onPressed: () {
248 | setState(() {
249 | controller.weekAndMonthViewChange(
250 | CalendarConstants.MODE_SHOW_ONLY_MONTH);
251 | });
252 | },
253 | color: _isMonthSelected == false ? Colors.teal : Colors.black38,
254 | ),
255 | ],
256 | ),
257 | ],
258 | ),
259 | );
260 | }
261 | }
262 |
--------------------------------------------------------------------------------
/example/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 | url: "https://pub.flutter-io.cn"
9 | source: hosted
10 | version: "2.4.2"
11 | boolean_selector:
12 | dependency: transitive
13 | description:
14 | name: boolean_selector
15 | url: "https://pub.flutter-io.cn"
16 | source: hosted
17 | version: "2.0.0"
18 | characters:
19 | dependency: transitive
20 | description:
21 | name: characters
22 | url: "https://pub.flutter-io.cn"
23 | source: hosted
24 | version: "1.0.0"
25 | charcode:
26 | dependency: transitive
27 | description:
28 | name: charcode
29 | url: "https://pub.flutter-io.cn"
30 | source: hosted
31 | version: "1.1.3"
32 | clock:
33 | dependency: transitive
34 | description:
35 | name: clock
36 | url: "https://pub.flutter-io.cn"
37 | source: hosted
38 | version: "1.0.1"
39 | collection:
40 | dependency: transitive
41 | description:
42 | name: collection
43 | url: "https://pub.flutter-io.cn"
44 | source: hosted
45 | version: "1.14.13"
46 | cupertino_icons:
47 | dependency: "direct main"
48 | description:
49 | name: cupertino_icons
50 | url: "https://pub.flutter-io.cn"
51 | source: hosted
52 | version: "0.1.3"
53 | fake_async:
54 | dependency: transitive
55 | description:
56 | name: fake_async
57 | url: "https://pub.flutter-io.cn"
58 | source: hosted
59 | version: "1.1.0"
60 | flutter:
61 | dependency: "direct main"
62 | description: flutter
63 | source: sdk
64 | version: "0.0.0"
65 | flutter_custom_calendar:
66 | dependency: "direct main"
67 | description:
68 | path: ".."
69 | relative: true
70 | source: path
71 | version: "1.0.4+0.5"
72 | flutter_test:
73 | dependency: "direct dev"
74 | description: flutter
75 | source: sdk
76 | version: "0.0.0"
77 | matcher:
78 | dependency: transitive
79 | description:
80 | name: matcher
81 | url: "https://pub.flutter-io.cn"
82 | source: hosted
83 | version: "0.12.8"
84 | meta:
85 | dependency: transitive
86 | description:
87 | name: meta
88 | url: "https://pub.flutter-io.cn"
89 | source: hosted
90 | version: "1.1.8"
91 | nested:
92 | dependency: transitive
93 | description:
94 | name: nested
95 | url: "https://pub.flutter-io.cn"
96 | source: hosted
97 | version: "0.0.4"
98 | path:
99 | dependency: transitive
100 | description:
101 | name: path
102 | url: "https://pub.flutter-io.cn"
103 | source: hosted
104 | version: "1.7.0"
105 | provider:
106 | dependency: transitive
107 | description:
108 | name: provider
109 | url: "https://pub.flutter-io.cn"
110 | source: hosted
111 | version: "4.1.3"
112 | sky_engine:
113 | dependency: transitive
114 | description: flutter
115 | source: sdk
116 | version: "0.0.99"
117 | source_span:
118 | dependency: transitive
119 | description:
120 | name: source_span
121 | url: "https://pub.flutter-io.cn"
122 | source: hosted
123 | version: "1.7.0"
124 | stack_trace:
125 | dependency: transitive
126 | description:
127 | name: stack_trace
128 | url: "https://pub.flutter-io.cn"
129 | source: hosted
130 | version: "1.9.5"
131 | stream_channel:
132 | dependency: transitive
133 | description:
134 | name: stream_channel
135 | url: "https://pub.flutter-io.cn"
136 | source: hosted
137 | version: "2.0.0"
138 | string_scanner:
139 | dependency: transitive
140 | description:
141 | name: string_scanner
142 | url: "https://pub.flutter-io.cn"
143 | source: hosted
144 | version: "1.0.5"
145 | term_glyph:
146 | dependency: transitive
147 | description:
148 | name: term_glyph
149 | url: "https://pub.flutter-io.cn"
150 | source: hosted
151 | version: "1.1.0"
152 | test_api:
153 | dependency: transitive
154 | description:
155 | name: test_api
156 | url: "https://pub.flutter-io.cn"
157 | source: hosted
158 | version: "0.2.17"
159 | typed_data:
160 | dependency: transitive
161 | description:
162 | name: typed_data
163 | url: "https://pub.flutter-io.cn"
164 | source: hosted
165 | version: "1.2.0"
166 | vector_math:
167 | dependency: transitive
168 | description:
169 | name: vector_math
170 | url: "https://pub.flutter-io.cn"
171 | source: hosted
172 | version: "2.0.8"
173 | sdks:
174 | dart: ">=2.9.0-14.0.dev <3.0.0"
175 | flutter: ">=1.16.0"
176 |
--------------------------------------------------------------------------------
/example/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: example
2 | description: flutter_custom_calendar example
3 |
4 | # The following line prevents the package from being accidentally published to
5 | # pub.dev using `pub publish`. This is preferred for private packages.
6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev
7 |
8 | # The following defines the version and build number for your application.
9 | # A version number is three numbers separated by dots, like 1.2.43
10 | # followed by an optional build number separated by a +.
11 | # Both the version and the builder number may be overridden in flutter
12 | # build by specifying --build-name and --build-number, respectively.
13 | # In Android, build-name is used as versionName while build-number used as versionCode.
14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning
15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
16 | # Read more about iOS versioning at
17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
18 | version: 1.0.0+1
19 |
20 | environment:
21 | sdk: ">=2.7.0 <3.0.0"
22 |
23 | dependencies:
24 | flutter:
25 | sdk: flutter
26 |
27 |
28 |
29 | # The following adds the Cupertino Icons font to your application.
30 | # Use with the CupertinoIcons class for iOS style icons.
31 | cupertino_icons: ^0.1.3
32 | flutter_custom_calendar:
33 | path: ../
34 |
35 | dev_dependencies:
36 | flutter_test:
37 | sdk: flutter
38 |
39 | # For information on the generic Dart part of this file, see the
40 | # following page: https://dart.dev/tools/pub/pubspec
41 |
42 | # The following section is specific to Flutter.
43 | flutter:
44 |
45 | # The following line ensures that the Material Icons font is
46 | # included with your application, so that you can use the icons in
47 | # the material Icons class.
48 | uses-material-design: true
49 |
50 | # To add assets to your application, add an assets section, like this:
51 | # assets:
52 | # - images/a_dot_burr.jpeg
53 | # - images/a_dot_ham.jpeg
54 |
55 | # An image asset can refer to one or more resolution-specific "variants", see
56 | # https://flutter.dev/assets-and-images/#resolution-aware.
57 |
58 | # For details regarding adding assets from package dependencies, see
59 | # https://flutter.dev/assets-and-images/#from-packages
60 |
61 | # To add custom fonts to your application, add a fonts section here,
62 | # in this "flutter" section. Each entry in this list should have a
63 | # "family" key with the font family name, and a "fonts" key with a
64 | # list giving the asset and other descriptors for the font. For
65 | # example:
66 | # fonts:
67 | # - family: Schyler
68 | # fonts:
69 | # - asset: fonts/Schyler-Regular.ttf
70 | # - asset: fonts/Schyler-Italic.ttf
71 | # style: italic
72 | # - family: Trajan Pro
73 | # fonts:
74 | # - asset: fonts/TrajanPro.ttf
75 | # - asset: fonts/TrajanPro_Bold.ttf
76 | # weight: 700
77 | #
78 | # For details regarding fonts from package dependencies,
79 | # see https://flutter.dev/custom-fonts/#from-packages
80 |
--------------------------------------------------------------------------------
/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 | void main() {
12 | // testWidgets('Counter increments smoke test', (WidgetTester tester) async {
13 | // // Build our app and trigger a frame.
14 | // await tester.pumpWidget(MyApp());
15 | //
16 | // // Verify that our counter starts at 0.
17 | // expect(find.text('0'), findsOneWidget);
18 | // expect(find.text('1'), findsNothing);
19 | //
20 | // // Tap the '+' icon and trigger a frame.
21 | // await tester.tap(find.byIcon(Icons.add));
22 | // await tester.pump();
23 | //
24 | // // Verify that our counter has incremented.
25 | // expect(find.text('0'), findsNothing);
26 | // expect(find.text('1'), findsOneWidget);
27 | // });
28 | }
29 |
--------------------------------------------------------------------------------
/flutter_custom_calendar.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/fluttercustomcalendar.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/img.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_custom_calendar/f5f822f5a73a55264f382545ca368ff3b7ca98ac/img.gif
--------------------------------------------------------------------------------
/lib/cache_data.dart:
--------------------------------------------------------------------------------
1 | import 'model/date_model.dart';
2 |
3 | /**
4 | * 保存一些缓存数据,不用再次去计算日子
5 | */
6 | class CacheData {
7 | //私有构造函数
8 | CacheData._();
9 |
10 | static CacheData _instance;
11 |
12 | static CacheData get instance => _instance;
13 |
14 | Map> monthListCache = Map();
15 |
16 | Map> weekListCache = Map();
17 |
18 | static CacheData getInstance() {
19 | if (_instance == null) {
20 | _instance = new CacheData._();
21 | }
22 | return _instance;
23 | }
24 |
25 | void clearData() {
26 | monthListCache.clear();
27 | weekListCache.clear();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/lib/calendar_provider.dart:
--------------------------------------------------------------------------------
1 | import 'dart:collection';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_custom_calendar/cache_data.dart';
5 | import 'package:flutter_custom_calendar/widget/month_view.dart';
6 | import 'configuration.dart';
7 | import 'constants/constants.dart';
8 | import 'flutter_custom_calendar.dart';
9 | import 'utils/LogUtil.dart';
10 | import 'utils/date_util.dart';
11 | import 'model/date_model.dart';
12 |
13 | /**
14 | * 引入provider的状态管理,保存一些临时信息
15 | *
16 | * 目前的情况:只需要获取状态,不需要监听rebuild
17 | */
18 | class CalendarProvider extends ChangeNotifier {
19 | double _totalHeight; //当前月视图的整体高度
20 | HashSet selectedDateList = new HashSet(); //被选中的日期,用于多选
21 | DateModel _selectDateModel; //当前选中的日期,用于单选
22 | ItemContainerState lastClickItemState;
23 | DateModel _lastClickDateModel;
24 |
25 | double get totalHeight => _totalHeight;
26 |
27 | ValueNotifier _generation =
28 | new ValueNotifier(0); //生成的int值,每次变化,都会刷新整个日历。
29 |
30 | ValueNotifier get generation => _generation;
31 |
32 | set generation(ValueNotifier value) {
33 | _generation = value;
34 | }
35 |
36 | set totalHeight(double value) {
37 | _totalHeight = value;
38 | }
39 |
40 | changeTotalHeight(double value) {
41 | _totalHeight = value;
42 | notifyListeners();
43 | }
44 |
45 | DateModel get lastClickDateModel =>
46 | _lastClickDateModel; //保存最后点击的一个日期,用于周视图与月视图之间的切换和同步
47 |
48 | set lastClickDateModel(DateModel value) {
49 | _lastClickDateModel = value;
50 | print("set lastClickDateModel:$lastClickDateModel");
51 | }
52 |
53 | DateModel get selectDateModel => _selectDateModel;
54 |
55 | set selectDateModel(DateModel value) {
56 | _selectDateModel = value;
57 | LogUtil.log(TAG: this.runtimeType, message: "selectDateModel change:$selectDateModel");
58 | // notifyListeners();
59 | }
60 |
61 | //根据lastClickDateModel,去计算需要展示的星期视图的初始index
62 | int get weekPageIndex {
63 | //计算当前星期视图的index
64 | print('计算当前星期视图的index = > lastClickDateModel$lastClickDateModel');
65 | DateModel dateModel = lastClickDateModel;
66 | DateTime firstWeek = calendarConfiguration.weekList[0].getDateTime();
67 | int index = 0;
68 | for (int i = 0; i < calendarConfiguration.weekList.length; i++) {
69 | DateTime nextWeek = firstWeek.add(Duration(days: 7));
70 | if (dateModel.getDateTime().isBefore(nextWeek)) {
71 | index = i;
72 | break;
73 | } else {
74 | firstWeek = nextWeek;
75 | index++;
76 | }
77 | }
78 |
79 | print("lastClickDateModel:$lastClickDateModel,weekPageIndex:$index, totalHeight:$totalHeight");
80 | return index;
81 | }
82 |
83 | //根据lastClickDateModel,去计算需要展示的月视图的index
84 | int get monthPageIndex {
85 | //计算当前月视图的index
86 | DateModel dateModel = lastClickDateModel;
87 | int index = 0;
88 | for (int i = 0; i < calendarConfiguration.monthList.length - 1; i++) {
89 | DateTime preMonth = calendarConfiguration.monthList[i].getDateTime();
90 | DateTime nextMonth = calendarConfiguration.monthList[i + 1].getDateTime();
91 | if (!dateModel.getDateTime().isBefore(preMonth) &&
92 | !dateModel.getDateTime().isAfter(nextMonth)) {
93 | index = i;
94 | break;
95 | } else {
96 | index++;
97 | }
98 | }
99 |
100 | print("lastClickDateModel:$lastClickDateModel, monthPageIndex:$index, totalHeight:$totalHeight");
101 | return index;
102 | }
103 |
104 | ValueNotifier expandStatus; //当前展开状态
105 |
106 | //配置类也放这里吧,这样的话,所有子树,都可以拿到配置的信息
107 | CalendarConfiguration calendarConfiguration;
108 | void weekAndMonthViewChange(int mode) {}
109 |
110 | void initData({
111 | Set selectedDateList,
112 | DateModel selectDateModel,
113 | CalendarConfiguration calendarConfiguration,
114 | EdgeInsetsGeometry padding,
115 | EdgeInsetsGeometry margin,
116 | double itemSize,
117 | double verticalSpacing,
118 | DayWidgetBuilder dayWidgetBuilder,
119 | WeekBarItemWidgetBuilder weekBarItemWidgetBuilder,
120 | }) {
121 | LogUtil.log(TAG: this.runtimeType, message: "CalendarProvider initData");
122 | this.calendarConfiguration = calendarConfiguration;
123 | this
124 | .selectedDateList
125 | .addAll(this.calendarConfiguration.defaultSelectedDateList);
126 | this.selectDateModel = this.calendarConfiguration.selectDateModel;
127 | this.calendarConfiguration.padding = padding;
128 | this.calendarConfiguration.margin = margin;
129 | this.calendarConfiguration.itemSize = itemSize;
130 | this.calendarConfiguration.verticalSpacing = verticalSpacing;
131 | this.calendarConfiguration.dayWidgetBuilder = dayWidgetBuilder;
132 | this.calendarConfiguration.weekBarItemWidgetBuilder = weekBarItemWidgetBuilder;
133 |
134 | //lastClickDateModel,默认是选中的item,如果为空的话,默认是当前的时间
135 | this.lastClickDateModel = selectDateModel != null ? selectDateModel : DateModel.fromDateTime(DateTime.now())..isSelected = true;
136 | //初始化展示状态
137 | if (calendarConfiguration.showMode == CalendarConstants.MODE_SHOW_ONLY_WEEK || calendarConfiguration.showMode == CalendarConstants.MODE_SHOW_WEEK_AND_MONTH) {
138 | expandStatus = ValueNotifier(false);
139 | } else {
140 | expandStatus = ValueNotifier(true);
141 | }
142 | //初始化item的大小。如果itemSize为空,默认是宽度/7。网页版的话是高度/7。需要减去padding和margin值
143 | if (calendarConfiguration.itemSize == null) {
144 | MediaQueryData mediaQueryData =
145 | MediaQueryData.fromWindow(WidgetsBinding.instance.window);
146 | if (mediaQueryData.orientation == Orientation.landscape) {
147 | calendarConfiguration.itemSize = (mediaQueryData.size.height -
148 | calendarConfiguration.padding.vertical -
149 | calendarConfiguration.margin.vertical) /
150 | 7;
151 | } else {
152 | calendarConfiguration.itemSize = (mediaQueryData.size.width -
153 | calendarConfiguration.padding.horizontal -
154 | calendarConfiguration.margin.horizontal) /
155 | 7;
156 | }
157 | } else {
158 | //如果指定了itemSize的大小,那就按照itemSize的大小去绘制
159 | }
160 |
161 | ///如果第一个页面展示的是月视图,需要计算下初始化的高度
162 | if (calendarConfiguration.showMode ==
163 | CalendarConstants.MODE_SHOW_ONLY_MONTH ||
164 | calendarConfiguration.showMode ==
165 | CalendarConstants.MODE_SHOW_MONTH_AND_WEEK) {
166 | int lineCount = DateUtil.getMonthViewLineCount(
167 | calendarConfiguration.nowYear,
168 | calendarConfiguration.nowMonth,
169 | calendarConfiguration.offset);
170 | totalHeight = calendarConfiguration.itemSize * (lineCount) + calendarConfiguration.verticalSpacing * (lineCount - 1);
171 | } else {
172 | totalHeight = calendarConfiguration.itemSize;
173 | }
174 | print('totalHeight: $totalHeight');
175 | }
176 |
177 | //退出的时候,清除数据
178 | void clearData() {
179 | LogUtil.log(TAG: this.runtimeType, message: "CalendarProvider clearData");
180 | CacheData.getInstance().clearData();
181 | selectedDateList.clear();
182 | selectDateModel = null;
183 | calendarConfiguration = null;
184 | }
185 | }
186 |
--------------------------------------------------------------------------------
/lib/configuration.dart:
--------------------------------------------------------------------------------
1 | import 'dart:collection';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter/foundation.dart';
5 |
6 | import 'constants/constants.dart';
7 | import 'flutter_custom_calendar.dart';
8 |
9 | import 'model/date_model.dart';
10 |
11 | /**
12 | * 配置信息类
13 | */
14 | class CalendarConfiguration {
15 | //默认是单选,可以配置为MODE_SINGLE_SELECT,MODE_MULTI_SELECT
16 | CalendarSelectedMode selectMode;
17 |
18 | //仅展示月视图,仅展示周视图,支持月视图和周视图切换
19 | int showMode;
20 |
21 | //日历显示的最小年份和最大年份
22 | int minYear;
23 | int maxYear;
24 |
25 | //日历显示的最小年份的月份,最大年份的月份
26 | int minYearMonth;
27 | int maxYearMonth;
28 |
29 | //日历显示的当前的年份和月份
30 | int nowYear;
31 | int nowMonth;
32 | // 周视图需要
33 | int nowDay;
34 |
35 | //可操作的范围设置,比如点击选择
36 | int minSelectYear;
37 | int minSelectMonth;
38 | int minSelectDay;
39 |
40 | int maxSelectYear;
41 | int maxSelectMonth;
42 | int maxSelectDay; //注意:不能超过对应月份的总天数
43 |
44 | DateModel selectDateModel; //默认被选中的item,用于单选
45 | HashSet defaultSelectedDateList; //默认被选中的日期set,用于多选
46 | int maxMultiSelectCount; //多选,最多选多少个
47 | Map extraDataMap = new Map(); //自定义额外的数据
48 |
49 | /**
50 | * UI绘制方面的绘制
51 | */
52 | double itemSize; //默认是屏幕宽度/7
53 | double verticalSpacing; //日历item之间的竖直方向间距,默认10
54 | BoxDecoration boxDecoration; //整体的背景设置
55 | EdgeInsetsGeometry padding;
56 | EdgeInsetsGeometry margin;
57 |
58 | //支持自定义绘制
59 | DayWidgetBuilder dayWidgetBuilder; //创建日历item
60 | WeekBarItemWidgetBuilder weekBarItemWidgetBuilder; //创建顶部的weekbar
61 |
62 | /**
63 | * 监听变化
64 | */
65 | //各种事件回调
66 | OnMonthChange monthChange; //月份切换事件 (已弃用,交给multiMonthChanges来实现)
67 | OnCalendarSelect calendarSelect; //点击选择事件
68 | OnCalendarSelect unCalendarSelect; //点击选择事件
69 | OnMultiSelectOutOfRange multiSelectOutOfRange; //多选超出指定范围
70 | OnMultiSelectOutOfSize multiSelectOutOfSize; //多选超出限制个数
71 |
72 | ObserverList monthChangeListeners =
73 | ObserverList(); //保存多个月份监听的事件
74 | ObserverList weekChangeListeners =
75 | ObserverList(); //周视图切换
76 |
77 | /**
78 | * 下面的信息不是配置的,是根据配置信息进行计算出来的
79 | */
80 | List monthList = new List(); //月份list
81 | List weekList = new List(); //星期list
82 | PageController monthController; //月份的controller
83 | PageController weekController; //星期的controller
84 | DateModel minSelectDate;
85 | DateModel maxSelectDate;
86 |
87 | /// 首日偏移量 first day offset
88 | /// first day = (first day of month or week) + offset
89 | final int offset;
90 |
91 | CalendarConfiguration(
92 | {this.selectMode,
93 | this.minYear,
94 | this.maxYear,
95 | this.minYearMonth,
96 | this.maxYearMonth,
97 | this.nowYear,
98 | this.nowMonth,
99 | this.minSelectYear,
100 | this.minSelectMonth,
101 | this.minSelectDay,
102 | this.maxSelectYear,
103 | this.maxSelectMonth,
104 | this.maxSelectDay,
105 | this.defaultSelectedDateList,
106 | this.selectDateModel,
107 | this.maxMultiSelectCount,
108 | this.extraDataMap,
109 | this.monthList,
110 | this.weekList,
111 | this.monthController,
112 | this.weekController,
113 | this.verticalSpacing,
114 | this.itemSize,
115 | this.showMode,
116 | this.padding,
117 | this.margin,
118 | this.offset = 0});
119 |
120 | @override
121 | String toString() {
122 | return 'CalendarConfiguration{selectMode: $selectMode, minYear: $minYear, maxYear: $maxYear, minYearMonth: $minYearMonth, maxYearMonth: $maxYearMonth, nowYear: $nowYear, nowMonth: $nowMonth, minSelectYear: $minSelectYear, minSelectMonth: $minSelectMonth, minSelectDay: $minSelectDay, maxSelectYear: $maxSelectYear, maxSelectMonth: $maxSelectMonth, maxSelectDay: $maxSelectDay, defaultSelectedDateList: $defaultSelectedDateList, maxMultiSelectCount: $maxMultiSelectCount, extraDataMap: $extraDataMap, monthList: $monthList, weekList: $weekList, monthController: $monthController, weekController: $weekController}';
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/lib/constants/constants.dart:
--------------------------------------------------------------------------------
1 | enum CalendarSelectedMode { singleSelect, multiSelect, mutltiStartToEndSelect }
2 |
3 | class CalendarConstants {
4 | //单选或者多选模式
5 | // 单选
6 | static const int MODE_SINGLE_SELECT = 1;
7 |
8 | /// 多选
9 |
10 | static const int MODE_MULTI_SELECT = 2;
11 |
12 | /// 选择开始和结束 中间的自动选择
13 |
14 | static const int MODE_MULTI_SELECT_START_TO_END = 3;
15 |
16 | //展示模式
17 | static const int MODE_SHOW_ONLY_MONTH = 1; //仅支持月视图
18 | static const int MODE_SHOW_ONLY_WEEK = 2; //仅支持星期视图
19 | static const int MODE_SHOW_WEEK_AND_MONTH = 3; //支持两种视图,先显示周视图
20 | static const int MODE_SHOW_MONTH_AND_WEEK = 4; //支持两种视图,先显示月视图
21 |
22 | /**
23 | * 一周七天
24 | */
25 | static const List WEEK_LIST = [
26 | "周一",
27 | "周二",
28 | "周三",
29 | "周四",
30 | "周五",
31 | "周六",
32 | "周日"
33 | ];
34 |
35 | /**
36 | * 农历的月份
37 | */
38 | static const List LUNAR_MONTH_TEXT = [
39 | "春节",
40 | "二月",
41 | "三月",
42 | "四月",
43 | "五月",
44 | "六月",
45 | "七月",
46 | "八月",
47 | "九月",
48 | "十月",
49 | "冬月",
50 | "腊月",
51 | ];
52 |
53 | /**
54 | * 农历的日期
55 | */
56 | static const List LUNAR_DAY_TEXT = [
57 | "初一",
58 | "初二",
59 | "初三",
60 | "初四",
61 | "初五",
62 | "初六",
63 | "初七",
64 | "初八",
65 | "初九",
66 | "初十",
67 | "十一",
68 | "十二",
69 | "十三",
70 | "十四",
71 | "十五",
72 | "十六",
73 | "十七",
74 | "十八",
75 | "十九",
76 | "二十",
77 | "廿一",
78 | "廿二",
79 | "廿三",
80 | "廿四",
81 | "廿五",
82 | "廿六",
83 | "廿七",
84 | "廿八",
85 | "廿九",
86 | "三十"
87 | ];
88 | }
89 |
--------------------------------------------------------------------------------
/lib/controller.dart:
--------------------------------------------------------------------------------
1 | import 'dart:collection';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'calendar_provider.dart';
5 | import 'configuration.dart';
6 | import 'constants/constants.dart';
7 | import 'flutter_custom_calendar.dart';
8 | import 'utils/LogUtil.dart';
9 | import 'utils/date_util.dart';
10 | import 'widget/default_combine_day_view.dart';
11 | import 'widget/default_custom_day_view.dart';
12 | import 'widget/default_week_bar.dart';
13 |
14 | import 'model/date_model.dart';
15 |
16 | /**
17 | * 利用controller来控制视图
18 | */
19 |
20 | class CalendarController {
21 | static const Set EMPTY_SET = {};
22 | static const Map EMPTY_MAP = {};
23 | static const Duration DEFAULT_DURATION = const Duration(milliseconds: 500);
24 |
25 | CalendarConfiguration calendarConfiguration;
26 |
27 | CalendarProvider calendarProvider = CalendarProvider();
28 |
29 | /**
30 | * 下面的信息不是配置的
31 | */
32 | List monthList = new List(); //月份list
33 | List weekList = new List(); //星期list
34 | PageController monthController; //月份的controller
35 | PageController weekController; //星期的controller
36 |
37 | CalendarController(
38 | {CalendarSelectedMode selectMode = CalendarSelectedMode.singleSelect,
39 | int showMode = CalendarConstants.MODE_SHOW_ONLY_MONTH,
40 | int minYear = 1971,
41 | int maxYear = 2055,
42 | int minYearMonth = 1,
43 | int maxYearMonth = 12,
44 | int nowYear,
45 | int nowMonth,
46 | int minSelectYear = 1971,
47 | int minSelectMonth = 1,
48 | int minSelectDay = 1,
49 | int maxSelectYear = 2055,
50 | int maxSelectMonth = 12,
51 | int maxSelectDay = 30,
52 | Set selectedDateTimeList = EMPTY_SET, //多选模式下,默认选中的item列表
53 | DateModel selectDateModel, //单选模式下,默认选中的item
54 | int maxMultiSelectCount = 9999,
55 | Map extraDataMap = EMPTY_MAP,
56 | int offset = 0 // 首日偏移量
57 | }) {
58 | assert(offset >= 0 && offset <= 6);
59 | LogUtil.log(TAG: this.runtimeType, message: "init CalendarConfiguration");
60 | //如果没有指定当前月份和年份,默认是当年时间
61 | if (nowYear == null) {
62 | nowYear = DateTime.now().year;
63 | }
64 | if (nowMonth == null) {
65 | nowMonth = DateTime.now().month;
66 | }
67 | calendarConfiguration = CalendarConfiguration(
68 | selectMode: selectMode,
69 | showMode: showMode,
70 | minYear: minYear,
71 | maxYear: maxYear,
72 | maxYearMonth: maxYearMonth,
73 | nowYear: nowYear,
74 | nowMonth: nowMonth,
75 | minSelectYear: minSelectYear,
76 | minSelectMonth: minSelectMonth,
77 | minYearMonth: minYearMonth,
78 | minSelectDay: minSelectDay,
79 | maxSelectYear: maxSelectYear,
80 | maxSelectMonth: maxSelectMonth,
81 | extraDataMap: extraDataMap,
82 | maxSelectDay: maxSelectDay,
83 | maxMultiSelectCount: maxMultiSelectCount,
84 | selectDateModel: selectDateModel,
85 | offset: offset);
86 |
87 | calendarConfiguration.defaultSelectedDateList = new HashSet();
88 | calendarConfiguration.defaultSelectedDateList
89 | .addAll(selectedDateTimeList.map((dateTime) {
90 | return DateModel.fromDateTime(dateTime);
91 | }).toSet());
92 | //将默认选中的数据,放到provider中
93 | calendarProvider.selectDateModel = selectDateModel;
94 | calendarProvider.selectedDateList =
95 | calendarConfiguration.defaultSelectedDateList;
96 | calendarConfiguration.minSelectDate = DateModel.fromDateTime(DateTime(
97 | calendarConfiguration.minSelectYear,
98 | calendarConfiguration.minSelectMonth,
99 | calendarConfiguration.minSelectDay));
100 | calendarConfiguration.maxSelectDate = DateModel.fromDateTime(DateTime(
101 | calendarConfiguration.maxSelectYear,
102 | calendarConfiguration.maxSelectMonth,
103 | calendarConfiguration.maxSelectDay));
104 |
105 | LogUtil.log(
106 | TAG: this.runtimeType,
107 | message: "start:${DateModel.fromDateTime(DateTime(
108 | minYear,
109 | minYearMonth,
110 | ))},end:${DateModel.fromDateTime(DateTime(
111 | maxYear,
112 | maxYearMonth,
113 | ))}");
114 | _weekAndMonthViewChange(showMode);
115 | }
116 | void _weekAndMonthViewChange(
117 | int showMode,
118 | ) {
119 | int minYear = calendarConfiguration.minYear;
120 | int maxYear = calendarConfiguration.maxYear;
121 | int minYearMonth = calendarConfiguration.minYearMonth;
122 | int maxYearMonth = calendarConfiguration.maxYearMonth;
123 | int nowYear = calendarConfiguration.nowYear;
124 | int nowMonth = calendarConfiguration.nowMonth;
125 | int nowDay = calendarConfiguration.selectDateModel?.day ?? -1;
126 |
127 | if (showMode != CalendarConstants.MODE_SHOW_ONLY_WEEK) {
128 | //初始化pageController,initialPage默认是当前时间对于的页面
129 | int initialPage = 0;
130 | int nowMonthIndex = 0;
131 | monthList.clear();
132 | for (int i = minYear; i <= maxYear; i++) {
133 | for (int j = 1; j <= 12; j++) {
134 | if (i == minYear && j < minYearMonth) {
135 | continue;
136 | }
137 | if (i == maxYear && j > maxYearMonth) {
138 | continue;
139 | }
140 | DateModel dateModel = new DateModel();
141 | dateModel.year = i;
142 | dateModel.month = j;
143 |
144 | if (i == nowYear && j == nowMonth) {
145 | initialPage = nowMonthIndex;
146 | }
147 | monthList.add(dateModel);
148 | nowMonthIndex++;
149 | }
150 | }
151 | this.monthController =
152 | new PageController(initialPage: initialPage, keepPage: true);
153 |
154 | LogUtil.log(
155 | TAG: this.runtimeType,
156 | message:
157 | "初始化月份视图的信息:一共有${monthList.length}个月,initialPage为$nowMonthIndex");
158 | }
159 |
160 | if (showMode != CalendarConstants.MODE_SHOW_ONLY_MONTH) {
161 | //计算一共多少周
162 | //计算方法:第一天是周几,最后一天是周几,中间的天数/7后加上2就是结果了
163 | int initialWeekPage = 0;
164 | weekList.clear();
165 | //如果没有配置当前时间,设置成当前的时间
166 | if (nowYear == -1 || nowMonth == -1) {
167 | nowYear = DateTime.now().year;
168 | nowMonth = DateTime.now().month;
169 | }
170 | int nowDay = 15; // 默认月中
171 | // 如果设置了 默认选择的时间 就取默认选择的时间天数,否则为当前时间
172 | DateModel currentModel = calendarProvider.selectDateModel ?? calendarProvider.selectedDateList?.toList()[0] ?? DateModel.fromDateTime(DateTime.now());
173 | if(currentModel != null){
174 | nowDay = currentModel.day;
175 | }
176 | DateTime nowTime = new DateTime(nowYear, nowMonth, nowDay);
177 | DateTime firstDayOfMonth = DateTime(minYear, minYearMonth, 1);
178 | //计算第一个星期的第一天的日期
179 | DateTime firstWeekDate = firstDayOfMonth.add(Duration(days: -(firstDayOfMonth.weekday - 1)));
180 |
181 | DateTime lastDay = DateTime(maxYear, maxYearMonth,
182 | DateUtil.getMonthDaysCount(maxYear, maxYearMonth));
183 | int temp = -1;
184 | for (DateTime dateTime = firstWeekDate;
185 | !dateTime.isAfter(lastDay);
186 | dateTime = dateTime.add(Duration(days: 7))) {
187 | DateModel dateModel = DateModel.fromDateTime(dateTime);
188 | weekList.add(dateModel);
189 | // print("nowTime.isBefore(dateTime)");
190 | // print("$nowTime,,,,$dateTime");
191 |
192 | if (nowTime.isAfter(dateTime)) {
193 | temp++;
194 | }
195 | }
196 | initialWeekPage = temp;
197 | LogUtil.log(
198 | TAG: this.runtimeType,
199 | message:
200 | "初始化星期视图的信息:一共有${weekList.length}个星期,initialPage为$initialWeekPage");
201 | this.weekController = new PageController(initialPage: initialWeekPage);
202 | }
203 | calendarConfiguration.monthList = monthList;
204 | calendarConfiguration.weekList = weekList;
205 | calendarConfiguration.monthController = monthController;
206 | calendarConfiguration.weekController = weekController;
207 | calendarProvider.weekAndMonthViewChange(showMode);
208 | }
209 |
210 | void weekAndMonthViewChange(
211 | int showMode,
212 | ) {
213 | calendarProvider.expandStatus.value =
214 | showMode == CalendarConstants.MODE_SHOW_ONLY_WEEK ? true : false;
215 | }
216 |
217 | //周视图切换
218 | void addWeekChangeListener(OnWeekChange listener) {
219 | this.calendarConfiguration.weekChangeListeners.add(listener);
220 | }
221 |
222 | //月份切换监听
223 | void addMonthChangeListener(OnMonthChange listener) {
224 | // this.calendarConfiguration.monthChange = listener;
225 | this.calendarConfiguration.monthChangeListeners.add(listener);
226 | }
227 |
228 | //点击选择监听
229 | void addOnCalendarSelectListener(OnCalendarSelect listener) {
230 | this.calendarConfiguration.calendarSelect = listener;
231 | }
232 |
233 | //点击选择取消监听
234 | void addOnCalendarUnSelectListener(OnCalendarUnSelect listener) {
235 | this.calendarConfiguration.unCalendarSelect = listener;
236 | }
237 |
238 | //多选超出指定范围
239 | void addOnMultiSelectOutOfRangeListener(OnMultiSelectOutOfRange listener) {
240 | this.calendarConfiguration.multiSelectOutOfRange = listener;
241 | }
242 |
243 | //多选超出限制个数
244 | void addOnMultiSelectOutOfSizeListener(OnMultiSelectOutOfSize listener) {
245 | this.calendarConfiguration.multiSelectOutOfSize = listener;
246 | }
247 |
248 | //切换展开状态
249 | void toggleExpandStatus() {
250 | calendarProvider.expandStatus.value = !calendarProvider.expandStatus.value;
251 | LogUtil.log(
252 | TAG: this.runtimeType,
253 | message: "toggleExpandStatus:${calendarProvider.expandStatus.value}");
254 | }
255 |
256 | //监听展开变化
257 | void addExpandChangeListener(ValueChanged expandChange) {
258 | calendarProvider.expandStatus.addListener(() {
259 | expandChange(calendarProvider.expandStatus.value);
260 | });
261 | }
262 |
263 | //可以动态修改extraDataMap
264 | void changeExtraData(Map newMap) {
265 | this.calendarConfiguration.extraDataMap = newMap;
266 | this.calendarProvider.generation.value++;
267 | }
268 |
269 | //可以动态修改默认选中的item。
270 | void changeDefaultSelectedDateList(Set defaultSelectedDateList) {
271 | this.calendarConfiguration.defaultSelectedDateList =
272 | defaultSelectedDateList;
273 | this.calendarProvider.generation.value++;
274 | }
275 |
276 | //可以动态修改默认选中的item
277 | void changeDefaultSelectedDateModel(DateModel dateModel) {
278 | this.calendarProvider.selectDateModel = dateModel;
279 | this.calendarProvider.generation.value++;
280 | }
281 |
282 | /**
283 | * 月份或者星期的上一页
284 | */
285 | Future previousPage() async {
286 | if (calendarProvider.expandStatus.value == true) {
287 | //月视图
288 | int currentIndex = calendarProvider.calendarConfiguration.monthController.page.toInt();
289 | if (currentIndex == 0) {
290 | return false;
291 | } else {
292 | calendarProvider.calendarConfiguration.monthController.previousPage(duration: DEFAULT_DURATION, curve: Curves.ease);
293 | calendarProvider.calendarConfiguration.monthChangeListeners.forEach((listener) {
294 | listener(monthList[currentIndex - 1].year, monthList[currentIndex - 1].month);
295 | });
296 | DateModel temp = new DateModel();
297 | temp.year = monthList[currentIndex].year;
298 | temp.month = monthList[currentIndex].month;
299 | temp.day = monthList[currentIndex].day + 14;
300 | print('298 周视图的变化: $temp');
301 | calendarProvider.lastClickDateModel = temp;
302 | return true;
303 | }
304 | } else {
305 | //周视图
306 | int currentIndex = calendarProvider.calendarConfiguration.weekController.page.toInt();
307 | if (currentIndex == 0) {
308 | return false;
309 | } else {
310 | calendarProvider.calendarConfiguration.weekController.previousPage(duration: DEFAULT_DURATION, curve: Curves.ease);
311 | return true;
312 | }
313 | }
314 | }
315 |
316 | /**
317 | * 月份或者星期的下一页
318 | * true:成功
319 | * false:是最后一页
320 | */
321 | Future nextPage() async {
322 | if (calendarProvider.expandStatus.value == true) {
323 | //月视图
324 | int currentIndex =
325 | calendarProvider.calendarConfiguration.monthController.page.toInt();
326 | if (monthList.length - 1 == currentIndex) {
327 | return false;
328 | } else {
329 | calendarProvider.calendarConfiguration.monthController
330 | .nextPage(duration: DEFAULT_DURATION, curve: Curves.ease);
331 | calendarProvider.calendarConfiguration.monthChangeListeners
332 | .forEach((listener) {
333 | listener(monthList[currentIndex + 1].year,
334 | monthList[currentIndex + 1].month);
335 | });
336 |
337 | DateModel temp = new DateModel();
338 | temp.year = monthList[currentIndex].year;
339 | temp.month = monthList[currentIndex].month;
340 | temp.day = monthList[currentIndex].day + 14;
341 | print('341 周视图的变化: $temp');
342 | calendarProvider.lastClickDateModel = temp;
343 | return true;
344 | }
345 | } else {
346 | //周视图
347 | int currentIndex =
348 | calendarProvider.calendarConfiguration.weekController.page.toInt();
349 | if (weekList.length - 1 == currentIndex) {
350 | return false;
351 | } else {
352 | calendarProvider.calendarConfiguration.weekController
353 | .nextPage(duration: DEFAULT_DURATION, curve: Curves.ease);
354 | return true;
355 | }
356 | }
357 | }
358 |
359 | //跳转到指定日期
360 | void moveToCalendar(int year, int month, int day,
361 | {bool needAnimation = false,
362 | Duration duration = const Duration(milliseconds: 500),
363 | Curve curve = Curves.ease}) {
364 | if (calendarProvider.expandStatus.value == true) {
365 | DateModel dateModel = DateModel.fromDateTime(DateTime(year, month, 1));
366 | //计算目标索引
367 | int targetPage = monthList.indexOf(dateModel);
368 | if (targetPage == -1) {
369 | return;
370 | }
371 | if (calendarProvider.calendarConfiguration.monthController.hasClients ==
372 | false) {
373 | return;
374 | }
375 | if (needAnimation) {
376 | calendarProvider.calendarConfiguration.monthController
377 | .animateToPage(targetPage, duration: duration, curve: curve);
378 | } else {
379 | calendarProvider.calendarConfiguration.monthController
380 | .jumpToPage(targetPage);
381 | }
382 | } else {
383 | DateModel dateModel = DateModel.fromDateTime(DateTime(year, month, 1));
384 | //计算目标索引
385 | int targetPage = 0;
386 | for (int i = 0; i < weekList.length - 1; i++) {
387 | DateModel first = weekList[i];
388 | DateModel next = weekList[i + 1];
389 | if (!first.isAfter(dateModel) && next.isAfter(dateModel)) {
390 | targetPage = i;
391 | return;
392 | }
393 | }
394 | if (calendarProvider.calendarConfiguration.weekController.hasClients ==
395 | false) {
396 | return;
397 | }
398 | if (needAnimation) {
399 | calendarProvider.calendarConfiguration.weekController
400 | .animateToPage(targetPage, duration: duration, curve: curve);
401 | } else {
402 | calendarProvider.calendarConfiguration.weekController
403 | .jumpToPage(targetPage);
404 | }
405 | }
406 | }
407 |
408 | //切换到下一年
409 | void moveToNextYear(
410 | {bool needAnimation = false,
411 | Duration duration = const Duration(milliseconds: 500),
412 | Curve curve = Curves.ease}) {
413 | DateTime targetDateTime = monthList[calendarProvider
414 | .calendarConfiguration.monthController.page
415 | .toInt() +
416 | 12]
417 | .getDateTime();
418 | moveToCalendar(
419 | targetDateTime.year, targetDateTime.month, targetDateTime.day,
420 | needAnimation: needAnimation, duration: duration, curve: curve);
421 | }
422 |
423 | //切换到上一年
424 | void moveToPreviousYear(
425 | {bool needAnimation = false,
426 | Duration duration = const Duration(milliseconds: 500),
427 | Curve curve = Curves.ease}) {
428 | DateTime targetDateTime = monthList[calendarProvider
429 | .calendarConfiguration.monthController.page
430 | .toInt() -
431 | 12]
432 | .getDateTime();
433 | moveToCalendar(
434 | targetDateTime.year, targetDateTime.month, targetDateTime.day,
435 | needAnimation: needAnimation, duration: duration, curve: curve);
436 | }
437 |
438 | //切换到下一个月份,
439 | void moveToNextMonth(
440 | {bool needAnimation = false,
441 | Duration duration = const Duration(milliseconds: 500),
442 | Curve curve = Curves.ease}) {
443 | // 如果当前显示的是周视图的话,需要计算出第一个月的index后,调用weekController
444 | if (calendarProvider.expandStatus.value == false) {
445 | int currentMonth = weekList[calendarProvider
446 | .calendarConfiguration.weekController.page
447 | .toInt()]
448 | .month;
449 | for (int i = calendarProvider.calendarConfiguration.weekController.page
450 | .toInt();
451 | i < weekList.length;
452 | i++) {
453 | if (weekList[i].month != currentMonth) {
454 | calendarProvider.calendarConfiguration.weekController.jumpToPage(i);
455 | break;
456 | }
457 | }
458 | return;
459 | }
460 |
461 | if ((calendarProvider.calendarConfiguration.monthController.page.toInt() +
462 | 1) >=
463 | monthList.length) {
464 | LogUtil.log(TAG: this.runtimeType, message: "moveToNextMonth:当前是最后一个月份");
465 | return;
466 | }
467 | DateTime targetDateTime = monthList[calendarProvider
468 | .calendarConfiguration.monthController.page
469 | .toInt() +
470 | 1]
471 | .getDateTime();
472 | moveToCalendar(
473 | targetDateTime.year, targetDateTime.month, targetDateTime.day,
474 | needAnimation: needAnimation, duration: duration, curve: curve);
475 | }
476 |
477 | //切换到上一个月份
478 | void moveToPreviousMonth(
479 | {bool needAnimation = false,
480 | Duration duration = const Duration(milliseconds: 500),
481 | Curve curve = Curves.ease}) {
482 | // 如果当前显示的是周视图的话,需要计算出第一个月的index后,调用weekController
483 | if (calendarProvider.expandStatus.value == false) {
484 | int currentMonth = weekList[weekController.page.toInt()].month;
485 | for (int i = calendarProvider.calendarConfiguration.weekController.page
486 | .toInt();
487 | i >= 0;
488 | i--) {
489 | if (weekList[i].month != currentMonth &&
490 | weekList[i].isAfter(DateModel.fromDateTime(DateTime(
491 | calendarConfiguration.minYear,
492 | calendarConfiguration.minYearMonth)))) {
493 | calendarProvider.calendarConfiguration.weekController.jumpToPage(i);
494 | break;
495 | }
496 | }
497 | return;
498 | }
499 |
500 | if ((calendarProvider.calendarConfiguration.monthController.page.toInt()) ==
501 | 0) {
502 | LogUtil.log(
503 | TAG: this.runtimeType, message: "moveToPreviousMonth:当前是第一个月份");
504 | return;
505 | }
506 | DateTime targetDateTime = monthList[calendarProvider
507 | .calendarConfiguration.monthController.page
508 | .toInt() -
509 | 1]
510 | .getDateTime();
511 | moveToCalendar(
512 | targetDateTime.year, targetDateTime.month, targetDateTime.day,
513 | needAnimation: needAnimation, duration: duration, curve: curve);
514 | }
515 |
516 | // 获取当前的月份
517 | DateModel getCurrentMonth() {
518 | return monthList[monthController.page.toInt()];
519 | }
520 |
521 | //获取被选中的日期,多选
522 | Set getMultiSelectCalendar() {
523 | return calendarProvider.selectedDateList;
524 | }
525 |
526 | //获取被选中的日期,单选
527 | DateModel getSingleSelectCalendar() {
528 | return calendarProvider.selectDateModel;
529 | }
530 |
531 | //清除数据
532 | void clearData() {
533 | monthList.clear();
534 | weekList.clear();
535 | calendarProvider.clearData();
536 | calendarConfiguration.weekChangeListeners = null;
537 | calendarConfiguration.monthChangeListeners = null;
538 | }
539 | }
540 |
541 | /**
542 | * 默认的weekBar
543 | */
544 | Widget defaultWeekBarWidget() {
545 | return const DefaultWeekBar();
546 | }
547 |
548 | /**
549 | * 使用canvas绘制item
550 | */
551 | Widget defaultCustomDayWidget(DateModel dateModel) {
552 | return DefaultCustomDayWidget(
553 | dateModel,
554 | );
555 | }
556 |
557 | /**
558 | * 使用组合widget的方式构造item
559 | */
560 | Widget defaultCombineDayWidget(DateModel dateModel) {
561 | return new DefaultCombineDayWidget(
562 | dateModel,
563 | );
564 | }
565 |
566 | /**
567 | * 判断是否在范围内,不在范围内的话,可以置灰
568 | */
569 | bool defaultInRange(DateModel dateModel) {
570 | return true;
571 | }
572 |
573 | /**
574 | * 周视图切换
575 | */
576 | typedef void OnWeekChange(int year, int month);
577 |
578 | /**
579 | * 月份切换事件
580 | */
581 | typedef void OnMonthChange(int year, int month);
582 |
583 | /**
584 | * 日期选择事件
585 | */
586 | typedef void OnCalendarSelect(DateModel dateModel);
587 | /**
588 | * 取消选择
589 | */
590 | typedef void OnCalendarUnSelect(DateModel dateModel);
591 |
592 | /**
593 | * 多选超出指定范围
594 | */
595 | typedef void OnMultiSelectOutOfRange();
596 |
597 | /**
598 | * 多选超出限制个数
599 | */
600 | typedef void OnMultiSelectOutOfSize();
601 |
602 | /**
603 | * 可以创建自定义样式的item
604 | */
605 | typedef Widget DayWidgetBuilder(DateModel dateModel);
606 |
607 | /**
608 | * 是否可以点击,外部来进行判断,默认都可以点击
609 | */
610 | typedef bool CanClick(DateModel dateModel);
611 |
612 | /**
613 | * 可以自定义绘制每个Item,这种扩展性好一点,以后可以提供给外部进行自定义绘制
614 | */
615 | typedef void DrawDayWidget(DateModel dateModel, Canvas canvas, Size size);
616 |
617 | /**
618 | * 自定义顶部weekBar
619 | */
620 | typedef Widget WeekBarItemWidgetBuilder();
621 |
--------------------------------------------------------------------------------
/lib/flutter_custom_calendar.dart:
--------------------------------------------------------------------------------
1 | library flutter_custom_calendar;
2 |
3 | export 'controller.dart';
4 | export 'widget/calendar_view.dart';
5 | export 'widget/base_day_view.dart';
6 | export 'widget/base_week_bar.dart';
7 | export 'constants/constants.dart';
8 | export 'model/date_model.dart';
9 | export 'widget/default_combine_day_view.dart';
10 | export 'widget/default_custom_day_view.dart';
11 | export 'widget/default_week_bar.dart';
12 | export 'configuration.dart';
13 |
14 | export 'configuration.dart';
15 | export 'calendar_provider.dart';
16 | export 'constants/constants.dart';
17 | export 'widget/base_day_view.dart';
18 |
--------------------------------------------------------------------------------
/lib/model/date_model.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_custom_calendar/utils/date_util.dart';
2 | import 'package:flutter_custom_calendar/utils/lunar_util.dart';
3 |
4 | /**
5 | * 日期的实体类
6 | */
7 | class DateModel {
8 | int year;
9 | int month;
10 | int day = 1;
11 |
12 | List lunar = List(3);
13 |
14 | // List get lunar {
15 | // if (lunar?.isNotEmpty == false) {
16 | // return lunar;
17 | // }
18 | //// return LunarUtil.solarToLunar(year, month, day);
19 | // }
20 |
21 | //农历字符串
22 | String get lunarString {
23 | if (solarTerm.isNotEmpty) {
24 | return solarTerm;
25 | } else if (gregorianFestival.isNotEmpty) {
26 | return gregorianFestival;
27 | } else if (traditionFestival.isNotEmpty) {
28 | return traditionFestival;
29 | } else {
30 | return LunarUtil.numToChinese(lunar[1], lunar[2], lunar[3]);
31 | }
32 | }
33 |
34 | //24节气
35 | String get solarTerm => LunarUtil.getSolarTerm(year, month, day);
36 |
37 | //公历节日
38 | String get gregorianFestival {
39 | String result = LunarUtil.gregorianFestival(month, day);
40 | if (result?.isNotEmpty == true) {
41 | return result;
42 | }
43 | return LunarUtil.getSpecialFestival(year, month, day);
44 | }
45 |
46 | //传统农历节日
47 | String get traditionFestival =>
48 | LunarUtil.getTraditionFestival(lunarYear, lunarMonth, lunarDay);
49 |
50 | bool isCurrentMonth; //是否是当前月份
51 |
52 | Object extraData; //自定义的额外数据
53 |
54 | bool isInRange = false; //是否在范围内,比如可以实现在某个范围外,设置置灰的功能
55 | bool isSelected; //是否被选中,用来实现一些标记或者选择功能
56 | bool isCanClick =
57 | true; //todo:是否可点击:设置范围外的日历不可点击,或者可以通过自定义拦截点击事件来设置true或者false
58 | //是否是周末
59 | bool get isWeekend => DateUtil.isWeekend(getDateTime());
60 |
61 | //是否是闰年
62 | bool get isLeapYear => DateUtil.isLeapYear(year);
63 |
64 | //是否是今天
65 | bool get isCurrentDay => DateUtil.isCurrentDay(year, month, day);
66 |
67 | int get lunarYear => lunar[0];
68 |
69 | int get lunarMonth => lunar[1];
70 |
71 | int get lunarDay => lunar[2];
72 |
73 | @override
74 | String toString() {
75 | return 'DateModel{year: $year, month: $month, day: $day}';
76 | } //如果是闰月,则返回闰月
77 |
78 | //转化成DateTime格式
79 | DateTime getDateTime() {
80 | return new DateTime(year, month, day);
81 | }
82 |
83 | //根据DateTime创建对应的model,并初始化农历和传统节日等信息
84 | static DateModel fromDateTime(DateTime dateTime) {
85 | DateModel dateModel = new DateModel()
86 | ..year = dateTime.year
87 | ..month = dateTime.month
88 | ..day = dateTime.day;
89 | List lunar =
90 | LunarUtil.solarToLunar(dateModel.year, dateModel.month, dateModel.day);
91 | dateModel.lunar = lunar;
92 |
93 | // 将数据的初始化放到各个get方法里面进行操作,类似懒加载,不然很浪费
94 | // LunarUtil.setupLunarCalendar(dateModel);
95 | return dateModel;
96 | }
97 |
98 | @override
99 | bool operator ==(Object other) =>
100 | identical(this, other) ||
101 | other is DateModel &&
102 | runtimeType == other.runtimeType &&
103 | year == other.year &&
104 | month == other.month &&
105 | day == other.day;
106 |
107 | @override
108 | int get hashCode => year.hashCode ^ month.hashCode ^ day.hashCode;
109 |
110 | //是否是同一天
111 | bool isSameWith(DateModel dateModel) {
112 | return year == dateModel.year &&
113 | month == dateModel.month &&
114 | day == dateModel.day;
115 | }
116 |
117 | //是否在某天之后
118 | bool isAfter(DateModel dateModel) {
119 | return this.getDateTime().isAfter(dateModel.getDateTime());
120 | }
121 |
122 | //是否在某天之前
123 | bool isBefore(DateModel dateModel) {
124 | return this.getDateTime().isBefore(dateModel.getDateTime());
125 | }
126 | }
127 |
128 | class X {
129 | var _y;
130 |
131 | get y => null == _y ? initY() : _y;
132 |
133 | initY() {
134 | //do some computation
135 | _y = "result";
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/lib/style/style.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | //顶部7天的文案
4 | TextStyle topWeekTextStyle = new TextStyle(fontSize: 12);
5 |
6 | //当前月份的日期的文字
7 | TextStyle currentMonthTextStyle =
8 | new TextStyle(color: Colors.black, fontSize: 16);
9 |
10 | //下一个月或者上一个月的日期的文字
11 | TextStyle preOrNextMonthTextStyle =
12 | new TextStyle(color: Colors.grey, fontSize: 18);
13 |
14 | //农历的字体
15 | TextStyle lunarTextStyle = new TextStyle(color: Colors.grey, fontSize: 12);
16 |
17 | //不是当前月份的日期的文字
18 | TextStyle notCurrentMonthTextStyle =
19 | new TextStyle(color: Colors.grey, fontSize: 16);
20 |
21 | TextStyle currentDayTextStyle = new TextStyle(color: Colors.red, fontSize: 16);
22 |
--------------------------------------------------------------------------------
/lib/utils/LogUtil.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/foundation.dart';
3 |
4 | /**
5 | * 打印日志,外部可以控制日历信息的打印显示,方便调试查错
6 | */
7 | class LogUtil {
8 | static bool _enableLog = false; //是否可以打印日志
9 |
10 | static set enableLog(bool value) {
11 | _enableLog = value;
12 | }
13 |
14 | /**
15 | * TAG:类名
16 | * message:一般就方法名+自定义信息吧
17 | */
18 | static void log({@required dynamic TAG, String message = ""}) {
19 | if (_enableLog && kDebugMode) {
20 | debugPrint("flutter_custom_calendar------$TAG------>$message");
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/lib/utils/date_util.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter_custom_calendar/flutter_custom_calendar.dart';
3 | import 'LogUtil.dart';
4 |
5 | /**
6 | * 工具类
7 | */
8 | class DateUtil {
9 | /**
10 | * 判断一个日期是否是周末,即周六日
11 | */
12 | static bool isWeekend(DateTime dateTime) {
13 | return dateTime.weekday == DateTime.saturday ||
14 | dateTime.weekday == DateTime.sunday;
15 | }
16 |
17 | /**
18 | * 获取某年的天数
19 | */
20 | static int getYearDaysCount(int year) {
21 | if (isLeapYear(year)) {
22 | return 366;
23 | }
24 | return 365;
25 | }
26 |
27 | /**
28 | * 获取某月的天数
29 | *
30 | * @param year 年
31 | * @param month 月
32 | * @return 某月的天数
33 | */
34 | static int getMonthDaysCount(int year, int month) {
35 | int count = 0;
36 | //判断大月份
37 | if (month == 1 ||
38 | month == 3 ||
39 | month == 5 ||
40 | month == 7 ||
41 | month == 8 ||
42 | month == 10 ||
43 | month == 12) {
44 | count = 31;
45 | }
46 |
47 | //判断小月
48 | if (month == 4 || month == 6 || month == 9 || month == 11) {
49 | count = 30;
50 | }
51 |
52 | //判断平年与闰年
53 | if (month == 2) {
54 | if (isLeapYear(year)) {
55 | count = 29;
56 | } else {
57 | count = 28;
58 | }
59 | }
60 | return count;
61 | }
62 |
63 | /**
64 | * 是否是今天
65 | */
66 | static bool isCurrentDay(int year, int month, int day) {
67 | DateTime now = DateTime.now();
68 | return now.year == year && now.month == month && now.day == day;
69 | }
70 |
71 | /**
72 | * 是否是闰年
73 | */
74 | static bool isLeapYear(int year) {
75 | return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
76 | }
77 |
78 | /**
79 | * 本月的第几周
80 | */
81 | static int getIndexWeekInMonth(DateTime dateTime) {
82 | DateTime firstdayInMonth = new DateTime(dateTime.year, dateTime.month, 1);
83 | Duration duration = dateTime.difference(firstdayInMonth);
84 | return (duration.inDays / 7).toInt() + 1;
85 | }
86 |
87 | /**
88 | * 本周的第几天
89 | */
90 | static int getIndexDayInWeek(DateTime dateTime) {
91 | DateTime firstdayInMonth = new DateTime(
92 | dateTime.year,
93 | dateTime.month,
94 | );
95 | Duration duration = dateTime.difference(firstdayInMonth);
96 | return (duration.inDays / 7).toInt() + 1;
97 | }
98 |
99 | /**
100 | * 本月第一天,是那一周的第几天,从1开始
101 | * @return 获取日期所在月视图对应的起始偏移量 the start diff with MonthView
102 | */
103 | static int getIndexOfFirstDayInMonth(DateTime dateTime, {int offset = 0}) {
104 | DateTime firstDayOfMonth = new DateTime(dateTime.year, dateTime.month, 1);
105 |
106 | int week = firstDayOfMonth.weekday + offset;
107 |
108 | return week;
109 | }
110 |
111 | static List initCalendarForMonthView(
112 | int year, int month, DateTime currentDate, int weekStart,
113 | {DateModel minSelectDate,
114 | DateModel maxSelectDate,
115 | Map extraDataMap,
116 | int offset = 0}) {
117 | print('initCalendarForMonthView start');
118 | weekStart = DateTime.monday;
119 | //获取月视图真实偏移量
120 | int mPreDiff =
121 | getIndexOfFirstDayInMonth(new DateTime(year, month), offset: offset);
122 | //获取该月的天数
123 | int monthDayCount = getMonthDaysCount(year, month);
124 |
125 | LogUtil.log(
126 | TAG: "DateUtil",
127 | message:
128 | "initCalendarForMonthView:$year年$month月,有$monthDayCount天,第一天的index为${mPreDiff}");
129 |
130 | List result = new List();
131 |
132 | int size = 42;
133 |
134 | DateTime firstDayOfMonth = new DateTime(year, month, 1);
135 | DateTime lastDayOfMonth = new DateTime(year, month, monthDayCount);
136 |
137 | for (int i = 0; i < size; i++) {
138 | DateTime temp;
139 | DateModel dateModel;
140 | if (i < mPreDiff - 1) {
141 | if (i < ((mPreDiff / 7).ceil() - 1) * 7) {
142 | size++;
143 | continue;
144 | }
145 | //这个上一月的几天
146 | temp = firstDayOfMonth.subtract(Duration(days: mPreDiff - i - 1));
147 |
148 | dateModel = DateModel.fromDateTime(temp);
149 | dateModel.isCurrentMonth = false;
150 | } else if (i >= monthDayCount + (mPreDiff - 1)) {
151 | //这是下一月的几天
152 | temp = lastDayOfMonth
153 | .add(Duration(days: i - mPreDiff - monthDayCount + 2));
154 | dateModel = DateModel.fromDateTime(temp);
155 | dateModel.isCurrentMonth = false;
156 | } else {
157 | //这个月的
158 | temp = new DateTime(year, month, i - mPreDiff + 2);
159 | dateModel = DateModel.fromDateTime(temp);
160 | dateModel.isCurrentMonth = true;
161 | }
162 |
163 | //判断是否在范围内
164 | if (dateModel.getDateTime().isAfter(minSelectDate.getDateTime()) &&
165 | dateModel.getDateTime().isBefore(maxSelectDate.getDateTime())) {
166 | dateModel.isInRange = true;
167 | } else {
168 | dateModel.isInRange = false;
169 | }
170 | //将自定义额外的数据,存储到相应的model中
171 | if (extraDataMap?.isNotEmpty == true) {
172 | if (extraDataMap.containsKey(dateModel)) {
173 | dateModel.extraData = extraDataMap[dateModel];
174 | } else {
175 | dateModel.extraData = null;
176 | }
177 | } else {
178 | dateModel.extraData = null;
179 | }
180 |
181 | result.add(dateModel);
182 | }
183 |
184 | print('initCalendarForMonthView end');
185 |
186 | return result;
187 | }
188 |
189 | /**
190 | * 月的行数
191 | */
192 | static int getMonthViewLineCount(int year, int month, int offset) {
193 | DateTime firstDayOfMonth = new DateTime(year, month, 1);
194 | int monthDayCount = getMonthDaysCount(year, month);
195 |
196 | int preIndex = (firstDayOfMonth.weekday - 1 + offset) % 7;
197 | int lineCount = ((preIndex + monthDayCount) / 7).ceil();
198 | LogUtil.log(
199 | TAG: "DateUtil",
200 | message: "getMonthViewLineCount:$year年$month月:有$lineCount行");
201 |
202 | return lineCount;
203 | }
204 |
205 | /**
206 | * 获取本周的7个item
207 | */
208 | static List initCalendarForWeekView(
209 | int year, int month, DateTime currentDate, int weekStart,
210 | {DateModel minSelectDate,
211 | DateModel maxSelectDate,
212 | Map extraDataMap,
213 | int offset = 0}) {
214 | List items = List();
215 |
216 | int weekDay = currentDate.weekday + offset;
217 |
218 | //计算本周的第一天
219 | DateTime firstDayOfWeek = currentDate.add(Duration(days: -weekDay));
220 |
221 | for (int i = 1; i <= 7; i++) {
222 | DateModel dateModel =
223 | DateModel.fromDateTime(firstDayOfWeek.add(Duration(days: i)));
224 |
225 | //判断是否在范围内
226 | if (dateModel.getDateTime().isAfter(minSelectDate.getDateTime()) &&
227 | dateModel.getDateTime().isBefore(maxSelectDate.getDateTime())) {
228 | dateModel.isInRange = true;
229 | } else {
230 | dateModel.isInRange = false;
231 | }
232 | if (month == dateModel.month) {
233 | dateModel.isCurrentMonth = true;
234 | } else {
235 | dateModel.isCurrentMonth = false;
236 | }
237 |
238 | //将自定义额外的数据,存储到相应的model中
239 | if (extraDataMap?.isNotEmpty == true) {
240 | if (extraDataMap.containsKey(dateModel)) {
241 | dateModel.extraData = extraDataMap[dateModel];
242 | }
243 | }
244 |
245 | items.add(dateModel);
246 | }
247 | return items;
248 | }
249 | }
250 |
--------------------------------------------------------------------------------
/lib/utils/math_util.dart:
--------------------------------------------------------------------------------
1 | class Math {
2 | static double abs(double num) {
3 | if (num < 0) {
4 | return -num;
5 | } else {
6 | return num;
7 | }
8 | }
9 | }
10 |
11 | class System {
12 | //todo:后面再检查这个方法是否正确
13 | // src:源数组;
14 | // srcPos:源数组要复制的起始位置;
15 | // dest:目的数组;
16 | // destPos:目的数组放置的起始位置;
17 | // length:复制的长度。
18 | static void arraycopy(List src, int srcPos, List dest,
19 | int destPos, int length) {
20 | List.copyRange(dest, destPos, src, srcPos, srcPos+length);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/lib/widget/base_day_view.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_custom_calendar/model/date_model.dart';
3 | import 'package:flutter_custom_calendar/flutter_custom_calendar.dart';
4 |
5 | import '../controller.dart';
6 |
7 | /**
8 | * 通过canvas自定义item,只需实现相关的方法就可以
9 | */
10 | abstract class BaseCustomDayWidget extends StatelessWidget {
11 | final DateModel dateModel;
12 |
13 | const BaseCustomDayWidget(
14 | this.dateModel,
15 | );
16 |
17 | @override
18 | Widget build(BuildContext context) {
19 | return Container(
20 | child: new CustomPaint(
21 | painter:
22 | //根据isSelected标志获取对应的item
23 | dateModel.isSelected
24 | ? new CustomDayWidgetPainter(dateModel,
25 | drawDayWidget: drawSelected)
26 | : new CustomDayWidgetPainter(dateModel,
27 | drawDayWidget: drawNormal),
28 | ),
29 | );
30 | }
31 |
32 | void drawNormal(DateModel dateModel, Canvas canvas, Size size);
33 |
34 | void drawSelected(DateModel dateModel, Canvas canvas, Size size);
35 | }
36 |
37 | class CustomDayWidgetPainter extends CustomPainter {
38 | DateModel dateModel;
39 |
40 | DrawDayWidget drawDayWidget; //普通样式是必须的
41 |
42 | CustomDayWidgetPainter(this.dateModel, {this.drawDayWidget});
43 |
44 | Paint textPaint;
45 |
46 | @override
47 | void paint(Canvas canvas, Size size) {
48 | drawDayWidget(dateModel, canvas, size);
49 | }
50 |
51 | @override
52 | bool shouldRepaint(CustomPainter oldDelegate) {
53 | return true;
54 | }
55 | }
56 |
57 | /**
58 | * 通过组合widget创建item,只需实现相关的方法就可以
59 | */
60 | abstract class BaseCombineDayWidget extends StatelessWidget {
61 | final DateModel dateModel;
62 |
63 | BaseCombineDayWidget(this.dateModel);
64 |
65 | @override
66 | Widget build(BuildContext context) {
67 | return dateModel.isSelected
68 | ? getSelectedWidget(dateModel)
69 | : getNormalWidget(dateModel);
70 | }
71 |
72 | Widget getNormalWidget(DateModel dateModel);
73 |
74 | Widget getSelectedWidget(DateModel dateModel);
75 | }
76 |
--------------------------------------------------------------------------------
/lib/widget/base_week_bar.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | /**
4 | * 顶部的固定的周显示
5 | */
6 | abstract class BaseWeekBar extends StatelessWidget {
7 | const BaseWeekBar({Key key}) : super(key: key);
8 |
9 | @override
10 | Widget build(BuildContext context) {
11 | return Container(
12 | child: new Row(
13 | children: getWeekDayWidget(),
14 | ),
15 | );
16 | }
17 |
18 | Widget getWeekBarItem(int index);
19 |
20 | List getWeekDayWidget() {
21 | return List.generate(7, (index) {
22 | return getChild(index);
23 | });
24 | }
25 |
26 | Widget getChild(int index) {
27 | return new Expanded(
28 | child: getWeekBarItem(index),
29 | );
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/lib/widget/calendar_view.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/foundation.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_custom_calendar/calendar_provider.dart';
4 | import 'package:flutter_custom_calendar/constants/constants.dart';
5 | import 'package:flutter_custom_calendar/controller.dart';
6 | import 'package:flutter_custom_calendar/utils/LogUtil.dart';
7 | import 'package:flutter_custom_calendar/utils/date_util.dart';
8 | import 'package:flutter_custom_calendar/widget/month_view_pager.dart';
9 | import 'package:flutter_custom_calendar/widget/week_view_pager.dart';
10 | import 'package:provider/provider.dart';
11 |
12 | /**
13 | * 暂时默认是周一开始的
14 | */
15 |
16 | //由于旧的代码关系。。所以现在需要抽出一个StatefulWidget放在StatelessWidget里面
17 | class CalendarViewWidget extends StatefulWidget {
18 | //整体的背景设置
19 | final BoxDecoration boxDecoration;
20 |
21 | //日历的padding和margin
22 | final EdgeInsetsGeometry padding;
23 | final EdgeInsetsGeometry margin;
24 |
25 | //默认是屏幕宽度/7
26 | final double itemSize;
27 |
28 | //日历item之间的竖直方向间距,默认10
29 | final double verticalSpacing;
30 |
31 | //自定义日历item
32 | final DayWidgetBuilder dayWidgetBuilder;
33 | final WeekBarItemWidgetBuilder weekBarItemWidgetBuilder;
34 |
35 | //控制器
36 | final CalendarController calendarController;
37 |
38 | CalendarViewWidget(
39 | {Key key,
40 | this.dayWidgetBuilder = defaultCustomDayWidget,
41 | this.weekBarItemWidgetBuilder = defaultWeekBarWidget,
42 | @required this.calendarController,
43 | this.boxDecoration,
44 | this.padding = EdgeInsets.zero,
45 | this.margin = EdgeInsets.zero,
46 | this.verticalSpacing = 10,
47 | this.itemSize})
48 | : super(key: key);
49 |
50 | @override
51 | _CalendarViewWidgetState createState() => _CalendarViewWidgetState();
52 | }
53 |
54 | class _CalendarViewWidgetState extends State {
55 | @override
56 | void initState() {
57 | //初始化一些数据,一些跟状态有关的要放到provider中
58 | widget.calendarController.calendarProvider.initData(
59 | calendarConfiguration: widget.calendarController.calendarConfiguration,
60 | padding: widget.padding,
61 | margin: widget.margin,
62 | itemSize: widget.itemSize,
63 | verticalSpacing: widget.verticalSpacing,
64 | dayWidgetBuilder: widget.dayWidgetBuilder,
65 | weekBarItemWidgetBuilder: widget.weekBarItemWidgetBuilder);
66 |
67 | super.initState();
68 | }
69 |
70 | @override
71 | void dispose() {
72 | // widget.calendarController.clearData();
73 | super.dispose();
74 | }
75 |
76 | @override
77 | Widget build(BuildContext context) {
78 | return ChangeNotifierProvider.value(
79 | value: widget.calendarController.calendarProvider,
80 | child: Container(
81 | //外部可以自定义背景设置
82 | decoration: widget.boxDecoration,
83 | padding: widget.padding,
84 | margin: widget.margin,
85 | //使用const,保证外界的setState不会刷新日历这个widget
86 | child: CalendarContainer(widget.calendarController)),
87 | );
88 | }
89 | }
90 |
91 | class CalendarContainer extends StatefulWidget {
92 | final CalendarController calendarController;
93 |
94 | const CalendarContainer(this.calendarController);
95 |
96 | @override
97 | CalendarContainerState createState() => CalendarContainerState();
98 | }
99 |
100 | class CalendarContainerState extends State
101 | with SingleTickerProviderStateMixin {
102 | double itemHeight;
103 | double totalHeight;
104 |
105 | bool expand;
106 |
107 | CalendarProvider calendarProvider;
108 |
109 | List widgets = [];
110 |
111 | int index = 0;
112 |
113 | @override
114 | void initState() {
115 | super.initState();
116 | calendarProvider = Provider.of(context, listen: false);
117 | expand = calendarProvider.expandStatus.value;
118 |
119 | if (calendarProvider.calendarConfiguration.showMode ==
120 | CalendarConstants.MODE_SHOW_ONLY_WEEK) {
121 | widgets.add(const WeekViewPager());
122 | } else if (calendarProvider.calendarConfiguration.showMode ==
123 | CalendarConstants.MODE_SHOW_WEEK_AND_MONTH) {
124 | widgets.add(const MonthViewPager());
125 | widgets.add(const WeekViewPager());
126 | index = 1;
127 | } else if (calendarProvider.calendarConfiguration.showMode ==
128 | CalendarConstants.MODE_SHOW_MONTH_AND_WEEK) {
129 | widgets.add(const MonthViewPager());
130 | widgets.add(const WeekViewPager());
131 | index = 0;
132 | } else {
133 | //默认是只显示月视图
134 | widgets.add(const MonthViewPager());
135 | }
136 | expand = calendarProvider.expandStatus.value;
137 |
138 | //如果需要视图切换的话,才需要添加监听,不然不需要监听变化
139 | if (calendarProvider.calendarConfiguration.showMode ==
140 | CalendarConstants.MODE_SHOW_WEEK_AND_MONTH ||
141 | calendarProvider.calendarConfiguration.showMode ==
142 | CalendarConstants.MODE_SHOW_MONTH_AND_WEEK) {
143 | calendarProvider.expandStatus.addListener(() {
144 | setState(() {
145 | print(
146 | "calendarProvider.expandStatus.value:${calendarProvider.expandStatus.value}");
147 | expand = calendarProvider.expandStatus.value;
148 | if (expand) {
149 | index = 0;
150 | //周视图切换到月视图,需要计算下初始化的高度
151 | int lineCount = DateUtil.getMonthViewLineCount(
152 | calendarProvider.calendarConfiguration.nowYear,
153 | calendarProvider.calendarConfiguration.nowMonth,
154 | calendarProvider.calendarConfiguration.offset);
155 | totalHeight = calendarProvider.calendarConfiguration.itemSize * (lineCount) + calendarProvider.calendarConfiguration.verticalSpacing * (lineCount - 1);
156 | calendarProvider.calendarConfiguration.monthController.jumpToPage(calendarProvider.monthPageIndex);
157 | } else {
158 | index = 1;
159 | //月视图切换到周视图
160 | calendarProvider.calendarConfiguration.weekController.jumpToPage(calendarProvider.weekPageIndex);
161 | }
162 | });
163 | });
164 | } else {
165 | index = 0;
166 | }
167 |
168 | widget.calendarController.addMonthChangeListener((year, month) {
169 | if (widget.calendarController.calendarProvider.calendarConfiguration
170 | .showMode !=
171 | CalendarConstants.MODE_SHOW_ONLY_WEEK) {
172 | //月份切换的时候,如果高度发生变化的话,需要setState使高度整体自适应
173 | int lineCount = DateUtil.getMonthViewLineCount(year, month, widget.calendarController.calendarConfiguration.offset);
174 | double newHeight = itemHeight * (lineCount) +
175 | calendarProvider.calendarConfiguration.verticalSpacing *
176 | (lineCount - 1);
177 | LogUtil.log(
178 | TAG: this.runtimeType,
179 | message: "totalHeight:$totalHeight,newHeight:$newHeight");
180 | if (totalHeight.toInt() != newHeight.toInt()) {
181 | LogUtil.log(TAG: this.runtimeType, message: "月份视图高度发生变化");
182 | setState(() {
183 | totalHeight = newHeight;
184 | });
185 | }
186 | }
187 | });
188 |
189 | itemHeight = calendarProvider.calendarConfiguration.itemSize;
190 | totalHeight = calendarProvider.totalHeight;
191 | }
192 |
193 | @override
194 | void dispose() {
195 | super.dispose();
196 | }
197 |
198 | @override
199 | Widget build(BuildContext context) {
200 | LogUtil.log(TAG: this.runtimeType, message: "CalendarContainerState build");
201 | return Container(
202 | width: itemHeight * 7,
203 | child: new Column(
204 | crossAxisAlignment: CrossAxisAlignment.stretch,
205 | mainAxisSize: MainAxisSize.min,
206 | children: [
207 | /**
208 | * 利用const,避免每次setState都会刷新到这顶部的view
209 | */
210 | calendarProvider.calendarConfiguration.weekBarItemWidgetBuilder(),
211 | AnimatedContainer(
212 | duration: Duration(milliseconds: 500),
213 | height: expand ? totalHeight : itemHeight,
214 | child: IndexedStack(
215 | index: index,
216 | children: widgets,
217 | )),
218 | ],
219 | ),
220 | );
221 | }
222 | }
223 |
--------------------------------------------------------------------------------
/lib/widget/default_combine_day_view.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_custom_calendar/flutter_custom_calendar.dart';
3 | import 'package:flutter_custom_calendar/style/style.dart';
4 |
5 | import 'base_day_view.dart';
6 |
7 | /**
8 | * 默认的利用组合widget的方式构造item
9 | */
10 | //class DefaultCombineDayWidget extends StatelessWidget {
11 | // DateModel dateModel;
12 | //
13 | // DefaultCombineDayWidget(this.dateModel);
14 | //
15 | // @override
16 | // Widget build(BuildContext context) {
17 | // return Container(
18 | // margin: EdgeInsets.only(top: 5, bottom: 5),
19 | // decoration: dateModel.isSelected
20 | // ? new BoxDecoration(color: Colors.red, shape: BoxShape.circle)
21 | // : null,
22 | // child: new Stack(
23 | // alignment: Alignment.center,
24 | // children: [
25 | // new Column(
26 | // mainAxisSize: MainAxisSize.max,
27 | // crossAxisAlignment: CrossAxisAlignment.center,
28 | // children: [
29 | // //公历
30 | // new Expanded(
31 | // child: Center(
32 | // child: new Text(
33 | // dateModel.day.toString(),
34 | // style: currentMonthTextStyle,
35 | // ),
36 | // ),
37 | // ),
38 | //
39 | // //农历
40 | // new Expanded(
41 | // child: Center(
42 | // child: new Text(
43 | // "${dateModel.lunarString}",
44 | // style: lunarTextStyle,
45 | // ),
46 | // ),
47 | // ),
48 | // ],
49 | // )
50 | // ],
51 | // ),
52 | // );
53 | // }
54 | //}
55 |
56 | class DefaultCombineDayWidget extends BaseCombineDayWidget {
57 | DefaultCombineDayWidget(DateModel dateModel) : super(dateModel);
58 |
59 | @override
60 | Widget getNormalWidget(DateModel dateModel) {
61 | return Container(
62 | margin: EdgeInsets.all(8),
63 | child: new Stack(
64 | alignment: Alignment.center,
65 | children: [
66 | new Column(
67 | mainAxisSize: MainAxisSize.max,
68 | crossAxisAlignment: CrossAxisAlignment.center,
69 | children: [
70 | //公历
71 | new Expanded(
72 | child: Center(
73 | child: new Text(
74 | dateModel.day.toString(),
75 | style: currentMonthTextStyle,
76 | ),
77 | ),
78 | ),
79 |
80 | //农历
81 | new Expanded(
82 | child: Center(
83 | child: new Text(
84 | "${dateModel.lunarString}",
85 | style: lunarTextStyle,
86 | ),
87 | ),
88 | ),
89 | ],
90 | )
91 | ],
92 | ),
93 | );
94 | }
95 |
96 | @override
97 | Widget getSelectedWidget(DateModel dateModel) {
98 | return Container(
99 | margin: EdgeInsets.all(8),
100 | foregroundDecoration:
101 | new BoxDecoration(border: Border.all(width: 2, color: Colors.blue)),
102 | child: new Stack(
103 | alignment: Alignment.center,
104 | children: [
105 | new Column(
106 | mainAxisSize: MainAxisSize.max,
107 | crossAxisAlignment: CrossAxisAlignment.center,
108 | children: [
109 | //公历
110 | new Expanded(
111 | child: Center(
112 | child: new Text(
113 | dateModel.day.toString(),
114 | style: currentMonthTextStyle,
115 | ),
116 | ),
117 | ),
118 |
119 | //农历
120 | new Expanded(
121 | child: Center(
122 | child: new Text(
123 | "${dateModel.lunarString}",
124 | style: lunarTextStyle,
125 | ),
126 | ),
127 | ),
128 | ],
129 | )
130 | ],
131 | ),
132 | );
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/lib/widget/default_custom_day_view.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_custom_calendar/flutter_custom_calendar.dart';
3 | import 'package:flutter_custom_calendar/style/style.dart';
4 |
5 | import 'base_day_view.dart';
6 |
7 | /**
8 | * 这里定义成一个StatelessWidget,状态是外部的父控件传进来参数控制就行,自己不弄state类
9 | */
10 |
11 | class DefaultCustomDayWidget extends BaseCustomDayWidget {
12 | const DefaultCustomDayWidget(DateModel dateModel) : super(dateModel);
13 |
14 | @override
15 | void drawNormal(DateModel dateModel, Canvas canvas, Size size) {
16 | defaultDrawNormal(dateModel, canvas, size);
17 | }
18 |
19 | @override
20 | void drawSelected(DateModel dateModel, Canvas canvas, Size size) {
21 | defaultDrawSelected(dateModel, canvas, size);
22 | }
23 | }
24 |
25 | //class DefaultCustomDayWidget extends StatelessWidget {
26 | // DateModel dateModel;
27 | //
28 | // DefaultCustomDayWidget(this.dateModel);
29 | //
30 | // @override
31 | // Widget build(BuildContext context) {
32 | // return Container(
33 | // child: new CustomPaint(
34 | // painter: CustomDayWidgetPainter(
35 | // dateModel,
36 | // ),
37 | // ),
38 | // );
39 | // }
40 | //}
41 | //
42 | //class CustomDayWidgetPainter extends CustomPainter {
43 | // DateModel dateModel;
44 | //
45 | // drawNormal normalDraw; //普通样式是必须的
46 | // drawSelected selectedDraw;
47 | //
48 | // CustomDayWidgetPainter(this.dateModel,
49 | // {this.normalDraw = defaultDrawNormal,
50 | // this.selectedDraw = defaultDrawSelected});
51 | //
52 | // Paint textPaint;
53 | //
54 | // @override
55 | // void paint(Canvas canvas, Size size) {
56 | //// print("paint:$size");
57 | // if (dateModel.isSelected) {
58 | // selectedDraw(dateModel, canvas, size);
59 | // } else {
60 | // normalDraw(dateModel, canvas, size);
61 | // }
62 | // }
63 | //
64 | // @override
65 | // bool shouldRepaint(CustomPainter oldDelegate) {
66 | // return true;
67 | // }
68 | //}
69 |
70 | /**
71 | * 默认的样式
72 | */
73 | void defaultDrawNormal(DateModel dateModel, Canvas canvas, Size size) {
74 | //顶部的文字
75 | TextPainter dayTextPainter = new TextPainter()
76 | ..text = TextSpan(
77 | text: dateModel.day.toString(),
78 | style: dateModel.isCurrentDay
79 | ? currentDayTextStyle
80 | : currentMonthTextStyle)
81 | ..textDirection = TextDirection.ltr
82 | ..textAlign = TextAlign.center;
83 |
84 | dayTextPainter.layout(minWidth: size.width, maxWidth: size.width);
85 | dayTextPainter.paint(canvas, Offset(0, 10));
86 |
87 | //下面的文字
88 | TextPainter lunarTextPainter = new TextPainter()
89 | ..text = new TextSpan(text: dateModel.lunarString, style: lunarTextStyle)
90 | ..textDirection = TextDirection.ltr
91 | ..textAlign = TextAlign.center;
92 |
93 | lunarTextPainter.layout(minWidth: size.width, maxWidth: size.width);
94 | lunarTextPainter.paint(canvas, Offset(0, size.height / 2));
95 | }
96 |
97 | /**
98 | * 被选中的样式
99 | */
100 | void defaultDrawSelected(DateModel dateModel, Canvas canvas, Size size) {
101 | //绘制背景
102 | Paint backGroundPaint = new Paint()
103 | ..color = Colors.blue
104 | ..style = PaintingStyle.stroke
105 | ..strokeWidth = 2;
106 | double padding = 8;
107 | canvas.drawRect(
108 | Rect.fromPoints(Offset(padding, padding),
109 | Offset(size.width - padding, size.height - padding)),
110 | backGroundPaint);
111 |
112 | //顶部的文字
113 | TextPainter dayTextPainter = new TextPainter()
114 | ..text =
115 | TextSpan(text: dateModel.day.toString(), style: currentMonthTextStyle)
116 | ..textDirection = TextDirection.ltr
117 | ..textAlign = TextAlign.center;
118 |
119 | dayTextPainter.layout(minWidth: size.width, maxWidth: size.width);
120 | dayTextPainter.paint(canvas, Offset(0, 10));
121 |
122 | //下面的文字
123 | TextPainter lunarTextPainter = new TextPainter()
124 | ..text = new TextSpan(text: dateModel.lunarString, style: lunarTextStyle)
125 | ..textDirection = TextDirection.ltr
126 | ..textAlign = TextAlign.center;
127 |
128 | lunarTextPainter.layout(minWidth: size.width, maxWidth: size.width);
129 | lunarTextPainter.paint(canvas, Offset(0, size.height / 2));
130 | }
131 |
--------------------------------------------------------------------------------
/lib/widget/default_week_bar.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_custom_calendar/constants/constants.dart';
3 | import 'package:flutter_custom_calendar/style/style.dart';
4 |
5 | import 'base_week_bar.dart';
6 |
7 | ///**
8 | // * 顶部的固定的周显示
9 | // */
10 | //class DefaultWeekBar extends StatelessWidget {
11 | // const DefaultWeekBar({Key key}) : super(key: key);
12 | //
13 | // @override
14 | // Widget build(BuildContext context) {
15 | // return Container(
16 | // child: new Row(
17 | // children: getWeekDayWidget(),
18 | // ),
19 | // );
20 | // }
21 | //}
22 | //
23 | //List getWeekDayWidget() {
24 | // return List.generate(7, (index) {
25 | // return getChild(Constants.WEEK_LIST[index]);
26 | // });
27 | //}
28 | //
29 | //Widget getChild(String title) {
30 | // return new Expanded(
31 | // child: new Container(
32 | // color: RandomColor.next(),
33 | // height: 40,
34 | // alignment: Alignment.center,
35 | // child: new Text(
36 | // title,
37 | // style: topWeekTextStyle,
38 | // ),
39 | // ));
40 | //}
41 |
42 | class DefaultWeekBar extends BaseWeekBar {
43 | const DefaultWeekBar({Key key}) : super(key: key);
44 |
45 | @override
46 | Widget getWeekBarItem(int index) {
47 | return new Container(
48 | height: 40,
49 | alignment: Alignment.center,
50 | child: new Text(
51 | CalendarConstants.WEEK_LIST[index],
52 | style: topWeekTextStyle,
53 | ),
54 | );
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/lib/widget/month_view.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/foundation.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_custom_calendar/cache_data.dart';
4 | import 'package:flutter_custom_calendar/configuration.dart';
5 | import 'package:flutter_custom_calendar/flutter_custom_calendar.dart';
6 | import 'package:flutter_custom_calendar/utils/LogUtil.dart';
7 | import 'package:flutter_custom_calendar/utils/date_util.dart';
8 | import 'package:provider/provider.dart';
9 |
10 | /**
11 | * 月视图,显示整个月的日子
12 | */
13 | class MonthView extends StatefulWidget {
14 | final int year;
15 | final int month;
16 | final int day;
17 |
18 | final CalendarConfiguration configuration;
19 |
20 | const MonthView({
21 | Key key,
22 | @required this.year,
23 | @required this.month,
24 | this.day,
25 | this.configuration,
26 | }) : super(key: key);
27 |
28 | @override
29 | _MonthViewState createState() => _MonthViewState();
30 | }
31 |
32 | class _MonthViewState extends State
33 | with AutomaticKeepAliveClientMixin {
34 | List items = List();
35 |
36 | int lineCount;
37 | Map extraDataMap; //自定义额外的数据
38 |
39 | @override
40 | void initState() {
41 | super.initState();
42 | extraDataMap = widget.configuration.extraDataMap;
43 | DateModel firstDayOfMonth =
44 | DateModel.fromDateTime(DateTime(widget.year, widget.month, 1));
45 | if (CacheData.getInstance().monthListCache[firstDayOfMonth]?.isNotEmpty ==
46 | true) {
47 | LogUtil.log(TAG: this.runtimeType, message: "缓存中有数据");
48 | items = CacheData.getInstance().monthListCache[firstDayOfMonth];
49 | } else {
50 | LogUtil.log(TAG: this.runtimeType, message: "缓存中无数据");
51 | getItems().then((_) {
52 | CacheData.getInstance().monthListCache[firstDayOfMonth] = items;
53 | });
54 | }
55 |
56 | lineCount = DateUtil.getMonthViewLineCount(
57 | widget.year, widget.month, widget.configuration.offset);
58 |
59 | //第一帧后,添加监听,generation发生变化后,需要刷新整个日历
60 | WidgetsBinding.instance.addPostFrameCallback((callback) {
61 | Provider.of(context, listen: false)
62 | .generation
63 | .addListener(() async {
64 | extraDataMap = widget.configuration.extraDataMap;
65 | await getItems();
66 | });
67 | });
68 | }
69 |
70 | Future getItems() async {
71 | items = await compute(initCalendarForMonthView, {
72 | 'year': widget.year,
73 | 'month': widget.month,
74 | 'minSelectDate': widget.configuration.minSelectDate,
75 | 'maxSelectDate': widget.configuration.maxSelectDate,
76 | 'extraDataMap': extraDataMap,
77 | 'offset': widget.configuration.offset
78 | });
79 | setState(() {});
80 | }
81 |
82 | static Future> initCalendarForMonthView(Map map) async {
83 | return DateUtil.initCalendarForMonthView(
84 | map['year'], map['month'], DateTime.now(), DateTime.sunday,
85 | minSelectDate: map['minSelectDate'],
86 | maxSelectDate: map['maxSelectDate'],
87 | extraDataMap: map['extraDataMap'],
88 | offset: map['offset']);
89 | }
90 |
91 | @override
92 | Widget build(BuildContext context) {
93 | super.build(context);
94 | LogUtil.log(TAG: this.runtimeType, message: "_MonthViewState build");
95 |
96 | CalendarProvider calendarProvider =
97 | Provider.of(context, listen: false);
98 | CalendarConfiguration configuration =
99 | calendarProvider.calendarConfiguration;
100 |
101 | return new GridView.builder(
102 | addAutomaticKeepAlives: true,
103 | padding: EdgeInsets.zero,
104 | physics: const NeverScrollableScrollPhysics(),
105 | gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
106 | crossAxisCount: 7, mainAxisSpacing: configuration.verticalSpacing),
107 | itemCount: items.isEmpty ? 0 : items.length,
108 | itemBuilder: (context, index) {
109 | DateModel dateModel = items[index];
110 | //判断是否被选择
111 | switch (configuration.selectMode) {
112 |
113 | /// 多选
114 | case CalendarSelectedMode.multiSelect:
115 | if (calendarProvider.selectedDateList.contains(dateModel)) {
116 | dateModel.isSelected = true;
117 | } else {
118 | dateModel.isSelected = false;
119 | }
120 | break;
121 |
122 | /// 选择开始和结束 中间的自动选择
123 |
124 | case CalendarSelectedMode.mutltiStartToEndSelect:
125 | if (calendarProvider.selectedDateList.contains(dateModel)) {
126 | dateModel.isSelected = true;
127 | } else {
128 | dateModel.isSelected = false;
129 | }
130 | break;
131 |
132 | /// 单选
133 | case CalendarSelectedMode.singleSelect:
134 | if (calendarProvider.selectDateModel == dateModel) {
135 | dateModel.isSelected = true;
136 | } else {
137 | dateModel.isSelected = false;
138 | }
139 | break;
140 | }
141 |
142 | return ItemContainer(
143 | dateModel: dateModel,
144 | key: ObjectKey(dateModel),
145 | clickCall: () {
146 | setState(() {});
147 | // if (configuration.selectMode ==
148 | // CalendarSelectedMode.mutltiStartToEndSelect)
149 |
150 | /// 如果是选择开始和结束则进行刷新日历
151 | },
152 | //这里使用objectKey,保证可以刷新。原因1:跟flutter的刷新机制有关。原因2:statefulElement持有state。
153 | );
154 | });
155 | }
156 |
157 | @override
158 | bool get wantKeepAlive => true;
159 | }
160 |
161 | /**
162 | * 多选模式,包装item,这样的话,就只需要刷新当前点击的item就行了,不需要刷新整个页面
163 | */
164 | class ItemContainer extends StatefulWidget {
165 | final DateModel dateModel;
166 |
167 | final GestureTapCallback clickCall;
168 | const ItemContainer({Key key, this.dateModel, this.clickCall})
169 | : super(key: key);
170 |
171 | @override
172 | ItemContainerState createState() => ItemContainerState();
173 | }
174 |
175 | class ItemContainerState extends State {
176 | DateModel dateModel;
177 | CalendarConfiguration configuration;
178 | CalendarProvider calendarProvider;
179 |
180 | ValueNotifier isSelected;
181 |
182 | @override
183 | void initState() {
184 | super.initState();
185 | dateModel = widget.dateModel;
186 | isSelected = ValueNotifier(dateModel.isSelected);
187 | }
188 |
189 | /**
190 | * 提供方法给外部,可以调用这个方法进行刷新item
191 | */
192 | void refreshItem(bool v) {
193 | /**
194 | Exception caught by gesture
195 | The following assertion was thrown while handling a gesture:
196 | setState() called after dispose()
197 | */
198 | v ??= false;
199 | if (mounted) {
200 | setState(() {
201 | dateModel.isSelected = v;
202 | });
203 |
204 | if (widget.clickCall != null) {
205 | widget.clickCall();
206 | }
207 | }
208 | }
209 |
210 | void _notifiCationUnCalendarSelect(DateModel element) {
211 | if (configuration.unCalendarSelect != null) {
212 | configuration.unCalendarSelect(element);
213 | }
214 | }
215 |
216 | void _notifiCationCalendarSelect(DateModel element) {
217 | if (configuration.calendarSelect != null) {
218 | configuration.calendarSelect(element);
219 | }
220 | }
221 |
222 | @override
223 | Widget build(BuildContext context) {
224 | // LogUtil.log(TAG: this.runtimeType, message: "ItemContainerState build");
225 | calendarProvider = Provider.of(context, listen: false);
226 | configuration = calendarProvider.calendarConfiguration;
227 |
228 | return GestureDetector(
229 | //点击整个item都会触发事件
230 | behavior: HitTestBehavior.opaque,
231 | onTap: () {
232 | LogUtil.log(
233 | TAG: this.runtimeType,
234 | message: "GestureDetector onTap: $dateModel}");
235 |
236 | //范围外不可点击
237 | if (!dateModel.isInRange) {
238 | //多选回调
239 | if (configuration.selectMode == CalendarSelectedMode.multiSelect) {
240 | configuration.multiSelectOutOfRange();
241 | }
242 | return;
243 | }
244 | print('244 周视图的变化: $dateModel');
245 | calendarProvider.lastClickDateModel = dateModel;
246 |
247 | switch (configuration.selectMode) {
248 | //简单多选
249 | case CalendarSelectedMode.multiSelect:
250 | if (calendarProvider.selectedDateList.contains(dateModel)) {
251 | calendarProvider.selectedDateList.remove(dateModel);
252 | _notifiCationUnCalendarSelect(dateModel);
253 | } else {
254 | //多选,判断是否超过限制,超过范围
255 | if (calendarProvider.selectedDateList.length ==
256 | configuration.maxMultiSelectCount) {
257 | if (configuration.multiSelectOutOfSize != null) {
258 | configuration.multiSelectOutOfSize();
259 | }
260 | return;
261 | }
262 | dateModel.isSelected = !dateModel.isSelected;
263 | calendarProvider.selectedDateList.add(dateModel);
264 | }
265 |
266 | //多选也可以弄这些单选的代码
267 | calendarProvider.selectDateModel = dateModel;
268 | break;
269 |
270 | /// 单选
271 | case CalendarSelectedMode.singleSelect:
272 |
273 | /// 加入已经选择了多个 则进行取消操作
274 | calendarProvider.selectedDateList.forEach((element) {
275 | element.isSelected = false;
276 | _notifiCationUnCalendarSelect(element);
277 | });
278 | calendarProvider.selectedDateList.clear();
279 |
280 | //单选需要刷新上一个item
281 | if (calendarProvider.lastClickItemState != this) {
282 | calendarProvider.lastClickItemState?.refreshItem(false);
283 | calendarProvider.lastClickItemState = this;
284 | }
285 | if(calendarProvider.selectedDateList.contains(dateModel)){
286 | // 如果已经选择就执行取消
287 | _notifiCationUnCalendarSelect(calendarProvider.selectDateModel);
288 | dateModel.isSelected = false;
289 | calendarProvider.selectedDateList.clear();
290 | calendarProvider.selectDateModel = null;
291 | _notifiCationUnCalendarSelect(dateModel);
292 | }else{
293 | _notifiCationUnCalendarSelect(calendarProvider.selectDateModel);
294 | dateModel.isSelected = true;
295 | calendarProvider.selectDateModel = dateModel;
296 | _notifiCationCalendarSelect(dateModel);
297 | }
298 |
299 | setState(() {});
300 |
301 | break;
302 |
303 | /// 选择范围
304 | case CalendarSelectedMode.mutltiStartToEndSelect:
305 | if (calendarProvider.selectedDateList.length == 0) {
306 | calendarProvider.selectedDateList.add(dateModel);
307 | } else if (calendarProvider.selectedDateList.length == 1) {
308 | DateModel d2 = calendarProvider.selectedDateList.first;
309 | if (calendarProvider.selectedDateList.contains(dateModel)) {
310 | /// 选择同一个第二次则进行取消
311 | dateModel.isSelected = false;
312 | _notifiCationUnCalendarSelect(dateModel);
313 | setState(() {});
314 | return;
315 | }
316 | DateTime t1, t2;
317 | if (d2.getDateTime().isAfter(dateModel.getDateTime())) {
318 | t2 = d2.getDateTime();
319 | t1 = dateModel.getDateTime();
320 | } else {
321 | t1 = d2.getDateTime();
322 | t2 = dateModel.getDateTime();
323 | }
324 | for (; t1.isBefore(t2);) {
325 | calendarProvider.selectedDateList
326 | .add(DateModel.fromDateTime(t1));
327 | t1 = t1.add(Duration(days: 1));
328 | }
329 | calendarProvider.selectedDateList.add(DateModel.fromDateTime(t1));
330 | } else {
331 | /// 加入已经选择了多个 则进行取消操作
332 | calendarProvider.selectedDateList.forEach((element) {
333 | element.isSelected = false;
334 | _notifiCationUnCalendarSelect(element);
335 | });
336 |
337 | /// 清空删除的 数组
338 | calendarProvider.selectedDateList.clear();
339 | setState(() {});
340 | }
341 |
342 | break;
343 | }
344 |
345 | /// 所有数组操作完了 进行通知分发
346 | if (configuration.calendarSelect != null &&
347 | calendarProvider.selectedDateList.length > 0) {
348 | calendarProvider.selectedDateList.forEach((element) {
349 | _notifiCationCalendarSelect(element);
350 | });
351 | }
352 |
353 | refreshItem(!this.dateModel.isSelected);
354 | },
355 | child: configuration.dayWidgetBuilder(dateModel),
356 | );
357 | }
358 |
359 | @override
360 | void deactivate() {
361 | // LogUtil.log(
362 | // TAG: this.runtimeType, message: "ItemContainerState deactivate");
363 | super.deactivate();
364 | }
365 |
366 | @override
367 | void dispose() {
368 | // LogUtil.log(TAG: this.runtimeType, message: "ItemContainerState dispose");
369 | super.dispose();
370 | }
371 |
372 | @override
373 | void didUpdateWidget(ItemContainer oldWidget) {
374 | // LogUtil.log(
375 | // TAG: this.runtimeType, message: "ItemContainerState didUpdateWidget");
376 | super.didUpdateWidget(oldWidget);
377 | }
378 | }
379 |
--------------------------------------------------------------------------------
/lib/widget/month_view_pager.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_custom_calendar/calendar_provider.dart';
3 | import 'package:flutter_custom_calendar/configuration.dart';
4 | import 'package:flutter_custom_calendar/flutter_custom_calendar.dart';
5 | import 'package:flutter_custom_calendar/utils/LogUtil.dart';
6 | import 'package:flutter_custom_calendar/utils/date_util.dart';
7 | import 'package:flutter_custom_calendar/widget/month_view.dart';
8 |
9 | import 'package:provider/provider.dart';
10 |
11 | class MonthViewPager extends StatefulWidget {
12 | const MonthViewPager({Key key}) : super(key: key);
13 |
14 | @override
15 | _MonthViewPagerState createState() => _MonthViewPagerState();
16 | }
17 |
18 | class _MonthViewPagerState extends State
19 | with AutomaticKeepAliveClientMixin {
20 | CalendarProvider calendarProvider;
21 |
22 | @override
23 | void initState() {
24 | super.initState();
25 | LogUtil.log(TAG: this.runtimeType, message: "MonthViewPager initState");
26 |
27 | calendarProvider = Provider.of(context, listen: false);
28 |
29 | //计算当前月视图的index
30 | DateModel dateModel = calendarProvider.lastClickDateModel;
31 | List monthList = calendarProvider.calendarConfiguration.monthList;
32 | int index = 0;
33 | for (int i = 0; i < monthList.length; i++) {
34 | DateModel firstDayOfMonth = monthList[i];
35 | DateModel lastDayOfMonth = DateModel.fromDateTime(firstDayOfMonth.getDateTime().add(Duration(days: DateUtil.getMonthDaysCount(firstDayOfMonth.year, firstDayOfMonth.month))));
36 |
37 | if ((dateModel.isAfter(firstDayOfMonth) ||
38 | dateModel.isSameWith(firstDayOfMonth)) &&
39 | dateModel.isBefore(lastDayOfMonth)) {
40 | index = i;
41 | break;
42 | }
43 | }
44 | WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
45 | calendarProvider.calendarConfiguration.monthController.jumpToPage(index);
46 | });
47 | }
48 |
49 | @override
50 | void dispose() {
51 | LogUtil.log(TAG: this.runtimeType, message: "MonthViewPager dispose");
52 | super.dispose();
53 | }
54 |
55 | @override
56 | Widget build(BuildContext context) {
57 | super.build(context);
58 | LogUtil.log(TAG: this.runtimeType, message: "MonthViewPager build");
59 | // 获取到当前的CalendarProvider对象,设置listen为false,不需要刷新
60 | calendarProvider = Provider.of(context, listen: false);
61 | CalendarConfiguration configuration =
62 | calendarProvider.calendarConfiguration;
63 |
64 | return PageView.builder(
65 | onPageChanged: (position) {
66 | if (calendarProvider.expandStatus.value == false) {
67 | return;
68 | }
69 | //月份的变化
70 | DateModel dateModel = configuration.monthList[position];
71 | configuration.monthChangeListeners.forEach((listener) {
72 | listener(dateModel.year, dateModel.month);
73 | });
74 | //用来保存临时变量,用于月视图切换到周视图的时候,
75 | if (calendarProvider.lastClickDateModel != null ||
76 | calendarProvider.lastClickDateModel.month != dateModel.month) {
77 | DateModel temp = new DateModel();
78 | temp.year = configuration.monthList[position].year;
79 | temp.month = configuration.monthList[position].month;
80 | temp.day = configuration.monthList[position].day + 14; // 默认月中
81 | // 如果设置了 默认选择的时间 就取默认选择的时间天数,否则为当前时间
82 | DateModel currentModel = calendarProvider.selectDateModel ?? calendarProvider.selectedDateList?.toList()[0];
83 | if(currentModel != null && temp.month == currentModel.month){
84 | temp.day = currentModel.day;
85 | }
86 | print('85 周视图的变化: $temp');
87 | calendarProvider.lastClickDateModel = temp;
88 | }
89 | },
90 | controller: configuration.monthController,
91 | itemBuilder: (context, index) {
92 | final DateModel dateModel = configuration.monthList[index];
93 | return new MonthView(
94 | configuration: configuration,
95 | year: dateModel.year,
96 | month: dateModel.month,
97 | );
98 | },
99 | itemCount: configuration.monthList.length,
100 | );
101 | }
102 |
103 | @override
104 | bool get wantKeepAlive => true;
105 | }
106 |
--------------------------------------------------------------------------------
/lib/widget/only_one_pointer_widget.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/gestures.dart';
3 |
4 | /**
5 | * 单点触摸的方案:https://xbuba.com/questions/51712287
6 | */
7 | class OnlyOnePointerRecognizer extends OneSequenceGestureRecognizer {
8 | int _p = 0;
9 |
10 | @override
11 | void addAllowedPointer(PointerDownEvent event) {
12 | startTrackingPointer(event.pointer);
13 | if (_p == 0) {
14 | resolve(GestureDisposition.rejected);
15 | _p = event.pointer;
16 | } else {
17 | resolve(GestureDisposition.accepted);
18 | }
19 | }
20 |
21 | @override
22 | String get debugDescription => 'only one pointer recognizer';
23 |
24 | @override
25 | void didStopTrackingLastPointer(int pointer) {}
26 |
27 | @override
28 | void handleEvent(PointerEvent event) {
29 | if (!event.down && event.pointer == _p) {
30 | _p = 0;
31 | }
32 | }
33 | }
34 |
35 | class OnlyOnePointerRecognizerWidget extends StatelessWidget {
36 | final Widget child;
37 |
38 | OnlyOnePointerRecognizerWidget({this.child});
39 |
40 | @override
41 | Widget build(BuildContext context) {
42 | return RawGestureDetector(
43 | gestures: {
44 | OnlyOnePointerRecognizer:
45 | GestureRecognizerFactoryWithHandlers(
46 | () => OnlyOnePointerRecognizer(),
47 | (OnlyOnePointerRecognizer instance) {
48 |
49 | },
50 | ),
51 | },
52 | child: child,
53 | );
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/lib/widget/week_view.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import 'package:flutter/foundation.dart';
4 | import 'package:flutter_custom_calendar/configuration.dart';
5 | import 'package:flutter_custom_calendar/flutter_custom_calendar.dart';
6 | import 'package:flutter_custom_calendar/utils/date_util.dart';
7 | import 'package:flutter_custom_calendar/widget/month_view.dart';
8 | import 'package:provider/provider.dart';
9 |
10 | /**
11 | * 周视图,只显示本周的日子
12 | */
13 | class WeekView extends StatefulWidget {
14 | final int year;
15 | final int month;
16 | final DateModel firstDayOfWeek;
17 | final CalendarConfiguration configuration;
18 |
19 | const WeekView(
20 | {@required this.year,
21 | @required this.month,
22 | this.firstDayOfWeek,
23 | this.configuration});
24 |
25 | @override
26 | _WeekViewState createState() => _WeekViewState();
27 | }
28 |
29 | class _WeekViewState extends State {
30 | List items;
31 |
32 | Map extraDataMap; //自定义额外的数据
33 |
34 | @override
35 | void initState() {
36 | super.initState();
37 | extraDataMap = widget.configuration.extraDataMap;
38 | items = DateUtil.initCalendarForWeekView(
39 | widget.year, widget.month, widget.firstDayOfWeek.getDateTime(), 0,
40 | minSelectDate: widget.configuration.minSelectDate,
41 | maxSelectDate: widget.configuration.maxSelectDate,
42 | extraDataMap: extraDataMap,
43 | offset: widget.configuration.offset);
44 |
45 | //第一帧后,添加监听,generation发生变化后,需要刷新整个日历
46 | WidgetsBinding.instance.addPostFrameCallback((callback) {
47 | Provider.of(context, listen: false)
48 | .generation
49 | .addListener(() async {
50 | items = DateUtil.initCalendarForWeekView(
51 | widget.year, widget.month, widget.firstDayOfWeek.getDateTime(), 0,
52 | minSelectDate: widget.configuration.minSelectDate,
53 | maxSelectDate: widget.configuration.maxSelectDate,
54 | extraDataMap: extraDataMap,
55 | offset: widget.configuration.offset);
56 | setState(() {});
57 | });
58 | });
59 | }
60 |
61 | @override
62 | Widget build(BuildContext context) {
63 | CalendarProvider calendarProvider =
64 | Provider.of(context, listen: false);
65 |
66 | CalendarConfiguration configuration =
67 | calendarProvider.calendarConfiguration;
68 | return new GridView.builder(
69 | physics: NeverScrollableScrollPhysics(),
70 | gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(
71 | crossAxisCount: 7, mainAxisSpacing: 10),
72 | itemCount: 7,
73 | itemBuilder: (context, index) {
74 | DateModel dateModel = items[index];
75 | //判断是否被选择
76 | switch (configuration.selectMode) {
77 | case CalendarSelectedMode.multiSelect:
78 | if (calendarProvider.selectedDateList.contains(dateModel)) {
79 | dateModel.isSelected = true;
80 | } else {
81 | dateModel.isSelected = false;
82 | }
83 | break;
84 | case CalendarSelectedMode.singleSelect:
85 | if (calendarProvider.selectDateModel == dateModel) {
86 | dateModel.isSelected = true;
87 | } else {
88 | dateModel.isSelected = false;
89 | }
90 | break;
91 | case CalendarSelectedMode.mutltiStartToEndSelect:
92 | if (calendarProvider.selectedDateList.contains(dateModel)) {
93 | dateModel.isSelected = true;
94 | } else {
95 | dateModel.isSelected = false;
96 | }
97 | break;
98 | }
99 |
100 | return ItemContainer(
101 | dateModel: dateModel,
102 | clickCall: () {
103 | setState(() {});
104 | });
105 | });
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/lib/widget/week_view_pager.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_custom_calendar/widget/week_view.dart';
3 | import 'package:provider/provider.dart';
4 | import 'package:flutter/foundation.dart';
5 | import 'package:flutter_custom_calendar/configuration.dart';
6 | import 'package:flutter_custom_calendar/flutter_custom_calendar.dart';
7 | import 'package:flutter_custom_calendar/utils/LogUtil.dart';
8 |
9 | class WeekViewPager extends StatefulWidget {
10 | const WeekViewPager({Key key}) : super(key: key);
11 |
12 | @override
13 | _WeekViewPagerState createState() => _WeekViewPagerState();
14 | }
15 |
16 | class _WeekViewPagerState extends State
17 | with AutomaticKeepAliveClientMixin {
18 | int lastMonth; //保存上一个月份,不然不知道月份发生了变化
19 | CalendarProvider calendarProvider;
20 |
21 | // PageController newPageController;
22 |
23 | @override
24 | void initState() {
25 | super.initState();
26 | LogUtil.log(TAG: this.runtimeType, message: "WeekViewPager initState");
27 |
28 | calendarProvider = Provider.of(context, listen: false);
29 |
30 | lastMonth = calendarProvider.lastClickDateModel.month;
31 | }
32 |
33 | @override
34 | void dispose() {
35 | LogUtil.log(TAG: this.runtimeType, message: "WeekViewPager dispose");
36 | super.dispose();
37 | }
38 |
39 | @override
40 | Widget build(BuildContext context) {
41 | super.build(context);
42 | LogUtil.log(TAG: this.runtimeType, message: "WeekViewPager build");
43 |
44 | // 获取到当前的CalendarProvider对象,设置listen为false,不需要刷新
45 | CalendarProvider calendarProvider =
46 | Provider.of(context, listen: false);
47 | CalendarConfiguration configuration =
48 | calendarProvider.calendarConfiguration;
49 | return Container(
50 | height: configuration.itemSize ?? MediaQuery.of(context).size.width / 7,
51 | child: PageView.builder(
52 | onPageChanged: (position) {
53 | if (calendarProvider.expandStatus.value == true) {
54 | return;
55 | }
56 |
57 | LogUtil.log(
58 | TAG: this.runtimeType,
59 | message:
60 | "WeekViewPager PageView onPageChanged,position:$position");
61 | DateModel firstDayOfWeek = configuration.weekList[position];
62 | int currentMonth = firstDayOfWeek.month;
63 | // 周视图的变化
64 | configuration.weekChangeListeners.forEach((listener) {
65 | listener(firstDayOfWeek.year, firstDayOfWeek.month);
66 | });
67 | if (lastMonth != currentMonth) {
68 | LogUtil.log(
69 | TAG: this.runtimeType,
70 | message:
71 | "WeekViewPager PageView monthChange:currentMonth:$currentMonth");
72 | configuration.monthChangeListeners.forEach((listener) {
73 | listener(firstDayOfWeek.year, firstDayOfWeek.month);
74 | });
75 | lastMonth = currentMonth;
76 | if (calendarProvider.lastClickDateModel == null || calendarProvider.lastClickDateModel.month != currentMonth) {
77 | DateModel temp = new DateModel();
78 | temp.year = firstDayOfWeek.year;
79 | temp.month = firstDayOfWeek.month;
80 | temp.day = firstDayOfWeek.day + 14;
81 | print('83 周视图的变化: $temp');
82 | calendarProvider.lastClickDateModel = temp;
83 | }
84 | }
85 | // calendarProvider.lastClickDateModel = configuration.weekList[position]
86 | // ..day += 4;
87 | },
88 | controller: calendarProvider.calendarConfiguration.weekController,
89 | itemBuilder: (context, index) {
90 | DateModel dateModel = configuration.weekList[index];
91 | print('dateModel: $dateModel');
92 | return new WeekView(
93 | year: dateModel.year,
94 | month: dateModel.month,
95 | firstDayOfWeek: dateModel,
96 | configuration: calendarProvider.calendarConfiguration,
97 | );
98 | },
99 | itemCount: configuration.weekList.length,
100 | ),
101 | );
102 | }
103 |
104 | @override
105 | bool get wantKeepAlive => true;
106 | }
107 |
--------------------------------------------------------------------------------
/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 | url: "https://pub.flutter-io.cn"
9 | source: hosted
10 | version: "2.4.2"
11 | boolean_selector:
12 | dependency: transitive
13 | description:
14 | name: boolean_selector
15 | url: "https://pub.flutter-io.cn"
16 | source: hosted
17 | version: "2.0.0"
18 | characters:
19 | dependency: transitive
20 | description:
21 | name: characters
22 | url: "https://pub.flutter-io.cn"
23 | source: hosted
24 | version: "1.0.0"
25 | charcode:
26 | dependency: transitive
27 | description:
28 | name: charcode
29 | url: "https://pub.flutter-io.cn"
30 | source: hosted
31 | version: "1.1.3"
32 | clock:
33 | dependency: transitive
34 | description:
35 | name: clock
36 | url: "https://pub.flutter-io.cn"
37 | source: hosted
38 | version: "1.0.1"
39 | collection:
40 | dependency: transitive
41 | description:
42 | name: collection
43 | url: "https://pub.flutter-io.cn"
44 | source: hosted
45 | version: "1.14.13"
46 | fake_async:
47 | dependency: transitive
48 | description:
49 | name: fake_async
50 | url: "https://pub.flutter-io.cn"
51 | source: hosted
52 | version: "1.1.0"
53 | flutter:
54 | dependency: "direct main"
55 | description: flutter
56 | source: sdk
57 | version: "0.0.0"
58 | flutter_test:
59 | dependency: "direct dev"
60 | description: flutter
61 | source: sdk
62 | version: "0.0.0"
63 | matcher:
64 | dependency: transitive
65 | description:
66 | name: matcher
67 | url: "https://pub.flutter-io.cn"
68 | source: hosted
69 | version: "0.12.8"
70 | meta:
71 | dependency: transitive
72 | description:
73 | name: meta
74 | url: "https://pub.flutter-io.cn"
75 | source: hosted
76 | version: "1.1.8"
77 | nested:
78 | dependency: transitive
79 | description:
80 | name: nested
81 | url: "https://pub.flutter-io.cn"
82 | source: hosted
83 | version: "0.0.4"
84 | path:
85 | dependency: transitive
86 | description:
87 | name: path
88 | url: "https://pub.flutter-io.cn"
89 | source: hosted
90 | version: "1.7.0"
91 | provider:
92 | dependency: "direct main"
93 | description:
94 | name: provider
95 | url: "https://pub.flutter-io.cn"
96 | source: hosted
97 | version: "4.1.3"
98 | sky_engine:
99 | dependency: transitive
100 | description: flutter
101 | source: sdk
102 | version: "0.0.99"
103 | source_span:
104 | dependency: transitive
105 | description:
106 | name: source_span
107 | url: "https://pub.flutter-io.cn"
108 | source: hosted
109 | version: "1.7.0"
110 | stack_trace:
111 | dependency: transitive
112 | description:
113 | name: stack_trace
114 | url: "https://pub.flutter-io.cn"
115 | source: hosted
116 | version: "1.9.5"
117 | stream_channel:
118 | dependency: transitive
119 | description:
120 | name: stream_channel
121 | url: "https://pub.flutter-io.cn"
122 | source: hosted
123 | version: "2.0.0"
124 | string_scanner:
125 | dependency: transitive
126 | description:
127 | name: string_scanner
128 | url: "https://pub.flutter-io.cn"
129 | source: hosted
130 | version: "1.0.5"
131 | term_glyph:
132 | dependency: transitive
133 | description:
134 | name: term_glyph
135 | url: "https://pub.flutter-io.cn"
136 | source: hosted
137 | version: "1.1.0"
138 | test_api:
139 | dependency: transitive
140 | description:
141 | name: test_api
142 | url: "https://pub.flutter-io.cn"
143 | source: hosted
144 | version: "0.2.17"
145 | typed_data:
146 | dependency: transitive
147 | description:
148 | name: typed_data
149 | url: "https://pub.flutter-io.cn"
150 | source: hosted
151 | version: "1.2.0"
152 | vector_math:
153 | dependency: transitive
154 | description:
155 | name: vector_math
156 | url: "https://pub.flutter-io.cn"
157 | source: hosted
158 | version: "2.0.8"
159 | sdks:
160 | dart: ">=2.9.0-14.0.dev <3.0.0"
161 | flutter: ">=1.16.0"
162 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: flutter_custom_calendar
2 | description: A calendar control of flutter that supports three selection modes
3 | version: 1.0.4+0.5
4 | homepage: http://www.fgyong.cn
5 | author: fgyong
6 | repository: https://github.com/ifgyong/flutter_custom_calendar
7 | issue_tracker: https://github.com/ifgyong/flutter_custom_calendar/issues
8 | environment:
9 | sdk: ">=2.7.0 <3.0.0"
10 |
11 | dependencies:
12 | flutter:
13 | sdk: flutter
14 |
15 | provider: ^4.1.3
16 | dev_dependencies:
17 | flutter_test:
18 | sdk: flutter
19 |
20 | # For information on the generic Dart part of this file, see the
21 | # following page: https://dart.dev/tools/pub/pubspec
22 |
23 | # The following section is specific to Flutter.
24 | flutter:
25 |
26 | # To add assets to your package, add an assets section, like this:
27 | # assets:
28 | # - images/a_dot_burr.jpeg
29 | # - images/a_dot_ham.jpeg
30 | #
31 | # For details regarding assets in packages, see
32 | # https://flutter.dev/assets-and-images/#from-packages
33 | #
34 | # An image asset can refer to one or more resolution-specific "variants", see
35 | # https://flutter.dev/assets-and-images/#resolution-aware.
36 |
37 | # To add custom fonts to your package, add a fonts section here,
38 | # in this "flutter" section. Each entry in this list should have a
39 | # "family" key with the font family name, and a "fonts" key with a
40 | # list giving the asset and other descriptors for the font. For
41 | # example:
42 | # fonts:
43 | # - family: Schyler
44 | # fonts:
45 | # - asset: fonts/Schyler-Regular.ttf
46 | # - asset: fonts/Schyler-Italic.ttf
47 | # style: italic
48 | # - family: Trajan Pro
49 | # fonts:
50 | # - asset: fonts/TrajanPro.ttf
51 | # - asset: fonts/TrajanPro_Bold.ttf
52 | # weight: 700
53 | #
54 | # For details regarding fonts in packages, see
55 | # https://flutter.dev/custom-fonts/#from-packages
56 |
--------------------------------------------------------------------------------
/test/fluttercustomcalendar_test.dart:
--------------------------------------------------------------------------------
1 | void main() {
2 | // test('adds one to input values', () {
3 | // final calculator = Calculator();
4 | // expect(calculator.addOne(2), 3);
5 | // expect(calculator.addOne(-7), -6);
6 | // expect(calculator.addOne(0), 1);
7 | // expect(() => calculator.addOne(null), throwsNoSuchMethodError);
8 | // });
9 | }
10 |
--------------------------------------------------------------------------------