summaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
authorJoel Winarske <joel.winarske@gmail.com>2024-09-05 14:01:44 -0700
committerScott Murray <scott.murray@konsulko.com>2024-09-09 15:37:22 +0000
commit0c24d7f6ebdb688d3aee492b5f84aa21e141f7e7 (patch)
treeae7dd19be2f0b482d8c58236822d7992888b0e71 /packages
parentcbc46db4690b89d2d5e983821d269931a358e508 (diff)
-add flutter_calendar_carousel as local package, and update intl version -address most of the analyze issues; not including flutter_calendar_carousel -update all packages Change-Id: I5db9234726e8e2f8d07e1431e8dac2787c521c08 Signed-off-by: Joel Winarske <joel.winarske@gmail.com> Signed-off-by: Joel Winarske <joel.winarske@toyotaconnected.com> Signed-off-by: Joel Winarske <joel.winarske@gmail.com> (cherry picked from commit d3ea8d7fa4518c258fca3c825ee895487fcaa8ec)
Diffstat (limited to 'packages')
-rw-r--r--packages/flutter_calendar_carousel/.coveralls.yml1
-rw-r--r--packages/flutter_calendar_carousel/.github/workflows/ci.yml43
-rw-r--r--packages/flutter_calendar_carousel/.github/workflows/stale.yml21
-rw-r--r--packages/flutter_calendar_carousel/.gitignore16
-rw-r--r--packages/flutter_calendar_carousel/.travis.yml90
-rw-r--r--packages/flutter_calendar_carousel/CHANGELOG.md306
-rw-r--r--packages/flutter_calendar_carousel/LICENSE21
-rw-r--r--packages/flutter_calendar_carousel/README.md188
-rw-r--r--packages/flutter_calendar_carousel/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java23
-rw-r--r--packages/flutter_calendar_carousel/android/local.properties3
-rw-r--r--packages/flutter_calendar_carousel/doc/calendar1.gifbin0 -> 506332 bytes
-rw-r--r--packages/flutter_calendar_carousel/doc/calendar2.gifbin0 -> 594129 bytes
-rw-r--r--packages/flutter_calendar_carousel/doc/calendar3.gifbin0 -> 213730 bytes
-rw-r--r--packages/flutter_calendar_carousel/doc/calendar4.gifbin0 -> 403834 bytes
-rw-r--r--packages/flutter_calendar_carousel/doc/calendar5.gifbin0 -> 646438 bytes
-rw-r--r--packages/flutter_calendar_carousel/issue_template.md9
-rw-r--r--packages/flutter_calendar_carousel/lib/classes/event.dart79
-rw-r--r--packages/flutter_calendar_carousel/lib/classes/event_list.dart40
-rw-r--r--packages/flutter_calendar_carousel/lib/classes/marked_date.dart46
-rw-r--r--packages/flutter_calendar_carousel/lib/classes/multiple_marked_dates.dart86
-rw-r--r--packages/flutter_calendar_carousel/lib/flutter_calendar_carousel.dart1214
-rw-r--r--packages/flutter_calendar_carousel/lib/src/calendar_header.dart73
-rw-r--r--packages/flutter_calendar_carousel/lib/src/default_styles.dart48
-rw-r--r--packages/flutter_calendar_carousel/lib/src/weekday_row.dart131
-rw-r--r--packages/flutter_calendar_carousel/pubspec.lock220
-rw-r--r--packages/flutter_calendar_carousel/pubspec.yaml53
-rw-r--r--packages/flutter_calendar_carousel/renovate.json6
-rw-r--r--packages/flutter_calendar_carousel/test/flutter_calendar_carousel_test.dart180
-rw-r--r--packages/flutter_calendar_carousel/test/src/header_test.dart104
-rw-r--r--packages/flutter_calendar_carousel/test/src/weekday_row_test.dart128
30 files changed, 3129 insertions, 0 deletions
diff --git a/packages/flutter_calendar_carousel/.coveralls.yml b/packages/flutter_calendar_carousel/.coveralls.yml
new file mode 100644
index 0000000..121a96a
--- /dev/null
+++ b/packages/flutter_calendar_carousel/.coveralls.yml
@@ -0,0 +1 @@
+repo_token: ZZjgwJha5lf2FFsNyvzmbNsswFz2fZuKR
diff --git a/packages/flutter_calendar_carousel/.github/workflows/ci.yml b/packages/flutter_calendar_carousel/.github/workflows/ci.yml
new file mode 100644
index 0000000..bc4461d
--- /dev/null
+++ b/packages/flutter_calendar_carousel/.github/workflows/ci.yml
@@ -0,0 +1,43 @@
+name: Flutter CI
+
+on:
+ push:
+ branches: [master]
+ pull_request:
+ branches: [master]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4
+ - uses: actions/setup-java@v4
+ with:
+ java-version: "14.x"
+
+ - uses: subosito/flutter-action@v2
+ with:
+ channel: "stable"
+ flutter-version: "3.x"
+
+ - run: flutter pub get
+
+ - run: flutter format --set-exit-if-changed .
+
+ # - run: flutter analyze .
+
+ - run: flutter test --coverage
+
+ - name: Upload coverage to Codecov
+ uses: codecov/codecov-action@v4
+ with:
+ token: ${{ secrets.CODECOV_TOKEN }}
+ file: coverage/lcov.info
+
+ # - run: flutter build apk
+
+ # Upload generated apk to the artifacts.
+ # - uses: actions/upload-artifact@v1
+ # with:
+ # name: release-apk
+ # path: build/app/outputs/apk/release/app-release.apk
diff --git a/packages/flutter_calendar_carousel/.github/workflows/stale.yml b/packages/flutter_calendar_carousel/.github/workflows/stale.yml
new file mode 100644
index 0000000..f2b97a0
--- /dev/null
+++ b/packages/flutter_calendar_carousel/.github/workflows/stale.yml
@@ -0,0 +1,21 @@
+name: Mark stale issues and pull requests
+
+on:
+ schedule:
+ - cron: "0 0 * * *"
+
+jobs:
+ stale:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/stale@v9
+ with:
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+ days-before-stale: 90
+ days-before-close: 7
+ stale-issue-message: 'This issue is stale because it has been open 90 days with no activity. Leave a comment or this will be closed in 7 days.'
+ stale-pr-message: 'This PR is stale because it has been open 90 days with no activity. Leave a comment or this will be closed in 7 days'
+ stale-issue-label: 'no-issue-activity'
+ stale-pr-label: 'no-pr-activity'
diff --git a/packages/flutter_calendar_carousel/.gitignore b/packages/flutter_calendar_carousel/.gitignore
new file mode 100644
index 0000000..f7ea22e
--- /dev/null
+++ b/packages/flutter_calendar_carousel/.gitignore
@@ -0,0 +1,16 @@
+.DS_Store
+.dart_tool/
+.vscode/
+
+.packages
+.pub/
+coverage
+
+build/
+ios/.generated/
+ios/Flutter/Generated.xcconfig
+ios/Runner/GeneratedPluginRegistrant.*
+
+.idea/
+flutter_calendar_carousel.iml
+flutter_export_environment.sh
diff --git a/packages/flutter_calendar_carousel/.travis.yml b/packages/flutter_calendar_carousel/.travis.yml
new file mode 100644
index 0000000..4a8e0f5
--- /dev/null
+++ b/packages/flutter_calendar_carousel/.travis.yml
@@ -0,0 +1,90 @@
+matrix:
+ # This causes the build to complete immediately upon first failure or once
+ # required jobs are green.
+ fast_finish: true
+
+ # Building APK/IPA takes a long time; do not wait for them to finish.
+ # allow_failures:
+ # - env: JOB=APK
+ # - env: JOB=IPA
+
+ include:
+ # Runs unit tests without emulator.
+ - env: JOB=PR
+ os: linux
+ language: dart
+ sudo: false
+ addons:
+ apt:
+ # Flutter depends on /usr/lib/x86_64-linux-gnu/libstdc++.so.6 version GLIBCXX_3.4.18
+ sources:
+ - ubuntu-toolchain-r-test # if we don't specify this, the libstdc++6 we get is the wrong version
+ packages:
+ - libstdc++6
+ install:
+ - echo 'Avoid default Travis CI install step'
+ before_script:
+ - git clone https://github.com/flutter/flutter.git -b stable
+ - export PATH=`pwd`/flutter/bin:`pwd`/flutter/bin/cache/dart-sdk/bin:$PATH
+ - flutter doctor
+ - gem install coveralls-lcov
+ script:
+ - ./flutter/bin/flutter test --coverage
+ after_success:
+ - coveralls-lcov coverage/lcov.info
+
+# # Builds an APK.
+# - env: JOB=APK
+# os: linux
+# language: android
+# licenses:
+# - 'android-sdk-preview-license-.+'
+# - 'android-sdk-license-.+'
+# - 'google-gdk-license-.+'
+# android:
+# components:
+# - tools
+# - platform-tools
+# - build-tools-25.0.3
+# - android-25
+# - sys-img-armeabi-v7a-google_apis-25
+# - extra-android-m2repository
+# - extra-google-m2repository
+# - extra-google-android-support
+# jdk: oraclejdk8
+# sudo: false
+# addons:
+# apt:
+# # Flutter depends on /usr/lib/x86_64-linux-gnu/libstdc++.so.6 version GLIBCXX_3.4.18
+# sources:
+# - ubuntu-toolchain-r-test # if we don't specify this, the libstdc++6 we get is the wrong version
+# packages:
+# - libstdc++6
+# - fonts-droid
+# before_script:
+# - wget http://services.gradle.org/distributions/gradle-3.5-bin.zip
+# - unzip -qq gradle-3.5-bin.zip
+# - export GRADLE_HOME=$PWD/gradle-3.5
+# - export PATH=$GRADLE_HOME/bin:$PATH
+# - git clone https://github.com/flutter/flutter.git -b beta
+# script:
+# - ./flutter/bin/flutter -v build apk
+#
+# # Builds an IPA.
+# - env: JOB=IPA
+# os: osx
+# language: generic
+# osx_image: xcode8.3
+# before_script:
+# - pip install six
+# - brew update
+# - brew install --HEAD libimobiledevice
+# - brew install ideviceinstaller
+# - brew install ios-deploy
+# - git clone https://github.com/flutter/flutter.git -b beta
+# script:
+# - ./flutter/bin/flutter -v build ios --no-codesign
+
+cache:
+ directories:
+ - $HOME/.pub-cache
diff --git a/packages/flutter_calendar_carousel/CHANGELOG.md b/packages/flutter_calendar_carousel/CHANGELOG.md
new file mode 100644
index 0000000..aa8ec71
--- /dev/null
+++ b/packages/flutter_calendar_carousel/CHANGELOG.md
@@ -0,0 +1,306 @@
+## [2.4.3]
+
+Upgrade intl to `^0.18.1`
+
+## [2.4.2]
+
+Support Flutter version 3+
+
+## [2.4.1]
+
+Update iOS podspec and `info.plist`
+
+## [2.1.0]
+
+Update build config on flutter V2 embedding (#293)
+
+## [2.0.3]
+
+Multiple days selection using `addRange` method [#285](https://github.com/dooboolab/flutter_calendar_carousel/pull/285)
+
+## [2.0.2]
+
+Multiple days selection [#282](https://github.com/dooboolab/flutter_calendar_carousel/pull/284)
+
+## [2.0.1]
+
+Null safety improvements [#272](https://github.com/dooboolab/flutter_calendar_carousel/pull/272)
+
+## [2.0.1]
+
+Added disableDayPressed option [#267](https://github.com/dooboolab/flutter_calendar_carousel/pull/267)
+
+## [2.0.0]
+
+Support null-safety [#260](https://github.com/dooboolab/flutter_calendar_carousel/pull/260)
+
+## [1.5.3]
+
+- Add `id` to event model [#257](https://github.com/dooboolab/flutter_calendar_carousel/pull/257)
+
+## [1.5.2]
+
+- Bump up `intl` dependency [#254](https://github.com/dooboolab/flutter_calendar_carousel/pull/254)
+
+## [1.5.1]
+
+- Bugfix when switching month - "The method 'call' was called on null." [#243](https://github.com/dooboolab/flutter_calendar_carousel/pull/243)
+
+## [1.5.0]
+
+- Add key to widget constructor [#234](https://github.com/dooboolab/flutter_calendar_carousel/pull/234/files)
+- Enhance initilizing page numbers [#231](https://github.com/dooboolab/flutter_calendar_carousel/pull/231)
+
+## [1.4.12]
+
+- Handle issue [#207](https://github.com/dooboolab/flutter_calendar_carousel/issues/207), [#209](https://github.com/dooboolab/flutter_calendar_carousel/issues/209)
+
+## [1.4.11]
+
+- Add first day of week offset to week builder [#204](https://github.com/dooboolab/flutter_calendar_carousel/pull/204)
+
+## [1.4.10]
+
+- Fix Calendar displays incorrectly when scrolling horizontally [#193](https://github.com/dooboolab/flutter_calendar_carousel/pull/193)
+
+## [1.4.9]
+
+- Target date for custom header
+
+## [1.4.8]
+
+- Add ability to set `targetDate` on header [#183](https://github.com/dooboolab/flutter_calendar_carousel/pull/183).
+
+## [1.4.7]
+
+- Fix current day showing incorrectly when using `showOnlyCurrentMonthDate` [#181](https://github.com/dooboolab/flutter_calendar_carousel/pull/182).
+
+## [1.4.6]
+
+- Set default `minSelectedDate` and `maxSelectedDate` [#179](https://github.com/dooboolab/flutter_calendar_carousel/pull/179).
+
+## [1.4.4]
+
+- Expose `pageScrollPhysics` for pageView.
+
+## [1.4.2]
+
+- Add option for setting scrollDirection [#166](https://github.com/dooboolab/flutter_calendar_carousel/pull/166)
+- Resolve [#123](https://github.com/dooboolab/flutter_calendar_carousel/issues/123) in [#165](https://github.com/dooboolab/flutter_calendar_carousel/pull/165).
+
+## [1.4.1]
+
+- Resolve [#164](https://github.com/dooboolab/flutter_calendar_carousel/issues/164).
+
+## [1.4.0]
+
+- Resolve [#154](https://github.com/dooboolab/flutter_calendar_carousel/issues/154).
+
+## [1.3.29]
+
+- Resolve [#157](https://github.com/dooboolab/flutter_calendar_carousel/issues/157).
+
+## [1.3.28]
+
+- Allow the use of generic type with Interface [#149](https://github.com/dooboolab/flutter_calendar_carousel/pull/149)
+- Added doc to custom weekday builder. Weekday number is now supplied to the builder [#150](https://github.com/dooboolab/flutter_calendar_carousel/pull/150)
+
+## [1.3.27]
+
+- customDayBuilder fix.
+- Remove date_utils dep.
+
+## [1.3.26]
+
+- Support custom day container feature [#145](https://github.com/dooboolab/flutter_calendar_carousel/pull/145).
+
+## [1.3.23]
+
+- Support intl >= 0.15.7 < 0.17.0 to inclease `pub` health
+- Removed deprecated methods ~~`markedDates`~~, ~~`markedDateColor`~~
+- Fixes [#101](https://github.com/dooboolab/flutter_calendar_carousel/issues/101)
+- Fixes [#104](https://github.com/dooboolab/flutter_calendar_carousel/issues/104)
+- Fixes [#112](https://github.com/dooboolab/flutter_calendar_carousel/issues/112)
+- Fixes [#119](https://github.com/dooboolab/flutter_calendar_carousel/issues/119)
+- Support long pressed as a feature request[#103](https://github.com/dooboolab/flutter_calendar_carousel/issues/103)
+- Support semantic label as a feature request [#139](https://github.com/dooboolab/flutter_calendar_carousel/issues/139)
+- Expose `dayCrossAxisAlignment` and `dayMainAxisAlignment` to resolve [#122](https://github.com/dooboolab/flutter_calendar_carousel/issues/122)
+- Expose `showIconBehindDayText` to resolve [#131](https://github.com/dooboolab/flutter_calendar_carousel/issues/131)
+- Fixes [#94](https://github.com/dooboolab/flutter_calendar_carousel/issues/94)
+
+## [1.3.20]
+
+- Support intl >= 0.15.7
+
+## [1.3.19]
+
+- Improved customizability for weekday containers [#141](https://github.com/dooboolab/flutter_calendar_carousel/pull/141)
+
+## [1.3.18]
+
+- Fix vertical scroll behavior for weekFormat calendar view.
+- Reformat code with dartfmt
+
+## [1.3.17]
+
+- Added feature to only show dates from today adding `showOnlyCurrentMonthDate` parameter.
+
+## [1.3.16]
+
+- Added feature for change first day of the week.
+
+## [1.3.15+]
+
+- Ability to disable horizontal scroll to change month with `isScrollable` param.
+ - Resolve [#74](https://github.com/dooboolab/flutter_calendar_carousel/issues/74)
+- Show events in `week` calendar.
+ - Resolve [#66](https://github.com/dooboolab/flutter_calendar_carousel/issues/66)
+- Update breaking docs in pub.
+- Expose event list to user.
+- Remove print.
+- Pass first date of week to onCalendarChanged in week view [#88](https://github.com/dooboolab/flutter_calendar_carousel/pull/88)
+- Support for passing in custom widgets for next and previous month arrow icons [#95](https://github.com/dooboolab/flutter_calendar_carousel/pull/95)
+
+## [1.3.14]
+
+- Code refactoring [#77](https://github.com/dooboolab/flutter_calendar_carousel/pull/77)
+ - Seperate weekday widget
+
+## [1.3.13]
+
+- Code refactoring [#73](https://github.com/dooboolab/flutter_calendar_carousel/pull/73)
+ - Seperate header.
+ - Add first basic test code.
+ - Add composable header widget
+
+## [1.3.12]
+
+- Setting dot icon per event [#71](https://github.com/dooboolab/flutter_calendar_carousel/pull/71)
+
+## [1.3.11]
+
+- Fixed `selectledDayTextStyle` property not being respected [#65](https://github.com/dooboolab/flutter_calendar_carousel/pull/65).
+
+## [1.3.10]
+
+- Add property for static six week format to keep calendar height consistent between months [#62](https://github.com/dooboolab/flutter_calendar_carousel/pull/62).
+
+## [1.3.9]
+
+- Changed priority for `today` higher than `prevMonth` and `nextMonth` and `thisMonth`.
+
+## [1.3.7]
+
+- Ability to choose the weekday format on the constructor [#47](https://github.com/dooboolab/flutter_calendar_carousel/pull/47).
+
+## [1.3.6]
+
+- custom event type added [#49](https://github.com/dooboolab/flutter_calendar_carousel/pull/49).
+
+## [1.3.5]
+
+- headerTitleTouchable and onHeaderTitlePressed props added [#44](https://github.com/dooboolab/flutter_calendar_carousel/pull/44).
+
+## [1.3.4]
+
+- Bug fix. PrevDaysTextStyle and PrevDaysTextStyle overwrite weekendTextStyle [#41](https://github.com/dooboolab/flutter_calendar_carousel/issues/41).
+
+## [1.3.3]
+
+- Fixed FlatButton fill and border color. [#37](https://github.com/dooboolab/flutter_calendar_carousel/pull/37)
+- EventList bug fixing. [#37](https://github.com/dooboolab/flutter_calendar_carousel/pull/36)
+
+## [1.3.2]
+
+- Mapping events for better performance. [#34](https://github.com/dooboolab/flutter_calendar_carousel/pull/34).
+
+## [1.3.1]
+
+- weekdays bug fix.
+
+## [1.3.0]
+
+- Better localization support for `weekDays`. Setting manually weekdays isn't required now. Related [#23](https://github.com/dooboolab/flutter_calendar_carousel/pull/23).
+- Add custom icons in event [#28](https://github.com/dooboolab/flutter_calendar_carousel/pull/28).
+
+## [1.2.3]
+
+- Add custom physics parameter. Feature in [#21](https://github.com/dooboolab/flutter_calendar_carousel/pull/21).
+
+## [1.2.2]
+
+- headerTextStyle fix[#17](https://github.com/dooboolab/flutter_calendar_carousel/issues/17).
+- Can show or hide header button with `showHeaderButton` attribute.
+
+## [1.2.1]
+
+- Week-format shows current week [#15](https://github.com/dooboolab/flutter_calendar_carousel/issues/15).
+
+## [1.2.0]
+
+- Support carousel week calendar.
+
+## [1.1.11]
+
+- Ability to customize weekend days [#13](https://github.com/dooboolab/flutter_calendar_carousel/issues/13).
+
+## [1.1.10]
+
+- Support weekFormat but without carousel.
+
+## [1.1.9]
+
+- Updated readme.
+
+## [1.1.8]
+
+- Render multiple marked dates.
+- `markedDates` is deprecated. Use `markedDatesMap` instead.
+
+## [1.1.3]
+
+- Mark dates with non-zero times.
+
+## [1.1.2]
+
+- Implemented a way to change the header text style.
+
+## [1.1.1]
+
+- Expose new variables.
+ - headerMargin, childAspectRatio, weekDayMargin
+
+## [1.1.0]
+
+- Give proper text color in weekend when it is today.
+- Compare month and year for marking today's date. Resolve #3.
+
+## [1.0.3]
+
+- Fixed pub broken image.
+
+## [1.0.2]
+
+- Use `Position` widget to mark the dates.
+
+## [1.0.1]
+
+- Show markedDates.
+
+## [0.2.0]
+
+- Customizable headerWidget.
+- Setting weekdays visibility
+- Customizable weekend color.
+
+## [0.1.3]
+
+- Rename the top-level "docs" directory to "doc".
+
+## [0.1.1]
+
+- Added readme.
+
+## [0.1.0]
+
+- First release
diff --git a/packages/flutter_calendar_carousel/LICENSE b/packages/flutter_calendar_carousel/LICENSE
new file mode 100644
index 0000000..74022ad
--- /dev/null
+++ b/packages/flutter_calendar_carousel/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 dooboolab
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/flutter_calendar_carousel/README.md b/packages/flutter_calendar_carousel/README.md
new file mode 100644
index 0000000..daff642
--- /dev/null
+++ b/packages/flutter_calendar_carousel/README.md
@@ -0,0 +1,188 @@
+# flutter_calendar_carousel
+
+[![Pub Version](https://img.shields.io/pub/v/flutter_calendar_carousel.svg?style=flat-square)](https://pub.dartlang.org/packages/flutter_calendar_carousel)
+[![Flutter CI](https://github.com/dooboolab/flutter_calendar_carousel/actions/workflows/ci.yml/badge.svg)](https://github.com/dooboolab/flutter_calendar_carousel/actions/workflows/ci.yml)
+[![Coverage Status](https://codecov.io/gh/dooboolab/flutter_calendar_carousel/branch/master/graph/badge.svg?token=KTrSs3fGsS)](https://codecov.io/gh/dooboolab/flutter_calendar_carousel)
+![License](https://img.shields.io/badge/license-MIT-blue.svg)
+
+Calendar widget for flutter that is swipeable horizontally. This widget can help you build your own calendar widget highly customizable. Now you can even add your icon for each event.
+
+## Notice
+This widget is compatible with flutter V3 from version `2.4.+`.
+
+## New Feature
+
+[2.0.3]
+
+- Multiple days selection using `addRange` method [#285](https://github.com/dooboolab/flutter_calendar_carousel/pull/285)
+
+- Check out great feature `customDayBuilder` work done by [maxgmer](https://github.com/maxgmer) :tada:.
+
+#### Rectangular style
+
+![image](https://raw.githubusercontent.com/dooboolab/flutter_calendar_carousel/master/doc/calendar1.gif)
+
+#### Circular style
+
+![image](https://raw.githubusercontent.com/dooboolab/flutter_calendar_carousel/master/doc/calendar2.gif)
+
+#### No border
+
+![image](https://raw.githubusercontent.com/dooboolab/flutter_calendar_carousel/master/doc/calendar3.gif)
+
+#### Marked Dates
+
+![image](https://raw.githubusercontent.com/dooboolab/flutter_calendar_carousel/master/doc/calendar4.gif)
+
+#### Custom Icon Events
+
+![image](https://raw.githubusercontent.com/icemanbsi/flutter_calendar_carousel/master/doc/calendar5.gif)
+
+## Getting Started
+
+For help getting started with Flutter, view our online
+[documentation](https://flutter.io/).
+
+## Props
+
+| props | types | defaultValues |
+| :----------------------------- | :------------------------: | :---------------------------------------------------------------------------------------------------------: |
+| viewPortFraction | `double` | 1.0 |
+| prevDaysTextStyle | `TextStyle` | |
+| daysTextStyle | `TextStyle` | |
+| nextDaysTextStyle | `TextStyle` | |
+| prevMonthDayBorderColor | `Color` | Colors.transparent |
+| thisMonthDayBorderColor | `Color` | Colors.transparent |
+| nextMonthDayBorderColor | `Color` | Colors.transparent |
+| dayPadding | `double` | 2.0 |
+| height | `double` | double.infinity |
+| width | `double` | double.infinity |
+| todayTextStyle | `TextStyle` | `fontSize: 14.0, color: Colors.white` |
+| dayButtonColor | `Color` | Colors.red |
+| todayBorderColor | `Color` | Colors.red |
+| todayButtonColor | `Colors` | Colors.red |
+| selectedDateTime | `DateTime` | |
+| selectedDayTextStyle | `TextStyle` | `fontSize: 14.0, color: Colors.white` |
+| selectedDayBorderColor | `Color` | Colors.green |
+| selectedDayButtonColor | `Color` | Colors.green |
+| daysHaveCircularBorder | `bool` | |
+| onDayPressed | `Func` | |
+| weekdayTextStyle | `TextStyle` | `fontSize: 14.0, color: Colors.deepOrange` |
+| iconColor | `Color` | Colors.blueAccent |
+| headerTextStyle | `TextStyle` | `fontSize: 20.0, color: Colors.blue` |
+| headerText | `Text` | `Text('${DateFormat.yMMM().format(this._dates[1])}'`) |
+| weekendTextStyle | `TextStyle` | `fontSize: 14.0, color: Colors.pinkAccent` |
+| markedDatesMap | `Events` | `null` |
+| markedDateWidget | `Widget` | `Positioned(child: Container(color: Colors.blueAccent, height: 4.0, width: 4.0), bottom: 4.0, left: 18.0);` |
+| markedDateShowIcon | `bool` | false |
+| markedDateIconBorderColor | `Color` | |
+| markedDateIconMaxShown | `int` | 2 |
+| markedDateIconMargin | `double` | 5.0 |
+| markedDateIconBuilder | `MarkedDateIconBuilder<T>` | |
+| markedDateIconOffset | `double` | 5.0 |
+| markedDateCustomShapeBorder | `ShapeBorder` | null |
+| markedDateCustomTextStyle | `TextStyle` | null |
+| markedDateMoreCustomDecoration | `Decoration` | |
+| markedDateMoreCustomTextStyle | `TextStyle` | |
+| headerMargin | `EdgetInsets` | `const EdgeInsets.symmetric(vertical: 16.0)` |
+| headerTitleTouchable | `bool` | `false` |
+| onHeaderTitlePressed | `Function` | `() => _selectDateFromPicker()` |
+| showHeader | `bool` | |
+| showHeaderButton | `bool` | |
+| childAspectRatio | `double` | `1.0` |
+| weekDayMargin | `EdgeInsets` | `const EdgeInsets.only(bottom: 4.0)` |
+| weekFormat | `bool` | `false` |
+| locale | `String` | `en` |
+| firstDayOfWeek | `int` | `null` |
+| onCalendarChanged | `Function(DateTime)` | |
+| minSelectedDate | `DateTime` | |
+| maxSelectedDate | `DateTime` | |
+| inactiveDaysTextStyle | `TextStyle` | |
+| inactiveWeekendTextStyle | `TextStyle` | |
+| weekDayFormat | `WeekdayFormat` | `short` |
+| staticSixWeekFormat | `bool` | `false` |
+| showOnlyCurrentMonthDate | `bool` | `false` |
+| dayCrossAxisAlignment | `CrossAxisAlignment` | `CrossAxisAlignment.center` |
+| dayMainAxisAlignment | `MainAxisAlignment` | `CrossAlignment.center` |
+| showIconBehindDayText | `bool` | `false` |
+| pageScrollPhysics | `ScrollPhysics` | `ScrollPhysics` |
+
+With `CalendarCarousel<YourEventClass>` and `EventList<YourEventClass>` you can specifiy a custom Event class.
+
+## Install
+
+Add `flutter_calendar_carousel` as a dependency in pubspec.yaml
+For help on adding as a dependency, view the [documentation](https://flutter.io/using-packages/).
+
+## Usage
+
+```dart
+import 'package:flutter_calendar_carousel/flutter_calendar_carousel.dart' show CalendarCarousel;
+Widget widget() {
+ return Container(
+ margin: EdgeInsets.symmetric(horizontal: 16.0),
+ child: CalendarCarousel<Event>(
+ onDayPressed: (DateTime date, List<Event> events) {
+ this.setState(() => _currentDate = date);
+ },
+ weekendTextStyle: TextStyle(
+ color: Colors.red,
+ ),
+ thisMonthDayBorderColor: Colors.grey,
+// weekDays: null, /// for pass null when you do not want to render weekDays
+// headerText: Container( /// Example for rendering custom header
+// child: Text('Custom Header'),
+// ),
+ customDayBuilder: ( /// you can provide your own build function to make custom day containers
+ bool isSelectable,
+ int index,
+ bool isSelectedDay,
+ bool isToday,
+ bool isPrevMonthDay,
+ TextStyle textStyle,
+ bool isNextMonthDay,
+ bool isThisMonthDay,
+ DateTime day,
+ ) {
+ /// If you return null, [CalendarCarousel] will build container for current [day] with default function.
+ /// This way you can build custom containers for specific days only, leaving rest as default.
+
+ // Example: every 15th of month, we have a flight, we can place an icon in the container like that:
+ if (day.day == 15) {
+ return Center(
+ child: Icon(Icons.local_airport),
+ );
+ } else {
+ return null;
+ }
+ },
+ weekFormat: false,
+ markedDatesMap: _markedDateMap,
+ height: 420.0,
+ selectedDateTime: _currentDate,
+ daysHaveCircularBorder: false, /// null for not rendering any border, true for circular border, false for rectangular border
+ ),
+ );
+}
+```
+
+### TODO
+
+- [x] Render weekdays.
+- [x] Customizable headerWidget.
+- [x] Set weekdays visibility.
+- [x] Customizable textStyles for days in weekend.
+- [x] Marked Dates.
+- [x] Multiple Marked Dates.
+- [x] Customizable weekend days.
+- [x] Week Calendar.
+- [x] Carousel Week Calendar.
+- [ ] Multiple days selections.
+- [x] Widget test.
+
+## Help Maintenance
+
+I've been maintaining quite many repos these days and burning out slowly. If you could help me cheer up, buying me a cup of coffee will make my life really happy and get much energy out of it.
+<br/>
+<a href="https://www.buymeacoffee.com/dooboolab" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/purple_img.png" alt="Buy Me A Coffee" style="height: auto !important;width: auto !important;" ></a>
+[![Paypal](https://www.paypalobjects.com/webstatic/mktg/Logo/pp-logo-100px.png)](https://paypal.me/dooboolab)
diff --git a/packages/flutter_calendar_carousel/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java b/packages/flutter_calendar_carousel/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java
new file mode 100644
index 0000000..d007606
--- /dev/null
+++ b/packages/flutter_calendar_carousel/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java
@@ -0,0 +1,23 @@
+package io.flutter.plugins;
+
+import io.flutter.plugin.common.PluginRegistry;
+
+/**
+ * Generated file. Do not edit.
+ */
+public final class GeneratedPluginRegistrant {
+ public static void registerWith(PluginRegistry registry) {
+ if (alreadyRegisteredWith(registry)) {
+ return;
+ }
+ }
+
+ private static boolean alreadyRegisteredWith(PluginRegistry registry) {
+ final String key = GeneratedPluginRegistrant.class.getCanonicalName();
+ if (registry.hasPlugin(key)) {
+ return true;
+ }
+ registry.registrarFor(key);
+ return false;
+ }
+}
diff --git a/packages/flutter_calendar_carousel/android/local.properties b/packages/flutter_calendar_carousel/android/local.properties
new file mode 100644
index 0000000..81dcc8b
--- /dev/null
+++ b/packages/flutter_calendar_carousel/android/local.properties
@@ -0,0 +1,3 @@
+sdk.dir=/Users/hyo/Library/Android/sdk
+flutter.sdk=/Users/hyo/Github/others/flutter
+flutter.versionName=0.0.1 \ No newline at end of file
diff --git a/packages/flutter_calendar_carousel/doc/calendar1.gif b/packages/flutter_calendar_carousel/doc/calendar1.gif
new file mode 100644
index 0000000..3974a5f
--- /dev/null
+++ b/packages/flutter_calendar_carousel/doc/calendar1.gif
Binary files differ
diff --git a/packages/flutter_calendar_carousel/doc/calendar2.gif b/packages/flutter_calendar_carousel/doc/calendar2.gif
new file mode 100644
index 0000000..258c372
--- /dev/null
+++ b/packages/flutter_calendar_carousel/doc/calendar2.gif
Binary files differ
diff --git a/packages/flutter_calendar_carousel/doc/calendar3.gif b/packages/flutter_calendar_carousel/doc/calendar3.gif
new file mode 100644
index 0000000..d5bda07
--- /dev/null
+++ b/packages/flutter_calendar_carousel/doc/calendar3.gif
Binary files differ
diff --git a/packages/flutter_calendar_carousel/doc/calendar4.gif b/packages/flutter_calendar_carousel/doc/calendar4.gif
new file mode 100644
index 0000000..5006372
--- /dev/null
+++ b/packages/flutter_calendar_carousel/doc/calendar4.gif
Binary files differ
diff --git a/packages/flutter_calendar_carousel/doc/calendar5.gif b/packages/flutter_calendar_carousel/doc/calendar5.gif
new file mode 100644
index 0000000..bd4cdb1
--- /dev/null
+++ b/packages/flutter_calendar_carousel/doc/calendar5.gif
Binary files differ
diff --git a/packages/flutter_calendar_carousel/issue_template.md b/packages/flutter_calendar_carousel/issue_template.md
new file mode 100644
index 0000000..751e4f3
--- /dev/null
+++ b/packages/flutter_calendar_carousel/issue_template.md
@@ -0,0 +1,9 @@
+### Version of flutter_calendar_carousel
+
+### Expected behavior
+
+### Actual behavior
+
+### flutter doctor result (run flutter doctor in terminal)
+
+### Steps to reproduce the behavior
diff --git a/packages/flutter_calendar_carousel/lib/classes/event.dart b/packages/flutter_calendar_carousel/lib/classes/event.dart
new file mode 100644
index 0000000..c852a94
--- /dev/null
+++ b/packages/flutter_calendar_carousel/lib/classes/event.dart
@@ -0,0 +1,79 @@
+import 'package:flutter/material.dart';
+
+class Event implements EventInterface {
+ final DateTime date;
+ final String? title;
+ final String? description;
+ final String? location;
+ final Widget? icon;
+ final Widget? dot;
+ final int? id;
+ Event({
+ this.id,
+ required this.date,
+ this.title,
+ this.description,
+ this.location,
+ this.icon,
+ this.dot,
+ });
+
+ @override
+ bool operator ==(dynamic other) {
+ return date == other.date &&
+ title == other.title &&
+ description == other.description &&
+ location == other.location &&
+ icon == other.icon &&
+ dot == other.dot &&
+ id == other.id;
+ }
+
+ @override
+ int get hashCode => Object.hash(date, description, location, title, icon, id);
+
+ @override
+ DateTime getDate() {
+ return date;
+ }
+
+ @override
+ int? getId() {
+ return id;
+ }
+
+ @override
+ Widget? getDot() {
+ return dot;
+ }
+
+ @override
+ Widget? getIcon() {
+ return icon;
+ }
+
+ @override
+ String? getTitle() {
+ return title;
+ }
+
+ @override
+ String? getDescription() {
+ return description;
+ }
+
+ @override
+ String? getLocation() {
+ return location;
+ }
+}
+
+abstract class EventInterface {
+ DateTime getDate();
+ String? getTitle();
+ String? getDescription();
+ String? getLocation();
+ Widget? getIcon();
+ Widget? getDot();
+ int? getId();
+}
diff --git a/packages/flutter_calendar_carousel/lib/classes/event_list.dart b/packages/flutter_calendar_carousel/lib/classes/event_list.dart
new file mode 100644
index 0000000..a8c9252
--- /dev/null
+++ b/packages/flutter_calendar_carousel/lib/classes/event_list.dart
@@ -0,0 +1,40 @@
+class EventList<T> {
+ Map<DateTime, List<T>> events;
+
+ EventList({
+ required this.events,
+ });
+
+ void add(DateTime date, T event) {
+ final eventsOfDate = events[date];
+ if (eventsOfDate == null)
+ events[date] = [event];
+ else
+ eventsOfDate.add(event);
+ }
+
+ void addAll(DateTime date, List<T> events) {
+ final eventsOfDate = this.events[date];
+ if (eventsOfDate == null)
+ this.events[date] = events;
+ else
+ eventsOfDate.addAll(events);
+ }
+
+ bool remove(DateTime date, T event) {
+ final eventsOfDate = events[date];
+ return eventsOfDate != null ? eventsOfDate.remove(event) : false;
+ }
+
+ List<T> removeAll(DateTime date) {
+ return events.remove(date) ?? [];
+ }
+
+ void clear() {
+ events.clear();
+ }
+
+ List<T> getEvents(DateTime date) {
+ return events[date] ?? [];
+ }
+}
diff --git a/packages/flutter_calendar_carousel/lib/classes/marked_date.dart b/packages/flutter_calendar_carousel/lib/classes/marked_date.dart
new file mode 100644
index 0000000..a3db2d5
--- /dev/null
+++ b/packages/flutter_calendar_carousel/lib/classes/marked_date.dart
@@ -0,0 +1,46 @@
+import 'package:flutter/material.dart';
+
+class MarkedDate implements MarkedDateInterface {
+ final Color color;
+ final int? id;
+ final TextStyle? textStyle;
+ final DateTime date;
+
+ MarkedDate({
+ required this.color,
+ this.id,
+ this.textStyle,
+ required this.date,
+ });
+
+ @override
+ bool operator ==(dynamic other) {
+ return date == other.date &&
+ color == other.color &&
+ textStyle == other.textStyle &&
+ id == other.id;
+ }
+
+ @override
+ DateTime getDate() => this.date;
+
+ @override
+ int? getId() => this.id;
+
+ @override
+ Color getColor() => this.color;
+
+ @override
+ TextStyle? getTextStyle() => this.textStyle;
+
+ @override
+ // TODO: implement hashCode
+ int get hashCode => super.hashCode;
+}
+
+abstract class MarkedDateInterface {
+ DateTime getDate();
+ Color getColor();
+ int? getId();
+ TextStyle? getTextStyle();
+}
diff --git a/packages/flutter_calendar_carousel/lib/classes/multiple_marked_dates.dart b/packages/flutter_calendar_carousel/lib/classes/multiple_marked_dates.dart
new file mode 100644
index 0000000..3934549
--- /dev/null
+++ b/packages/flutter_calendar_carousel/lib/classes/multiple_marked_dates.dart
@@ -0,0 +1,86 @@
+import 'marked_date.dart';
+import 'package:flutter/material.dart';
+
+class MultipleMarkedDates {
+ List<MarkedDate> markedDates;
+
+ MultipleMarkedDates({required this.markedDates});
+
+ void add(MarkedDate markedDate) {
+ markedDates.add(markedDate);
+ }
+
+ void addRange(MarkedDate markedDate, {int plus = 0, int minus = 0}) {
+ this.add(markedDate);
+
+ if (plus > 0) {
+ int start = 1;
+ MarkedDate newAddMarkedDate;
+
+ while (start <= plus) {
+ newAddMarkedDate = new MarkedDate(
+ color: markedDate.color,
+ date: markedDate.date.add(Duration(days: start)),
+ textStyle: markedDate.textStyle,
+ );
+
+ this.add(newAddMarkedDate);
+
+ start += 1;
+ }
+ }
+
+ if (minus > 0) {
+ int start = 1;
+ MarkedDate newSubMarkedDate;
+
+ while (start <= minus) {
+ newSubMarkedDate = new MarkedDate(
+ color: markedDate.color,
+ date: markedDate.date.subtract(Duration(days: start)),
+ textStyle: markedDate.textStyle,
+ );
+
+ this.add(newSubMarkedDate);
+
+ start += 1;
+ }
+ }
+ }
+
+ void addAll(List<MarkedDate> markedDates) {
+ this.markedDates.addAll(markedDates);
+ }
+
+ bool remove(MarkedDate markedDate) {
+ return markedDates.remove(markedDate);
+ }
+
+ void clear() {
+ markedDates.clear();
+ }
+
+ bool isMarked(DateTime date) {
+ final results = markedDates.firstWhere((element) => element.date == date,
+ orElse: () => MarkedDate(color: Colors.black, date: DateTime(0)));
+ return results.date.year == date.year;
+ }
+
+ Color getColor(DateTime date) {
+ final results = markedDates.firstWhere((element) => element.date == date,
+ orElse: () => MarkedDate(color: Colors.black, date: DateTime(0)));
+ return results.color;
+ }
+
+ DateTime getDate(DateTime date) {
+ final results = markedDates.firstWhere((element) => element.date == date,
+ orElse: () => MarkedDate(color: Colors.black, date: DateTime(0)));
+ return results.date;
+ }
+
+ TextStyle? getTextStyle(DateTime date) {
+ final results = markedDates.firstWhere((element) => element.date == date,
+ orElse: () => MarkedDate(color: Colors.black, date: DateTime(0)));
+ return results.textStyle;
+ }
+}
diff --git a/packages/flutter_calendar_carousel/lib/flutter_calendar_carousel.dart b/packages/flutter_calendar_carousel/lib/flutter_calendar_carousel.dart
new file mode 100644
index 0000000..14e6cd8
--- /dev/null
+++ b/packages/flutter_calendar_carousel/lib/flutter_calendar_carousel.dart
@@ -0,0 +1,1214 @@
+library flutter_calendar_dooboo;
+
+import 'dart:async';
+
+import 'package:flutter/material.dart';
+import 'package:flutter_calendar_carousel/classes/event.dart';
+import 'package:flutter_calendar_carousel/classes/event_list.dart';
+import 'package:flutter_calendar_carousel/src/calendar_header.dart';
+import 'package:flutter_calendar_carousel/src/default_styles.dart';
+import 'package:flutter_calendar_carousel/src/weekday_row.dart';
+import 'package:intl/date_symbol_data_local.dart';
+import 'package:intl/intl.dart' show DateFormat;
+
+import 'classes/multiple_marked_dates.dart';
+
+export 'package:flutter_calendar_carousel/classes/event_list.dart';
+
+typedef MarkedDateIconBuilder<T> = Widget? Function(T event);
+typedef void OnDayLongPressed(DateTime day);
+
+/// This builder is called for every day in the calendar.
+/// If you want to build only few custom day containers, return null for the days you want to leave with default looks
+/// All characteristics like circle border are also applied to the custom day container [DayBuilder] provides.
+/// (if supplied function returns null, Calendar's function will be called for [day]).
+/// [isSelectable] - is between [CalendarCarousel.minSelectedDate] and [CalendarCarousel.maxSelectedDate]
+/// [index] - DOES NOT equal day number! Index of the day built in current visible field
+/// [isSelectedDay] - if the day is selected
+/// [isToday] - if the day is similar to [DateTime.now()]
+/// [isPrevMonthDay] - if the day is from previous month
+/// [textStyle] - text style that would have been applied by the calendar if it was to build the day.
+/// Example: if the user provided [CalendarCarousel.todayTextStyle] and [isToday] is true,
+/// [CalendarCarousel.todayTextStyle] would be sent into [DayBuilder]'s [textStyle]. If user didn't
+/// provide it, default [CalendarCarousel]'s textStyle would be sent. Same applies to all text styles like
+/// [CalendarCarousel.prevDaysTextStyle], [CalendarCarousel.daysTextStyle] etc.
+/// [isNextMonthDay] - if the day is from next month
+/// [isThisMonthDay] - if the day is from next month
+/// [day] - day being built.
+typedef Widget? DayBuilder(
+ bool isSelectable,
+ int index,
+ bool isSelectedDay,
+ bool isToday,
+ bool isPrevMonthDay,
+ TextStyle textStyle,
+ bool isNextMonthDay,
+ bool isThisMonthDay,
+ DateTime day);
+
+/// This builder is called for every weekday container (7 times, from Mon to Sun).
+/// [weekday] - weekday built, from 0 to 6.
+/// [weekdayName] - string representation of the weekday (Mon, Tue, Wed, etc).
+typedef Widget WeekdayBuilder(int weekday, String weekdayName);
+
+class CalendarCarousel<T extends EventInterface> extends StatefulWidget {
+ final double viewportFraction;
+ final TextStyle? prevDaysTextStyle;
+ final TextStyle? daysTextStyle;
+ final TextStyle? nextDaysTextStyle;
+ final Color prevMonthDayBorderColor;
+ final Color thisMonthDayBorderColor;
+ final Color nextMonthDayBorderColor;
+ final double dayPadding;
+ final double height;
+ final double width;
+ final TextStyle? todayTextStyle;
+ final Color dayButtonColor;
+ final Color todayBorderColor;
+ final Color todayButtonColor;
+ final DateTime? selectedDateTime;
+ final DateTime? targetDateTime;
+ final TextStyle? selectedDayTextStyle;
+ final Color selectedDayButtonColor;
+ final Color selectedDayBorderColor;
+ final bool? daysHaveCircularBorder;
+ final bool disableDayPressed;
+ final Function(DateTime, List<T>)? onDayPressed;
+ final TextStyle? weekdayTextStyle;
+ final Color iconColor;
+ final TextStyle? headerTextStyle;
+ final String? headerText;
+ final TextStyle? weekendTextStyle;
+ final EventList<T>? markedDatesMap;
+
+ /// Change `makredDateWidget` when `markedDateShowIcon` is set to false.
+ final Widget? markedDateWidget;
+
+ /// Change `OutlinedBorder` when `markedDateShowIcon` is set to false.
+ final OutlinedBorder? markedDateCustomShapeBorder;
+
+ /// Change `TextStyle` when `markedDateShowIcon` is set to false.
+ final TextStyle? markedDateCustomTextStyle;
+
+ /// Icon will overlap the [Day] widget when `markedDateShowIcon` is set to true.
+ /// This will also make below parameters work.
+ final bool markedDateShowIcon;
+ final Color? markedDateIconBorderColor;
+ final int markedDateIconMaxShown;
+ final double markedDateIconMargin;
+ final double markedDateIconOffset;
+ final MarkedDateIconBuilder<T>? markedDateIconBuilder;
+
+ /// null - no indicator, true - show the total events, false - show the total of hidden events
+ final bool? markedDateMoreShowTotal;
+ final Decoration? markedDateMoreCustomDecoration;
+ final TextStyle? markedDateMoreCustomTextStyle;
+ final EdgeInsets headerMargin;
+ final double childAspectRatio;
+ final EdgeInsets weekDayMargin;
+ final EdgeInsets weekDayPadding;
+ final WeekdayBuilder? customWeekDayBuilder;
+ final DayBuilder? customDayBuilder;
+ final Color weekDayBackgroundColor;
+ final bool weekFormat;
+ final bool showWeekDays;
+ final bool showHeader;
+ final bool showHeaderButton;
+ final MultipleMarkedDates? multipleMarkedDates;
+ final Widget? leftButtonIcon;
+ final Widget? rightButtonIcon;
+ final ScrollPhysics? customGridViewPhysics;
+ final Function(DateTime)? onCalendarChanged;
+ final String locale;
+ final int? firstDayOfWeek;
+ final DateTime? minSelectedDate;
+ final DateTime? maxSelectedDate;
+ final TextStyle? inactiveDaysTextStyle;
+ final TextStyle? inactiveWeekendTextStyle;
+ final bool headerTitleTouchable;
+ final Function? onHeaderTitlePressed;
+ final Function? onLeftArrowPressed;
+ final Function? onRightArrowPressed;
+ final WeekdayFormat weekDayFormat;
+ final bool staticSixWeekFormat;
+ final bool isScrollable;
+ final Axis scrollDirection;
+ final bool showOnlyCurrentMonthDate;
+ final bool pageSnapping;
+ final OnDayLongPressed? onDayLongPressed;
+ final CrossAxisAlignment dayCrossAxisAlignment;
+ final MainAxisAlignment dayMainAxisAlignment;
+ final bool showIconBehindDayText;
+ final ScrollPhysics pageScrollPhysics;
+ final bool shouldShowTransform;
+
+ CalendarCarousel({
+ Key? key,
+ this.viewportFraction = 1.0,
+ this.prevDaysTextStyle,
+ this.daysTextStyle,
+ this.nextDaysTextStyle,
+ this.prevMonthDayBorderColor = Colors.transparent,
+ this.thisMonthDayBorderColor = Colors.transparent,
+ this.nextMonthDayBorderColor = Colors.transparent,
+ this.dayPadding = 2.0,
+ this.height = double.infinity,
+ this.width = double.infinity,
+ this.todayTextStyle,
+ this.dayButtonColor = Colors.transparent,
+ this.todayBorderColor = Colors.red,
+ this.todayButtonColor = Colors.red,
+ this.selectedDateTime,
+ this.targetDateTime,
+ this.selectedDayTextStyle,
+ this.selectedDayBorderColor = Colors.green,
+ this.selectedDayButtonColor = Colors.green,
+ this.daysHaveCircularBorder,
+ this.disableDayPressed = false,
+ this.onDayPressed,
+ this.weekdayTextStyle = const TextStyle(),
+ this.iconColor = Colors.blueAccent,
+ this.headerTextStyle,
+ this.headerText,
+ this.weekendTextStyle,
+ this.markedDatesMap,
+ this.markedDateShowIcon = false,
+ this.markedDateIconBorderColor,
+ this.markedDateIconMaxShown = 2,
+ this.markedDateIconMargin = 5.0,
+ this.markedDateIconOffset = 5.0,
+ this.markedDateIconBuilder,
+ this.markedDateMoreShowTotal,
+ this.markedDateMoreCustomDecoration,
+ this.markedDateCustomShapeBorder,
+ this.markedDateCustomTextStyle,
+ this.markedDateMoreCustomTextStyle,
+ this.markedDateWidget,
+ this.multipleMarkedDates,
+ this.headerMargin = const EdgeInsets.symmetric(vertical: 16.0),
+ this.childAspectRatio = 1.0,
+ this.weekDayMargin = const EdgeInsets.only(bottom: 4.0),
+ this.weekDayPadding = const EdgeInsets.all(0.0),
+ this.weekDayBackgroundColor = Colors.transparent,
+ this.customWeekDayBuilder,
+ this.customDayBuilder,
+ this.showWeekDays = true,
+ this.weekFormat = false,
+ this.showHeader = true,
+ this.showHeaderButton = true,
+ this.leftButtonIcon,
+ this.rightButtonIcon,
+ this.customGridViewPhysics,
+ this.onCalendarChanged,
+ this.locale = "en",
+ this.firstDayOfWeek,
+ this.minSelectedDate,
+ this.maxSelectedDate,
+ this.inactiveDaysTextStyle,
+ this.inactiveWeekendTextStyle,
+ this.headerTitleTouchable = false,
+ this.onHeaderTitlePressed,
+ this.onLeftArrowPressed,
+ this.onRightArrowPressed,
+ this.weekDayFormat = WeekdayFormat.short,
+ this.staticSixWeekFormat = false,
+ this.isScrollable = true,
+ this.scrollDirection = Axis.horizontal,
+ this.showOnlyCurrentMonthDate = false,
+ this.pageSnapping = false,
+ this.onDayLongPressed,
+ this.dayCrossAxisAlignment = CrossAxisAlignment.center,
+ this.dayMainAxisAlignment = MainAxisAlignment.center,
+ this.showIconBehindDayText = false,
+ this.pageScrollPhysics = const ScrollPhysics(),
+ this.shouldShowTransform = true,
+ }) : super(key: key);
+
+ @override
+ _CalendarState<T> createState() => _CalendarState<T>();
+}
+
+enum WeekdayFormat {
+ weekdays,
+ standalone,
+ short,
+ standaloneShort,
+ narrow,
+ standaloneNarrow,
+}
+
+class _CalendarState<T extends EventInterface>
+ extends State<CalendarCarousel<T>> {
+ late PageController _controller;
+ late List<DateTime> _dates;
+ late List<List<DateTime>> _weeks;
+ DateTime _selectedDate = DateTime.now();
+ late DateTime _targetDate;
+ int _startWeekday = 0;
+ int _endWeekday = 0;
+ late DateFormat _localeDate;
+ int _pageNum = 0;
+ late DateTime minDate;
+ late DateTime maxDate;
+
+ /// When FIRSTDAYOFWEEK is 0 in dart-intl, it represents Monday. However it is the second day in the arrays of Weekdays.
+ /// Therefore we need to add 1 modulo 7 to pick the right weekday from intl. (cf. [GlobalMaterialLocalizations])
+ late int firstDayOfWeek;
+
+ /// If the setState called from this class, don't reload the selectedDate, but it should reload selected date if called from external class
+
+ @override
+ initState() {
+ super.initState();
+ initializeDateFormatting();
+
+ minDate = widget.minSelectedDate ?? DateTime(2018);
+ maxDate = widget.maxSelectedDate ??
+ DateTime(
+ DateTime.now().year + 1, DateTime.now().month, DateTime.now().day);
+
+ final selectedDateTime = widget.selectedDateTime;
+ if (selectedDateTime != null) _selectedDate = selectedDateTime;
+
+ _init();
+
+ /// setup pageController
+ _controller = PageController(
+ initialPage: this._pageNum,
+ keepPage: true,
+ viewportFraction: widget.viewportFraction,
+
+ /// width percentage
+ );
+
+ _localeDate = DateFormat.yMMM(widget.locale);
+ firstDayOfWeek = widget.firstDayOfWeek ??
+ (_localeDate.dateSymbols.FIRSTDAYOFWEEK + 1) % 7;
+
+ _setDate();
+ }
+
+ @override
+ void didUpdateWidget(CalendarCarousel<T> oldWidget) {
+ if (widget.targetDateTime != null && widget.targetDateTime != _targetDate) {
+ _init();
+ _setDate(_pageNum);
+ }
+
+ super.didUpdateWidget(oldWidget);
+ }
+
+ @override
+ dispose() {
+ _controller.dispose();
+ super.dispose();
+ }
+
+ _init() {
+ final targetDateTime = widget.targetDateTime;
+ if (targetDateTime != null) {
+ if (targetDateTime.difference(minDate).inDays < 0) {
+ _targetDate = minDate;
+ } else if (targetDateTime.difference(maxDate).inDays > 0) {
+ _targetDate = maxDate;
+ } else {
+ _targetDate = targetDateTime;
+ }
+ } else {
+ _targetDate = _selectedDate;
+ }
+ if (widget.weekFormat) {
+ _pageNum = _targetDate.difference(_firstDayOfWeek(minDate)).inDays ~/ 7;
+ } else {
+ _pageNum = (_targetDate.year - minDate.year) * 12 +
+ _targetDate.month -
+ minDate.month;
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final headerText = widget.headerText;
+ return Container(
+ width: widget.width,
+ height: widget.height,
+ child: Column(
+ children: <Widget>[
+ CalendarHeader(
+ showHeader: widget.showHeader,
+ headerMargin: widget.headerMargin,
+ headerTitle: headerText != null
+ ? headerText
+ : widget.weekFormat
+ ? '${_localeDate.format(this._weeks[this._pageNum].first)}'
+ : '${_localeDate.format(this._dates[this._pageNum])}',
+ headerTextStyle: widget.headerTextStyle,
+ showHeaderButtons: widget.showHeaderButton,
+ headerIconColor: widget.iconColor,
+ leftButtonIcon: widget.leftButtonIcon,
+ rightButtonIcon: widget.rightButtonIcon,
+ onLeftButtonPressed: () {
+ widget.onLeftArrowPressed?.call();
+
+ if (this._pageNum > 0) _setDate(this._pageNum - 1);
+ },
+ onRightButtonPressed: () {
+ widget.onRightArrowPressed?.call();
+
+ if (widget.weekFormat) {
+ if (this._weeks.length - 1 > this._pageNum)
+ _setDate(this._pageNum + 1);
+ } else {
+ if (this._dates.length - 1 > this._pageNum)
+ _setDate(this._pageNum + 1);
+ }
+ },
+ onHeaderTitlePressed: widget.headerTitleTouchable
+ ? () {
+ final onHeaderTitlePressed = widget.onHeaderTitlePressed;
+ if (onHeaderTitlePressed != null) {
+ onHeaderTitlePressed();
+ } else {
+ _selectDateFromPicker();
+ }
+ }
+ : null,
+ ),
+ WeekdayRow(
+ firstDayOfWeek,
+ widget.customWeekDayBuilder,
+ showWeekdays: widget.showWeekDays,
+ weekdayFormat: widget.weekDayFormat,
+ weekdayMargin: widget.weekDayMargin,
+ weekdayPadding: widget.weekDayPadding,
+ weekdayBackgroundColor: widget.weekDayBackgroundColor,
+ weekdayTextStyle: widget.weekdayTextStyle,
+ localeDate: _localeDate,
+ ),
+ Expanded(
+ child: PageView.builder(
+ itemCount:
+ widget.weekFormat ? this._weeks.length : this._dates.length,
+ physics: widget.isScrollable
+ ? widget.pageScrollPhysics
+ : NeverScrollableScrollPhysics(),
+ scrollDirection: widget.scrollDirection,
+ onPageChanged: (index) {
+ this._setDate(index);
+ },
+ controller: _controller,
+ itemBuilder: (context, index) {
+ return widget.weekFormat ? weekBuilder(index) : builder(index);
+ },
+ pageSnapping: widget.pageSnapping,
+ )),
+ ],
+ ),
+ );
+ }
+
+ Widget getDefaultDayContainer(
+ bool isSelectable,
+ int index,
+ bool isSelectedDay,
+ bool isToday,
+ bool isPrevMonthDay,
+ TextStyle? textStyle,
+ TextStyle defaultTextStyle,
+ bool isNextMonthDay,
+ bool isThisMonthDay,
+ DateTime now,
+ ) {
+ return Container(
+ width: double.infinity,
+ height: double.infinity,
+ child: Row(
+ crossAxisAlignment: widget.dayCrossAxisAlignment,
+ mainAxisAlignment: widget.dayMainAxisAlignment,
+ children: <Widget>[
+ DefaultTextStyle(
+ style: getDefaultDayStyle(
+ isSelectable,
+ index,
+ isSelectedDay,
+ isToday,
+ isPrevMonthDay,
+ textStyle,
+ defaultTextStyle,
+ isNextMonthDay,
+ isThisMonthDay),
+ child: Text(
+ '${now.day}',
+ semanticsLabel: now.day.toString(),
+ style: getDayStyle(
+ isSelectable,
+ index,
+ isSelectedDay,
+ isToday,
+ isPrevMonthDay,
+ textStyle,
+ defaultTextStyle,
+ isNextMonthDay,
+ isThisMonthDay,
+ now),
+ maxLines: 1,
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+
+ Widget renderDay(
+ bool isSelectable,
+ int index,
+ bool isSelectedDay,
+ bool isToday,
+ bool isPrevMonthDay,
+ TextStyle? textStyle,
+ TextStyle defaultTextStyle,
+ bool isNextMonthDay,
+ bool isThisMonthDay,
+ DateTime now,
+ ) {
+ // If day is in Multiple selection mode, get its color
+ bool isMultipleMarked = widget.multipleMarkedDates?.isMarked(now) ?? false;
+ Color? multipleMarkedColor = widget.multipleMarkedDates?.getColor(now);
+
+ final markedDatesMap = widget.markedDatesMap;
+ return Container(
+ margin: EdgeInsets.all(widget.dayPadding),
+ child: GestureDetector(
+ onLongPress: () => _onDayLongPressed(now),
+ child: TextButton(
+ style: TextButton.styleFrom(
+ shape: widget.markedDateCustomShapeBorder != null &&
+ markedDatesMap != null &&
+ markedDatesMap.getEvents(now).length > 0
+ ? widget.markedDateCustomShapeBorder
+ : widget.daysHaveCircularBorder == null
+ ? CircleBorder()
+ : widget.daysHaveCircularBorder ?? false
+ ? CircleBorder(
+ side: BorderSide(
+ color: isSelectedDay
+ ? widget.selectedDayBorderColor
+ : isToday
+ ? widget.todayBorderColor
+ : isPrevMonthDay
+ ? widget.prevMonthDayBorderColor
+ : isNextMonthDay
+ ? widget.nextMonthDayBorderColor
+ : widget.thisMonthDayBorderColor,
+ ),
+ )
+ : RoundedRectangleBorder(
+ side: BorderSide(
+ color: isSelectedDay
+ ? widget.selectedDayBorderColor
+ : isToday
+ ? widget.todayBorderColor
+ : isPrevMonthDay
+ ? widget.prevMonthDayBorderColor
+ : isNextMonthDay
+ ? widget.nextMonthDayBorderColor
+ : widget.thisMonthDayBorderColor,
+ ),
+ ),
+ backgroundColor: isSelectedDay
+ ? widget.selectedDayButtonColor
+ : isToday
+ ? widget.todayButtonColor
+
+ // If day is in Multiple selection mode, apply a different color
+ : isMultipleMarked
+ ? multipleMarkedColor
+ : widget.dayButtonColor,
+ padding: EdgeInsets.all(widget.dayPadding),
+ ),
+ onPressed: widget.disableDayPressed ? null : () => _onDayPressed(now),
+ child: Stack(
+ children: widget.showIconBehindDayText
+ ? <Widget>[
+ widget.markedDatesMap != null
+ ? _renderMarkedMapContainer(now)
+ : Container(),
+ getDayContainer(
+ isSelectable,
+ index,
+ isSelectedDay,
+ isToday,
+ isPrevMonthDay,
+ textStyle,
+ defaultTextStyle,
+ isNextMonthDay,
+ isThisMonthDay,
+ now),
+ ]
+ : <Widget>[
+ getDayContainer(
+ isSelectable,
+ index,
+ isSelectedDay,
+ isToday,
+ isPrevMonthDay,
+ textStyle,
+ defaultTextStyle,
+ isNextMonthDay,
+ isThisMonthDay,
+ now),
+ widget.markedDatesMap != null
+ ? _renderMarkedMapContainer(now)
+ : Container(),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+
+ AnimatedBuilder builder(int slideIndex) {
+ _startWeekday = _dates[slideIndex].weekday - firstDayOfWeek;
+ if (_startWeekday == 7) {
+ _startWeekday = 0;
+ }
+ _endWeekday =
+ DateTime(_dates[slideIndex].year, _dates[slideIndex].month + 1, 1)
+ .weekday -
+ firstDayOfWeek;
+ double screenWidth = MediaQuery.of(context).size.width;
+ int totalItemCount = widget.staticSixWeekFormat
+ ? 42
+ : DateTime(
+ _dates[slideIndex].year,
+ _dates[slideIndex].month + 1,
+ 0,
+ ).day +
+ _startWeekday +
+ (7 - _endWeekday);
+ int year = _dates[slideIndex].year;
+ int month = _dates[slideIndex].month;
+
+ return AnimatedBuilder(
+ animation: _controller,
+ builder: (context, child) {
+ if (!widget.shouldShowTransform) {
+ return child!;
+ }
+ double value = 1.0;
+ if (_controller.position.haveDimensions) {
+ value = _controller.page! - slideIndex;
+ value = (1 - (value.abs() * .5)).clamp(0.0, 1.0);
+ }
+
+ return Center(
+ child: SizedBox(
+ height: Curves.easeOut.transform(value) * widget.height,
+ width: Curves.easeOut.transform(value) * screenWidth,
+ child: child,
+ ),
+ );
+ },
+ child: Stack(
+ children: <Widget>[
+ Positioned(
+ child: Container(
+ width: double.infinity,
+ height: double.infinity,
+ child: GridView.count(
+ physics: widget.customGridViewPhysics,
+ crossAxisCount: 7,
+ childAspectRatio: widget.childAspectRatio,
+ padding: EdgeInsets.zero,
+ children: List.generate(totalItemCount,
+
+ /// last day of month + weekday
+ (index) {
+ final selectedDateTime = widget.selectedDateTime;
+ bool isToday =
+ DateTime.now().day == index + 1 - _startWeekday &&
+ DateTime.now().month == month &&
+ DateTime.now().year == year;
+ bool isSelectedDay = selectedDateTime != null &&
+ selectedDateTime.year == year &&
+ selectedDateTime.month == month &&
+ selectedDateTime.day == index + 1 - _startWeekday;
+ bool isPrevMonthDay = index < _startWeekday;
+ bool isNextMonthDay = index >=
+ (DateTime(year, month + 1, 0).day) + _startWeekday;
+ bool isThisMonthDay = !isPrevMonthDay && !isNextMonthDay;
+
+ DateTime now = DateTime(year, month, 1);
+ TextStyle? textStyle;
+ TextStyle defaultTextStyle;
+ if (isPrevMonthDay && !widget.showOnlyCurrentMonthDate) {
+ now = now.subtract(Duration(days: _startWeekday - index));
+ textStyle = widget.prevDaysTextStyle;
+ defaultTextStyle = defaultPrevDaysTextStyle;
+ } else if (isThisMonthDay) {
+ now = DateTime(year, month, index + 1 - _startWeekday);
+ textStyle = isSelectedDay
+ ? widget.selectedDayTextStyle
+ : isToday
+ ? widget.todayTextStyle
+ : widget.daysTextStyle;
+ defaultTextStyle = isSelectedDay
+ ? defaultSelectedDayTextStyle
+ : isToday
+ ? defaultTodayTextStyle
+ : defaultDaysTextStyle;
+ } else if (!widget.showOnlyCurrentMonthDate) {
+ now = DateTime(year, month, index + 1 - _startWeekday);
+ textStyle = widget.nextDaysTextStyle;
+ defaultTextStyle = defaultNextDaysTextStyle;
+ } else {
+ return Container();
+ }
+ final markedDatesMap = widget.markedDatesMap;
+ if (widget.markedDateCustomTextStyle != null &&
+ markedDatesMap != null &&
+ markedDatesMap.getEvents(now).length > 0) {
+ textStyle = widget.markedDateCustomTextStyle;
+ }
+ bool isSelectable = true;
+ if (now.millisecondsSinceEpoch <
+ minDate.millisecondsSinceEpoch)
+ isSelectable = false;
+ else if (now.millisecondsSinceEpoch >
+ maxDate.millisecondsSinceEpoch) isSelectable = false;
+ return renderDay(
+ isSelectable,
+ index,
+ isSelectedDay,
+ isToday,
+ isPrevMonthDay,
+ textStyle,
+ defaultTextStyle,
+ isNextMonthDay,
+ isThisMonthDay,
+ now);
+ }),
+ ),
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+
+ AnimatedBuilder weekBuilder(int slideIndex) {
+ double screenWidth = MediaQuery.of(context).size.width;
+ List<DateTime> weekDays = _weeks[slideIndex];
+
+ weekDays = weekDays
+ .map((weekDay) => weekDay.add(Duration(days: firstDayOfWeek)))
+ .toList();
+
+ return AnimatedBuilder(
+ animation: _controller,
+ builder: (context, child) {
+ double value = 1.0;
+ if (_controller.position.haveDimensions) {
+ value = _controller.page! - slideIndex;
+ value = (1 - (value.abs() * .5)).clamp(0.0, 1.0);
+ }
+
+ return Center(
+ child: SizedBox(
+ height: Curves.easeOut.transform(value) * widget.height,
+ width: Curves.easeOut.transform(value) * screenWidth,
+ child: child,
+ ),
+ );
+ },
+ child: Stack(
+ children: <Widget>[
+ Positioned(
+ child: Container(
+ width: double.infinity,
+ height: double.infinity,
+ child: GridView.count(
+ physics: widget.customGridViewPhysics,
+ crossAxisCount: 7,
+ childAspectRatio: widget.childAspectRatio,
+ padding: EdgeInsets.zero,
+ children: List.generate(weekDays.length, (index) {
+ /// last day of month + weekday
+ bool isToday = weekDays[index].day == DateTime.now().day &&
+ weekDays[index].month == DateTime.now().month &&
+ weekDays[index].year == DateTime.now().year;
+ bool isSelectedDay =
+ this._selectedDate.year == weekDays[index].year &&
+ this._selectedDate.month == weekDays[index].month &&
+ this._selectedDate.day == weekDays[index].day;
+ bool isPrevMonthDay =
+ weekDays[index].month < this._targetDate.month;
+ bool isNextMonthDay =
+ weekDays[index].month > this._targetDate.month;
+ bool isThisMonthDay = !isPrevMonthDay && !isNextMonthDay;
+
+ DateTime now = DateTime(weekDays[index].year,
+ weekDays[index].month, weekDays[index].day);
+ TextStyle? textStyle;
+ TextStyle defaultTextStyle;
+ if (isPrevMonthDay && !widget.showOnlyCurrentMonthDate) {
+ textStyle = widget.prevDaysTextStyle;
+ defaultTextStyle = defaultPrevDaysTextStyle;
+ } else if (isThisMonthDay) {
+ textStyle = isSelectedDay
+ ? widget.selectedDayTextStyle
+ : isToday
+ ? widget.todayTextStyle
+ : widget.daysTextStyle;
+ defaultTextStyle = isSelectedDay
+ ? defaultSelectedDayTextStyle
+ : isToday
+ ? defaultTodayTextStyle
+ : defaultDaysTextStyle;
+ } else if (!widget.showOnlyCurrentMonthDate) {
+ textStyle = widget.nextDaysTextStyle;
+ defaultTextStyle = defaultNextDaysTextStyle;
+ } else {
+ return Container();
+ }
+ bool isSelectable = true;
+ if (now.millisecondsSinceEpoch <
+ minDate.millisecondsSinceEpoch)
+ isSelectable = false;
+ else if (now.millisecondsSinceEpoch >
+ maxDate.millisecondsSinceEpoch) isSelectable = false;
+ return renderDay(
+ isSelectable,
+ index,
+ isSelectedDay,
+ isToday,
+ isPrevMonthDay,
+ textStyle,
+ defaultTextStyle,
+ isNextMonthDay,
+ isThisMonthDay,
+ now);
+ }),
+ ),
+ ),
+ ),
+ ],
+ ));
+ }
+
+ List<DateTime> _getDaysInWeek([DateTime? selectedDate]) {
+ if (selectedDate == null) selectedDate = new DateTime.now();
+
+ var firstDayOfCurrentWeek = _firstDayOfWeek(selectedDate);
+ var lastDayOfCurrentWeek = _lastDayOfWeek(selectedDate);
+
+ return _daysInRange(firstDayOfCurrentWeek, lastDayOfCurrentWeek).toList();
+ }
+
+ DateTime _firstDayOfWeek(DateTime date) {
+ var day = _createUTCMiddayDateTime(date);
+ return day.subtract(new Duration(days: date.weekday % 7));
+ }
+
+ DateTime _lastDayOfWeek(DateTime date) {
+ var day = _createUTCMiddayDateTime(date);
+ return day.add(new Duration(days: 7 - day.weekday % 7));
+ }
+
+ DateTime _createUTCMiddayDateTime(DateTime date) {
+ // Magic const: 12 is to maintain compatibility with date_utils
+ return new DateTime.utc(date.year, date.month, date.day, 12, 0, 0);
+ }
+
+ Iterable<DateTime> _daysInRange(DateTime start, DateTime end) {
+ var offset = start.timeZoneOffset;
+
+ return List<int>.generate(end.difference(start).inDays, (i) => i + 1)
+ .map((int i) {
+ var d = start.add(Duration(days: i - 1));
+
+ var timeZoneDiff = d.timeZoneOffset - offset;
+ if (timeZoneDiff.inSeconds != 0) {
+ offset = d.timeZoneOffset;
+ d = d.subtract(new Duration(seconds: timeZoneDiff.inSeconds));
+ }
+ return d;
+ });
+ }
+
+ void _onDayLongPressed(DateTime picked) {
+ widget.onDayLongPressed?.call(picked);
+ }
+
+ void _onDayPressed(DateTime picked) {
+ if (picked.millisecondsSinceEpoch < minDate.millisecondsSinceEpoch) return;
+ if (picked.millisecondsSinceEpoch > maxDate.millisecondsSinceEpoch) return;
+
+ setState(() {
+ _selectedDate = picked;
+ });
+ widget.onDayPressed
+ ?.call(picked, widget.markedDatesMap?.getEvents(picked) ?? const []);
+ }
+
+ Future<Null> _selectDateFromPicker() async {
+ DateTime? selected = await showDatePicker(
+ context: context,
+ initialDate: _selectedDate,
+ firstDate: minDate,
+ lastDate: maxDate,
+ );
+
+ if (selected != null) {
+ // updating selected date range based on selected week
+ setState(() {
+ _selectedDate = selected;
+ });
+ widget.onDayPressed?.call(
+ selected, widget.markedDatesMap?.getEvents(selected) ?? const []);
+ }
+ }
+
+ void _setDatesAndWeeks() {
+ /// Setup default calendar format
+ List<DateTime> date = [];
+ int currentDateIndex = 0;
+ for (int _cnt = 0;
+ 0 >=
+ DateTime(minDate.year, minDate.month + _cnt)
+ .difference(DateTime(maxDate.year, maxDate.month))
+ .inDays;
+ _cnt++) {
+ date.add(DateTime(minDate.year, minDate.month + _cnt, 1));
+ if (0 ==
+ date.last
+ .difference(
+ DateTime(this._targetDate.year, this._targetDate.month))
+ .inDays) {
+ currentDateIndex = _cnt;
+ }
+ }
+
+ /// Setup week-only format
+ List<List<DateTime>> week = [];
+ for (int _cnt = 0;
+ 0 >=
+ minDate
+ .add(Duration(days: 7 * _cnt))
+ .difference(maxDate.add(Duration(days: 7)))
+ .inDays;
+ _cnt++) {
+ week.add(_getDaysInWeek(minDate.add(new Duration(days: 7 * _cnt))));
+ }
+
+ _startWeekday = date[currentDateIndex].weekday - firstDayOfWeek;
+ /*if (widget.showOnlyCurrentMonthDate) {
+ _startWeekday--;
+ }*/
+ if (/*widget.showOnlyCurrentMonthDate && */ _startWeekday == 7) {
+ _startWeekday = 0;
+ }
+ _endWeekday = DateTime(date[currentDateIndex].year,
+ date[currentDateIndex].month + 1, 1)
+ .weekday -
+ firstDayOfWeek;
+ this._dates = date;
+ this._weeks = week;
+// this._selectedDate = widget.selectedDateTime != null
+// ? widget.selectedDateTime
+// : DateTime.now();
+ }
+
+ void _setDate([int page = -1]) {
+ if (page == -1) {
+ setState(() {
+ _setDatesAndWeeks();
+ });
+ } else {
+ if (widget.weekFormat) {
+ setState(() {
+ this._pageNum = page;
+ this._targetDate = this._weeks[page].first;
+ });
+
+ _controller.animateToPage(page,
+ duration: Duration(milliseconds: 1), curve: Threshold(0.0));
+ } else {
+ setState(() {
+ this._pageNum = page;
+ this._targetDate = this._dates[page];
+ _startWeekday = _dates[page].weekday - firstDayOfWeek;
+ _endWeekday = _lastDayOfWeek(_dates[page]).weekday - firstDayOfWeek;
+ });
+ _controller.animateToPage(page,
+ duration: Duration(milliseconds: 1), curve: Threshold(0.0));
+ }
+
+ //call callback
+ final onCalendarChanged = widget.onCalendarChanged;
+ if (onCalendarChanged != null) {
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ onCalendarChanged(!widget.weekFormat
+ ? this._dates[page]
+ : this._weeks[page][firstDayOfWeek]);
+ });
+ }
+ }
+ }
+
+ Widget _renderMarkedMapContainer(DateTime now) {
+ if (widget.markedDateShowIcon) {
+ return Stack(
+ children: _renderMarkedMap(now),
+ );
+ } else {
+ return Container(
+ height: double.infinity,
+ padding: EdgeInsets.only(bottom: 4.0),
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.end,
+ mainAxisSize: MainAxisSize.max,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: _renderMarkedMap(now),
+ ),
+ );
+ }
+ }
+
+ List<Widget> _renderMarkedMap(DateTime now) {
+ final markedEvents = widget.markedDatesMap?.getEvents(now) ?? [];
+ final markedDateIconBuilder = widget.markedDateIconBuilder;
+ final markedDateWidget = widget.markedDateWidget;
+ final markedDateMoreShowTotal = widget.markedDateMoreShowTotal;
+ final markedDateMoreCustomTextStyle = widget.markedDateMoreCustomTextStyle;
+ final markedDateIconMargin = widget.markedDateIconMargin;
+ final markedDateShowIcon = widget.markedDateShowIcon;
+ final markedDateIconMaxShown = widget.markedDateIconMaxShown;
+ final markedDateIconOffset = widget.markedDateIconOffset;
+ final markedDateMoreCustomDecoration =
+ widget.markedDateMoreCustomDecoration;
+
+ if (markedEvents.length > 0) {
+ List<Widget> tmp = [];
+ int count = 0;
+ int eventIndex = 0;
+ double offset = 0.0;
+ double padding = markedDateIconMargin;
+ markedEvents.forEach((T event) {
+ if (markedDateShowIcon) {
+ if (tmp.length > 0 && tmp.length < markedDateIconMaxShown) {
+ offset += markedDateIconOffset;
+ }
+ if (tmp.length < markedDateIconMaxShown &&
+ markedDateIconBuilder != null) {
+ tmp.add(Center(
+ child: new Container(
+ padding: EdgeInsets.only(
+ top: padding + offset,
+ left: padding + offset,
+ right: padding - offset,
+ bottom: padding - offset,
+ ),
+ width: double.infinity,
+ height: double.infinity,
+ child: markedDateIconBuilder(event),
+ )));
+ } else {
+ count++;
+ }
+ if (count > 0 && markedDateMoreShowTotal != null) {
+ tmp.add(
+ Positioned(
+ bottom: 0.0,
+ right: 0.0,
+ child: Container(
+ padding: EdgeInsets.all(4.0),
+ width: markedDateMoreShowTotal ? 18 : null,
+ height: markedDateMoreShowTotal ? 18 : null,
+ decoration: markedDateMoreCustomDecoration == null
+ ? new BoxDecoration(
+ color: Colors.red,
+ borderRadius:
+ BorderRadius.all(Radius.circular(1000.0)),
+ )
+ : markedDateMoreCustomDecoration,
+ child: Center(
+ child: Text(
+ markedDateMoreShowTotal
+ ? (count + markedDateIconMaxShown).toString()
+ : (count.toString() + '+'),
+ semanticsLabel: markedDateMoreShowTotal
+ ? (count + markedDateIconMaxShown).toString()
+ : (count.toString() + '+'),
+ style: markedDateMoreCustomTextStyle == null
+ ? TextStyle(
+ fontSize: 9.0,
+ color: Colors.white,
+ fontWeight: FontWeight.normal)
+ : markedDateMoreCustomTextStyle,
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+ } else {
+ //max 5 dots
+ if (eventIndex < 5) {
+ Widget? widget;
+
+ if (markedDateIconBuilder != null) {
+ widget = markedDateIconBuilder(event);
+ }
+
+ if (widget != null) {
+ tmp.add(widget);
+ } else {
+ final dot = event.getDot();
+ if (dot != null) {
+ tmp.add(dot);
+ } else if (markedDateWidget != null) {
+ tmp.add(markedDateWidget);
+ } else {
+ tmp.add(defaultMarkedDateWidget);
+ }
+ }
+ }
+ }
+
+ eventIndex++;
+ });
+ return tmp;
+ }
+ return [];
+ }
+
+ TextStyle getDefaultDayStyle(
+ bool isSelectable,
+ int index,
+ bool isSelectedDay,
+ bool isToday,
+ bool isPrevMonthDay,
+ TextStyle? textStyle,
+ TextStyle defaultTextStyle,
+ bool isNextMonthDay,
+ bool isThisMonthDay,
+ ) {
+ return !isSelectable
+ ? defaultInactiveDaysTextStyle
+ : (_localeDate.dateSymbols.WEEKENDRANGE
+ .contains((index - 1 + firstDayOfWeek) % 7)) &&
+ !isSelectedDay &&
+ !isToday
+ ? (isPrevMonthDay
+ ? defaultPrevDaysTextStyle
+ : isNextMonthDay
+ ? defaultNextDaysTextStyle
+ : isSelectable
+ ? defaultWeekendTextStyle
+ : defaultInactiveWeekendTextStyle)
+ : isToday
+ ? defaultTodayTextStyle
+ : isSelectable && textStyle != null
+ ? textStyle
+ : defaultTextStyle;
+ }
+
+ TextStyle? getDayStyle(
+ bool isSelectable,
+ int index,
+ bool isSelectedDay,
+ bool isToday,
+ bool isPrevMonthDay,
+ TextStyle? textStyle,
+ TextStyle defaultTextStyle,
+ bool isNextMonthDay,
+ bool isThisMonthDay,
+ DateTime now) {
+ // If day is in multiple selection get its style(if available)
+ bool isMultipleMarked = widget.multipleMarkedDates?.isMarked(now) ?? false;
+ TextStyle? mutipleMarkedTextStyle =
+ widget.multipleMarkedDates?.getTextStyle(now);
+
+ return isSelectedDay && widget.selectedDayTextStyle != null
+ ? widget.selectedDayTextStyle
+ : isMultipleMarked
+ ? mutipleMarkedTextStyle
+ : (_localeDate.dateSymbols.WEEKENDRANGE
+ .contains((index - 1 + firstDayOfWeek) % 7)) &&
+ !isSelectedDay &&
+ isThisMonthDay &&
+ !isToday
+ ? (isSelectable
+ ? widget.weekendTextStyle
+ : widget.inactiveWeekendTextStyle)
+ : !isSelectable
+ ? widget.inactiveDaysTextStyle
+ : isPrevMonthDay
+ ? widget.prevDaysTextStyle
+ : isNextMonthDay
+ ? widget.nextDaysTextStyle
+ : isToday
+ ? widget.todayTextStyle
+ : widget.daysTextStyle;
+ }
+
+ Widget getDayContainer(
+ bool isSelectable,
+ int index,
+ bool isSelectedDay,
+ bool isToday,
+ bool isPrevMonthDay,
+ TextStyle? textStyle,
+ TextStyle defaultTextStyle,
+ bool isNextMonthDay,
+ bool isThisMonthDay,
+ DateTime now) {
+ final customDayBuilder = widget.customDayBuilder;
+
+ Widget? dayContainer;
+ if (customDayBuilder != null) {
+ final appTextStyle = DefaultTextStyle.of(context).style;
+
+ final dayStyle = getDayStyle(
+ isSelectable,
+ index,
+ isSelectedDay,
+ isToday,
+ isPrevMonthDay,
+ textStyle,
+ defaultTextStyle,
+ isNextMonthDay,
+ isThisMonthDay,
+ now,
+ );
+
+ final styleForBuilder = appTextStyle.merge(dayStyle);
+
+ dayContainer = customDayBuilder(
+ isSelectable,
+ index,
+ isSelectedDay,
+ isToday,
+ isPrevMonthDay,
+ styleForBuilder,
+ isNextMonthDay,
+ isThisMonthDay,
+ now);
+ }
+
+ return dayContainer ??
+ getDefaultDayContainer(
+ isSelectable,
+ index,
+ isSelectedDay,
+ isToday,
+ isPrevMonthDay,
+ textStyle,
+ defaultTextStyle,
+ isNextMonthDay,
+ isThisMonthDay,
+ now,
+ );
+ }
+}
diff --git a/packages/flutter_calendar_carousel/lib/src/calendar_header.dart b/packages/flutter_calendar_carousel/lib/src/calendar_header.dart
new file mode 100644
index 0000000..7fcfd81
--- /dev/null
+++ b/packages/flutter_calendar_carousel/lib/src/calendar_header.dart
@@ -0,0 +1,73 @@
+import 'package:flutter/material.dart';
+import 'default_styles.dart' show defaultHeaderTextStyle;
+
+class CalendarHeader extends StatelessWidget {
+ /// Passing in values for [leftButtonIcon] or [rightButtonIcon] will override [headerIconColor]
+ CalendarHeader(
+ {required this.headerTitle,
+ this.headerMargin,
+ required this.showHeader,
+ this.headerTextStyle,
+ this.showHeaderButtons = true,
+ this.headerIconColor,
+ this.leftButtonIcon,
+ this.rightButtonIcon,
+ required this.onLeftButtonPressed,
+ required this.onRightButtonPressed,
+ this.onHeaderTitlePressed})
+ : isTitleTouchable = onHeaderTitlePressed != null;
+
+ final String headerTitle;
+ final EdgeInsetsGeometry? headerMargin;
+ final bool showHeader;
+ final TextStyle? headerTextStyle;
+ final bool showHeaderButtons;
+ final Color? headerIconColor;
+ final Widget? leftButtonIcon;
+ final Widget? rightButtonIcon;
+ final VoidCallback onLeftButtonPressed;
+ final VoidCallback onRightButtonPressed;
+ final bool isTitleTouchable;
+ final VoidCallback? onHeaderTitlePressed;
+
+ TextStyle get getTextStyle => headerTextStyle ?? defaultHeaderTextStyle;
+
+ Widget _leftButton() => IconButton(
+ onPressed: onLeftButtonPressed,
+ icon:
+ leftButtonIcon ?? Icon(Icons.chevron_left, color: headerIconColor),
+ );
+
+ Widget _rightButton() => IconButton(
+ onPressed: onRightButtonPressed,
+ icon: rightButtonIcon ??
+ Icon(Icons.chevron_right, color: headerIconColor),
+ );
+
+ Widget _headerTouchable() => TextButton(
+ onPressed: onHeaderTitlePressed,
+ child: Text(
+ headerTitle,
+ semanticsLabel: headerTitle,
+ style: getTextStyle,
+ ),
+ );
+
+ @override
+ Widget build(BuildContext context) => showHeader
+ ? Container(
+ margin: headerMargin,
+ child: DefaultTextStyle(
+ style: getTextStyle,
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: <Widget>[
+ showHeaderButtons ? _leftButton() : Container(),
+ isTitleTouchable
+ ? _headerTouchable()
+ : Text(headerTitle, style: getTextStyle),
+ showHeaderButtons ? _rightButton() : Container(),
+ ])),
+ )
+ : Container();
+}
diff --git a/packages/flutter_calendar_carousel/lib/src/default_styles.dart b/packages/flutter_calendar_carousel/lib/src/default_styles.dart
new file mode 100644
index 0000000..cfeddc9
--- /dev/null
+++ b/packages/flutter_calendar_carousel/lib/src/default_styles.dart
@@ -0,0 +1,48 @@
+import 'package:flutter/material.dart';
+
+const TextStyle defaultHeaderTextStyle = const TextStyle(
+ fontSize: 20.0,
+ color: Colors.blue,
+);
+const TextStyle defaultPrevDaysTextStyle = const TextStyle(
+ color: Colors.grey,
+ fontSize: 14.0,
+);
+const TextStyle defaultNextDaysTextStyle = const TextStyle(
+ color: Colors.grey,
+ fontSize: 14.0,
+);
+const TextStyle defaultDaysTextStyle = const TextStyle(
+ color: Colors.black,
+ fontSize: 14.0,
+);
+const TextStyle defaultTodayTextStyle = const TextStyle(
+ color: Colors.white,
+ fontSize: 14.0,
+);
+const TextStyle defaultSelectedDayTextStyle = const TextStyle(
+ color: Colors.white,
+ fontSize: 14.0,
+);
+const TextStyle defaultWeekdayTextStyle = const TextStyle(
+ color: Colors.deepOrange,
+ fontSize: 14.0,
+);
+const TextStyle defaultWeekendTextStyle = const TextStyle(
+ color: Colors.pinkAccent,
+ fontSize: 14.0,
+);
+const TextStyle defaultInactiveDaysTextStyle = const TextStyle(
+ color: Colors.black38,
+ fontSize: 14.0,
+);
+final TextStyle defaultInactiveWeekendTextStyle = TextStyle(
+ color: Colors.pinkAccent.withOpacity(0.6),
+ fontSize: 14.0,
+);
+final Widget defaultMarkedDateWidget = Container(
+ margin: EdgeInsets.symmetric(horizontal: 1.0),
+ color: Colors.blueAccent,
+ height: 4.0,
+ width: 4.0,
+);
diff --git a/packages/flutter_calendar_carousel/lib/src/weekday_row.dart b/packages/flutter_calendar_carousel/lib/src/weekday_row.dart
new file mode 100644
index 0000000..48f5b3a
--- /dev/null
+++ b/packages/flutter_calendar_carousel/lib/src/weekday_row.dart
@@ -0,0 +1,131 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_calendar_carousel/flutter_calendar_carousel.dart';
+import 'package:flutter_calendar_carousel/src/default_styles.dart'
+ show defaultWeekdayTextStyle;
+import 'package:intl/intl.dart';
+
+class WeekdayRow extends StatelessWidget {
+ WeekdayRow(this.firstDayOfWeek, this.customWeekdayBuilder,
+ {required this.showWeekdays,
+ required this.weekdayFormat,
+ required this.weekdayMargin,
+ required this.weekdayPadding,
+ required this.weekdayBackgroundColor,
+ required this.weekdayTextStyle,
+ required this.localeDate});
+
+ final WeekdayBuilder? customWeekdayBuilder;
+ final bool showWeekdays;
+ final WeekdayFormat weekdayFormat;
+ final EdgeInsets weekdayMargin;
+ final EdgeInsets weekdayPadding;
+ final Color weekdayBackgroundColor;
+ final TextStyle? weekdayTextStyle;
+ final DateFormat localeDate;
+ final int firstDayOfWeek;
+
+ Widget _weekdayContainer(int weekday, String weekDayName) {
+ final customWeekdayBuilder = this.customWeekdayBuilder;
+ return customWeekdayBuilder != null
+ ? customWeekdayBuilder(weekday, weekDayName)
+ : Expanded(
+ child: Container(
+ decoration: BoxDecoration(
+ border: Border.all(color: weekdayBackgroundColor),
+ color: weekdayBackgroundColor,
+ ),
+ margin: weekdayMargin,
+ padding: weekdayPadding,
+ child: Center(
+ child: DefaultTextStyle(
+ style: defaultWeekdayTextStyle,
+ child: Text(
+ weekDayName,
+ semanticsLabel: weekDayName,
+ style: weekdayTextStyle,
+ ),
+ ),
+ ),
+ ));
+ }
+
+// List<Widget> _generateWeekdays() {
+// switch (weekdayFormat) {
+// case WeekdayFormat.weekdays:
+// return localeDate.dateSymbols.WEEKDAYS
+// .map<Widget>(_weekdayContainer)
+// .toList();
+// case WeekdayFormat.standalone:
+// return localeDate.dateSymbols.STANDALONEWEEKDAYS
+// .map<Widget>(_weekdayContainer)
+// .toList();
+// case WeekdayFormat.short:
+// return localeDate.dateSymbols.SHORTWEEKDAYS
+// .map<Widget>(_weekdayContainer)
+// .toList();
+// case WeekdayFormat.standaloneShort:
+// return localeDate.dateSymbols.STANDALONESHORTWEEKDAYS
+// .map<Widget>(_weekdayContainer)
+// .toList();
+// case WeekdayFormat.narrow:
+// return localeDate.dateSymbols.NARROWWEEKDAYS
+// .map<Widget>(_weekdayContainer)
+// .toList();
+// case WeekdayFormat.standaloneNarrow:
+// return localeDate.dateSymbols.STANDALONENARROWWEEKDAYS
+// .map<Widget>(_weekdayContainer)
+// .toList();
+// default:
+// return localeDate.dateSymbols.STANDALONEWEEKDAYS
+// .map<Widget>(_weekdayContainer)
+// .toList();
+// }
+// }
+
+ // TODO - locale issues
+ List<Widget> _renderWeekDays() {
+ List<Widget> list = [];
+
+ /// because of number of days in a week is 7, so it would be easier to count it til 7.
+ for (var i = firstDayOfWeek, count = 0;
+ count < 7;
+ i = (i + 1) % 7, count++) {
+ String weekDay;
+
+ switch (weekdayFormat) {
+ case WeekdayFormat.weekdays:
+ weekDay = localeDate.dateSymbols.WEEKDAYS[i];
+ break;
+ case WeekdayFormat.standalone:
+ weekDay = localeDate.dateSymbols.STANDALONEWEEKDAYS[i];
+ break;
+ case WeekdayFormat.short:
+ weekDay = localeDate.dateSymbols.SHORTWEEKDAYS[i];
+ break;
+ case WeekdayFormat.standaloneShort:
+ weekDay = localeDate.dateSymbols.STANDALONESHORTWEEKDAYS[i];
+ break;
+ case WeekdayFormat.narrow:
+ weekDay = localeDate.dateSymbols.NARROWWEEKDAYS[i];
+ break;
+ case WeekdayFormat.standaloneNarrow:
+ weekDay = localeDate.dateSymbols.STANDALONENARROWWEEKDAYS[i];
+ break;
+ default:
+ weekDay = localeDate.dateSymbols.STANDALONEWEEKDAYS[i];
+ break;
+ }
+ list.add(_weekdayContainer(count, weekDay));
+ }
+
+ return list;
+ }
+
+ @override
+ Widget build(BuildContext context) => showWeekdays
+ ? Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: _renderWeekDays(),
+ )
+ : Container();
+}
diff --git a/packages/flutter_calendar_carousel/pubspec.lock b/packages/flutter_calendar_carousel/pubspec.lock
new file mode 100644
index 0000000..b1a64dc
--- /dev/null
+++ b/packages/flutter_calendar_carousel/pubspec.lock
@@ -0,0 +1,220 @@
+# Generated by pub
+# See https://dart.dev/tools/pub/glossary#lockfile
+packages:
+ async:
+ dependency: transitive
+ description:
+ name: async
+ sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.11.0"
+ boolean_selector:
+ dependency: transitive
+ description:
+ name: boolean_selector
+ sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.1"
+ characters:
+ dependency: transitive
+ description:
+ name: characters
+ sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.0"
+ clock:
+ dependency: transitive
+ description:
+ name: clock
+ sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.1"
+ collection:
+ dependency: transitive
+ description:
+ name: collection
+ sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.18.0"
+ crypto:
+ dependency: transitive
+ description:
+ name: crypto
+ sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.3"
+ fake_async:
+ dependency: transitive
+ description:
+ name: fake_async
+ sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.1"
+ flutter:
+ dependency: "direct main"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ flutter_test:
+ dependency: "direct dev"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ intl:
+ dependency: "direct main"
+ description:
+ name: intl
+ sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.18.1"
+ leak_tracker:
+ dependency: transitive
+ description:
+ name: leak_tracker
+ sha256: "7e108028e3d258667d079986da8c0bc32da4cb57431c2af03b1dc1038621a9dc"
+ url: "https://pub.dev"
+ source: hosted
+ version: "9.0.13"
+ leak_tracker_testing:
+ dependency: transitive
+ description:
+ name: leak_tracker_testing
+ sha256: b06739349ec2477e943055aea30172c5c7000225f79dad4702e2ec0eda79a6ff
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.5"
+ matcher:
+ dependency: transitive
+ description:
+ name: matcher
+ sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.12.16"
+ material_color_utilities:
+ dependency: transitive
+ description:
+ name: material_color_utilities
+ sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.8.0"
+ meta:
+ dependency: transitive
+ description:
+ name: meta
+ sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.11.0"
+ path:
+ dependency: transitive
+ description:
+ name: path
+ sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.8.3"
+ sky_engine:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.99"
+ source_span:
+ dependency: transitive
+ description:
+ name: source_span
+ sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.10.0"
+ stack_trace:
+ dependency: transitive
+ description:
+ name: stack_trace
+ sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.11.1"
+ stream_channel:
+ dependency: transitive
+ description:
+ name: stream_channel
+ sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.2"
+ string_scanner:
+ dependency: transitive
+ description:
+ name: string_scanner
+ sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.2.0"
+ term_glyph:
+ dependency: transitive
+ description:
+ name: term_glyph
+ sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.2.1"
+ test_api:
+ dependency: transitive
+ description:
+ name: test_api
+ sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.6.1"
+ typed_data:
+ dependency: transitive
+ description:
+ name: typed_data
+ sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.2"
+ vector_math:
+ dependency: transitive
+ description:
+ name: vector_math
+ sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.4"
+ vm_service:
+ dependency: transitive
+ description:
+ name: vm_service
+ sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957
+ url: "https://pub.dev"
+ source: hosted
+ version: "13.0.0"
+ web:
+ dependency: transitive
+ description:
+ name: web
+ sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.3.0"
+ web_socket_channel:
+ dependency: transitive
+ description:
+ name: web_socket_channel
+ sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.0"
+sdks:
+ dart: ">=3.2.0-194.0.dev <4.0.0"
diff --git a/packages/flutter_calendar_carousel/pubspec.yaml b/packages/flutter_calendar_carousel/pubspec.yaml
new file mode 100644
index 0000000..9258aa7
--- /dev/null
+++ b/packages/flutter_calendar_carousel/pubspec.yaml
@@ -0,0 +1,53 @@
+name: flutter_calendar_carousel
+description: Calendar widget for flutter that is swipeable. This widget can help you build customizable calendar with scrollable actions.
+version: 2.4.3
+homepage: https://github.com/dooboolab/flutter_calendar_carousel
+
+environment:
+ sdk: ">=2.14.0 < 4.0.0"
+
+dependencies:
+ intl: ^0.19.0
+ flutter:
+ sdk: flutter
+
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
+
+# For information on the generic Dart part of this file, see the
+# following page: https://www.dartlang.org/tools/pub/pubspec
+
+# The following section is specific to Flutter.
+flutter:
+
+ # To add assets to your package, add an assets section, like this:
+ # assets:
+ # - images/a_dot_burr.jpeg
+ # - images/a_dot_ham.jpeg
+ #
+ # For details regarding assets in packages, see
+ # https://flutter.io/assets-and-images/#from-packages
+ #
+ # An image asset can refer to one or more resolution-specific "variants", see
+ # https://flutter.io/assets-and-images/#resolution-aware.
+
+ # To add custom fonts to your package, add a fonts section here,
+ # in this "flutter" section. Each entry in this list should have a
+ # "family" key with the font family name, and a "fonts" key with a
+ # list giving the asset and other descriptors for the font. For
+ # example:
+ # fonts:
+ # - family: Schyler
+ # fonts:
+ # - asset: fonts/Schyler-Regular.ttf
+ # - asset: fonts/Schyler-Italic.ttf
+ # style: italic
+ # - family: Trajan Pro
+ # fonts:
+ # - asset: fonts/TrajanPro.ttf
+ # - asset: fonts/TrajanPro_Bold.ttf
+ # weight: 700
+ #
+ # For details regarding fonts in packages, see
+ # https://flutter.io/custom-fonts/#from-packages
diff --git a/packages/flutter_calendar_carousel/renovate.json b/packages/flutter_calendar_carousel/renovate.json
new file mode 100644
index 0000000..39a2b6e
--- /dev/null
+++ b/packages/flutter_calendar_carousel/renovate.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "https://docs.renovatebot.com/renovate-schema.json",
+ "extends": [
+ "config:base"
+ ]
+}
diff --git a/packages/flutter_calendar_carousel/test/flutter_calendar_carousel_test.dart b/packages/flutter_calendar_carousel/test/flutter_calendar_carousel_test.dart
new file mode 100644
index 0000000..1a17149
--- /dev/null
+++ b/packages/flutter_calendar_carousel/test/flutter_calendar_carousel_test.dart
@@ -0,0 +1,180 @@
+// This is a basic Flutter widget test.
+// To perform an interaction with a widget in your test, use the WidgetTester utility that Flutter
+// provides. For example, you can send tap and scroll gestures. You can also use WidgetTester to
+// find child widgets in the widget tree, read text, and verify that the values of widget properties
+// are correct.
+
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:flutter_calendar_carousel/flutter_calendar_carousel.dart';
+
+import '../lib/classes/event.dart';
+
+Type typeOf<T>() => T;
+
+void main() {
+ testWidgets('Default test for Calendar Carousel',
+ (WidgetTester tester) async {
+ DateTime? pressedDay;
+ // Build our app and trigger a frame.
+ final carousel = CalendarCarousel(
+ daysHaveCircularBorder: null,
+ weekendTextStyle: TextStyle(
+ color: Colors.red,
+ ),
+ thisMonthDayBorderColor: Colors.grey,
+ headerText: 'Custom Header',
+ weekFormat: true,
+ height: 200.0,
+ showIconBehindDayText: true,
+ customGridViewPhysics: NeverScrollableScrollPhysics(),
+ markedDateShowIcon: true,
+ markedDateIconMaxShown: 2,
+ selectedDayTextStyle: TextStyle(
+ color: Colors.yellow,
+ ),
+ todayTextStyle: TextStyle(
+ color: Colors.blue,
+ ),
+ markedDateIconBuilder: (Event event) {
+ return event.icon ?? Icon(Icons.help_outline);
+ },
+ todayButtonColor: Colors.transparent,
+ todayBorderColor: Colors.green,
+ markedDateMoreShowTotal: true,
+ // null for not showing hidden events indicator
+ onDayPressed: (date, event) {
+ pressedDay = date;
+ },
+ );
+ await tester.pumpWidget(MaterialApp(
+ home: Scaffold(
+ body: Container(
+ child: carousel,
+ ),
+ ),
+ ));
+
+ expect(find.byWidget(carousel), findsOneWidget);
+ expect(pressedDay, isNull);
+ });
+
+ testWidgets(
+ 'make sure onDayPressed is called when the user tap',
+ (WidgetTester tester) async {
+ DateTime? pressedDay;
+
+ final carousel = CalendarCarousel(
+ weekFormat: true,
+ height: 200.0,
+ onDayPressed: (date, event) {
+ pressedDay = date;
+ },
+ );
+ await tester.pumpWidget(
+ MaterialApp(
+ home: Scaffold(
+ body: Container(
+ child: carousel,
+ ),
+ ),
+ ),
+ );
+
+ expect(find.byWidget(carousel), findsOneWidget);
+
+ expect(pressedDay, isNull);
+
+ await tester.tap(
+ find.text(DateTime.now().subtract(Duration(days: 1)).day.toString()));
+
+ await tester.pump();
+
+ expect(pressedDay, isNotNull);
+ },
+ );
+
+ testWidgets(
+ 'should do nothing when the user tap and onDayPressed is not provided',
+ (WidgetTester tester) async {
+ final carousel = CalendarCarousel(
+ weekFormat: true,
+ height: 200.0,
+ );
+ await tester.pumpWidget(
+ MaterialApp(
+ home: Scaffold(
+ body: Container(
+ child: carousel,
+ ),
+ ),
+ ),
+ );
+
+ expect(find.byWidget(carousel), findsOneWidget);
+
+ await tester.tap(
+ find.text(DateTime.now().subtract(Duration(days: 1)).day.toString()));
+ await tester.pump();
+ },
+ );
+
+ testWidgets(
+ 'make sure onDayLongPressed is called when the user press and hold',
+ (WidgetTester tester) async {
+ DateTime? longPressedDay;
+
+ final carousel = CalendarCarousel(
+ weekFormat: true,
+ height: 200.0,
+ onDayLongPressed: (date) {
+ longPressedDay = date;
+ },
+ );
+ await tester.pumpWidget(
+ MaterialApp(
+ home: Scaffold(
+ body: Container(
+ child: carousel,
+ ),
+ ),
+ ),
+ );
+
+ expect(find.byWidget(carousel), findsOneWidget);
+
+ expect(longPressedDay, isNull);
+
+ await tester.longPress(
+ find.text(DateTime.now().subtract(Duration(days: 1)).day.toString()));
+ await tester.pump();
+
+ expect(longPressedDay, isNotNull);
+ },
+ );
+
+ testWidgets(
+ 'should do nothing when the user press and hold and onDayLongPressed is not provided',
+ (WidgetTester tester) async {
+ final carousel = CalendarCarousel(
+ weekFormat: true,
+ height: 200.0,
+ );
+ await tester.pumpWidget(
+ MaterialApp(
+ home: Scaffold(
+ body: Container(
+ child: carousel,
+ ),
+ ),
+ ),
+ );
+
+ expect(find.byWidget(carousel), findsOneWidget);
+
+ await tester.longPress(
+ find.text(DateTime.now().subtract(Duration(days: 1)).day.toString()));
+ await tester.pump();
+ },
+ );
+}
diff --git a/packages/flutter_calendar_carousel/test/src/header_test.dart b/packages/flutter_calendar_carousel/test/src/header_test.dart
new file mode 100644
index 0000000..2f9b603
--- /dev/null
+++ b/packages/flutter_calendar_carousel/test/src/header_test.dart
@@ -0,0 +1,104 @@
+import 'package:flutter_test/flutter_test.dart';
+import 'package:flutter_calendar_carousel/src/calendar_header.dart';
+
+import 'package:flutter/material.dart';
+
+void main() {
+ final title = "Test title";
+ final margin = const EdgeInsets.symmetric(vertical: 16.0);
+ final iconColor = Colors.blueAccent;
+
+ testWidgets('Verify Header Defaults', (WidgetTester tester) async {
+ var headerTapped = false;
+ var leftPressed = false;
+ var rightPressed = false;
+
+ await tester.pumpWidget(wrapped(CalendarHeader(
+ headerTitle: title,
+ headerMargin: margin,
+ showHeader: true,
+ showHeaderButtons: true,
+ headerIconColor: iconColor,
+ onHeaderTitlePressed: () => headerTapped = true,
+ onRightButtonPressed: () => rightPressed = true,
+ onLeftButtonPressed: () => leftPressed = true,
+ )));
+
+ expect(find.text(title), findsOneWidget);
+
+ await tester.tap(find.byType(TextButton));
+
+ await tester.pump();
+
+ expect(headerTapped, equals(true));
+
+ await tester.tap(find.widgetWithIcon(IconButton, Icons.chevron_right));
+
+ await tester.pump();
+
+ expect(rightPressed, equals(true));
+
+ await tester.tap(find.widgetWithIcon(IconButton, Icons.chevron_left));
+
+ await tester.pump();
+
+ expect(leftPressed, equals(true));
+ });
+
+ testWidgets('Verify No header Renders', (WidgetTester tester) async {
+ final noHeaderEmpty = CalendarHeader(
+ showHeader: false,
+ headerTitle: '',
+ onLeftButtonPressed: () {},
+ onHeaderTitlePressed: () {},
+ onRightButtonPressed: () {},
+ );
+
+ await tester.pumpWidget(Container(child: noHeaderEmpty));
+
+ expect(find.byWidget(noHeaderEmpty), findsOneWidget);
+ });
+
+ testWidgets('Verify Header Is Not Touchable', (WidgetTester tester) async {
+ await tester.pumpWidget(wrapped(CalendarHeader(
+ headerTitle: title,
+ headerMargin: margin,
+ showHeader: true,
+ showHeaderButtons: true,
+ headerIconColor: iconColor,
+ onHeaderTitlePressed: null,
+ onRightButtonPressed: () {},
+ onLeftButtonPressed: () {},
+ )));
+
+ // the header TextButton Should not render
+ final touchableHeader = find.byType(TextButton);
+
+ expect(touchableHeader, findsNothing);
+ });
+
+ testWidgets('Verify No Header Buttons', (WidgetTester tester) async {
+ await tester.pumpWidget(wrapped(CalendarHeader(
+ headerTitle: title,
+ headerMargin: margin,
+ showHeader: true,
+ showHeaderButtons: false,
+ headerIconColor: iconColor,
+ onHeaderTitlePressed: () {},
+ onRightButtonPressed: () {},
+ onLeftButtonPressed: () {},
+ )));
+
+ // the header IconButtons Should not render
+ final headerButton = find.byType(IconButton);
+
+ expect(headerButton, findsNothing);
+ });
+}
+
+// header uses Row which requires MaterialApp as an ancestor
+Widget wrapped(Widget widget) => MaterialApp(
+ home: Container(
+ child: Material(child: widget),
+ ),
+ );
diff --git a/packages/flutter_calendar_carousel/test/src/weekday_row_test.dart b/packages/flutter_calendar_carousel/test/src/weekday_row_test.dart
new file mode 100644
index 0000000..95090e4
--- /dev/null
+++ b/packages/flutter_calendar_carousel/test/src/weekday_row_test.dart
@@ -0,0 +1,128 @@
+import 'package:flutter_test/flutter_test.dart';
+import 'package:flutter_calendar_carousel/flutter_calendar_carousel.dart'
+ show WeekdayFormat;
+import 'package:intl/intl.dart' show DateFormat;
+
+import 'package:flutter_calendar_carousel/src/weekday_row.dart';
+import 'package:flutter/material.dart';
+
+void main() {
+ final locale = DateFormat.yMMM("en_US");
+ final margin = const EdgeInsets.only(bottom: 4.0);
+
+ testWidgets('test short weekday row', (WidgetTester tester) async {
+ await tester.pumpWidget(wrapped(
+ WeekdayRow(
+ 0,
+ null,
+ weekdayPadding: EdgeInsets.all(0),
+ weekdayBackgroundColor: Colors.transparent,
+ showWeekdays: true,
+ weekdayFormat: WeekdayFormat.short,
+ weekdayMargin: margin,
+ weekdayTextStyle: null,
+ localeDate: locale,
+ ),
+ ));
+
+ expect(find.text('Sun'), findsOneWidget);
+ expect(find.text('Mon'), findsOneWidget);
+ expect(find.text('Tue'), findsOneWidget);
+ expect(find.text('Wed'), findsOneWidget);
+ expect(find.text('Thu'), findsOneWidget);
+ expect(find.text('Fri'), findsOneWidget);
+ expect(find.text('Sat'), findsOneWidget);
+ });
+
+ testWidgets('test narrow weekday row', (WidgetTester tester) async {
+ await tester.pumpWidget(wrapped(WeekdayRow(
+ 0,
+ null,
+ weekdayPadding: EdgeInsets.all(0),
+ weekdayBackgroundColor: Colors.transparent,
+ showWeekdays: true,
+ weekdayFormat: WeekdayFormat.standaloneNarrow,
+ weekdayMargin: margin,
+ weekdayTextStyle: null,
+ localeDate: locale,
+ )));
+
+ // sat and sun
+ expect(find.text('S'), findsNWidgets(2));
+ // thurs and tues
+ expect(find.text('T'), findsNWidgets(2));
+
+ expect(find.text('M'), findsOneWidget);
+ expect(find.text('W'), findsOneWidget);
+ expect(find.text('F'), findsOneWidget);
+ });
+
+ testWidgets('test standalone weekday row', (WidgetTester tester) async {
+ await tester.pumpWidget(wrapped(WeekdayRow(
+ 0,
+ null,
+ weekdayPadding: EdgeInsets.all(0),
+ weekdayBackgroundColor: Colors.transparent,
+ showWeekdays: true,
+ weekdayFormat: WeekdayFormat.standalone,
+ weekdayMargin: margin,
+ weekdayTextStyle: null,
+ localeDate: locale,
+ )));
+
+ expect(find.text('Sunday'), findsOneWidget);
+ expect(find.text('Monday'), findsOneWidget);
+ expect(find.text('Tuesday'), findsOneWidget);
+ expect(find.text('Wednesday'), findsOneWidget);
+ expect(find.text('Thursday'), findsOneWidget);
+ expect(find.text('Friday'), findsOneWidget);
+ expect(find.text('Saturday'), findsOneWidget);
+ });
+
+ testWidgets('test standalone short weekday row', (WidgetTester tester) async {
+ await tester.pumpWidget(wrapped(WeekdayRow(
+ 0,
+ null,
+ weekdayPadding: EdgeInsets.all(0),
+ weekdayBackgroundColor: Colors.transparent,
+ showWeekdays: true,
+ weekdayFormat: WeekdayFormat.standaloneShort,
+ weekdayMargin: margin,
+ weekdayTextStyle: null,
+ localeDate: locale,
+ )));
+
+ expect(find.text('Sun'), findsOneWidget);
+ expect(find.text('Mon'), findsOneWidget);
+ expect(find.text('Tue'), findsOneWidget);
+ expect(find.text('Wed'), findsOneWidget);
+ expect(find.text('Thu'), findsOneWidget);
+ expect(find.text('Fri'), findsOneWidget);
+ expect(find.text('Sat'), findsOneWidget);
+ });
+
+ testWidgets('test row does not render', (WidgetTester tester) async {
+ final emptyContainer = WeekdayRow(
+ 0,
+ null,
+ weekdayPadding: EdgeInsets.all(0),
+ weekdayBackgroundColor: Colors.transparent,
+ showWeekdays: false,
+ weekdayFormat: WeekdayFormat.standaloneNarrow,
+ weekdayMargin: margin,
+ weekdayTextStyle: null,
+ localeDate: locale,
+ );
+
+ await tester.pumpWidget(emptyContainer);
+
+ expect(find.byType(Container), findsOneWidget);
+
+ expect(find.byType(Row), findsNothing);
+ });
+}
+
+Widget wrapped(Widget widget) => Directionality(
+ textDirection: TextDirection.ltr,
+ child: widget,
+ );