From 2cd1c7cabc3025bd227ce8cc2a32075d942a5df6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Tue, 1 Sep 2020 13:49:21 +0200 Subject: [PATCH 001/164] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2d63eb40..f9d03240 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Highly customizable, feature-packed Flutter Calendar with gestures, animations a ## Usage -Make sure to check out [example project](https://github.com/aleksanderwozniak/table_calendar/tree/master/example). +Make sure to check out [example project](https://github.com/aleksanderwozniak/table_calendar/blob/2.2.3-stable/example/lib/main.dart). For additional info please refer to [API docs](https://pub.dartlang.org/documentation/table_calendar/latest/table_calendar/table_calendar-library.html). ### Installation @@ -162,4 +162,4 @@ markersBuilder: (context, date, events, holidays) { return children; }, -``` \ No newline at end of file +``` From 33d3d05af392222059662b259ab9981f5329a285 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Thu, 15 Oct 2020 20:46:17 +0200 Subject: [PATCH 002/164] AndroidX migration --- .gitignore | 72 ++++++- .idea/libraries/Dart_SDK.xml | 22 +-- .metadata | 4 +- .vscode/launch.json | 13 -- .../plugins/GeneratedPluginRegistrant.java | 23 --- example/.gitignore | 51 +---- example/.metadata | 4 +- example/android/.gitignore | 11 ++ example/android/app/build.gradle | 14 +- .../android/app/src/debug/AndroidManifest.xml | 7 + .../android/app/src/main/AndroidManifest.xml | 36 ++-- .../com/example/example/MainActivity.java | 13 -- .../com/example/example/MainActivity.kt | 6 + .../app/src/main/res/values/styles.xml | 10 + .../app/src/profile/AndroidManifest.xml | 7 + example/android/build.gradle | 4 +- example/android/gradle.properties | 2 + .../gradle/wrapper/gradle-wrapper.properties | 2 +- example/android/settings.gradle | 18 +- example/ios/.gitignore | 32 ++++ example/ios/Flutter/AppFrameworkInfo.plist | 4 +- example/ios/Runner.xcodeproj/project.pbxproj | 83 ++++----- .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 4 +- .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + example/ios/Runner/AppDelegate.h | 6 - example/ios/Runner/AppDelegate.m | 13 -- example/ios/Runner/AppDelegate.swift | 13 ++ .../Icon-App-1024x1024@1x.png | Bin 11112 -> 10932 bytes example/ios/Runner/Info.plist | 2 +- example/ios/Runner/Runner-Bridging-Header.h | 1 + example/ios/Runner/main.m | 9 - example/lib/main.dart | 4 +- example/pubspec.lock | 175 ++++++++++++++++++ example/pubspec.yaml | 23 ++- pubspec.lock | 109 ++++------- pubspec.yaml | 7 +- table_calendar.iml | 5 +- 40 files changed, 522 insertions(+), 319 deletions(-) delete mode 100644 .vscode/launch.json delete mode 100644 android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java create mode 100644 example/android/.gitignore create mode 100644 example/android/app/src/debug/AndroidManifest.xml delete mode 100644 example/android/app/src/main/java/com/example/example/MainActivity.java create mode 100644 example/android/app/src/main/kotlin/com/example/example/MainActivity.kt create mode 100644 example/android/app/src/profile/AndroidManifest.xml create mode 100644 example/ios/.gitignore create mode 100644 example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings delete mode 100644 example/ios/Runner/AppDelegate.h delete mode 100644 example/ios/Runner/AppDelegate.m create mode 100644 example/ios/Runner/AppDelegate.swift create mode 100644 example/ios/Runner/Runner-Bridging-Header.h delete mode 100644 example/ios/Runner/main.m create mode 100644 example/pubspec.lock diff --git a/.gitignore b/.gitignore index 446ed0d1..d5ccb7e2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,72 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp .DS_Store -.dart_tool/ +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ +# Visual Studio Code related +.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies .packages +.pub-cache/ .pub/ - build/ -ios/.generated/ -ios/Flutter/Generated.xcconfig -ios/Runner/GeneratedPluginRegistrant.* + +# Android related +**/android/**/gradle-wrapper.jar +**/android/.gradle +**/android/captures/ +**/android/gradlew +**/android/gradlew.bat +**/android/local.properties +**/android/**/GeneratedPluginRegistrant.java + +# iOS/XCode related +**/ios/**/*.mode1v3 +**/ios/**/*.mode2v3 +**/ios/**/*.moved-aside +**/ios/**/*.pbxuser +**/ios/**/*.perspectivev3 +**/ios/**/*sync/ +**/ios/**/.sconsign.dblite +**/ios/**/.tags* +**/ios/**/.vagrant/ +**/ios/**/DerivedData/ +**/ios/**/Icon? +**/ios/**/Pods/ +**/ios/**/.symlinks/ +**/ios/**/profile +**/ios/**/xcuserdata +**/ios/.generated/ +**/ios/Flutter/App.framework +**/ios/Flutter/Flutter.framework +**/ios/Flutter/Flutter.podspec +**/ios/Flutter/Generated.xcconfig +**/ios/Flutter/app.flx +**/ios/Flutter/app.zip +**/ios/Flutter/flutter_assets/ +**/ios/Flutter/flutter_export_environment.sh +**/ios/ServiceDefinitions.json +**/ios/Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!**/ios/**/default.mode1v3 +!**/ios/**/default.mode2v3 +!**/ios/**/default.pbxuser +!**/ios/**/default.perspectivev3 diff --git a/.idea/libraries/Dart_SDK.xml b/.idea/libraries/Dart_SDK.xml index 98ceccb1..4b939a0b 100644 --- a/.idea/libraries/Dart_SDK.xml +++ b/.idea/libraries/Dart_SDK.xml @@ -1,17 +1,17 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/.metadata b/.metadata index 6209bc9f..30b175a6 100644 --- a/.metadata +++ b/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: 5391447fae6209bb21a89e6a5a6583cac1af9b4b - channel: stable + revision: f30b7f4db93ee747cd727df747941a28ead25ff5 + channel: beta project_type: package diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 3287bb67..00000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Flutter", - "request": "launch", - "type": "dart" - } - ] -} \ No newline at end of file diff --git a/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java b/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java deleted file mode 100644 index d007606a..00000000 --- a/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java +++ /dev/null @@ -1,23 +0,0 @@ -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/example/.gitignore b/example/.gitignore index 01f4ac79..b4e3d223 100644 --- a/example/.gitignore +++ b/example/.gitignore @@ -1,6 +1,5 @@ # Miscellaneous *.class -*.lock *.log *.pyc *.swp @@ -21,52 +20,20 @@ # Flutter/Dart/Pub related **/doc/api/ +**/ios/Flutter/.last_build_id .dart_tool/ .flutter-plugins +.flutter-plugins-dependencies .packages .pub-cache/ .pub/ -build/ +/build/ -# Android related -**/android/**/gradle-wrapper.jar -**/android/.gradle -**/android/captures/ -**/android/gradlew -**/android/gradlew.bat -**/android/local.properties -**/android/**/GeneratedPluginRegistrant.java +# Web related +lib/generated_plugin_registrant.dart -# iOS/XCode related -**/ios/**/*.mode1v3 -**/ios/**/*.mode2v3 -**/ios/**/*.moved-aside -**/ios/**/*.pbxuser -**/ios/**/*.perspectivev3 -**/ios/**/*sync/ -**/ios/**/.sconsign.dblite -**/ios/**/.tags* -**/ios/**/.vagrant/ -**/ios/**/DerivedData/ -**/ios/**/Icon? -**/ios/**/Pods/ -**/ios/**/.symlinks/ -**/ios/**/profile -**/ios/**/xcuserdata -**/ios/.generated/ -**/ios/Flutter/App.framework -**/ios/Flutter/Flutter.framework -**/ios/Flutter/Generated.xcconfig -**/ios/Flutter/app.flx -**/ios/Flutter/app.zip -**/ios/Flutter/flutter_assets/ -**/ios/Flutter/flutter_export_environment.sh -**/ios/ServiceDefinitions.json -**/ios/Runner/GeneratedPluginRegistrant.* +# Symbolication related +app.*.symbols -# Exceptions to above rules. -!**/ios/**/default.mode1v3 -!**/ios/**/default.mode2v3 -!**/ios/**/default.pbxuser -!**/ios/**/default.perspectivev3 -!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages +# Obfuscation related +app.*.map.json diff --git a/example/.metadata b/example/.metadata index 460bc20b..f6caaefd 100644 --- a/example/.metadata +++ b/example/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: 5391447fae6209bb21a89e6a5a6583cac1af9b4b - channel: stable + revision: f30b7f4db93ee747cd727df747941a28ead25ff5 + channel: beta project_type: app diff --git a/example/android/.gitignore b/example/android/.gitignore new file mode 100644 index 00000000..0a741cb4 --- /dev/null +++ b/example/android/.gitignore @@ -0,0 +1,11 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 4ed2e254..3932aa91 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -22,10 +22,15 @@ if (flutterVersionName == null) { } apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 28 + compileSdkVersion 29 + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } lintOptions { disable 'InvalidPackage' @@ -35,10 +40,9 @@ android { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.example.example" minSdkVersion 16 - targetSdkVersion 28 + targetSdkVersion 29 versionCode flutterVersionCode.toInteger() versionName flutterVersionName - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { @@ -55,7 +59,5 @@ flutter { } dependencies { - testImplementation 'junit:junit:4.12' - androidTestImplementation 'com.android.support.test:runner:1.0.2' - androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" } diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 00000000..c208884f --- /dev/null +++ b/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 1b515f8b..55ca830c 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -1,12 +1,5 @@ - - - - + + android:name="io.flutter.embedding.android.NormalTheme" + android:resource="@style/NormalTheme" + /> + + + + diff --git a/example/android/app/src/main/java/com/example/example/MainActivity.java b/example/android/app/src/main/java/com/example/example/MainActivity.java deleted file mode 100644 index 84f8920f..00000000 --- a/example/android/app/src/main/java/com/example/example/MainActivity.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.example.example; - -import android.os.Bundle; -import io.flutter.app.FlutterActivity; -import io.flutter.plugins.GeneratedPluginRegistrant; - -public class MainActivity extends FlutterActivity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - GeneratedPluginRegistrant.registerWith(this); - } -} diff --git a/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt b/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt new file mode 100644 index 00000000..e793a000 --- /dev/null +++ b/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt @@ -0,0 +1,6 @@ +package com.example.example + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/example/android/app/src/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml index 00fa4417..1f83a33f 100644 --- a/example/android/app/src/main/res/values/styles.xml +++ b/example/android/app/src/main/res/values/styles.xml @@ -1,8 +1,18 @@ + + + diff --git a/example/android/app/src/profile/AndroidManifest.xml b/example/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 00000000..c208884f --- /dev/null +++ b/example/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/example/android/build.gradle b/example/android/build.gradle index bb8a3038..3100ad2d 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,11 +1,13 @@ buildscript { + ext.kotlin_version = '1.3.50' repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.android.tools.build:gradle:3.5.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/example/android/gradle.properties b/example/android/gradle.properties index 7be3d8b4..a6738207 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -1,2 +1,4 @@ org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true android.enableR8=true diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index 2819f022..296b146b 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip diff --git a/example/android/settings.gradle b/example/android/settings.gradle index 5a2f14fb..44e62bcf 100644 --- a/example/android/settings.gradle +++ b/example/android/settings.gradle @@ -1,15 +1,11 @@ include ':app' -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } -} +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory -} +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/example/ios/.gitignore b/example/ios/.gitignore new file mode 100644 index 00000000..e96ef602 --- /dev/null +++ b/example/ios/.gitignore @@ -0,0 +1,32 @@ +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist index 9367d483..f2872cf4 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -3,7 +3,7 @@ CFBundleDevelopmentRegion - en + $(DEVELOPMENT_LANGUAGE) CFBundleExecutable App CFBundleIdentifier @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 8.0 + 9.0 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 5229fcae..1aec2aa0 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -8,15 +8,8 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */ = {isa = PBXBuildFile; fileRef = 2D5378251FAA1A9400D5DBA9 /* flutter_assets */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; - 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; - 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; @@ -29,8 +22,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -40,17 +31,13 @@ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 2D5378251FAA1A9400D5DBA9 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = Flutter/flutter_assets; sourceTree = SOURCE_ROOT; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; - 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; @@ -62,8 +49,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -73,10 +58,7 @@ 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( - 2D5378251FAA1A9400D5DBA9 /* flutter_assets */, - 3B80C3931E831B6300D905FE /* App.framework */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEBA1CF902C7004384FC /* Flutter.framework */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 9740EEB31CF90195004384FC /* Generated.xcconfig */, @@ -90,7 +72,6 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, - CF3B75C9A7D2FA2A4C99F110 /* Frameworks */, ); sourceTree = ""; }; @@ -105,27 +86,18 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( - 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, - 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, 97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 97C147021CF9000F007C117D /* Info.plist */, - 97C146F11CF9000F007C117D /* Supporting Files */, 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, ); path = Runner; sourceTree = ""; }; - 97C146F11CF9000F007C117D /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 97C146F21CF9000F007C117D /* main.m */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -155,17 +127,18 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0910; - ORGANIZATIONNAME = "The Chromium Authors"; + LastUpgradeCheck = 1020; + ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; }; }; }; buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, @@ -188,9 +161,7 @@ files = ( 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, - 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, - 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -210,7 +181,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; @@ -233,8 +204,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, - 97C146F31CF9000F007C117D /* main.m in Sources */, + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -263,7 +233,6 @@ /* Begin XCBuildConfiguration section */ 249021D3217E4FDB00AE95B9 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -275,12 +244,14 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -301,9 +272,10 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -314,8 +286,8 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = S8QB4VV633; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -329,13 +301,14 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.example.example; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; }; name = Profile; }; 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -347,12 +320,14 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -379,7 +354,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -389,7 +364,6 @@ }; 97C147041CF9000F007C117D /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -401,12 +375,14 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -427,9 +403,11 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -440,6 +418,7 @@ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -454,6 +433,9 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.example.example; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; @@ -463,6 +445,7 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -477,6 +460,8 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.example.example; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..f9b0d7c5 --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 786d6aad..a28140cf 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ @@ -46,7 +45,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..f9b0d7c5 --- /dev/null +++ b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/example/ios/Runner/AppDelegate.h b/example/ios/Runner/AppDelegate.h deleted file mode 100644 index 36e21bbf..00000000 --- a/example/ios/Runner/AppDelegate.h +++ /dev/null @@ -1,6 +0,0 @@ -#import -#import - -@interface AppDelegate : FlutterAppDelegate - -@end diff --git a/example/ios/Runner/AppDelegate.m b/example/ios/Runner/AppDelegate.m deleted file mode 100644 index 59a72e90..00000000 --- a/example/ios/Runner/AppDelegate.m +++ /dev/null @@ -1,13 +0,0 @@ -#include "AppDelegate.h" -#include "GeneratedPluginRegistrant.h" - -@implementation AppDelegate - -- (BOOL)application:(UIApplication *)application - didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - [GeneratedPluginRegistrant registerWithRegistry:self]; - // Override point for customization after application launch. - return [super application:application didFinishLaunchingWithOptions:launchOptions]; -} - -@end diff --git a/example/ios/Runner/AppDelegate.swift b/example/ios/Runner/AppDelegate.swift new file mode 100644 index 00000000..70693e4a --- /dev/null +++ b/example/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import UIKit +import Flutter + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png index 3d43d11e66f4de3da27ed045ca4fe38ad8b48094..dc9ada4725e9b0ddb1deab583e5b5102493aa332 100644 GIT binary patch literal 10932 zcmeHN2~<R zh`|8`A_PQ1nSu(UMFx?8j8PC!!VDphaL#`F42fd#7Vlc`zIE4n%Y~eiz4y1j|NDpi z?<@|pSJ-HM`qifhf@m%MamgwK83`XpBA<+azdF#2QsT{X@z0A9Bq>~TVErigKH1~P zRX-!h-f0NJ4Mh++{D}J+K>~~rq}d%o%+4dogzXp7RxX4C>Km5XEI|PAFDmo;DFm6G zzjVoB`@qW98Yl0Kvc-9w09^PrsobmG*Eju^=3f?0o-t$U)TL1B3;sZ^!++3&bGZ!o-*6w?;oOhf z=A+Qb$scV5!RbG+&2S}BQ6YH!FKb0``VVX~T$dzzeSZ$&9=X$3)_7Z{SspSYJ!lGE z7yig_41zpQ)%5dr4ff0rh$@ky3-JLRk&DK)NEIHecf9c*?Z1bUB4%pZjQ7hD!A0r-@NF(^WKdr(LXj|=UE7?gBYGgGQV zidf2`ZT@pzXf7}!NH4q(0IMcxsUGDih(0{kRSez&z?CFA0RVXsVFw3^u=^KMtt95q z43q$b*6#uQDLoiCAF_{RFc{!H^moH_cmll#Fc^KXi{9GDl{>%+3qyfOE5;Zq|6#Hb zp^#1G+z^AXfRKaa9HK;%b3Ux~U@q?xg<2DXP%6k!3E)PA<#4$ui8eDy5|9hA5&{?v z(-;*1%(1~-NTQ`Is1_MGdQ{+i*ccd96ab$R$T3=% zw_KuNF@vI!A>>Y_2pl9L{9h1-C6H8<)J4gKI6{WzGBi<@u3P6hNsXG=bRq5c+z;Gc3VUCe;LIIFDmQAGy+=mRyF++u=drBWV8-^>0yE9N&*05XHZpPlE zxu@?8(ZNy7rm?|<+UNe0Vs6&o?l`Pt>P&WaL~M&#Eh%`rg@Mbb)J&@DA-wheQ>hRV z<(XhigZAT z>=M;URcdCaiO3d^?H<^EiEMDV+7HsTiOhoaMX%P65E<(5xMPJKxf!0u>U~uVqnPN7T!X!o@_gs3Ct1 zlZ_$5QXP4{Aj645wG_SNT&6m|O6~Tsl$q?nK*)(`{J4b=(yb^nOATtF1_aS978$x3 zx>Q@s4i3~IT*+l{@dx~Hst21fR*+5}S1@cf>&8*uLw-0^zK(+OpW?cS-YG1QBZ5q! zgTAgivzoF#`cSz&HL>Ti!!v#?36I1*l^mkrx7Y|K6L#n!-~5=d3;K<;Zqi|gpNUn_ z_^GaQDEQ*jfzh;`j&KXb66fWEk1K7vxQIMQ_#Wu_%3 z4Oeb7FJ`8I>Px;^S?)}2+4D_83gHEq>8qSQY0PVP?o)zAv3K~;R$fnwTmI-=ZLK`= zTm+0h*e+Yfr(IlH3i7gUclNH^!MU>id$Jw>O?2i0Cila#v|twub21@e{S2v}8Z13( zNDrTXZVgris|qYm<0NU(tAPouG!QF4ZNpZPkX~{tVf8xY690JqY1NVdiTtW+NqyRP zZ&;T0ikb8V{wxmFhlLTQ&?OP7 z;(z*<+?J2~z*6asSe7h`$8~Se(@t(#%?BGLVs$p``;CyvcT?7Y!{tIPva$LxCQ&4W z6v#F*);|RXvI%qnoOY&i4S*EL&h%hP3O zLsrFZhv&Hu5tF$Lx!8(hs&?!Kx5&L(fdu}UI5d*wn~A`nPUhG&Rv z2#ixiJdhSF-K2tpVL=)5UkXRuPAFrEW}7mW=uAmtVQ&pGE-&az6@#-(Te^n*lrH^m@X-ftVcwO_#7{WI)5v(?>uC9GG{lcGXYJ~Q8q zbMFl7;t+kV;|;KkBW2!P_o%Czhw&Q(nXlxK9ak&6r5t_KH8#1Mr-*0}2h8R9XNkr zto5-b7P_auqTJb(TJlmJ9xreA=6d=d)CVbYP-r4$hDn5|TIhB>SReMfh&OVLkMk-T zYf%$taLF0OqYF?V{+6Xkn>iX@TuqQ?&cN6UjC9YF&%q{Ut3zv{U2)~$>-3;Dp)*(? zg*$mu8^i=-e#acaj*T$pNowo{xiGEk$%DusaQiS!KjJH96XZ-hXv+jk%ard#fu=@Q z$AM)YWvE^{%tDfK%nD49=PI|wYu}lYVbB#a7wtN^Nml@CE@{Gv7+jo{_V?I*jkdLD zJE|jfdrmVbkfS>rN*+`#l%ZUi5_bMS<>=MBDNlpiSb_tAF|Zy`K7kcp@|d?yaTmB^ zo?(vg;B$vxS|SszusORgDg-*Uitzdi{dUV+glA~R8V(?`3GZIl^egW{a919!j#>f` znL1o_^-b`}xnU0+~KIFLQ)$Q6#ym%)(GYC`^XM*{g zv3AM5$+TtDRs%`2TyR^$(hqE7Y1b&`Jd6dS6B#hDVbJlUXcG3y*439D8MrK!2D~6gn>UD4Imctb z+IvAt0iaW73Iq$K?4}H`7wq6YkTMm`tcktXgK0lKPmh=>h+l}Y+pDtvHnG>uqBA)l zAH6BV4F}v$(o$8Gfo*PB>IuaY1*^*`OTx4|hM8jZ?B6HY;F6p4{`OcZZ(us-RVwDx zUzJrCQlp@mz1ZFiSZ*$yX3c_#h9J;yBE$2g%xjmGF4ca z&yL`nGVs!Zxsh^j6i%$a*I3ZD2SoNT`{D%mU=LKaEwbN(_J5%i-6Va?@*>=3(dQy` zOv%$_9lcy9+(t>qohkuU4r_P=R^6ME+wFu&LA9tw9RA?azGhjrVJKy&8=*qZT5Dr8g--d+S8zAyJ$1HlW3Olryt`yE zFIph~Z6oF&o64rw{>lgZISC6p^CBer9C5G6yq%?8tC+)7*d+ib^?fU!JRFxynRLEZ zj;?PwtS}Ao#9whV@KEmwQgM0TVP{hs>dg(1*DiMUOKHdQGIqa0`yZnHk9mtbPfoLx zo;^V6pKUJ!5#n`w2D&381#5#_t}AlTGEgDz$^;u;-vxDN?^#5!zN9ngytY@oTv!nc zp1Xn8uR$1Z;7vY`-<*?DfPHB;x|GUi_fI9@I9SVRv1)qETbNU_8{5U|(>Du84qP#7 z*l9Y$SgA&wGbj>R1YeT9vYjZuC@|{rajTL0f%N@>3$DFU=`lSPl=Iv;EjuGjBa$Gw zHD-;%YOE@<-!7-Mn`0WuO3oWuL6tB2cpPw~Nvuj|KM@))ixuDK`9;jGMe2d)7gHin zS<>k@!x;!TJEc#HdL#RF(`|4W+H88d4V%zlh(7#{q2d0OQX9*FW^`^_<3r$kabWAB z$9BONo5}*(%kx zOXi-yM_cmB3>inPpI~)duvZykJ@^^aWzQ=eQ&STUa}2uT@lV&WoRzkUoE`rR0)`=l zFT%f|LA9fCw>`enm$p7W^E@U7RNBtsh{_-7vVz3DtB*y#*~(L9+x9*wn8VjWw|Q~q zKFsj1Yl>;}%MG3=PY`$g$_mnyhuV&~O~u~)968$0b2!Jkd;2MtAP#ZDYw9hmK_+M$ zb3pxyYC&|CuAbtiG8HZjj?MZJBFbt`ryf+c1dXFuC z0*ZQhBzNBd*}s6K_G}(|Z_9NDV162#y%WSNe|FTDDhx)K!c(mMJh@h87@8(^YdK$&d*^WQe8Z53 z(|@MRJ$Lk-&ii74MPIs80WsOFZ(NX23oR-?As+*aq6b?~62@fSVmM-_*cb1RzZ)`5$agEiL`-E9s7{GM2?(KNPgK1(+c*|-FKoy}X(D_b#etO|YR z(BGZ)0Ntfv-7R4GHoXp?l5g#*={S1{u-QzxCGng*oWr~@X-5f~RA14b8~B+pLKvr4 zfgL|7I>jlak9>D4=(i(cqYf7#318!OSR=^`xxvI!bBlS??`xxWeg?+|>MxaIdH1U~#1tHu zB{QMR?EGRmQ_l4p6YXJ{o(hh-7Tdm>TAX380TZZZyVkqHNzjUn*_|cb?T? zt;d2s-?B#Mc>T-gvBmQZx(y_cfkXZO~{N zT6rP7SD6g~n9QJ)8F*8uHxTLCAZ{l1Y&?6v)BOJZ)=R-pY=Y=&1}jE7fQ>USS}xP#exo57uND0i*rEk@$;nLvRB@u~s^dwRf?G?_enN@$t* zbL%JO=rV(3Ju8#GqUpeE3l_Wu1lN9Y{D4uaUe`g>zlj$1ER$6S6@{m1!~V|bYkhZA z%CvrDRTkHuajMU8;&RZ&itnC~iYLW4DVkP<$}>#&(`UO>!n)Po;Mt(SY8Yb`AS9lt znbX^i?Oe9r_o=?})IHKHoQGKXsps_SE{hwrg?6dMI|^+$CeC&z@*LuF+P`7LfZ*yr+KN8B4{Nzv<`A(wyR@!|gw{zB6Ha ziwPAYh)oJ(nlqSknu(8g9N&1hu0$vFK$W#mp%>X~AU1ay+EKWcFdif{% z#4!4aoVVJ;ULmkQf!ke2}3hqxLK>eq|-d7Ly7-J9zMpT`?dxo6HdfJA|t)?qPEVBDv z{y_b?4^|YA4%WW0VZd8C(ZgQzRI5(I^)=Ub`Y#MHc@nv0w-DaJAqsbEHDWG8Ia6ju zo-iyr*sq((gEwCC&^TYBWt4_@|81?=B-?#P6NMff(*^re zYqvDuO`K@`mjm_Jd;mW_tP`3$cS?R$jR1ZN09$YO%_iBqh5ftzSpMQQtxKFU=FYmP zeY^jph+g<4>YO;U^O>-NFLn~-RqlHvnZl2yd2A{Yc1G@Ga$d+Q&(f^tnPf+Z7serIU};17+2DU_f4Z z@GaPFut27d?!YiD+QP@)T=77cR9~MK@bd~pY%X(h%L={{OIb8IQmf-!xmZkm8A0Ga zQSWONI17_ru5wpHg3jI@i9D+_Y|pCqVuHJNdHUauTD=R$JcD2K_liQisqG$(sm=k9;L* z!L?*4B~ql7uioSX$zWJ?;q-SWXRFhz2Jt4%fOHA=Bwf|RzhwqdXGr78y$J)LR7&3T zE1WWz*>GPWKZ0%|@%6=fyx)5rzUpI;bCj>3RKzNG_1w$fIFCZ&UR0(7S?g}`&Pg$M zf`SLsz8wK82Vyj7;RyKmY{a8G{2BHG%w!^T|Njr!h9TO2LaP^_f22Q1=l$QiU84ao zHe_#{S6;qrC6w~7{y(hs-?-j?lbOfgH^E=XcSgnwW*eEz{_Z<_iT7q6h&WAVr806i~>Gqn6rM z>3}bMG&oq%DIriqR35=rtEdos5L6z)YC*Xq0U-$_+Il@RaU zXYX%+``hR28`(B*uJ6G9&iz>|)PS%!)9N`7=LcmcxH}k69HPyT-%S zH7+jBCC<%76cg_H-n41cTqnKn`u_V9p~XaTLUe3s{KRPSTeK6apP4Jg%VQ$e#72ms zxyWzmGSRwN?=fRgpx!?W&ZsrLfuhAsRxm%;_|P@3@3~BJwY4ZVBJ3f&$5x>`^fD?d zI+z!v#$!gz%FtL*%mR^Uwa*8LJFZ_;X!y$cD??W#c)31l@ervOa_Qk86R{HJiZb$f z&&&0xYmB{@D@yl~^l5IXtB_ou{xFiYP(Jr<9Ce{jCN z<3Rf2TD%}_N?y>bgWq|{`RKd}n>P4e8Z-D+(fn^4)+|pv$DcR&i+RHNhv$71F*McT zl`phYBlb;wO`b7)*10XF6UXhY9`@UR*6-#(Zp`vyU(__*te6xYtV&N0(zjMtev{tZ zapmGin===teMXjsS0>CYxUy<2izOKOPai0}!B9+6q$s3CF8W{xUwz?A0ADO5&BsiB z{SFt|KehNd-S#eiDq!y&+mW9N_!wH-i~q|oNm=mEzkx}B?Ehe%q$tK8f=QY#*6rH9 zNHHaG(9WBqzP!!TMEktSVuh$i$4A^b25LK}&1*4W?ul*5pZYjL1OZ@X9?3W7Y|T6} z1SXx0Wn-|!A;fZGGlYn9a1Jz5^8)~v#mXhmm>um{QiGG459N}L<&qyD+sy_ixD@AP zW0XV6w#3(JW>TEV}MD=O0O>k5H>p#&|O zD2mGf0Cz7+>l7`NuzGobt;(o@vb9YiOpHN8QJ9Uva|i7R?7nnq;L_iq+ZqPv*oGu! zN@GuJ9fm;yrEFga63m?1qy|5&fd32<%$yP$llh}Udrp>~fb>M>R55I@BsGYhCj8m1 zC=ziFh4@hoytpfrJlr}FsV|C(aV4PZ^8^`G29(+!Bk8APa#PemJqkF zE{IzwPaE)I&r`OxGk*vPErm6sGKaQJ&6FODW$;gAl_4b_j!oH4yE@ zP~Cl4?kp>Ccc~Nm+0kjIb`U0N7}zrQEN5!Ju|}t}LeXi!baZOyhlWha5lq{Ld2rdo zGz7hAJQt<6^cxXTe0xZjmADL85cC&H+~Lt2siIIh{$~+U#&#^{Ub22IA|ea6 z5j12XLc`~dh$$1>3o0Cgvo*ybi$c*z>n=5L&X|>Wy1~eagk;lcEnf^2^2xB=e58Z` z@Rw{1ssK)NRV+2O6c<8qFl%efHE;uy!mq(Xi1P*H2}LMi z3EqWN2U?eW{J$lSFxDJg-=&RH!=6P9!y|S~gmjg)gPKGMxq6r9cNIhW` zS})-obO}Ao_`;=>@fAwU&=|5$J;?~!s4LN2&XiMXEl>zk9M}tVEg#kkIkbKp%Ig2QJ2aCILCM1E=aN*iuz>;q#T_I7aVM=E4$m_#OWLnXQnFUnu?~(X>$@NP zBJ@Zw>@bmErSuW7SR2=6535wh-R`WZ+5dLqwTvw}Ks8~4F#hh0$Qn^l-z=;>D~St( z-1yEjCCgd*z5qXa*bJ7H2Tk54KiX&=Vd}z?%dcc z`N8oeYUKe17&|B5A-++RHh8WQ%;gN{vf%05@jZF%wn1Z_yk#M~Cn(i@MB_mpcbLj5 zR#QAtC`k=tZ*h|){Mjz`7bNL zGWOW=bjQhX@`Vw^xn#cVwn28c2D9vOb0TLLy~-?-%gOyHSeJ9a>P}5OF5$n}k-pvUa*pvLw)KvG~>QjNWS3LY1f*OkFwPZ5qC@+3^Bt=HZbf`alKY#{pn zdY}NEIgo1sd)^TPxVzO{uvU$|Z-jkK0p1x##LexgQ$zx1^bNPOG*u2RmZkIM!zFVz zz|IsP3I?qrlmjGS2w_(azCvGTnf~flqogV@Q%mH{76uLU(>UB zQZ?*ys3BO&TV{Pj_qEa-hkH7mOMe_Bnu3%CXCgu90XNKf$N)PUc3Ei-&~@tT zI^49Lm^+=TrI=h4h=W@jW{GjWd{_kVuSzAL6Pi@HKYYnnNbtcYdIRww+jY$(30=#p8*if(mzbvau z00#}4Qf+gH&ce_&8y3Z@CZV>b%&Zr7xuPSSqOmoaP@arwPrMx^jQBQQi>YvBUdpBn zI``MZ3I3HLqp)@vk^E|~)zw$0$VI_RPsL9u(kqulmS`tnb%4U)hm{)h@bG*jw@Y*#MX;Th1wu3TrO}Srn_+YWYesEgkO1 zv?P8uWB)is;#&=xBBLf+y5e4?%y>_8$1KwkAJ8UcW|0CIz89{LydfJKr^RF=JFPi}MAv|ecbuZ!YcTSxsD$(Pr#W*oytl?@+2 zXBFb32Kf_G3~EgOS7C`8w!tx}DcCT%+#qa76VSbnHo;4(oJ7)}mm?b5V65ir`7Z}s zR2)m15b#E}z_2@rf34wo!M^CnVoi# ze+S(IK({C6u=Sm{1>F~?)8t&fZpOOPcby;I3jO;7^xmLKM(<%i-nyj9mgw9F1Lq4|DZUHZ4)V9&6fQM(ZxbG{h+}(koiTu`SQw6#6q2Yg z-d+1+MRp$zYT2neIR2cKij2!R;C~ooQ3<;^8)_Gch&ZyEtiQwmF0Mb_)6)4lVEBF< zklXS7hvtu30uJR`3OzcqUNOdYsfrKSGkIQAk|4=&#ggxdU4^Y(;)$8}fQ>lTgQdJ{ zzie8+1$3@E;|a`kzuFh9Se}%RHTmBg)h$eH;gttjL_)pO^10?!bNev6{mLMaQpY<< z7M^ZXrg>tw;vU@9H=khbff?@nu)Yw4G% zGxobPTUR2p_ed7Lvx?dkrN^>Cv$Axuwk;Wj{5Z@#$sK@f4{7SHg%2bpcS{(~s;L(mz@9r$cK@m~ef&vf%1@ z@8&@LLO2lQso|bJD6}+_L1*D^}>oqg~$NipL>QlP3 zM#ATSy@ycMkKs5-0X8nFAtMhO_=$DlWR+@EaZ}`YduRD4A2@!at3NYRHmlENea9IF zN*s>mi?zy*Vv+F+&4-o`Wj}P3mLGM*&M(z|;?d82>hQkkY?e-hJ47mWOLCPL*MO04 z3lE(n2RM=IIo;Z?I=sKJ_h=iJHbQ2<}WW0b@I6Qf-{T=Qn#@N0yG5xH&ofEy^mZMPzd22nR`t!Q)VkNgf*VOxE z$XhOunG3ZN#`Ks$Hp~}`OX5vmHP={GYUJ+-g0%PS$*Qi5+-40M47zJ24vK1#? zb$s^%r?+>#lw$mpZaMa1aO%wlPm3~cno_(S%U&-R;6eK(@`CjswAW2)HfZ>ptItaZ|XqQ z&sHVVL>WCe|E4iPb2~gS5ITs6xfg(kmt&3$YcI=zTuqj37t|+9ojCr(G^ul#p{>k) zM94pI>~5VZ$!*Qurq<@RIXgP3sx-2kL$1Q~da%rnNIh?)&+c~*&e~CYPDhPYjb+Xu zKg5w^XB3(_9{Waa4E(-J-Kq_u6t_k?a8kEHqai-N-4#`SRerO!h}!cS%SMC<)tGix zOzVP^_t!HN&HIPL-ZpcgWitHM&yFRC7!k4zSI+-<_uQ}|tX)n{Ib;X>Xx>i_d*KkH zCzogKQFpP1408_2!ofU|iBq2R8hW6G zuqJs9Tyw{u%-uWczPLkM!MfKfflt+NK9Vk8E!C>AsJwNDRoe2~cL+UvqNP|5J8t)( z0$iMa!jhudJ+fqFn+um&@Oj6qXJd_3-l`S^I1#0fnt!z3?D*hAHr*u(*wR@`4O z#avrtg%s`Fh{?$FtBFM^$@@hW!8ZfF4;=n0<8In&X}-Rp=cd0TqT_ne46$j^r}FzE z26vX^!PzScuQfFfl1HEZ{zL?G88mcc76zHGizWiykBf4m83Z${So-+dZ~YGhm*RO7 zB1gdIdqnFi?qw+lPRFW5?}CQ3Me3G^muvll&4iN+*5#_mmIu;loULMwb4lu9U*dFM z-Sr**(0Ei~u=$3<6>C-G6z4_LNCx||6YtjS)<;hf)YJTPKXW+w%hhCTUAInIse9>r zl2YU6nRb$u-FJlWN*{{%sm_gi_UP5{=?5}5^D2vPzM=oPfNw~azZQ#P zl5z8RtSSiTIpEohC15i-Q1Bk{3&ElsD0uGAOxvbk29VUDmmA0w;^v`W#0`};O3DVE z&+-ca*`YcN%z*#VXWK9Qa-OEME#fykF%|7o=1Y+eF;Rtv0W4~kKRDx9YBHOWhC%^I z$Jec0cC7o37}Xt}cu)NH5R}NT+=2Nap*`^%O)vz?+{PV<2~qX%TzdJOGeKj5_QjqR&a3*K@= P-1+_A+?hGkL;m(J7kc&K diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist index 0513117f..a060db61 100644 --- a/example/ios/Runner/Info.plist +++ b/example/ios/Runner/Info.plist @@ -3,7 +3,7 @@ CFBundleDevelopmentRegion - en + $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier diff --git a/example/ios/Runner/Runner-Bridging-Header.h b/example/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 00000000..308a2a56 --- /dev/null +++ b/example/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/example/ios/Runner/main.m b/example/ios/Runner/main.m deleted file mode 100644 index dff6597e..00000000 --- a/example/ios/Runner/main.m +++ /dev/null @@ -1,9 +0,0 @@ -#import -#import -#import "AppDelegate.h" - -int main(int argc, char* argv[]) { - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } -} diff --git a/example/lib/main.dart b/example/lib/main.dart index 795ccd8c..84e30b1b 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -249,7 +249,9 @@ class _MyHomePageState extends State with TickerProviderStateMixin { shape: BoxShape.rectangle, color: _calendarController.isSelected(date) ? Colors.brown[500] - : _calendarController.isToday(date) ? Colors.brown[300] : Colors.blue[400], + : _calendarController.isToday(date) + ? Colors.brown[300] + : Colors.blue[400], ), width: 16.0, height: 16.0, diff --git a/example/pubspec.lock b/example/pubspec.lock new file mode 100644 index 00000000..3a8fee5d --- /dev/null +++ b/example/pubspec.lock @@ -0,0 +1,175 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.5.0-nullsafety.1" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety.1" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.3" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety.1" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.1" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.15.0-nullsafety.3" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety.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: transitive + description: + name: intl + url: "https://pub.dartlang.org" + source: hosted + version: "0.16.1" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.10-nullsafety.1" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0-nullsafety.3" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0-nullsafety.1" + simple_gesture_detector: + dependency: transitive + description: + name: simple_gesture_detector + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.4" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0-nullsafety.2" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0-nullsafety.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety.1" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.1" + table_calendar: + dependency: "direct main" + description: + path: ".." + relative: true + source: path + version: "2.2.3" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety.1" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.19-nullsafety.2" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0-nullsafety.3" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety.3" +sdks: + dart: ">=2.10.0-110 <2.11.0" + flutter: ">=1.17.0 <2.0.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index b164eef7..6663bdd2 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -6,11 +6,15 @@ description: A short demo of table_calendar package. # followed by an optional build number separated by a +. # Both the version and the builder number may be overridden in flutter # build by specifying --build-name and --build-number, respectively. -# Read more about versioning at semver.org. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html version: 1.0.0+1 environment: - sdk: ">=2.1.0 <3.0.0" + sdk: ">=2.7.0 <3.0.0" dependencies: flutter: @@ -18,7 +22,7 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^0.1.2 + cupertino_icons: ^1.0.0 table_calendar: path: ../ @@ -27,9 +31,8 @@ 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 +# following page: https://dart.dev/tools/pub/pubspec # The following section is specific to Flutter. flutter: @@ -41,14 +44,14 @@ flutter: # To add assets to your application, add an assets section, like this: # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.io/assets-and-images/#resolution-aware. + # https://flutter.dev/assets-and-images/#resolution-aware. # For details regarding adding assets from package dependencies, see - # https://flutter.io/assets-and-images/#from-packages + # https://flutter.dev/assets-and-images/#from-packages # To add custom fonts to your application, add a fonts section here, # in this "flutter" section. Each entry in this list should have a @@ -68,4 +71,4 @@ flutter: # weight: 700 # # For details regarding fonts from package dependencies, - # see https://flutter.io/custom-fonts/#from-packages + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/pubspec.lock b/pubspec.lock index cda66d9a..7e966689 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,62 +1,55 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: - archive: - dependency: transitive - description: - name: archive - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.11" - args: - dependency: transitive - description: - name: args - url: "https://pub.dartlang.org" - source: hosted - version: "1.5.2" async: dependency: transitive description: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.4.0" + version: "2.5.0-nullsafety.1" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "1.0.5" + version: "2.1.0-nullsafety.1" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.3" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.1.2" - collection: + version: "1.2.0-nullsafety.1" + clock: dependency: transitive description: - name: collection + name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.14.11" - convert: + version: "1.1.0-nullsafety.1" + collection: dependency: transitive description: - name: convert + name: collection url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" - crypto: + version: "1.15.0-nullsafety.3" + fake_async: dependency: transitive description: - name: crypto + name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "2.1.3" + version: "1.2.0-nullsafety.1" flutter: dependency: "direct main" description: flutter @@ -67,69 +60,41 @@ packages: description: flutter source: sdk version: "0.0.0" - image: - dependency: transitive - description: - name: image - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.4" intl: dependency: "direct main" description: name: intl url: "https://pub.dartlang.org" source: hosted - version: "0.16.0" + version: "0.16.1" matcher: dependency: transitive description: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.6" + version: "0.12.10-nullsafety.1" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.1.8" + version: "1.3.0-nullsafety.3" path: dependency: transitive description: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.6.4" - pedantic: - dependency: transitive - description: - name: pedantic - url: "https://pub.dartlang.org" - source: hosted - version: "1.8.0+1" - petitparser: - dependency: transitive - description: - name: petitparser - url: "https://pub.dartlang.org" - source: hosted - version: "2.4.0" - quiver: - dependency: transitive - description: - name: quiver - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.5" + version: "1.8.0-nullsafety.1" simple_gesture_detector: dependency: "direct main" description: name: simple_gesture_detector url: "https://pub.dartlang.org" source: hosted - version: "0.1.3" + version: "0.1.4" sky_engine: dependency: transitive description: flutter @@ -141,62 +106,56 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.5.5" + version: "1.8.0-nullsafety.2" stack_trace: dependency: transitive description: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.9.3" + version: "1.10.0-nullsafety.1" stream_channel: dependency: transitive description: name: stream_channel url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.1.0-nullsafety.1" string_scanner: dependency: transitive description: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.0.5" + version: "1.1.0-nullsafety.1" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.0-nullsafety.1" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.11" + version: "0.2.19-nullsafety.2" typed_data: dependency: transitive description: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.1.6" + version: "1.3.0-nullsafety.3" vector_math: dependency: transitive description: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.0.8" - xml: - dependency: transitive - description: - name: xml - url: "https://pub.dartlang.org" - source: hosted - version: "3.5.0" + version: "2.1.0-nullsafety.3" sdks: - dart: ">=2.4.0 <3.0.0" + dart: ">=2.10.0-110 <2.11.0" + flutter: ">=1.17.0 <2.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index 9f247263..b5f09f11 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,14 +5,15 @@ author: Aleksander Woźniak homepage: https://github.com/aleksanderwozniak/table_calendar environment: - sdk: ">=2.2.2 <3.0.0" + sdk: ">=2.7.0 <3.0.0" + flutter: ">=1.17.0 <2.0.0" dependencies: flutter: sdk: flutter - + intl: ">=0.15.0 <0.17.0" - simple_gesture_detector: ^0.1.3 + simple_gesture_detector: ^0.1.4 dev_dependencies: flutter_test: diff --git a/table_calendar.iml b/table_calendar.iml index 499f388f..6048a33b 100644 --- a/table_calendar.iml +++ b/table_calendar.iml @@ -9,11 +9,10 @@ - - - + + From 3710547bfa4a998b608980c932fc19d979a6eef4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Thu, 15 Oct 2020 20:56:38 +0200 Subject: [PATCH 003/164] Add holidays to onDaySelected --- example/lib/main.dart | 16 ++++++++-------- lib/src/calendar.dart | 14 +++++++++++--- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 84e30b1b..61b03306 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -7,11 +7,11 @@ import 'package:table_calendar/table_calendar.dart'; // Example holidays final Map _holidays = { - DateTime(2019, 1, 1): ['New Year\'s Day'], - DateTime(2019, 1, 6): ['Epiphany'], - DateTime(2019, 2, 14): ['Valentine\'s Day'], - DateTime(2019, 4, 21): ['Easter Sunday'], - DateTime(2019, 4, 22): ['Easter Monday'], + DateTime(2020, 1, 1): ['New Year\'s Day'], + DateTime(2020, 1, 6): ['Epiphany'], + DateTime(2020, 2, 14): ['Valentine\'s Day'], + DateTime(2020, 4, 21): ['Easter Sunday'], + DateTime(2020, 4, 22): ['Easter Monday'], }; void main() { @@ -87,7 +87,7 @@ class _MyHomePageState extends State with TickerProviderStateMixin { super.dispose(); } - void _onDaySelected(DateTime day, List events) { + void _onDaySelected(DateTime day, List events, List holidays) { print('CALLBACK: _onDaySelected'); setState(() { _selectedEvents = events; @@ -233,8 +233,8 @@ class _MyHomePageState extends State with TickerProviderStateMixin { return children; }, ), - onDaySelected: (date, events) { - _onDaySelected(date, events); + onDaySelected: (date, events, holidays) { + _onDaySelected(date, events, holidays); _animationController.forward(from: 0.0); }, onVisibleDaysChanged: _onVisibleDaysChanged, diff --git a/lib/src/calendar.dart b/lib/src/calendar.dart index 09f21a94..52533a72 100644 --- a/lib/src/calendar.dart +++ b/lib/src/calendar.dart @@ -4,7 +4,7 @@ part of table_calendar; /// Callback exposing currently selected day. -typedef void OnDaySelected(DateTime day, List events); +typedef void OnDaySelected(DateTime day, List events, List holidays); /// Callback exposing currently visible days (first and last of them), as well as current `CalendarFormat`. typedef void OnVisibleDaysChanged(DateTime first, DateTime last, CalendarFormat format); @@ -248,7 +248,11 @@ class _TableCalendarState extends State with SingleTickerProvider void _selectedDayCallback(DateTime day) { if (widget.onDaySelected != null) { - widget.onDaySelected(day, widget.calendarController.visibleEvents[_getEventKey(day)] ?? []); + widget.onDaySelected( + day, + widget.calendarController.visibleEvents[_getEventKey(day)] ?? [], + widget.calendarController.visibleHolidays[_getHolidayKey(day)] ?? [], + ); } } @@ -273,7 +277,11 @@ class _TableCalendarState extends State with SingleTickerProvider void _onDayLongPressed(DateTime day) { if (widget.onDayLongPressed != null) { - widget.onDayLongPressed(day, widget.calendarController.visibleEvents[_getEventKey(day)] ?? []); + widget.onDayLongPressed( + day, + widget.calendarController.visibleEvents[_getEventKey(day)] ?? [], + widget.calendarController.visibleHolidays[_getHolidayKey(day)] ?? [], + ); } } From 9617d5cb37c071e04aff965bf20d3c93b1dffade Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Thu, 15 Oct 2020 21:42:40 +0200 Subject: [PATCH 004/164] Replace deprecated overflow property --- lib/src/calendar.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/calendar.dart b/lib/src/calendar.dart index 52533a72..7c22aa9a 100644 --- a/lib/src/calendar.dart +++ b/lib/src/calendar.dart @@ -622,7 +622,7 @@ class _TableCalendarState extends State with SingleTickerProvider content = Stack( alignment: widget.calendarStyle.markersAlignment, children: children, - overflow: widget.calendarStyle.canEventMarkersOverflow ? Overflow.visible : Overflow.clip, + clipBehavior: widget.calendarStyle.canEventMarkersOverflow ? Clip.none : Clip.hardEdge, ); } } From dc67941276338dec6b887e84fdbc4c47b1c6726a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Thu, 15 Oct 2020 21:45:32 +0200 Subject: [PATCH 005/164] Use default formatting --- example/lib/main.dart | 72 ++++++-- lib/src/calendar.dart | 167 +++++++++++++----- lib/src/calendar_controller.dart | 29 +-- lib/src/customization/calendar_builders.dart | 9 +- lib/src/customization/calendar_style.dart | 24 ++- lib/src/customization/days_of_week_style.dart | 6 +- lib/src/customization/header_style.dart | 12 +- lib/src/widgets/cell_widget.dart | 17 +- 8 files changed, 243 insertions(+), 93 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 61b03306..c8e93738 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -52,21 +52,57 @@ class _MyHomePageState extends State with TickerProviderStateMixin { final _selectedDay = DateTime.now(); _events = { - _selectedDay.subtract(Duration(days: 30)): ['Event A0', 'Event B0', 'Event C0'], + _selectedDay.subtract(Duration(days: 30)): [ + 'Event A0', + 'Event B0', + 'Event C0' + ], _selectedDay.subtract(Duration(days: 27)): ['Event A1'], - _selectedDay.subtract(Duration(days: 20)): ['Event A2', 'Event B2', 'Event C2', 'Event D2'], + _selectedDay.subtract(Duration(days: 20)): [ + 'Event A2', + 'Event B2', + 'Event C2', + 'Event D2' + ], _selectedDay.subtract(Duration(days: 16)): ['Event A3', 'Event B3'], - _selectedDay.subtract(Duration(days: 10)): ['Event A4', 'Event B4', 'Event C4'], - _selectedDay.subtract(Duration(days: 4)): ['Event A5', 'Event B5', 'Event C5'], + _selectedDay.subtract(Duration(days: 10)): [ + 'Event A4', + 'Event B4', + 'Event C4' + ], + _selectedDay.subtract(Duration(days: 4)): [ + 'Event A5', + 'Event B5', + 'Event C5' + ], _selectedDay.subtract(Duration(days: 2)): ['Event A6', 'Event B6'], _selectedDay: ['Event A7', 'Event B7', 'Event C7', 'Event D7'], - _selectedDay.add(Duration(days: 1)): ['Event A8', 'Event B8', 'Event C8', 'Event D8'], - _selectedDay.add(Duration(days: 3)): Set.from(['Event A9', 'Event A9', 'Event B9']).toList(), - _selectedDay.add(Duration(days: 7)): ['Event A10', 'Event B10', 'Event C10'], + _selectedDay.add(Duration(days: 1)): [ + 'Event A8', + 'Event B8', + 'Event C8', + 'Event D8' + ], + _selectedDay.add(Duration(days: 3)): + Set.from(['Event A9', 'Event A9', 'Event B9']).toList(), + _selectedDay.add(Duration(days: 7)): [ + 'Event A10', + 'Event B10', + 'Event C10' + ], _selectedDay.add(Duration(days: 11)): ['Event A11', 'Event B11'], - _selectedDay.add(Duration(days: 17)): ['Event A12', 'Event B12', 'Event C12', 'Event D12'], + _selectedDay.add(Duration(days: 17)): [ + 'Event A12', + 'Event B12', + 'Event C12', + 'Event D12' + ], _selectedDay.add(Duration(days: 22)): ['Event A13', 'Event B13'], - _selectedDay.add(Duration(days: 26)): ['Event A14', 'Event B14', 'Event C14'], + _selectedDay.add(Duration(days: 26)): [ + 'Event A14', + 'Event B14', + 'Event C14' + ], }; _selectedEvents = _events[_selectedDay] ?? []; @@ -94,11 +130,13 @@ class _MyHomePageState extends State with TickerProviderStateMixin { }); } - void _onVisibleDaysChanged(DateTime first, DateTime last, CalendarFormat format) { + void _onVisibleDaysChanged( + DateTime first, DateTime last, CalendarFormat format) { print('CALLBACK: _onVisibleDaysChanged'); } - void _onCalendarCreated(DateTime first, DateTime last, CalendarFormat format) { + void _onCalendarCreated( + DateTime first, DateTime last, CalendarFormat format) { print('CALLBACK: _onCalendarCreated'); } @@ -138,7 +176,8 @@ class _MyHomePageState extends State with TickerProviderStateMixin { outsideDaysVisible: false, ), headerStyle: HeaderStyle( - formatButtonTextStyle: TextStyle().copyWith(color: Colors.white, fontSize: 15.0), + formatButtonTextStyle: + TextStyle().copyWith(color: Colors.white, fontSize: 15.0), formatButtonDecoration: BoxDecoration( color: Colors.deepOrange[400], borderRadius: BorderRadius.circular(16.0), @@ -296,7 +335,8 @@ class _MyHomePageState extends State with TickerProviderStateMixin { child: Text('2 weeks'), onPressed: () { setState(() { - _calendarController.setCalendarFormat(CalendarFormat.twoWeeks); + _calendarController + .setCalendarFormat(CalendarFormat.twoWeeks); }); }, ), @@ -312,7 +352,8 @@ class _MyHomePageState extends State with TickerProviderStateMixin { ), const SizedBox(height: 8.0), RaisedButton( - child: Text('Set day ${dateTime.day}-${dateTime.month}-${dateTime.year}'), + child: Text( + 'Set day ${dateTime.day}-${dateTime.month}-${dateTime.year}'), onPressed: () { _calendarController.setSelectedDay( DateTime(dateTime.year, dateTime.month, dateTime.day), @@ -332,7 +373,8 @@ class _MyHomePageState extends State with TickerProviderStateMixin { border: Border.all(width: 0.8), borderRadius: BorderRadius.circular(12.0), ), - margin: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0), + margin: + const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0), child: ListTile( title: Text(event.toString()), onTap: () => print('$event tapped!'), diff --git a/lib/src/calendar.dart b/lib/src/calendar.dart index 7c22aa9a..7b9b0083 100644 --- a/lib/src/calendar.dart +++ b/lib/src/calendar.dart @@ -7,10 +7,12 @@ part of table_calendar; typedef void OnDaySelected(DateTime day, List events, List holidays); /// Callback exposing currently visible days (first and last of them), as well as current `CalendarFormat`. -typedef void OnVisibleDaysChanged(DateTime first, DateTime last, CalendarFormat format); +typedef void OnVisibleDaysChanged( + DateTime first, DateTime last, CalendarFormat format); /// Callback exposing initially visible days (first and last of them), as well as initial `CalendarFormat`. -typedef void OnCalendarCreated(DateTime first, DateTime last, CalendarFormat format); +typedef void OnCalendarCreated( + DateTime first, DateTime last, CalendarFormat format); /// Signature for reacting to header gestures. Exposes current month and year as a `DateTime` object. typedef void HeaderGestureCallback(DateTime focusedDay); @@ -35,7 +37,15 @@ enum FormatAnimation { slide, scale } /// * `StartingDayOfWeek.friday`: Friday - Thursday /// * `StartingDayOfWeek.saturday`: Saturday - Friday /// * `StartingDayOfWeek.sunday`: Sunday - Saturday -enum StartingDayOfWeek { monday, tuesday, wednesday, thursday, friday, saturday, sunday } +enum StartingDayOfWeek { + monday, + tuesday, + wednesday, + thursday, + friday, + saturday, + sunday +} int _getWeekdayNumber(StartingDayOfWeek weekday) { return StartingDayOfWeek.values.indexOf(weekday) + 1; @@ -205,7 +215,8 @@ class TableCalendar extends StatefulWidget { assert(availableCalendarFormats.length <= CalendarFormat.values.length), assert(weekendDays != null), assert(weekendDays.isNotEmpty - ? weekendDays.every((day) => day >= DateTime.monday && day <= DateTime.sunday) + ? weekendDays.every( + (day) => day >= DateTime.monday && day <= DateTime.sunday) : true), super(key: key); @@ -213,7 +224,8 @@ class TableCalendar extends StatefulWidget { _TableCalendarState createState() => _TableCalendarState(); } -class _TableCalendarState extends State with SingleTickerProviderStateMixin { +class _TableCalendarState extends State + with SingleTickerProviderStateMixin { @override void initState() { super.initState(); @@ -326,13 +338,19 @@ class _TableCalendarState extends State with SingleTickerProvider } bool _isDayUnavailable(DateTime day) { - return (widget.startDay != null && day.isBefore(widget.calendarController._normalizeDate(widget.startDay))) || - (widget.endDay != null && day.isAfter(widget.calendarController._normalizeDate(widget.endDay))) || + return (widget.startDay != null && + day.isBefore( + widget.calendarController._normalizeDate(widget.startDay))) || + (widget.endDay != null && + day.isAfter( + widget.calendarController._normalizeDate(widget.endDay))) || (!_isDayEnabled(day)); } bool _isDayEnabled(DateTime day) { - return widget.enabledDayPredicate == null ? true : widget.enabledDayPredicate(day); + return widget.enabledDayPredicate == null + ? true + : widget.enabledDayPredicate(day); } DateTime _getEventKey(DateTime day) { @@ -373,10 +391,14 @@ class _TableCalendarState extends State with SingleTickerProvider onLongPress: _onHeaderLongPressed, child: Text( widget.headerStyle.titleTextBuilder != null - ? widget.headerStyle.titleTextBuilder(widget.calendarController.focusedDay, widget.locale) - : DateFormat.yMMMM(widget.locale).format(widget.calendarController.focusedDay), + ? widget.headerStyle.titleTextBuilder( + widget.calendarController.focusedDay, widget.locale) + : DateFormat.yMMMM(widget.locale) + .format(widget.calendarController.focusedDay), style: widget.headerStyle.titleTextStyle, - textAlign: widget.headerStyle.centerHeaderTitle ? TextAlign.center : TextAlign.start, + textAlign: widget.headerStyle.centerHeaderTitle + ? TextAlign.center + : TextAlign.start, ), ), ), @@ -388,7 +410,8 @@ class _TableCalendarState extends State with SingleTickerProvider ), ]; - if (widget.headerStyle.formatButtonVisible && widget.availableCalendarFormats.length > 1) { + if (widget.headerStyle.formatButtonVisible && + widget.availableCalendarFormats.length > 1) { children.insert(2, const SizedBox(width: 8.0)); children.insert(3, _buildFormatButton()); } @@ -421,7 +444,11 @@ class _TableCalendarState extends State with SingleTickerProvider Widget _buildCalendarContent() { if (widget.formatAnimation == FormatAnimation.slide) { return AnimatedSize( - duration: Duration(milliseconds: widget.calendarController.calendarFormat == CalendarFormat.month ? 330 : 220), + duration: Duration( + milliseconds: + widget.calendarController.calendarFormat == CalendarFormat.month + ? 330 + : 220), curve: Curves.fastOutSlowIn, alignment: Alignment(0, -1), vsync: this, @@ -482,7 +509,8 @@ class _TableCalendarState extends State with SingleTickerProvider child: child, onVerticalSwipe: (direction) { setState(() { - widget.calendarController.swipeCalendarFormat(isSwipeUp: direction == SwipeDirection.up); + widget.calendarController + .swipeCalendarFormat(isSwipeUp: direction == SwipeDirection.up); }); }, swipeConfig: widget.simpleSwipeConfig, @@ -495,8 +523,10 @@ class _TableCalendarState extends State with SingleTickerProvider switchInCurve: Curves.decelerate, transitionBuilder: (child, animation) { return SlideTransition( - position: - Tween(begin: Offset(widget.calendarController._dx, 0), end: Offset(0, 0)).animate(animation), + position: Tween( + begin: Offset(widget.calendarController._dx, 0), + end: Offset(0, 0)) + .animate(animation), child: child, ); }, @@ -519,7 +549,10 @@ class _TableCalendarState extends State with SingleTickerProvider int x = 0; while (x < widget.calendarController._visibleDays.value.length) { - children.add(_buildTableRow(widget.calendarController._visibleDays.value.skip(x).take(daysInWeek).toList())); + children.add(_buildTableRow(widget.calendarController._visibleDays.value + .skip(x) + .take(daysInWeek) + .toList())); x += daysInWeek; } @@ -532,11 +565,13 @@ class _TableCalendarState extends State with SingleTickerProvider TableRow _buildDaysOfWeek() { return TableRow( - children: widget.calendarController._visibleDays.value.take(7).map((date) { + children: + widget.calendarController._visibleDays.value.take(7).map((date) { final weekdayString = widget.daysOfWeekStyle.dowTextBuilder != null ? widget.daysOfWeekStyle.dowTextBuilder(date, widget.locale) : DateFormat.E(widget.locale).format(date); - final isWeekend = widget.calendarController._isWeekend(date, widget.weekendDays); + final isWeekend = + widget.calendarController._isWeekend(date, widget.weekendDays); if (isWeekend && widget.builders.dowWeekendBuilder != null) { return widget.builders.dowWeekendBuilder(context, weekdayString); @@ -547,7 +582,9 @@ class _TableCalendarState extends State with SingleTickerProvider return Center( child: Text( weekdayString, - style: isWeekend ? widget.daysOfWeekStyle.weekendStyle : widget.daysOfWeekStyle.weekdayStyle, + style: isWeekend + ? widget.daysOfWeekStyle.weekendStyle + : widget.daysOfWeekStyle.weekdayStyle, ), ); }).toList(), @@ -555,7 +592,8 @@ class _TableCalendarState extends State with SingleTickerProvider } TableRow _buildTableRow(List days) { - return TableRow(children: days.map((date) => _buildTableCell(date)).toList()); + return TableRow( + children: days.map((date) => _buildTableCell(date)).toList()); } // TableCell will have equal width and height @@ -586,8 +624,12 @@ class _TableCalendarState extends State with SingleTickerProvider if (key != null) { final children = [content]; - final events = eventKey != null ? widget.calendarController.visibleEvents[eventKey] : []; - final holidays = holidayKey != null ? widget.calendarController.visibleHolidays[holidayKey] : []; + final events = eventKey != null + ? widget.calendarController.visibleEvents[eventKey] + : []; + final holidays = holidayKey != null + ? widget.calendarController.visibleHolidays[holidayKey] + : []; if (!_isDayUnavailable(date)) { if (widget.builders.markersBuilder != null) { @@ -622,15 +664,21 @@ class _TableCalendarState extends State with SingleTickerProvider content = Stack( alignment: widget.calendarStyle.markersAlignment, children: children, - clipBehavior: widget.calendarStyle.canEventMarkersOverflow ? Clip.none : Clip.hardEdge, + clipBehavior: widget.calendarStyle.canEventMarkersOverflow + ? Clip.none + : Clip.hardEdge, ); } } return GestureDetector( behavior: widget.dayHitTestBehavior, - onTap: () => _isDayUnavailable(date) ? _onUnavailableDaySelected() : _selectDay(date), - onLongPress: () => _isDayUnavailable(date) ? _onUnavailableDayLongPressed() : _onDayLongPressed(date), + onTap: () => _isDayUnavailable(date) + ? _onUnavailableDaySelected() + : _selectDay(date), + onLongPress: () => _isDayUnavailable(date) + ? _onUnavailableDayLongPressed() + : _onDayLongPressed(date), child: content, ); } @@ -642,39 +690,64 @@ class _TableCalendarState extends State with SingleTickerProvider final tIsSelected = widget.calendarController.isSelected(date); final tIsToday = widget.calendarController.isToday(date); final tIsOutside = widget.calendarController._isExtraDay(date); - final tIsHoliday = widget.calendarController.visibleHolidays.containsKey(_getHolidayKey(date)); - final tIsWeekend = widget.calendarController._isWeekend(date, widget.weekendDays); - - final isUnavailable = widget.builders.unavailableDayBuilder != null && tIsUnavailable; - final isSelected = widget.builders.selectedDayBuilder != null && tIsSelected; + final tIsHoliday = widget.calendarController.visibleHolidays + .containsKey(_getHolidayKey(date)); + final tIsWeekend = + widget.calendarController._isWeekend(date, widget.weekendDays); + + final isUnavailable = + widget.builders.unavailableDayBuilder != null && tIsUnavailable; + final isSelected = + widget.builders.selectedDayBuilder != null && tIsSelected; final isToday = widget.builders.todayDayBuilder != null && tIsToday; - final isOutsideHoliday = widget.builders.outsideHolidayDayBuilder != null && tIsOutside && tIsHoliday; - final isHoliday = widget.builders.holidayDayBuilder != null && !tIsOutside && tIsHoliday; - final isOutsideWeekend = - widget.builders.outsideWeekendDayBuilder != null && tIsOutside && tIsWeekend && !tIsHoliday; - final isOutside = widget.builders.outsideDayBuilder != null && tIsOutside && !tIsWeekend && !tIsHoliday; - final isWeekend = widget.builders.weekendDayBuilder != null && !tIsOutside && tIsWeekend && !tIsHoliday; + final isOutsideHoliday = widget.builders.outsideHolidayDayBuilder != null && + tIsOutside && + tIsHoliday; + final isHoliday = + widget.builders.holidayDayBuilder != null && !tIsOutside && tIsHoliday; + final isOutsideWeekend = widget.builders.outsideWeekendDayBuilder != null && + tIsOutside && + tIsWeekend && + !tIsHoliday; + final isOutside = widget.builders.outsideDayBuilder != null && + tIsOutside && + !tIsWeekend && + !tIsHoliday; + final isWeekend = widget.builders.weekendDayBuilder != null && + !tIsOutside && + tIsWeekend && + !tIsHoliday; if (isUnavailable) { - return widget.builders.unavailableDayBuilder(context, date, widget.calendarController.visibleEvents[eventKey]); + return widget.builders.unavailableDayBuilder( + context, date, widget.calendarController.visibleEvents[eventKey]); } else if (isSelected && widget.calendarStyle.renderSelectedFirst) { - return widget.builders.selectedDayBuilder(context, date, widget.calendarController.visibleEvents[eventKey]); + return widget.builders.selectedDayBuilder( + context, date, widget.calendarController.visibleEvents[eventKey]); } else if (isToday) { - return widget.builders.todayDayBuilder(context, date, widget.calendarController.visibleEvents[eventKey]); + return widget.builders.todayDayBuilder( + context, date, widget.calendarController.visibleEvents[eventKey]); } else if (isSelected) { - return widget.builders.selectedDayBuilder(context, date, widget.calendarController.visibleEvents[eventKey]); + return widget.builders.selectedDayBuilder( + context, date, widget.calendarController.visibleEvents[eventKey]); } else if (isOutsideHoliday) { - return widget.builders.outsideHolidayDayBuilder(context, date, widget.calendarController.visibleEvents[eventKey]); + return widget.builders.outsideHolidayDayBuilder( + context, date, widget.calendarController.visibleEvents[eventKey]); } else if (isHoliday) { - return widget.builders.holidayDayBuilder(context, date, widget.calendarController.visibleEvents[eventKey]); + return widget.builders.holidayDayBuilder( + context, date, widget.calendarController.visibleEvents[eventKey]); } else if (isOutsideWeekend) { - return widget.builders.outsideWeekendDayBuilder(context, date, widget.calendarController.visibleEvents[eventKey]); + return widget.builders.outsideWeekendDayBuilder( + context, date, widget.calendarController.visibleEvents[eventKey]); } else if (isOutside) { - return widget.builders.outsideDayBuilder(context, date, widget.calendarController.visibleEvents[eventKey]); + return widget.builders.outsideDayBuilder( + context, date, widget.calendarController.visibleEvents[eventKey]); } else if (isWeekend) { - return widget.builders.weekendDayBuilder(context, date, widget.calendarController.visibleEvents[eventKey]); + return widget.builders.weekendDayBuilder( + context, date, widget.calendarController.visibleEvents[eventKey]); } else if (widget.builders.dayBuilder != null) { - return widget.builders.dayBuilder(context, date, widget.calendarController.visibleEvents[eventKey]); + return widget.builders.dayBuilder( + context, date, widget.calendarController.visibleEvents[eventKey]); } else { return _CellWidget( text: '${date.day}', diff --git a/lib/src/calendar_controller.dart b/lib/src/calendar_controller.dart index 7539268a..721265bf 100644 --- a/lib/src/calendar_controller.dart +++ b/lib/src/calendar_controller.dart @@ -35,9 +35,10 @@ class CalendarController { CalendarFormat get calendarFormat => _calendarFormat.value; /// List of currently visible days. - List get visibleDays => calendarFormat == CalendarFormat.month && !_includeInvisibleDays - ? _visibleDays.value.where((day) => !_isExtraDay(day)).toList() - : _visibleDays.value; + List get visibleDays => + calendarFormat == CalendarFormat.month && !_includeInvisibleDays + ? _visibleDays.value.where((day) => !_isExtraDay(day)).toList() + : _visibleDays.value; /// `Map` of currently visible events. Map get visibleEvents { @@ -240,8 +241,9 @@ class CalendarController { return formats[id]; } - String _getFormatButtonText() => - _useNextCalendarFormat ? _availableCalendarFormats[_nextFormat()] : _availableCalendarFormats[_calendarFormat.value]; + String _getFormatButtonText() => _useNextCalendarFormat + ? _availableCalendarFormats[_nextFormat()] + : _availableCalendarFormats[_calendarFormat.value]; void _selectPrevious() { if (calendarFormat == CalendarFormat.month) { @@ -283,7 +285,8 @@ class CalendarController { _focusedDay = _previousWeek(_focusedDay); } else { // in bottom row OR not visible - _focusedDay = _previousWeek(_focusedDay.subtract(const Duration(days: 7))); + _focusedDay = + _previousWeek(_focusedDay.subtract(const Duration(days: 7))); } } @@ -394,7 +397,9 @@ class CalendarController { } DateTime _lastDayOfMonth(DateTime month) { - final date = month.month < 12 ? DateTime.utc(month.year, month.month + 1, 1, 12) : DateTime.utc(month.year + 1, 1, 1, 12); + final date = month.month < 12 + ? DateTime.utc(month.year, month.month + 1, 1, 12) + : DateTime.utc(month.year + 1, 1, 1, 12); return date.subtract(const Duration(days: 1)); } @@ -436,11 +441,13 @@ class CalendarController { } DateTime _getEventKey(DateTime day) { - return visibleEvents.keys.firstWhere((it) => _isSameDay(it, day), orElse: () => null); + return visibleEvents.keys + .firstWhere((it) => _isSameDay(it, day), orElse: () => null); } DateTime _getHolidayKey(DateTime day) { - return visibleHolidays.keys.firstWhere((it) => _isSameDay(it, day), orElse: () => null); + return visibleHolidays.keys + .firstWhere((it) => _isSameDay(it, day), orElse: () => null); } /// Returns true if `day` is currently selected. @@ -454,7 +461,9 @@ class CalendarController { } bool _isSameDay(DateTime dayA, DateTime dayB) { - return dayA.year == dayB.year && dayA.month == dayB.month && dayA.day == dayB.day; + return dayA.year == dayB.year && + dayA.month == dayB.month && + dayA.day == dayB.day; } bool _isWeekend(DateTime day, List weekendDays) { diff --git a/lib/src/customization/calendar_builders.dart b/lib/src/customization/calendar_builders.dart index 90e6d107..731815fa 100644 --- a/lib/src/customization/calendar_builders.dart +++ b/lib/src/customization/calendar_builders.dart @@ -6,18 +6,21 @@ part of table_calendar; /// Main Builder signature for `TableCalendar`. Contains `date` and list of all `events` associated with that `date`. /// Note that most of the time, `events` param will be ommited, however it is there if needed. /// `events` param can be null. -typedef FullBuilder = Widget Function(BuildContext context, DateTime date, List events); +typedef FullBuilder = Widget Function( + BuildContext context, DateTime date, List events); /// Builder signature for a list of event markers. Contains `date` and list of all `events` associated with that `date`. /// Both `events` and `holidays` params can be null. -typedef FullListBuilder = List Function(BuildContext context, DateTime date, List events, List holidays); +typedef FullListBuilder = List Function( + BuildContext context, DateTime date, List events, List holidays); /// Builder signature for weekday names row. Contains `weekday` string, which is formatted by `dowTextBuilder` /// or by default function (DateFormat.E(widget.locale).format(date)), if `dowTextBuilder` is null. typedef DowBuilder = Widget Function(BuildContext context, String weekday); /// Builder signature for a single event marker. Contains `date` and a single `event` associated with that `date`. -typedef SingleMarkerBuilder = Widget Function(BuildContext context, DateTime date, dynamic event); +typedef SingleMarkerBuilder = Widget Function( + BuildContext context, DateTime date, dynamic event); /// Class containing all custom Builders for `TableCalendar`. class CalendarBuilders { diff --git a/lib/src/customization/calendar_style.dart b/lib/src/customization/calendar_style.dart index a450e02d..ee5a341a 100644 --- a/lib/src/customization/calendar_style.dart +++ b/lib/src/customization/calendar_style.dart @@ -91,13 +91,20 @@ class CalendarStyle { const CalendarStyle({ this.weekdayStyle = const TextStyle(), - this.weekendStyle = const TextStyle(color: const Color(0xFFF44336)), // Material red[500] - this.holidayStyle = const TextStyle(color: const Color(0xFFF44336)), // Material red[500] - this.selectedStyle = const TextStyle(color: const Color(0xFFFAFAFA), fontSize: 16.0), // Material grey[50] - this.todayStyle = const TextStyle(color: const Color(0xFFFAFAFA), fontSize: 16.0), // Material grey[50] - this.outsideStyle = const TextStyle(color: const Color(0xFF9E9E9E)), // Material grey[500] - this.outsideWeekendStyle = const TextStyle(color: const Color(0xFFEF9A9A)), // Material red[200] - this.outsideHolidayStyle = const TextStyle(color: const Color(0xFFEF9A9A)), // Material red[200] + this.weekendStyle = + const TextStyle(color: const Color(0xFFF44336)), // Material red[500] + this.holidayStyle = + const TextStyle(color: const Color(0xFFF44336)), // Material red[500] + this.selectedStyle = const TextStyle( + color: const Color(0xFFFAFAFA), fontSize: 16.0), // Material grey[50] + this.todayStyle = const TextStyle( + color: const Color(0xFFFAFAFA), fontSize: 16.0), // Material grey[50] + this.outsideStyle = + const TextStyle(color: const Color(0xFF9E9E9E)), // Material grey[500] + this.outsideWeekendStyle = + const TextStyle(color: const Color(0xFFEF9A9A)), // Material red[200] + this.outsideHolidayStyle = + const TextStyle(color: const Color(0xFFEF9A9A)), // Material red[200] this.unavailableStyle = const TextStyle(color: const Color(0xFFBFBFBF)), this.selectedColor = const Color(0xFF5C6BC0), // Material indigo[400] this.todayColor = const Color(0xFF9FA8DA), // Material indigo[200] @@ -111,7 +118,8 @@ class CalendarStyle { this.outsideDaysVisible = true, this.renderSelectedFirst = true, this.renderDaysOfWeek = true, - this.contentPadding = const EdgeInsets.only(bottom: 4.0, left: 8.0, right: 8.0), + this.contentPadding = + const EdgeInsets.only(bottom: 4.0, left: 8.0, right: 8.0), this.canEventMarkersOverflow = false, this.highlightSelected = true, this.highlightToday = true, diff --git a/lib/src/customization/days_of_week_style.dart b/lib/src/customization/days_of_week_style.dart index 93419ee3..05ba8aec 100644 --- a/lib/src/customization/days_of_week_style.dart +++ b/lib/src/customization/days_of_week_style.dart @@ -23,7 +23,9 @@ class DaysOfWeekStyle { const DaysOfWeekStyle({ this.dowTextBuilder, - this.weekdayStyle = const TextStyle(color: const Color(0xFF616161)), // Material grey[700] - this.weekendStyle = const TextStyle(color: const Color(0xFFF44336)), // Material red[500] + this.weekdayStyle = + const TextStyle(color: const Color(0xFF616161)), // Material grey[700] + this.weekendStyle = + const TextStyle(color: const Color(0xFFF44336)), // Material red[500] }); } diff --git a/lib/src/customization/header_style.dart b/lib/src/customization/header_style.dart index 0738a09e..5aac09d8 100644 --- a/lib/src/customization/header_style.dart +++ b/lib/src/customization/header_style.dart @@ -76,18 +76,24 @@ class HeaderStyle { this.titleTextStyle = const TextStyle(fontSize: 17.0), this.formatButtonTextStyle = const TextStyle(), this.formatButtonDecoration = const BoxDecoration( - border: const Border(top: BorderSide(), bottom: BorderSide(), left: BorderSide(), right: BorderSide()), + border: const Border( + top: BorderSide(), + bottom: BorderSide(), + left: BorderSide(), + right: BorderSide()), borderRadius: const BorderRadius.all(Radius.circular(12.0)), ), this.headerMargin, this.headerPadding = const EdgeInsets.symmetric(vertical: 8.0), - this.formatButtonPadding = const EdgeInsets.symmetric(horizontal: 10.0, vertical: 4.0), + this.formatButtonPadding = + const EdgeInsets.symmetric(horizontal: 10.0, vertical: 4.0), this.leftChevronPadding = const EdgeInsets.all(12.0), this.rightChevronPadding = const EdgeInsets.all(12.0), this.leftChevronMargin = const EdgeInsets.symmetric(horizontal: 8.0), this.rightChevronMargin = const EdgeInsets.symmetric(horizontal: 8.0), this.leftChevronIcon = const Icon(Icons.chevron_left, color: Colors.black), - this.rightChevronIcon = const Icon(Icons.chevron_right, color: Colors.black), + this.rightChevronIcon = + const Icon(Icons.chevron_right, color: Colors.black), this.decoration = const BoxDecoration(), }); } diff --git a/lib/src/widgets/cell_widget.dart b/lib/src/widgets/cell_widget.dart index 444b998e..ffb090d1 100644 --- a/lib/src/widgets/cell_widget.dart +++ b/lib/src/widgets/cell_widget.dart @@ -42,12 +42,17 @@ class _CellWidget extends StatelessWidget { } Decoration _buildCellDecoration() { - if (isSelected && calendarStyle.renderSelectedFirst && calendarStyle.highlightSelected) { - return BoxDecoration(shape: BoxShape.circle, color: calendarStyle.selectedColor); + if (isSelected && + calendarStyle.renderSelectedFirst && + calendarStyle.highlightSelected) { + return BoxDecoration( + shape: BoxShape.circle, color: calendarStyle.selectedColor); } else if (isToday && calendarStyle.highlightToday) { - return BoxDecoration(shape: BoxShape.circle, color: calendarStyle.todayColor); + return BoxDecoration( + shape: BoxShape.circle, color: calendarStyle.todayColor); } else if (isSelected && calendarStyle.highlightSelected) { - return BoxDecoration(shape: BoxShape.circle, color: calendarStyle.selectedColor); + return BoxDecoration( + shape: BoxShape.circle, color: calendarStyle.selectedColor); } else { return BoxDecoration(shape: BoxShape.circle); } @@ -56,7 +61,9 @@ class _CellWidget extends StatelessWidget { TextStyle _buildCellTextStyle() { if (isUnavailable) { return calendarStyle.unavailableStyle; - } else if (isSelected && calendarStyle.renderSelectedFirst && calendarStyle.highlightSelected) { + } else if (isSelected && + calendarStyle.renderSelectedFirst && + calendarStyle.highlightSelected) { return calendarStyle.selectedStyle; } else if (isToday && calendarStyle.highlightToday) { return calendarStyle.todayStyle; From dde2919dad1d136c740465d6f538361932aea446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Thu, 15 Oct 2020 21:54:55 +0200 Subject: [PATCH 006/164] Release version 2.3.0 --- CHANGELOG.md | 6 ++++++ README.md | 4 ++-- example/pubspec.lock | 2 +- pubspec.yaml | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e025dc0..4b03cb97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## [2.3.0] + +* Migrated to AndroidX +* Added holidays to onDaySelected callback +* Replaced deprecated overflow property with clipBehavior + ## [2.2.3] * Added onCalendarCreated callback diff --git a/README.md b/README.md index 2d63eb40..7733cda5 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Highly customizable, feature-packed Flutter Calendar with gestures, animations a ## Usage -Make sure to check out [example project](https://github.com/aleksanderwozniak/table_calendar/tree/master/example). +Make sure to check out [example project](https://github.com/aleksanderwozniak/table_calendar/tree/2.3.0-stable/example). For additional info please refer to [API docs](https://pub.dartlang.org/documentation/table_calendar/latest/table_calendar/table_calendar-library.html). ### Installation @@ -36,7 +36,7 @@ Add to pubspec.yaml: ```yaml dependencies: - table_calendar: ^2.2.3 + table_calendar: ^2.3.0 ``` Then import it to your project: diff --git a/example/pubspec.lock b/example/pubspec.lock index 3a8fee5d..1cddc900 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -141,7 +141,7 @@ packages: path: ".." relative: true source: path - version: "2.2.3" + version: "2.3.0" term_glyph: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index b5f09f11..5ce93f6f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: table_calendar description: Highly customizable, feature-packed Flutter Calendar with gestures, animations and multiple formats. -version: 2.2.3 +version: 2.3.0 author: Aleksander Woźniak homepage: https://github.com/aleksanderwozniak/table_calendar From 889220ffcdc4ffd4e41b9602b00ac1b570d46313 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Tue, 27 Oct 2020 10:28:51 +0100 Subject: [PATCH 007/164] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f9d03240..f6b8a8d2 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Highly customizable, feature-packed Flutter Calendar with gestures, animations a ## Usage -Make sure to check out [example project](https://github.com/aleksanderwozniak/table_calendar/blob/2.2.3-stable/example/lib/main.dart). +Make sure to check out [example project](https://github.com/aleksanderwozniak/table_calendar/blob/2.3.0-stable/example/lib/main.dart). For additional info please refer to [API docs](https://pub.dartlang.org/documentation/table_calendar/latest/table_calendar/table_calendar-library.html). ### Installation @@ -36,7 +36,7 @@ Add to pubspec.yaml: ```yaml dependencies: - table_calendar: ^2.2.3 + table_calendar: ^2.3.0 ``` Then import it to your project: From e99ef063c6f84ec0dcf8694030970c25653f6cba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 22 Nov 2020 18:01:24 +0100 Subject: [PATCH 008/164] Improve naming and docs --- lib/src/calendar.dart | 4 ++-- lib/src/customization/calendar_style.dart | 2 +- lib/src/customization/header_style.dart | 26 +++++++++++++---------- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/lib/src/calendar.dart b/lib/src/calendar.dart index 47737842..1f4861ba 100644 --- a/lib/src/calendar.dart +++ b/lib/src/calendar.dart @@ -384,7 +384,7 @@ class _TableCalendarState extends State Widget _buildHeader() { final children = [ - widget.headerStyle.showLeftChevron + widget.headerStyle.leftChevronVisible ? _CustomIconButton( icon: widget.headerStyle.leftChevronIcon, onTap: _selectPrevious, @@ -409,7 +409,7 @@ class _TableCalendarState extends State ), ), ), - widget.headerStyle.showRightChevron + widget.headerStyle.rightChevronVisible ? _CustomIconButton( icon: widget.headerStyle.rightChevronIcon, onTap: _selectNext, diff --git a/lib/src/customization/calendar_style.dart b/lib/src/customization/calendar_style.dart index 2904ef10..36194ecb 100644 --- a/lib/src/customization/calendar_style.dart +++ b/lib/src/customization/calendar_style.dart @@ -84,7 +84,7 @@ class CalendarStyle { /// Padding of `TableCalendar`'s content. final EdgeInsets contentPadding; - /// Margin of Cells' decoration. + /// Margin of each individual cell. final EdgeInsets cellMargin; /// Specifies if event markers rendered for a day cell can overflow cell's boundaries. diff --git a/lib/src/customization/header_style.dart b/lib/src/customization/header_style.dart index 4d056dc0..f78f1cc7 100644 --- a/lib/src/customization/header_style.dart +++ b/lib/src/customization/header_style.dart @@ -57,17 +57,20 @@ class HeaderStyle { final EdgeInsets rightChevronMargin; /// Icon used for left chevron. - /// Defaults to black `Icons.chevron_left`. + /// Defaults to `Icons.chevron_left`. final Icon leftChevronIcon; /// Icon used for right chevron. - /// Defaults to black `Icons.chevron_right`. + /// Defaults to `Icons.chevron_right`. final Icon rightChevronIcon; - /// Show or hide chevrons. + /// Determines left chevron's visibility. /// Defaults to `true`. - final bool showLeftChevron; - final bool showRightChevron; + final bool leftChevronVisible; + + /// Determines right chevron's visibility. + /// Defaults to `true`. + final bool rightChevronVisible; /// Header decoration, used to draw border or shadow or change color of the header /// Defaults to empty BoxDecoration. @@ -82,10 +85,11 @@ class HeaderStyle { this.formatButtonTextStyle = const TextStyle(), this.formatButtonDecoration = const BoxDecoration( border: const Border( - top: BorderSide(), - bottom: BorderSide(), - left: BorderSide(), - right: BorderSide()), + top: BorderSide(), + bottom: BorderSide(), + left: BorderSide(), + right: BorderSide(), + ), borderRadius: const BorderRadius.all(Radius.circular(12.0)), ), this.headerMargin, @@ -98,8 +102,8 @@ class HeaderStyle { this.rightChevronMargin = const EdgeInsets.symmetric(horizontal: 8.0), this.leftChevronIcon = const Icon(Icons.chevron_left), this.rightChevronIcon = const Icon(Icons.chevron_right), - this.showLeftChevron = true, - this.showRightChevron = true, + this.leftChevronVisible = true, + this.rightChevronVisible = true, this.decoration = const BoxDecoration(), }); } From 50c9c04a676ba93f86228e604d781d0a4ca54bb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 22 Nov 2020 18:13:21 +0100 Subject: [PATCH 009/164] Release version 2.3.1 --- CHANGELOG.md | 9 +++++++++ README.md | 4 ++-- example/pubspec.lock | 2 +- pubspec.yaml | 2 +- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b03cb97..0e1b8008 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## [2.3.1] + +* Added chevron visibility properties to HeaderStyle +* Added cellMargin property to CalendarStyle +* Added eventDayStyle property to CalendarStyle +* Added availableCalendarFormats dynamic update +* Added optional BoxDecoration for each calendar row +* Added optional BoxDecoration for days of week row + ## [2.3.0] * Migrated to AndroidX diff --git a/README.md b/README.md index 796179c3..ecd4a85e 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Highly customizable, feature-packed Flutter Calendar with gestures, animations a ## Usage -Make sure to check out [example project](https://github.com/aleksanderwozniak/table_calendar/tree/2.3.0-stable/example). +Make sure to check out [example project](https://github.com/aleksanderwozniak/table_calendar/blob/master/example). For additional info please refer to [API docs](https://pub.dartlang.org/documentation/table_calendar/latest/table_calendar/table_calendar-library.html). ### Installation @@ -36,7 +36,7 @@ Add to pubspec.yaml: ```yaml dependencies: - table_calendar: ^2.3.0 + table_calendar: ^2.3.1 ``` Then import it to your project: diff --git a/example/pubspec.lock b/example/pubspec.lock index 1cddc900..ec50b473 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -141,7 +141,7 @@ packages: path: ".." relative: true source: path - version: "2.3.0" + version: "2.3.1" term_glyph: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 5ce93f6f..771ac11a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: table_calendar description: Highly customizable, feature-packed Flutter Calendar with gestures, animations and multiple formats. -version: 2.3.0 +version: 2.3.1 author: Aleksander Woźniak homepage: https://github.com/aleksanderwozniak/table_calendar From 1b2f89df5d8ece9e638d30318fe0e2d22d06a678 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Thu, 10 Dec 2020 14:35:45 +0100 Subject: [PATCH 010/164] Add previousPage and nextPage methods --- lib/src/calendar.dart | 4 ++-- lib/src/calendar_controller.dart | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/src/calendar.dart b/lib/src/calendar.dart index 1f4861ba..3a0d5969 100644 --- a/lib/src/calendar.dart +++ b/lib/src/calendar.dart @@ -275,13 +275,13 @@ class _TableCalendarState extends State void _selectPrevious() { setState(() { - widget.calendarController._selectPrevious(); + widget.calendarController.previousPage(); }); } void _selectNext() { setState(() { - widget.calendarController._selectNext(); + widget.calendarController.nextPage(); }); } diff --git a/lib/src/calendar_controller.dart b/lib/src/calendar_controller.dart index 721265bf..ac729cde 100644 --- a/lib/src/calendar_controller.dart +++ b/lib/src/calendar_controller.dart @@ -245,7 +245,8 @@ class CalendarController { ? _availableCalendarFormats[_nextFormat()] : _availableCalendarFormats[_calendarFormat.value]; - void _selectPrevious() { + /// Displays the previous Calendar page. + void previousPage() { if (calendarFormat == CalendarFormat.month) { _selectPreviousMonth(); } else if (calendarFormat == CalendarFormat.twoWeeks) { @@ -258,7 +259,8 @@ class CalendarController { _decrementPage(); } - void _selectNext() { + /// Displays the next Calendar page. + void nextPage() { if (calendarFormat == CalendarFormat.month) { _selectNextMonth(); } else if (calendarFormat == CalendarFormat.twoWeeks) { From b839da162d2d94e03e0e5e5604315477b11fc86d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Thu, 10 Dec 2020 14:39:20 +0100 Subject: [PATCH 011/164] Release version 2.3.2 --- CHANGELOG.md | 4 ++++ README.md | 2 +- example/pubspec.lock | 2 +- pubspec.yaml | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e1b8008..2ee00007 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## [2.3.2] + +* Added previousPage and nextPage methods to CalendarController + ## [2.3.1] * Added chevron visibility properties to HeaderStyle diff --git a/README.md b/README.md index ecd4a85e..737345ca 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Add to pubspec.yaml: ```yaml dependencies: - table_calendar: ^2.3.1 + table_calendar: ^2.3.2 ``` Then import it to your project: diff --git a/example/pubspec.lock b/example/pubspec.lock index ec50b473..24e7a092 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -141,7 +141,7 @@ packages: path: ".." relative: true source: path - version: "2.3.1" + version: "2.3.2" term_glyph: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 771ac11a..72fb94db 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: table_calendar description: Highly customizable, feature-packed Flutter Calendar with gestures, animations and multiple formats. -version: 2.3.1 +version: 2.3.2 author: Aleksander Woźniak homepage: https://github.com/aleksanderwozniak/table_calendar From e6a230a75d279a9c0d78cf8aa4edf617606855f4 Mon Sep 17 00:00:00 2001 From: Stefano Saitta Date: Thu, 17 Dec 2020 02:02:31 +0100 Subject: [PATCH 012/164] upgrade dependency for supporting intl with null-safety Fixes https://github.com/aleksanderwozniak/table_calendar/issues/363 --- pubspec.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index 72fb94db..ecfba733 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,11 +12,11 @@ dependencies: flutter: sdk: flutter - intl: ">=0.15.0 <0.17.0" + intl: ">=0.15.0 <0.18.0" simple_gesture_detector: ^0.1.4 dev_dependencies: flutter_test: sdk: flutter -flutter: \ No newline at end of file +flutter: From 64d5f297e44708445e65161c83867c7f2b5d0fb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Thu, 17 Dec 2020 15:47:27 +0100 Subject: [PATCH 013/164] Update dependencies --- example/pubspec.lock | 2 +- pubspec.lock | 2 +- pubspec.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index 24e7a092..e31339ab 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -101,7 +101,7 @@ packages: name: simple_gesture_detector url: "https://pub.dartlang.org" source: hosted - version: "0.1.4" + version: "0.1.5" sky_engine: dependency: transitive description: flutter diff --git a/pubspec.lock b/pubspec.lock index 7e966689..107c2c3a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -94,7 +94,7 @@ packages: name: simple_gesture_detector url: "https://pub.dartlang.org" source: hosted - version: "0.1.4" + version: "0.1.5" sky_engine: dependency: transitive description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index ecfba733..9ef89980 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,7 +13,7 @@ dependencies: sdk: flutter intl: ">=0.15.0 <0.18.0" - simple_gesture_detector: ^0.1.4 + simple_gesture_detector: ^0.1.5 dev_dependencies: flutter_test: From 5721feea4e555af6a8ab6d75f373fba173cd27f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Thu, 17 Dec 2020 15:48:20 +0100 Subject: [PATCH 014/164] Release version 2.3.3 --- CHANGELOG.md | 4 ++++ README.md | 2 +- example/pubspec.lock | 2 +- pubspec.yaml | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ee00007..2fe4c088 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## [2.3.3] + +* Updated dependencies + ## [2.3.2] * Added previousPage and nextPage methods to CalendarController diff --git a/README.md b/README.md index 737345ca..1f5e6099 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Add to pubspec.yaml: ```yaml dependencies: - table_calendar: ^2.3.2 + table_calendar: ^2.3.3 ``` Then import it to your project: diff --git a/example/pubspec.lock b/example/pubspec.lock index e31339ab..a637b40b 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -141,7 +141,7 @@ packages: path: ".." relative: true source: path - version: "2.3.2" + version: "2.3.3" term_glyph: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 9ef89980..af2bd55d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: table_calendar description: Highly customizable, feature-packed Flutter Calendar with gestures, animations and multiple formats. -version: 2.3.2 +version: 2.3.3 author: Aleksander Woźniak homepage: https://github.com/aleksanderwozniak/table_calendar From 691f1a1af6e29455dd6e0e2ff0c7ffc46112f3be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Tue, 5 Jan 2021 15:30:29 +0100 Subject: [PATCH 015/164] Rework internal components --- lib/src/calendar.dart | 1115 +++++++++++------- lib/src/calendar_controller.dart | 496 -------- lib/src/customization/calendar_builders.dart | 36 +- lib/src/customization/calendar_style.dart | 4 - lib/src/customization/header_style.dart | 7 +- lib/src/widgets/cell_widget.dart | 5 +- lib/table_calendar.dart | 3 +- 7 files changed, 705 insertions(+), 961 deletions(-) delete mode 100644 lib/src/calendar_controller.dart diff --git a/lib/src/calendar.dart b/lib/src/calendar.dart index 3a0d5969..2bb7b485 100644 --- a/lib/src/calendar.dart +++ b/lib/src/calendar.dart @@ -4,15 +4,17 @@ part of table_calendar; /// Callback exposing currently selected day. -typedef void OnDaySelected(DateTime day, List events, List holidays); +typedef void OnDaySelected(DateTime day, List events, List holidays); -/// Callback exposing currently visible days (first and last of them), as well as current `CalendarFormat`. -typedef void OnVisibleDaysChanged( - DateTime first, DateTime last, CalendarFormat format); +/// Callback exposing currently focused day along with first and last visible day. +typedef void OnPageChanged(DateTime focusedDay, DateTime first, DateTime last); -/// Callback exposing initially visible days (first and last of them), as well as initial `CalendarFormat`. -typedef void OnCalendarCreated( - DateTime first, DateTime last, CalendarFormat format); +/// Callback exposing currently used `CalendarFormat` along with first and last visible day. +typedef void OnFormatChanged( + CalendarFormat format, DateTime first, DateTime last); + +/// Callback exposing first and last initially visible day. +typedef void OnCalendarCreated(DateTime first, DateTime last); /// Signature for reacting to header gestures. Exposes current month and year as a `DateTime` object. typedef void HeaderGestureCallback(DateTime focusedDay); @@ -26,8 +28,8 @@ typedef bool EnabledDayPredicate(DateTime day); /// Format to display the `TableCalendar` with. enum CalendarFormat { month, twoWeeks, week } -/// Available animations to update the `CalendarFormat` with. -enum FormatAnimation { slide, scale } +/// Gestures available to `TableCalendar`. +enum AvailableGestures { none, verticalSwipe, horizontalSwipe, all } /// Available day of week formats. `TableCalendar` will start the week with chosen day. /// * `StartingDayOfWeek.monday`: Monday - Sunday @@ -51,15 +53,10 @@ int _getWeekdayNumber(StartingDayOfWeek weekday) { return StartingDayOfWeek.values.indexOf(weekday) + 1; } -/// Gestures available to interal `TableCalendar`'s logic. -enum AvailableGestures { none, verticalSwipe, horizontalSwipe, all } +const int _initialPage = 10000; /// Highly customizable, feature-packed Flutter Calendar with gestures, animations and multiple formats. -class TableCalendar extends StatefulWidget { - /// Controller required for `TableCalendar`. - /// Use it to update `events`, `holidays`, etc. - final CalendarController calendarController; - +class TableCalendar extends StatefulWidget { /// Locale to format `TableCalendar` dates with, for example: `'en_US'`. /// /// If nothing is provided, a default locale will be used. @@ -67,17 +64,21 @@ class TableCalendar extends StatefulWidget { /// `Map` of events. /// Each `DateTime` inside this `Map` should get its own `List` of objects (i.e. events). - final Map events; + /// + /// Use of `LinkedHashMap` is highly encouraged. + final Map> events; /// `Map` of holidays. /// This property allows you to provide custom holiday rules. - final Map holidays; + /// + /// Use of `LinkedHashMap` is highly encouraged. + final Map> holidays; /// Called whenever any day gets tapped. - final OnDaySelected onDaySelected; + final OnDaySelected onDaySelected; /// Called whenever any day gets long pressed. - final OnDaySelected onDayLongPressed; + final OnDaySelected onDayLongPressed; /// Called whenever any unavailable day gets tapped. /// Replaces `onDaySelected` for those days. @@ -93,14 +94,20 @@ class TableCalendar extends StatefulWidget { /// Called whenever header gets long pressed. final HeaderGestureCallback onHeaderLongPressed; - /// Called whenever the range of visible days changes. - final OnVisibleDaysChanged onVisibleDaysChanged; + /// Called whenever currently visible calendar page is changed. + final OnPageChanged onPageChanged; + + /// Called whenever `calendarFormat` is changed. + final OnFormatChanged onFormatChanged; - /// Called once when the CalendarController gets initialized. + /// Called once when the `TableCalendar` widget gets initialized. final OnCalendarCreated onCalendarCreated; - /// Initially selected DateTime. Usually it will be `DateTime.now()`. - final DateTime initialSelectedDay; + /// DateTime to highlight as currently selected. + final DateTime selectedDay; + + /// DateTime that determines which days are currently visible and focused. + final DateTime focusedDay; /// The first day of `TableCalendar`. /// Days before it will use `unavailableStyle` and run `onUnavailableDaySelected` callback. @@ -111,16 +118,16 @@ class TableCalendar extends StatefulWidget { final DateTime endDay; /// List of days treated as weekend days. - /// Use built-in `DateTime` weekday constants (e.g. `DateTime.monday`) instead of `int` literals (e.q. `1`). + /// Use built-in `DateTime` weekday constants (e.g. `DateTime.monday`) instead of `int` literals (e.g. `1`). final List weekendDays; - /// `CalendarFormat` which will be displayed first. - final CalendarFormat initialCalendarFormat; + /// Specifies `TableCalendar`'s format. + final CalendarFormat calendarFormat; /// `Map` of `CalendarFormat`s and `String` names associated with them. /// Those `CalendarFormat`s will be used by internal logic to manage displayed format. /// - /// To ensure proper vertical Swipe behavior, `CalendarFormat`s should be in descending order (eg. from biggest to smallest). + /// To ensure proper vertical Swipe behavior, `CalendarFormat`s should be in descending order (i.e. from biggest to smallest). /// /// For example: /// ```dart @@ -131,9 +138,12 @@ class TableCalendar extends StatefulWidget { /// ``` final Map availableCalendarFormats; - /// Used to show/hide Header. + /// Determines whether calendar header should be visible. final bool headerVisible; + /// Determines whether the row of days of the week should be visible. + final bool daysOfWeekVisible; + /// Function deciding whether given day should be enabled or not. /// If `false` is returned, this day will be unavailable. final EnabledDayPredicate enabledDayPredicate; @@ -141,8 +151,8 @@ class TableCalendar extends StatefulWidget { /// Used for setting the height of `TableCalendar`'s rows. final double rowHeight; - /// Animation to run when `CalendarFormat` gets changed. - final FormatAnimation formatAnimation; + /// Used for setting the height of `TableCalendar`'s days of week row. + final double daysOfWeekHeight; /// `TableCalendar` will start weeks with provided day. /// Use `StartingDayOfWeek.monday` for Monday - Sunday week format. @@ -152,8 +162,8 @@ class TableCalendar extends StatefulWidget { /// `HitTestBehavior` for every day cell inside `TableCalendar`. final HitTestBehavior dayHitTestBehavior; - /// Specify Gestures available to `TableCalendar`. - /// If `AvailableGestures.none` is used, the Calendar will only be interactive via buttons. + /// Specifies gestures available to `TableCalendar`. + /// If `AvailableGestures.none` is used, the calendar will only be interactive via buttons. final AvailableGestures availableGestures; /// Configuration for vertical Swipe detector. @@ -169,11 +179,10 @@ class TableCalendar extends StatefulWidget { final HeaderStyle headerStyle; /// Set of Builders for `TableCalendar` to work with. - final CalendarBuilders builders; + final CalendarBuilders builders; TableCalendar({ Key key, - @required this.calendarController, this.locale, this.events = const {}, this.holidays = const {}, @@ -183,22 +192,25 @@ class TableCalendar extends StatefulWidget { this.onUnavailableDayLongPressed, this.onHeaderTapped, this.onHeaderLongPressed, - this.onVisibleDaysChanged, + this.onPageChanged, + this.onFormatChanged, this.onCalendarCreated, - this.initialSelectedDay, + this.selectedDay, + this.focusedDay, this.startDay, this.endDay, this.weekendDays = const [DateTime.saturday, DateTime.sunday], - this.initialCalendarFormat = CalendarFormat.month, + this.calendarFormat = CalendarFormat.month, this.availableCalendarFormats = const { CalendarFormat.month: 'Month', CalendarFormat.twoWeeks: '2 weeks', CalendarFormat.week: 'Week', }, this.headerVisible = true, + this.daysOfWeekVisible = true, this.enabledDayPredicate, - this.rowHeight, - this.formatAnimation = FormatAnimation.slide, + this.rowHeight = 52.0, + this.daysOfWeekHeight = 16.0, this.startingDayOfWeek = StartingDayOfWeek.sunday, this.dayHitTestBehavior = HitTestBehavior.deferToChild, this.availableGestures = AvailableGestures.all, @@ -210,8 +222,8 @@ class TableCalendar extends StatefulWidget { this.daysOfWeekStyle = const DaysOfWeekStyle(), this.headerStyle = const HeaderStyle(), this.builders = const CalendarBuilders(), - }) : assert(calendarController != null), - assert(availableCalendarFormats.keys.contains(initialCalendarFormat)), + }) : assert(calendarFormat != null), + assert(availableCalendarFormats.keys.contains(calendarFormat)), assert(availableCalendarFormats.length <= CalendarFormat.values.length), assert(weekendDays != null), assert(weekendDays.isNotEmpty @@ -221,100 +233,255 @@ class TableCalendar extends StatefulWidget { super(key: key); @override - _TableCalendarState createState() => _TableCalendarState(); + TableCalendarState createState() => TableCalendarState(); } -class _TableCalendarState extends State +class TableCalendarState extends State> with SingleTickerProviderStateMixin { + PageController _pageController; + CalendarFormat _calendarFormat; + ValueNotifier _calendarHeight; + ValueNotifier _focusedDay; + DateTime _selectedDay; + DateTime _baseDay; + DateTime _firstActiveDay; + DateTime _lastActiveDay; + int _pageIndex; + @override void initState() { super.initState(); + _pageController = PageController(initialPage: _initialPage); - widget.calendarController._init( - events: widget.events, - holidays: widget.holidays, - initialDay: widget.initialSelectedDay, - initialFormat: widget.initialCalendarFormat, - availableCalendarFormats: widget.availableCalendarFormats, - useNextCalendarFormat: widget.headerStyle.formatButtonShowsNext, - startingDayOfWeek: widget.startingDayOfWeek, - selectedDayCallback: _selectedDayCallback, - onVisibleDaysChanged: widget.onVisibleDaysChanged, - onCalendarCreated: widget.onCalendarCreated, - includeInvisibleDays: widget.calendarStyle.outsideDaysVisible, - ); + final now = DateTime.now(); + _baseDay = widget.focusedDay ?? DateTime.utc(now.year, now.month, now.day); + _focusedDay = ValueNotifier(_baseDay); + _selectedDay = widget.selectedDay; + + _pageIndex = _initialPage; + _calendarFormat = widget.calendarFormat; + + final days = _getVisibleDays(_baseDay); + + if (_calendarFormat == CalendarFormat.month) { + _firstActiveDay = _firstDayOfMonth(_baseDay); + _lastActiveDay = _lastDayOfMonth(_baseDay); + } else { + _firstActiveDay = days.first; + _lastActiveDay = days.last; + } + + _calendarHeight = ValueNotifier(_getPageHeight(dayCount: days.length)); + + if (widget.onCalendarCreated != null) { + if (!widget.calendarStyle.outsideDaysVisible && + _calendarFormat == CalendarFormat.month) { + widget.onCalendarCreated( + _firstDayOfMonth(_baseDay), _lastDayOfMonth(_baseDay)); + } else { + widget.onCalendarCreated(days.first, days.last); + } + } } @override void didUpdateWidget(TableCalendar oldWidget) { super.didUpdateWidget(oldWidget); - if (oldWidget.events != widget.events) { - widget.calendarController._events = widget.events; + if (widget.selectedDay == null) { + _selectedDay = null; + } else { + if (_selectedDay != widget.selectedDay) { + final day = widget.selectedDay; + _selectedDay = day; + + if (widget.onDaySelected != null) { + widget.onDaySelected( + day, + widget.events[day] ?? [], + widget.holidays[day] ?? [], + ); + } + } } - if (oldWidget.holidays != widget.holidays) { - widget.calendarController._holidays = widget.holidays; + if (widget.focusedDay != null && _focusedDay.value != widget.focusedDay) { + final day = widget.focusedDay; + + _focusedDay.value = day; + _baseDay = day; + _calendarHeight.value = _getPageHeight(baseDay: _baseDay); } - if (oldWidget.availableCalendarFormats != widget.availableCalendarFormats) { - widget.calendarController._availableCalendarFormats = - widget.availableCalendarFormats; + if (_calendarFormat != widget.calendarFormat) { + _calendarFormat = widget.calendarFormat; + + final days = _getVisibleDays(_baseDay); + + if (_calendarFormat == CalendarFormat.month) { + _firstActiveDay = _firstDayOfMonth(_baseDay); + _lastActiveDay = _lastDayOfMonth(_baseDay); + } else { + _firstActiveDay = days.first; + _lastActiveDay = days.last; + } + + _calendarHeight.value = _getPageHeight(dayCount: days.length); + + if (widget.onFormatChanged != null) { + if (!widget.calendarStyle.outsideDaysVisible && + _calendarFormat == CalendarFormat.month) { + widget.onFormatChanged(_calendarFormat, _firstDayOfMonth(_baseDay), + _lastDayOfMonth(_baseDay)); + } else { + widget.onFormatChanged(_calendarFormat, days.first, days.last); + } + } + } + + if (oldWidget.rowHeight != widget.rowHeight || + oldWidget.daysOfWeekHeight != widget.daysOfWeekHeight) { + _calendarHeight.value = _getPageHeight(baseDay: _baseDay); } } - void _selectedDayCallback(DateTime day) { - if (widget.onDaySelected != null) { - widget.onDaySelected( - day, - widget.calendarController.visibleEvents[_getEventKey(day)] ?? [], - widget.calendarController.visibleHolidays[_getHolidayKey(day)] ?? [], - ); + @override + void dispose() { + _focusedDay.dispose(); + _calendarHeight.dispose(); + _pageController.dispose(); + super.dispose(); + } + + void previousPage({ + Duration duration = const Duration(milliseconds: 350), + Curve curve = Curves.easeOut, + }) { + if (widget.startDay != null) { + if (widget.startDay.isAfter(_firstActiveDay) || + _isSameDay(widget.startDay, _firstActiveDay)) { + return; + } } + + _pageController.previousPage(duration: duration, curve: curve); } - void _selectPrevious() { - setState(() { - widget.calendarController.previousPage(); - }); + void nextPage({ + Duration duration = const Duration(milliseconds: 350), + Curve curve = Curves.easeOut, + }) { + if (widget.endDay != null) { + if (widget.endDay.isBefore(_lastActiveDay) || + _isSameDay(widget.endDay, _lastActiveDay)) { + return; + } + } + + _pageController.nextPage(duration: duration, curve: curve); } - void _selectNext() { + void toggleCalendarFormat() { + if (widget.availableCalendarFormats.keys.length <= 1) { + return; + } + setState(() { - widget.calendarController.nextPage(); + _calendarFormat = _nextFormat(); }); + + final days = _getVisibleDays(_baseDay); + + if (_calendarFormat == CalendarFormat.month) { + _firstActiveDay = _firstDayOfMonth(_baseDay); + _lastActiveDay = _lastDayOfMonth(_baseDay); + } else { + _firstActiveDay = days.first; + _lastActiveDay = days.last; + } + + _calendarHeight.value = _getPageHeight(dayCount: days.length); + + if (widget.onFormatChanged != null) { + if (!widget.calendarStyle.outsideDaysVisible && + _calendarFormat == CalendarFormat.month) { + widget.onFormatChanged(_calendarFormat, _firstDayOfMonth(_baseDay), + _lastDayOfMonth(_baseDay)); + } else { + widget.onFormatChanged(_calendarFormat, days.first, days.last); + } + } } - void _selectDay(DateTime day) { + void swipeCalendarFormat({@required bool isSwipeUp}) { + assert(isSwipeUp != null); + + final formats = widget.availableCalendarFormats.keys.toList(); + + if (formats.length <= 1) { + return; + } + + int id = formats.indexOf(_calendarFormat); + + // Order of CalendarFormats must be from biggest to smallest, + // eg.: [month, twoWeeks, week] + if (isSwipeUp) { + id = min(formats.length - 1, id + 1); + } else { + id = max(0, id - 1); + } + + if (_calendarFormat == formats[id]) { + return; + } + setState(() { - widget.calendarController.setSelectedDay(day, isProgrammatic: false); - _selectedDayCallback(day); + _calendarFormat = formats[id]; }); - } - void _onDayLongPressed(DateTime day) { - if (widget.onDayLongPressed != null) { - widget.onDayLongPressed( - day, - widget.calendarController.visibleEvents[_getEventKey(day)] ?? [], - widget.calendarController.visibleHolidays[_getHolidayKey(day)] ?? [], - ); + final days = _getVisibleDays(_baseDay); + + if (_calendarFormat == CalendarFormat.month) { + _firstActiveDay = _firstDayOfMonth(_baseDay); + _lastActiveDay = _lastDayOfMonth(_baseDay); + } else { + _firstActiveDay = days.first; + _lastActiveDay = days.last; + } + + _calendarHeight.value = _getPageHeight(dayCount: days.length); + + if (widget.onFormatChanged != null) { + if (!widget.calendarStyle.outsideDaysVisible && + _calendarFormat == CalendarFormat.month) { + widget.onFormatChanged(_calendarFormat, _firstDayOfMonth(_baseDay), + _lastDayOfMonth(_baseDay)); + } else { + widget.onFormatChanged(_calendarFormat, days.first, days.last); + } } } - void _toggleCalendarFormat() { + void _onDaySelected(DateTime day, List events, List holidays) { setState(() { - widget.calendarController.toggleCalendarFormat(); + _focusedDay.value = day; + _selectedDay = day; + + if (_calendarFormat == CalendarFormat.month) { + _baseDay = day; + _calendarHeight.value = _getPageHeight(baseDay: _baseDay); + } }); + + if (widget.onDaySelected != null) { + widget.onDaySelected(day, events, holidays); + } } - void _onHorizontalSwipe(DismissDirection direction) { - if (direction == DismissDirection.startToEnd) { - // Swipe right - _selectPrevious(); - } else { - // Swipe left - _selectNext(); + void _onDayLongPressed(DateTime day, List events, List holidays) { + if (widget.onDayLongPressed != null) { + widget.onDayLongPressed(day, events, holidays); } } @@ -332,382 +499,296 @@ class _TableCalendarState extends State void _onHeaderTapped() { if (widget.onHeaderTapped != null) { - widget.onHeaderTapped(widget.calendarController.focusedDay); + widget.onHeaderTapped(_focusedDay.value); } } void _onHeaderLongPressed() { if (widget.onHeaderLongPressed != null) { - widget.onHeaderLongPressed(widget.calendarController.focusedDay); + widget.onHeaderLongPressed(_focusedDay.value); } } - bool _isDayUnavailable(DateTime day) { - return (widget.startDay != null && - day.isBefore( - widget.calendarController._normalizeDate(widget.startDay))) || - (widget.endDay != null && - day.isAfter( - widget.calendarController._normalizeDate(widget.endDay))) || - (!_isDayEnabled(day)); + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (widget.headerVisible) _buildHeader(), + Padding( + padding: widget.calendarStyle.contentPadding, + child: _buildPageView(), + ), + ], + ); } - bool _isDayEnabled(DateTime day) { - return widget.enabledDayPredicate == null - ? true - : widget.enabledDayPredicate(day); + Widget _buildPageView() { + return SimpleGestureDetector( + onVerticalSwipe: widget.availableGestures == AvailableGestures.all || + widget.availableGestures == AvailableGestures.verticalSwipe + ? (swipeDirection) => swipeCalendarFormat( + isSwipeUp: swipeDirection == SwipeDirection.up) + : null, + swipeConfig: widget.simpleSwipeConfig, + child: ValueListenableBuilder( + valueListenable: _calendarHeight, + builder: (context, value, child) { + return AnimatedSize( + vsync: this, + alignment: Alignment.topCenter, + duration: const Duration(milliseconds: 200), + child: SizedBox(height: value, child: child), + ); + }, + child: PageView.custom( + controller: _pageController, + physics: widget.availableGestures == AvailableGestures.all || + widget.availableGestures == AvailableGestures.horizontalSwipe + ? PageScrollPhysics() + : NeverScrollableScrollPhysics(), + childrenDelegate: SliverChildBuilderDelegate( + (context, index) { + final baseDay = _getPageBaseDay(index, _calendarFormat); + final days = _getVisibleDays(baseDay); + + final focusedDay = _getPageFocusedDay(index, _calendarFormat); + + final daysInWeek = 7; + + final children = [ + if (widget.daysOfWeekVisible) + _buildDaysOfWeek(days.take(daysInWeek).toList()), + ]; + + int x = 0; + while (x < days.length) { + children.add(_buildTableRow( + days.skip(x).take(daysInWeek).toList(), focusedDay)); + x += daysInWeek; + } + + return Table( + defaultColumnWidth: FractionColumnWidth(1.0 / daysInWeek), + children: children, + ); + }, + addAutomaticKeepAlives: false, + addRepaintBoundaries: true, + ), + onPageChanged: (index) { + _baseDay = _getPageBaseDay(index, _calendarFormat); + _focusedDay.value = _baseDay; + _pageIndex = index; + + final days = _getVisibleDays(_baseDay); + _calendarHeight.value = _getPageHeight(dayCount: days.length); + + if (_calendarFormat == CalendarFormat.month) { + _firstActiveDay = _firstDayOfMonth(_baseDay); + _lastActiveDay = _lastDayOfMonth(_baseDay); + } else { + _firstActiveDay = days.first; + _lastActiveDay = days.last; + } + + if (widget.onPageChanged != null) { + if (!widget.calendarStyle.outsideDaysVisible && + _calendarFormat == CalendarFormat.month) { + widget.onPageChanged(_focusedDay.value, + _firstDayOfMonth(_baseDay), _lastDayOfMonth(_baseDay)); + } else { + widget.onPageChanged(_focusedDay.value, days.first, days.last); + } + } + }, + ), + ), + ); } - DateTime _getEventKey(DateTime day) { - return widget.calendarController._getEventKey(day); - } + TableRow _buildDaysOfWeek(List days) { + return TableRow( + decoration: widget.daysOfWeekStyle.decoration, + children: days.map((date) { + final weekdayString = widget.daysOfWeekStyle.dowTextBuilder != null + ? widget.daysOfWeekStyle.dowTextBuilder(date, widget.locale) + : DateFormat.E(widget.locale).format(date); + final isWeekend = _isWeekend(date, widget.weekendDays); - DateTime _getHolidayKey(DateTime day) { - return widget.calendarController._getHolidayKey(day); + Widget child; + + if (isWeekend && widget.builders.dowWeekendBuilder != null) { + child = widget.builders.dowWeekendBuilder(context, weekdayString); + } else if (widget.builders.dowWeekdayBuilder != null) { + child = widget.builders.dowWeekdayBuilder(context, weekdayString); + } else { + child = Center( + child: Text( + weekdayString, + style: isWeekend + ? widget.daysOfWeekStyle.weekendStyle + : widget.daysOfWeekStyle.weekdayStyle, + ), + ); + } + + return SizedBox(height: widget.daysOfWeekHeight, child: child); + }).toList(), + ); } - @override - Widget build(BuildContext context) { - return ClipRect( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - if (widget.headerVisible) _buildHeader(), - Padding( - padding: widget.calendarStyle.contentPadding, - child: _buildCalendarContent(), - ), - ], - ), + TableRow _buildTableRow(List days, DateTime focusedDay) { + return TableRow( + decoration: widget.calendarStyle.contentDecoration, + children: days.map( + (date) { + return SizedBox( + height: widget.rowHeight, + child: _buildCell(date, focusedDay), + ); + }, + ).toList(), ); } Widget _buildHeader() { - final children = [ - widget.headerStyle.leftChevronVisible - ? _CustomIconButton( + return Container( + decoration: widget.headerStyle.decoration, + margin: widget.headerStyle.headerMargin, + padding: widget.headerStyle.headerPadding, + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + if (widget.headerStyle.leftChevronVisible) + _CustomIconButton( icon: widget.headerStyle.leftChevronIcon, - onTap: _selectPrevious, + onTap: previousPage, margin: widget.headerStyle.leftChevronMargin, padding: widget.headerStyle.leftChevronPadding, - ) - : Container(), - Expanded( - child: GestureDetector( - onTap: _onHeaderTapped, - onLongPress: _onHeaderLongPressed, - child: Text( - widget.headerStyle.titleTextBuilder != null - ? widget.headerStyle.titleTextBuilder( - widget.calendarController.focusedDay, widget.locale) - : DateFormat.yMMMM(widget.locale) - .format(widget.calendarController.focusedDay), - style: widget.headerStyle.titleTextStyle, - textAlign: widget.headerStyle.centerHeaderTitle - ? TextAlign.center - : TextAlign.start, + ), + Expanded( + child: GestureDetector( + onTap: _onHeaderTapped, + onLongPress: _onHeaderLongPressed, + child: ValueListenableBuilder( + valueListenable: _focusedDay, + builder: (context, value, child) => Text( + widget.headerStyle.titleTextBuilder != null + ? widget.headerStyle + .titleTextBuilder(value, widget.locale) + : DateFormat.yMMMM(widget.locale).format(value), + style: widget.headerStyle.titleTextStyle, + textAlign: widget.headerStyle.centerHeaderTitle + ? TextAlign.center + : TextAlign.start, + ), + ), + ), ), - ), - ), - widget.headerStyle.rightChevronVisible - ? _CustomIconButton( + if (widget.headerStyle.formatButtonVisible && + widget.availableCalendarFormats.length > 1) + Padding( + padding: const EdgeInsets.only(left: 8.0), + child: _buildFormatButton(), + ), + if (widget.headerStyle.rightChevronVisible) + _CustomIconButton( icon: widget.headerStyle.rightChevronIcon, - onTap: _selectNext, + onTap: nextPage, margin: widget.headerStyle.rightChevronMargin, padding: widget.headerStyle.rightChevronPadding, - ) - : Container() - ]; - - if (widget.headerStyle.formatButtonVisible && - widget.availableCalendarFormats.length > 1) { - children.insert(2, const SizedBox(width: 8.0)); - children.insert(3, _buildFormatButton()); - } - - return Container( - decoration: widget.headerStyle.decoration, - margin: widget.headerStyle.headerMargin, - padding: widget.headerStyle.headerPadding, - child: Row( - mainAxisSize: MainAxisSize.max, - children: children, + ), + ], ), ); } Widget _buildFormatButton() { return GestureDetector( - onTap: _toggleCalendarFormat, + onTap: toggleCalendarFormat, child: Container( decoration: widget.headerStyle.formatButtonDecoration, padding: widget.headerStyle.formatButtonPadding, child: Text( - widget.calendarController._getFormatButtonText(), + _getFormatButtonText(), style: widget.headerStyle.formatButtonTextStyle, ), ), ); } - Widget _buildCalendarContent() { - if (widget.formatAnimation == FormatAnimation.slide) { - return AnimatedSize( - duration: Duration( - milliseconds: - widget.calendarController.calendarFormat == CalendarFormat.month - ? 330 - : 220), - curve: Curves.fastOutSlowIn, - alignment: Alignment(0, -1), - vsync: this, - child: _buildWrapper(), - ); - } else { - return AnimatedSwitcher( - duration: const Duration(milliseconds: 350), - transitionBuilder: (child, animation) { - return SizeTransition( - sizeFactor: animation, - child: ScaleTransition( - scale: animation, - child: child, - ), - ); - }, - child: _buildWrapper( - key: ValueKey(widget.calendarController.calendarFormat), - ), - ); - } - } - - Widget _buildWrapper({Key key}) { - Widget wrappedChild = _buildTable(); - - switch (widget.availableGestures) { - case AvailableGestures.all: - wrappedChild = _buildVerticalSwipeWrapper( - child: _buildHorizontalSwipeWrapper( - child: wrappedChild, - ), - ); - break; - case AvailableGestures.verticalSwipe: - wrappedChild = _buildVerticalSwipeWrapper( - child: wrappedChild, - ); - break; - case AvailableGestures.horizontalSwipe: - wrappedChild = _buildHorizontalSwipeWrapper( - child: wrappedChild, - ); - break; - case AvailableGestures.none: - break; + Widget _buildCell(DateTime date, DateTime focusedDay) { + if (!widget.calendarStyle.outsideDaysVisible && + date.month != focusedDay.month && + _calendarFormat == CalendarFormat.month) { + return Container(); } - return Container( - key: key, - child: wrappedChild, - ); - } - - Widget _buildVerticalSwipeWrapper({Widget child}) { - return SimpleGestureDetector( - child: child, - onVerticalSwipe: (direction) { - setState(() { - widget.calendarController - .swipeCalendarFormat(isSwipeUp: direction == SwipeDirection.up); - }); - }, - swipeConfig: widget.simpleSwipeConfig, - ); - } - - Widget _buildHorizontalSwipeWrapper({Widget child}) { - return AnimatedSwitcher( - duration: const Duration(milliseconds: 350), - switchInCurve: Curves.decelerate, - transitionBuilder: (child, animation) { - return SlideTransition( - position: Tween( - begin: Offset(widget.calendarController._dx, 0), - end: Offset(0, 0)) - .animate(animation), - child: child, - ); - }, - layoutBuilder: (currentChild, _) => currentChild, - child: Dismissible( - key: ValueKey(widget.calendarController._pageId), - resizeDuration: null, - onDismissed: _onHorizontalSwipe, - direction: DismissDirection.horizontal, - child: child, - ), - ); - } + final events = widget.events[date] ?? []; + final holidays = widget.holidays[date] ?? []; - Widget _buildTable() { - final daysInWeek = 7; - final children = [ - if (widget.calendarStyle.renderDaysOfWeek) _buildDaysOfWeek(), - ]; + Widget content = _buildCellContent(date, focusedDay, events); + final children = [content]; - int x = 0; - while (x < widget.calendarController._visibleDays.value.length) { - children.add(_buildTableRow(widget.calendarController._visibleDays.value - .skip(x) - .take(daysInWeek) - .toList())); - x += daysInWeek; - } - - return Table( - // Makes this Table fill its parent horizontally - defaultColumnWidth: FractionColumnWidth(1.0 / daysInWeek), - children: children, - ); - } - - TableRow _buildDaysOfWeek() { - return TableRow( - decoration: widget.daysOfWeekStyle.decoration, - children: - widget.calendarController._visibleDays.value.take(7).map((date) { - final weekdayString = widget.daysOfWeekStyle.dowTextBuilder != null - ? widget.daysOfWeekStyle.dowTextBuilder(date, widget.locale) - : DateFormat.E(widget.locale).format(date); - final isWeekend = - widget.calendarController._isWeekend(date, widget.weekendDays); - - if (isWeekend && widget.builders.dowWeekendBuilder != null) { - return widget.builders.dowWeekendBuilder(context, weekdayString); - } - if (widget.builders.dowWeekdayBuilder != null) { - return widget.builders.dowWeekdayBuilder(context, weekdayString); - } - return Center( - child: Text( - weekdayString, - style: isWeekend - ? widget.daysOfWeekStyle.weekendStyle - : widget.daysOfWeekStyle.weekdayStyle, + if (!_isDayUnavailable(date)) { + if (widget.builders.markersBuilder != null) { + children.addAll( + widget.builders.markersBuilder( + context, + date, + events, + holidays, ), ); - }).toList(), - ); - } - - TableRow _buildTableRow(List days) { - return TableRow( - decoration: widget.calendarStyle.contentDecoration, - children: days.map((date) => _buildTableCell(date)).toList(), - ); - } - - // TableCell will have equal width and height - Widget _buildTableCell(DateTime date) { - return LayoutBuilder( - builder: (context, constraints) => ConstrainedBox( - constraints: BoxConstraints( - maxHeight: widget.rowHeight ?? constraints.maxWidth, - minHeight: widget.rowHeight ?? constraints.maxWidth, - ), - child: _buildCell(date), - ), - ); - } - - Widget _buildCell(DateTime date) { - if (!widget.calendarStyle.outsideDaysVisible && - widget.calendarController._isExtraDay(date) && - widget.calendarController.calendarFormat == CalendarFormat.month) { - return Container(); - } - - Widget content = _buildCellContent(date); - - final eventKey = _getEventKey(date); - final holidayKey = _getHolidayKey(date); - final key = eventKey ?? holidayKey; - - if (key != null) { - final children = [content]; - final events = eventKey != null - ? widget.calendarController.visibleEvents[eventKey] - : []; - final holidays = holidayKey != null - ? widget.calendarController.visibleHolidays[holidayKey] - : []; - - if (!_isDayUnavailable(date)) { - if (widget.builders.markersBuilder != null) { - children.addAll( - widget.builders.markersBuilder( - context, - key, - events, - holidays, + } else if (events.isNotEmpty) { + children.add( + Positioned( + top: widget.calendarStyle.markersPositionTop, + bottom: widget.calendarStyle.markersPositionBottom, + left: widget.calendarStyle.markersPositionLeft, + right: widget.calendarStyle.markersPositionRight, + child: Row( + mainAxisSize: MainAxisSize.min, + children: events + .take(widget.calendarStyle.markersMaxAmount) + .map((event) => _buildMarker(date, event)) + .toList(), ), - ); - } else { - children.add( - Positioned( - top: widget.calendarStyle.markersPositionTop, - bottom: widget.calendarStyle.markersPositionBottom, - left: widget.calendarStyle.markersPositionLeft, - right: widget.calendarStyle.markersPositionRight, - child: Row( - mainAxisSize: MainAxisSize.min, - children: events - .take(widget.calendarStyle.markersMaxAmount) - .map((event) => _buildMarker(eventKey, event)) - .toList(), - ), - ), - ); - } - } - - if (children.length > 1) { - content = Stack( - alignment: widget.calendarStyle.markersAlignment, - children: children, - clipBehavior: widget.calendarStyle.canEventMarkersOverflow - ? Clip.none - : Clip.hardEdge, + ), ); } } + if (children.length > 1) { + content = Stack( + alignment: widget.calendarStyle.markersAlignment, + children: children, + clipBehavior: widget.calendarStyle.canEventMarkersOverflow + ? Clip.none + : Clip.hardEdge, + ); + } + return GestureDetector( behavior: widget.dayHitTestBehavior, onTap: () => _isDayUnavailable(date) ? _onUnavailableDaySelected() - : _selectDay(date), + : _onDaySelected(date, events, holidays), onLongPress: () => _isDayUnavailable(date) ? _onUnavailableDayLongPressed() - : _onDayLongPressed(date), + : _onDayLongPressed(date, events, holidays), child: content, ); } - Widget _buildCellContent(DateTime date) { - final eventKey = _getEventKey(date); - + Widget _buildCellContent(DateTime date, DateTime focusedDay, List events) { final tIsUnavailable = _isDayUnavailable(date); - final tIsSelected = widget.calendarController.isSelected(date); - final tIsToday = widget.calendarController.isToday(date); - final tIsOutside = widget.calendarController._isExtraDay(date); - final tIsHoliday = widget.calendarController.visibleHolidays - .containsKey(_getHolidayKey(date)); - final tIsWeekend = - widget.calendarController._isWeekend(date, widget.weekendDays); - final tIsEventDay = - widget.calendarController.visibleEvents.containsKey(eventKey); + final tIsSelected = _isSameDay(date, _selectedDay); + final tIsToday = _isSameDay(date, DateTime.now().toUtc()); + final tIsOutside = date.month != focusedDay.month; + final tIsHoliday = widget.holidays.containsKey(date); + final tIsWeekend = _isWeekend(date, widget.weekendDays); final isUnavailable = widget.builders.unavailableDayBuilder != null && tIsUnavailable; @@ -733,35 +814,25 @@ class _TableCalendarState extends State !tIsHoliday; if (isUnavailable) { - return widget.builders.unavailableDayBuilder( - context, date, widget.calendarController.visibleEvents[eventKey]); + return widget.builders.unavailableDayBuilder(context, date, events); } else if (isSelected && widget.calendarStyle.renderSelectedFirst) { - return widget.builders.selectedDayBuilder( - context, date, widget.calendarController.visibleEvents[eventKey]); + return widget.builders.selectedDayBuilder(context, date, events); } else if (isToday) { - return widget.builders.todayDayBuilder( - context, date, widget.calendarController.visibleEvents[eventKey]); + return widget.builders.todayDayBuilder(context, date, events); } else if (isSelected) { - return widget.builders.selectedDayBuilder( - context, date, widget.calendarController.visibleEvents[eventKey]); + return widget.builders.selectedDayBuilder(context, date, events); } else if (isOutsideHoliday) { - return widget.builders.outsideHolidayDayBuilder( - context, date, widget.calendarController.visibleEvents[eventKey]); + return widget.builders.outsideHolidayDayBuilder(context, date, events); } else if (isHoliday) { - return widget.builders.holidayDayBuilder( - context, date, widget.calendarController.visibleEvents[eventKey]); + return widget.builders.holidayDayBuilder(context, date, events); } else if (isOutsideWeekend) { - return widget.builders.outsideWeekendDayBuilder( - context, date, widget.calendarController.visibleEvents[eventKey]); + return widget.builders.outsideWeekendDayBuilder(context, date, events); } else if (isOutside) { - return widget.builders.outsideDayBuilder( - context, date, widget.calendarController.visibleEvents[eventKey]); + return widget.builders.outsideDayBuilder(context, date, events); } else if (isWeekend) { - return widget.builders.weekendDayBuilder( - context, date, widget.calendarController.visibleEvents[eventKey]); + return widget.builders.weekendDayBuilder(context, date, events); } else if (widget.builders.dayBuilder != null) { - return widget.builders.dayBuilder( - context, date, widget.calendarController.visibleEvents[eventKey]); + return widget.builders.dayBuilder(context, date, events); } else { return _CellWidget( text: '${date.day}', @@ -771,13 +842,13 @@ class _TableCalendarState extends State isWeekend: tIsWeekend, isOutsideMonth: tIsOutside, isHoliday: tIsHoliday, - isEventDay: tIsEventDay, + isEventDay: events.isNotEmpty, calendarStyle: widget.calendarStyle, ); } } - Widget _buildMarker(DateTime date, dynamic event) { + Widget _buildMarker(DateTime date, T event) { if (widget.builders.singleMarkerBuilder != null) { return widget.builders.singleMarkerBuilder(context, date, event); } else { @@ -792,4 +863,184 @@ class _TableCalendarState extends State ); } } + + DateTime _getPageBaseDay(int index, CalendarFormat format) { + if (index == _pageIndex) { + if (format == CalendarFormat.month) { + return _focusedDay.value; + } + + return _baseDay; + } + + final pageDifference = index - _pageIndex; + + if (format == CalendarFormat.month) { + return DateTime.utc( + _baseDay.year, _baseDay.month + pageDifference, _baseDay.day); + } else if (format == CalendarFormat.twoWeeks) { + return DateTime.utc( + _baseDay.year, + _baseDay.month, + _baseDay.day + pageDifference * 14, + ); + } else { + return DateTime.utc( + _baseDay.year, _baseDay.month, _baseDay.day + pageDifference * 7); + } + } + + DateTime _getPageFocusedDay(int index, CalendarFormat format) { + if (index == _pageIndex) { + return _focusedDay.value; + } + + final pageDifference = index - _pageIndex; + + if (format == CalendarFormat.month) { + return DateTime.utc(_focusedDay.value.year, + _focusedDay.value.month + pageDifference, _focusedDay.value.day); + } else if (format == CalendarFormat.twoWeeks) { + return DateTime.utc( + _focusedDay.value.year, + _focusedDay.value.month, + _focusedDay.value.day + pageDifference * 14, + ); + } else { + return DateTime.utc(_focusedDay.value.year, _focusedDay.value.month, + _focusedDay.value.day + pageDifference * 7); + } + } + + List _getVisibleDays(DateTime baseDay) { + if (_calendarFormat == CalendarFormat.month) { + return _daysInMonth(baseDay); + } else if (_calendarFormat == CalendarFormat.twoWeeks) { + return _daysInWeek(baseDay) + ..addAll(_daysInWeek( + baseDay.add(const Duration(days: 7)), + )); + } else { + return _daysInWeek(baseDay); + } + } + + List _daysInMonth(DateTime month) { + final first = _firstDayOfMonth(month); + final daysBefore = _getDaysBefore(first); + final firstToDisplay = first.subtract(Duration(days: daysBefore)); + + final last = _lastDayOfMonth(month); + final daysAfter = _getDaysAfter(last); + + final lastToDisplay = last.add(Duration(days: daysAfter)); + return _daysInRange(firstToDisplay, lastToDisplay).toList(); + } + + int _getDaysBefore(DateTime firstDay) { + return (firstDay.weekday + + 7 - + _getWeekdayNumber(widget.startingDayOfWeek)) % + 7; + } + + int _getDaysAfter(DateTime lastDay) { + int invertedStartingWeekday = + 8 - _getWeekdayNumber(widget.startingDayOfWeek); + + int daysAfter = 7 - ((lastDay.weekday + invertedStartingWeekday) % 7) + 1; + if (daysAfter == 8) { + daysAfter = 1; + } + + return daysAfter; + } + + List _daysInWeek(DateTime week) { + final first = _firstDayOfWeek(week); + final last = _lastDayOfWeek(week); + + return _daysInRange(first, last).toList(); + } + + DateTime _firstDayOfWeek(DateTime day) { + final decreaseNum = _getDaysBefore(day); + return day.subtract(Duration(days: decreaseNum)); + } + + DateTime _lastDayOfWeek(DateTime day) { + final increaseNum = _getDaysBefore(day); + return day.add(Duration(days: 7 - increaseNum)); + } + + DateTime _firstDayOfMonth(DateTime month) { + return DateTime.utc(month.year, month.month, 1); + } + + DateTime _lastDayOfMonth(DateTime month) { + final date = month.month < 12 + ? DateTime.utc(month.year, month.month + 1, 1) + : DateTime.utc(month.year + 1, 1, 1); + return date.subtract(const Duration(days: 1)); + } + + Iterable _daysInRange(DateTime firstDay, DateTime lastDay) sync* { + var temp = firstDay; + + while (temp.isBefore(lastDay)) { + yield temp; + temp = temp.add(const Duration(days: 1)); + } + } + + bool _isSameDay(DateTime a, DateTime b) { + if (a == null || b == null) { + return false; + } + + return a.year == b.year && a.month == b.month && a.day == b.day; + } + + bool _isDayUnavailable(DateTime day) { + return (widget.startDay != null && day.isBefore(widget.startDay)) || + (widget.endDay != null && day.isAfter(widget.endDay)) || + (!_isDayEnabled(day)); + } + + bool _isDayEnabled(DateTime day) { + return widget.enabledDayPredicate == null + ? true + : widget.enabledDayPredicate(day); + } + + bool _isWeekend(DateTime day, List weekendDays) { + return weekendDays.contains(day.weekday); + } + + CalendarFormat _nextFormat() { + final formats = widget.availableCalendarFormats.keys.toList(); + int id = formats.indexOf(_calendarFormat); + id = (id + 1) % formats.length; + + return formats[id]; + } + + String _getFormatButtonText() => widget.headerStyle.formatButtonShowsNext + ? widget.availableCalendarFormats[_nextFormat()] + : widget.availableCalendarFormats[_calendarFormat]; + + double _getPageHeight({int dayCount, DateTime baseDay}) { + var rowCount; + + if (dayCount != null) { + rowCount = dayCount ~/ 7; + } else { + final base = baseDay ?? _getPageBaseDay(_pageIndex, _calendarFormat); + final days = _getVisibleDays(base); + rowCount = days.length ~/ 7; + } + + final dowHeight = widget.daysOfWeekVisible ? widget.daysOfWeekHeight : 0.0; + return rowCount * widget.rowHeight + dowHeight; + } } diff --git a/lib/src/calendar_controller.dart b/lib/src/calendar_controller.dart deleted file mode 100644 index ac729cde..00000000 --- a/lib/src/calendar_controller.dart +++ /dev/null @@ -1,496 +0,0 @@ -// Copyright (c) 2019 Aleksander Woźniak -// Licensed under Apache License v2.0 - -part of table_calendar; - -const double _dxMax = 1.2; -const double _dxMin = -1.2; - -typedef void _SelectedDayCallback(DateTime day); - -/// Controller required for `TableCalendar`. -/// -/// Should be created in `initState()`, and then disposed in `dispose()`: -/// ```dart -/// @override -/// void initState() { -/// super.initState(); -/// _calendarController = CalendarController(); -/// } -/// -/// @override -/// void dispose() { -/// _calendarController.dispose(); -/// super.dispose(); -/// } -/// ``` -class CalendarController { - /// Currently focused day (used to determine which year/month should be visible). - DateTime get focusedDay => _focusedDay; - - /// Currently selected day. - DateTime get selectedDay => _selectedDay; - - /// Currently visible calendar format. - CalendarFormat get calendarFormat => _calendarFormat.value; - - /// List of currently visible days. - List get visibleDays => - calendarFormat == CalendarFormat.month && !_includeInvisibleDays - ? _visibleDays.value.where((day) => !_isExtraDay(day)).toList() - : _visibleDays.value; - - /// `Map` of currently visible events. - Map get visibleEvents { - if (_events == null) { - return {}; - } - - return Map.fromEntries( - _events.entries.where((entry) { - for (final day in visibleDays) { - if (_isSameDay(day, entry.key)) { - return true; - } - } - - return false; - }), - ); - } - - /// `Map` of currently visible holidays. - Map get visibleHolidays { - if (_holidays == null) { - return {}; - } - - return Map.fromEntries( - _holidays.entries.where((entry) { - for (final day in visibleDays) { - if (_isSameDay(day, entry.key)) { - return true; - } - } - - return false; - }), - ); - } - - Map _events; - Map _holidays; - DateTime _focusedDay; - DateTime _selectedDay; - StartingDayOfWeek _startingDayOfWeek; - ValueNotifier _calendarFormat; - ValueNotifier> _visibleDays; - Map _availableCalendarFormats; - DateTime _previousFirstDay; - DateTime _previousLastDay; - int _pageId; - double _dx; - bool _useNextCalendarFormat; - bool _includeInvisibleDays; - _SelectedDayCallback _selectedDayCallback; - - void _init({ - @required Map events, - @required Map holidays, - @required DateTime initialDay, - @required CalendarFormat initialFormat, - @required Map availableCalendarFormats, - @required bool useNextCalendarFormat, - @required StartingDayOfWeek startingDayOfWeek, - @required _SelectedDayCallback selectedDayCallback, - @required OnVisibleDaysChanged onVisibleDaysChanged, - @required OnCalendarCreated onCalendarCreated, - @required bool includeInvisibleDays, - }) { - _events = events; - _holidays = holidays; - _availableCalendarFormats = availableCalendarFormats; - _startingDayOfWeek = startingDayOfWeek; - _useNextCalendarFormat = useNextCalendarFormat; - _selectedDayCallback = selectedDayCallback; - _includeInvisibleDays = includeInvisibleDays; - - _pageId = 0; - _dx = 0; - - final now = DateTime.now(); - _focusedDay = initialDay ?? _normalizeDate(now); - _selectedDay = _focusedDay; - _calendarFormat = ValueNotifier(initialFormat); - _visibleDays = ValueNotifier(_getVisibleDays()); - _previousFirstDay = _visibleDays.value.first; - _previousLastDay = _visibleDays.value.last; - - _calendarFormat.addListener(() { - _visibleDays.value = _getVisibleDays(); - }); - - if (onVisibleDaysChanged != null) { - _visibleDays.addListener(() { - if (!_isSameDay(_visibleDays.value.first, _previousFirstDay) || - !_isSameDay(_visibleDays.value.last, _previousLastDay)) { - _previousFirstDay = _visibleDays.value.first; - _previousLastDay = _visibleDays.value.last; - onVisibleDaysChanged( - _getFirstDay(includeInvisible: _includeInvisibleDays), - _getLastDay(includeInvisible: _includeInvisibleDays), - _calendarFormat.value, - ); - } - }); - } - - if (onCalendarCreated != null) { - onCalendarCreated( - _getFirstDay(includeInvisible: _includeInvisibleDays), - _getLastDay(includeInvisible: _includeInvisibleDays), - _calendarFormat.value, - ); - } - } - - /// Disposes the controller. - /// ```dart - /// @override - /// void dispose() { - /// _calendarController.dispose(); - /// super.dispose(); - /// } - /// ``` - void dispose() { - _calendarFormat?.dispose(); - _visibleDays?.dispose(); - } - - /// Toggles calendar format. Same as using `FormatButton`. - void toggleCalendarFormat() { - _calendarFormat.value = _nextFormat(); - } - - /// Sets calendar format by emulating swipe. - void swipeCalendarFormat({@required bool isSwipeUp}) { - assert(isSwipeUp != null); - - final formats = _availableCalendarFormats.keys.toList(); - int id = formats.indexOf(_calendarFormat.value); - - // Order of CalendarFormats must be from biggest to smallest, - // eg.: [month, twoWeeks, week] - if (isSwipeUp) { - id = _clamp(0, formats.length - 1, id + 1); - } else { - id = _clamp(0, formats.length - 1, id - 1); - } - _calendarFormat.value = formats[id]; - } - - /// Sets calendar format to a given `value`. - void setCalendarFormat(CalendarFormat value) { - _calendarFormat.value = value; - } - - /// Sets selected day to a given `value`. - /// Use `runCallback: true` if this should trigger `OnDaySelected` callback. - void setSelectedDay( - DateTime value, { - bool isProgrammatic = true, - bool animate = true, - bool runCallback = false, - }) { - final normalizedDate = _normalizeDate(value); - - if (animate) { - if (normalizedDate.isBefore(_getFirstDay(includeInvisible: false))) { - _decrementPage(); - } else if (normalizedDate.isAfter(_getLastDay(includeInvisible: false))) { - _incrementPage(); - } - } - - _selectedDay = normalizedDate; - _focusedDay = normalizedDate; - _updateVisibleDays(isProgrammatic); - - if (isProgrammatic && runCallback && _selectedDayCallback != null) { - _selectedDayCallback(normalizedDate); - } - } - - /// Sets displayed month/year without changing the currently selected day. - void setFocusedDay(DateTime value) { - _focusedDay = _normalizeDate(value); - _updateVisibleDays(true); - } - - void _updateVisibleDays(bool isProgrammatic) { - if (calendarFormat != CalendarFormat.twoWeeks || isProgrammatic) { - _visibleDays.value = _getVisibleDays(); - } - } - - CalendarFormat _nextFormat() { - final formats = _availableCalendarFormats.keys.toList(); - int id = formats.indexOf(_calendarFormat.value); - id = (id + 1) % formats.length; - - return formats[id]; - } - - String _getFormatButtonText() => _useNextCalendarFormat - ? _availableCalendarFormats[_nextFormat()] - : _availableCalendarFormats[_calendarFormat.value]; - - /// Displays the previous Calendar page. - void previousPage() { - if (calendarFormat == CalendarFormat.month) { - _selectPreviousMonth(); - } else if (calendarFormat == CalendarFormat.twoWeeks) { - _selectPreviousTwoWeeks(); - } else { - _selectPreviousWeek(); - } - - _visibleDays.value = _getVisibleDays(); - _decrementPage(); - } - - /// Displays the next Calendar page. - void nextPage() { - if (calendarFormat == CalendarFormat.month) { - _selectNextMonth(); - } else if (calendarFormat == CalendarFormat.twoWeeks) { - _selectNextTwoWeeks(); - } else { - _selectNextWeek(); - } - - _visibleDays.value = _getVisibleDays(); - _incrementPage(); - } - - void _selectPreviousMonth() { - _focusedDay = _previousMonth(_focusedDay); - } - - void _selectNextMonth() { - _focusedDay = _nextMonth(_focusedDay); - } - - void _selectPreviousTwoWeeks() { - if (_visibleDays.value.take(7).contains(_focusedDay)) { - // in top row - _focusedDay = _previousWeek(_focusedDay); - } else { - // in bottom row OR not visible - _focusedDay = - _previousWeek(_focusedDay.subtract(const Duration(days: 7))); - } - } - - void _selectNextTwoWeeks() { - if (!_visibleDays.value.skip(7).contains(_focusedDay)) { - // not in bottom row [eg: in top row OR not visible] - _focusedDay = _nextWeek(_focusedDay); - } - } - - void _selectPreviousWeek() { - _focusedDay = _previousWeek(_focusedDay); - } - - void _selectNextWeek() { - _focusedDay = _nextWeek(_focusedDay); - } - - DateTime _getFirstDay({@required bool includeInvisible}) { - if (_calendarFormat.value == CalendarFormat.month && !includeInvisible) { - return _firstDayOfMonth(_focusedDay); - } else { - return _visibleDays.value.first; - } - } - - DateTime _getLastDay({@required bool includeInvisible}) { - if (_calendarFormat.value == CalendarFormat.month && !includeInvisible) { - return _lastDayOfMonth(_focusedDay); - } else { - return _visibleDays.value.last; - } - } - - List _getVisibleDays() { - if (calendarFormat == CalendarFormat.month) { - return _daysInMonth(_focusedDay); - } else if (calendarFormat == CalendarFormat.twoWeeks) { - return _daysInWeek(_focusedDay) - ..addAll(_daysInWeek( - _focusedDay.add(const Duration(days: 7)), - )); - } else { - return _daysInWeek(_focusedDay); - } - } - - void _decrementPage() { - _pageId--; - _dx = _dxMin; - } - - void _incrementPage() { - _pageId++; - _dx = _dxMax; - } - - List _daysInMonth(DateTime month) { - final first = _firstDayOfMonth(month); - final daysBefore = _getDaysBefore(first); - final firstToDisplay = first.subtract(Duration(days: daysBefore)); - - final last = _lastDayOfMonth(month); - final daysAfter = _getDaysAfter(last); - - final lastToDisplay = last.add(Duration(days: daysAfter)); - return _daysInRange(firstToDisplay, lastToDisplay).toList(); - } - - int _getDaysBefore(DateTime firstDay) { - return (firstDay.weekday + 7 - _getWeekdayNumber(_startingDayOfWeek)) % 7; - } - - int _getDaysAfter(DateTime lastDay) { - int invertedStartingWeekday = 8 - _getWeekdayNumber(_startingDayOfWeek); - - int daysAfter = 7 - ((lastDay.weekday + invertedStartingWeekday) % 7) + 1; - if (daysAfter == 8) { - daysAfter = 1; - } - - return daysAfter; - } - - List _daysInWeek(DateTime week) { - final first = _firstDayOfWeek(week); - final last = _lastDayOfWeek(week); - - return _daysInRange(first, last).toList(); - } - - DateTime _firstDayOfWeek(DateTime day) { - day = _normalizeDate(day); - - final decreaseNum = _getDaysBefore(day); - return day.subtract(Duration(days: decreaseNum)); - } - - DateTime _lastDayOfWeek(DateTime day) { - day = _normalizeDate(day); - - final increaseNum = _getDaysBefore(day); - return day.add(Duration(days: 7 - increaseNum)); - } - - DateTime _firstDayOfMonth(DateTime month) { - return DateTime.utc(month.year, month.month, 1, 12); - } - - DateTime _lastDayOfMonth(DateTime month) { - final date = month.month < 12 - ? DateTime.utc(month.year, month.month + 1, 1, 12) - : DateTime.utc(month.year + 1, 1, 1, 12); - return date.subtract(const Duration(days: 1)); - } - - DateTime _previousWeek(DateTime week) { - return week.subtract(const Duration(days: 7)); - } - - DateTime _nextWeek(DateTime week) { - return week.add(const Duration(days: 7)); - } - - DateTime _previousMonth(DateTime month) { - if (month.month == 1) { - return DateTime(month.year - 1, 12); - } else { - return DateTime(month.year, month.month - 1); - } - } - - DateTime _nextMonth(DateTime month) { - if (month.month == 12) { - return DateTime(month.year + 1, 1); - } else { - return DateTime(month.year, month.month + 1); - } - } - - Iterable _daysInRange(DateTime firstDay, DateTime lastDay) sync* { - var temp = firstDay; - - while (temp.isBefore(lastDay)) { - yield _normalizeDate(temp); - temp = temp.add(const Duration(days: 1)); - } - } - - DateTime _normalizeDate(DateTime value) { - return DateTime.utc(value.year, value.month, value.day, 12); - } - - DateTime _getEventKey(DateTime day) { - return visibleEvents.keys - .firstWhere((it) => _isSameDay(it, day), orElse: () => null); - } - - DateTime _getHolidayKey(DateTime day) { - return visibleHolidays.keys - .firstWhere((it) => _isSameDay(it, day), orElse: () => null); - } - - /// Returns true if `day` is currently selected. - bool isSelected(DateTime day) { - return _isSameDay(day, selectedDay); - } - - /// Returns true if `day` is the same day as `DateTime.now()`. - bool isToday(DateTime day) { - return _isSameDay(day, DateTime.now()); - } - - bool _isSameDay(DateTime dayA, DateTime dayB) { - return dayA.year == dayB.year && - dayA.month == dayB.month && - dayA.day == dayB.day; - } - - bool _isWeekend(DateTime day, List weekendDays) { - return weekendDays.contains(day.weekday); - } - - bool _isExtraDay(DateTime day) { - return _isExtraDayBefore(day) || _isExtraDayAfter(day); - } - - bool _isExtraDayBefore(DateTime day) { - return day.month < _focusedDay.month; - } - - bool _isExtraDayAfter(DateTime day) { - return day.month > _focusedDay.month; - } - - int _clamp(int min, int max, int value) { - if (value > max) { - return max; - } else if (value < min) { - return min; - } else { - return value; - } - } -} diff --git a/lib/src/customization/calendar_builders.dart b/lib/src/customization/calendar_builders.dart index 731815fa..28dcd235 100644 --- a/lib/src/customization/calendar_builders.dart +++ b/lib/src/customization/calendar_builders.dart @@ -6,52 +6,52 @@ part of table_calendar; /// Main Builder signature for `TableCalendar`. Contains `date` and list of all `events` associated with that `date`. /// Note that most of the time, `events` param will be ommited, however it is there if needed. /// `events` param can be null. -typedef FullBuilder = Widget Function( - BuildContext context, DateTime date, List events); +typedef FullBuilder = Widget Function( + BuildContext context, DateTime date, List events); /// Builder signature for a list of event markers. Contains `date` and list of all `events` associated with that `date`. /// Both `events` and `holidays` params can be null. -typedef FullListBuilder = List Function( - BuildContext context, DateTime date, List events, List holidays); +typedef FullListBuilder = List Function( + BuildContext context, DateTime date, List events, List holidays); /// Builder signature for weekday names row. Contains `weekday` string, which is formatted by `dowTextBuilder` /// or by default function (DateFormat.E(widget.locale).format(date)), if `dowTextBuilder` is null. typedef DowBuilder = Widget Function(BuildContext context, String weekday); /// Builder signature for a single event marker. Contains `date` and a single `event` associated with that `date`. -typedef SingleMarkerBuilder = Widget Function( - BuildContext context, DateTime date, dynamic event); +typedef SingleMarkerBuilder = Widget Function( + BuildContext context, DateTime date, T event); /// Class containing all custom Builders for `TableCalendar`. -class CalendarBuilders { +class CalendarBuilders { /// The most general custom Builder. Use to provide your own UI for every day cell. /// If `dayBuilder` is not specified, a default day cell will be displayed. /// Default day cells are customizable with `CalendarStyle`. - final FullBuilder dayBuilder; + final FullBuilder dayBuilder; /// Custom Builder for currently selected day. Will overwrite `dayBuilder` on selected day. - final FullBuilder selectedDayBuilder; + final FullBuilder selectedDayBuilder; /// Custom Builder for today. Will overwrite `dayBuilder` on today. - final FullBuilder todayDayBuilder; + final FullBuilder todayDayBuilder; /// Custom Builder for holidays. Will overwrite `dayBuilder` on holidays. - final FullBuilder holidayDayBuilder; + final FullBuilder holidayDayBuilder; /// Custom Builder for weekends. Will overwrite `dayBuilder` on weekends. - final FullBuilder weekendDayBuilder; + final FullBuilder weekendDayBuilder; /// Custom Builder for days outside of current month. Will overwrite `dayBuilder` on days outside of current month. - final FullBuilder outsideDayBuilder; + final FullBuilder outsideDayBuilder; /// Custom Builder for weekends outside of current month. Will overwrite `dayBuilder`on weekends outside of current month. - final FullBuilder outsideWeekendDayBuilder; + final FullBuilder outsideWeekendDayBuilder; /// Custom Builder for holidays outside of current month. Will overwrite `dayBuilder` on holidays outside of current month. - final FullBuilder outsideHolidayDayBuilder; + final FullBuilder outsideHolidayDayBuilder; /// Custom Builder for days outside of `startDay` - `endDay` Date range. Will overwrite `dayBuilder` for aforementioned days. - final FullBuilder unavailableDayBuilder; + final FullBuilder unavailableDayBuilder; /// Custom Builder for a whole group of event markers. Use to provide your own marker UI for each day cell. /// Every `Widget` passed here will be placed in a `Stack`, above the cell content. @@ -59,14 +59,14 @@ class CalendarBuilders { /// /// If `markersBuilder` is not specified, `TableCalendar` will try to use `singleMarkerBuilder` or default markers (customizable with `CalendarStyle`). /// Mutually exclusive with `singleMarkerBuilder`. - final FullListBuilder markersBuilder; + final FullListBuilder markersBuilder; /// Custom Builder for a single event marker. Each of those will be displayed in a `Row` above of the day cell. /// You can adjust markers' position with `CalendarStyle` properties. /// /// If `singleMarkerBuilder` is not specified, a default event marker will be displayed (customizable with `CalendarStyle`). /// Mutually exclusive with `markersBuilder`. - final SingleMarkerBuilder singleMarkerBuilder; + final SingleMarkerBuilder singleMarkerBuilder; /// Custom builder for dow weekday names (displayed between `HeaderRow` and calendar days). /// Will overwrite `weekdayStyle` and `weekendStyle` from `DaysOfWeekStyle`. diff --git a/lib/src/customization/calendar_style.dart b/lib/src/customization/calendar_style.dart index 36194ecb..99538c33 100644 --- a/lib/src/customization/calendar_style.dart +++ b/lib/src/customization/calendar_style.dart @@ -78,9 +78,6 @@ class CalendarStyle { /// * `false` - Today will have higher priority than SelectedDay final bool renderSelectedFirst; - /// Determines whether the row of days of the week should be rendered or not. - final bool renderDaysOfWeek; - /// Padding of `TableCalendar`'s content. final EdgeInsets contentPadding; @@ -128,7 +125,6 @@ class CalendarStyle { this.markersMaxAmount = 4, this.outsideDaysVisible = true, this.renderSelectedFirst = true, - this.renderDaysOfWeek = true, this.contentPadding = const EdgeInsets.only(bottom: 4.0, left: 8.0, right: 8.0), this.cellMargin = const EdgeInsets.all(6.0), diff --git a/lib/src/customization/header_style.dart b/lib/src/customization/header_style.dart index f78f1cc7..03b10359 100644 --- a/lib/src/customization/header_style.dart +++ b/lib/src/customization/header_style.dart @@ -84,12 +84,7 @@ class HeaderStyle { this.titleTextStyle = const TextStyle(fontSize: 17.0), this.formatButtonTextStyle = const TextStyle(), this.formatButtonDecoration = const BoxDecoration( - border: const Border( - top: BorderSide(), - bottom: BorderSide(), - left: BorderSide(), - right: BorderSide(), - ), + border: const Border.fromBorderSide(BorderSide()), borderRadius: const BorderRadius.all(Radius.circular(12.0)), ), this.headerMargin, diff --git a/lib/src/widgets/cell_widget.dart b/lib/src/widgets/cell_widget.dart index 3b0a57e6..6260e576 100644 --- a/lib/src/widgets/cell_widget.dart +++ b/lib/src/widgets/cell_widget.dart @@ -36,10 +36,7 @@ class _CellWidget extends StatelessWidget { decoration: _buildCellDecoration(), margin: calendarStyle.cellMargin, alignment: Alignment.center, - child: Text( - text, - style: _buildCellTextStyle(), - ), + child: Text(text, style: _buildCellTextStyle()), ); } diff --git a/lib/table_calendar.dart b/lib/table_calendar.dart index f0649629..889038f9 100644 --- a/lib/table_calendar.dart +++ b/lib/table_calendar.dart @@ -3,12 +3,13 @@ library table_calendar; +import 'dart:math'; + import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:simple_gesture_detector/simple_gesture_detector.dart'; part 'src/calendar.dart'; -part 'src/calendar_controller.dart'; part 'src/customization/calendar_builders.dart'; part 'src/customization/calendar_style.dart'; part 'src/customization/days_of_week_style.dart'; From b193e4a8bee3a4460f29672d36fd4f3549f68101 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Tue, 5 Jan 2021 17:42:33 +0100 Subject: [PATCH 016/164] Return selectedDay in unavailableDaySelected callback --- lib/src/calendar.dart | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/src/calendar.dart b/lib/src/calendar.dart index 2bb7b485..5bb579bf 100644 --- a/lib/src/calendar.dart +++ b/lib/src/calendar.dart @@ -82,11 +82,11 @@ class TableCalendar extends StatefulWidget { /// Called whenever any unavailable day gets tapped. /// Replaces `onDaySelected` for those days. - final VoidCallback onUnavailableDaySelected; + final Function(DateTime) onUnavailableDaySelected; /// Called whenever any unavailable day gets long pressed. /// Replaces `onDaySelected` for those days. - final VoidCallback onUnavailableDayLongPressed; + final Function(DateTime) onUnavailableDayLongPressed; /// Called whenever header gets tapped. final HeaderGestureCallback onHeaderTapped; @@ -485,15 +485,15 @@ class TableCalendarState extends State> } } - void _onUnavailableDaySelected() { + void _onUnavailableDaySelected(DateTime day) { if (widget.onUnavailableDaySelected != null) { - widget.onUnavailableDaySelected(); + widget.onUnavailableDaySelected(day); } } - void _onUnavailableDayLongPressed() { + void _onUnavailableDayLongPressed(DateTime day) { if (widget.onUnavailableDayLongPressed != null) { - widget.onUnavailableDayLongPressed(); + widget.onUnavailableDayLongPressed(day); } } @@ -773,10 +773,10 @@ class TableCalendarState extends State> return GestureDetector( behavior: widget.dayHitTestBehavior, onTap: () => _isDayUnavailable(date) - ? _onUnavailableDaySelected() + ? _onUnavailableDaySelected(date) : _onDaySelected(date, events, holidays), onLongPress: () => _isDayUnavailable(date) - ? _onUnavailableDayLongPressed() + ? _onUnavailableDayLongPressed(date) : _onDayLongPressed(date, events, holidays), child: content, ); From e3d2e363679c61537030c0c08fd63872560a67c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Tue, 5 Jan 2021 17:44:26 +0100 Subject: [PATCH 017/164] Improve selectedDay updates --- lib/src/calendar.dart | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/lib/src/calendar.dart b/lib/src/calendar.dart index 5bb579bf..45643498 100644 --- a/lib/src/calendar.dart +++ b/lib/src/calendar.dart @@ -288,24 +288,20 @@ class TableCalendarState extends State> void didUpdateWidget(TableCalendar oldWidget) { super.didUpdateWidget(oldWidget); - if (widget.selectedDay == null) { - _selectedDay = null; - } else { - if (_selectedDay != widget.selectedDay) { - final day = widget.selectedDay; - _selectedDay = day; - - if (widget.onDaySelected != null) { - widget.onDaySelected( - day, - widget.events[day] ?? [], - widget.holidays[day] ?? [], - ); - } + if (_selectedDay != widget.selectedDay && widget.selectedDay != null) { + final day = widget.selectedDay; + _selectedDay = day; + + if (widget.onDaySelected != null) { + widget.onDaySelected( + day, + widget.events[day] ?? [], + widget.holidays[day] ?? [], + ); } } - if (widget.focusedDay != null && _focusedDay.value != widget.focusedDay) { + if (_focusedDay.value != widget.focusedDay && widget.focusedDay != null) { final day = widget.focusedDay; _focusedDay.value = day; From 0657745f4221a436cfc644b861e0cf16ef9ef9f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Wed, 6 Jan 2021 13:43:52 +0100 Subject: [PATCH 018/164] Update height on startingDay change --- lib/src/calendar.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/src/calendar.dart b/lib/src/calendar.dart index 45643498..8f194f29 100644 --- a/lib/src/calendar.dart +++ b/lib/src/calendar.dart @@ -339,6 +339,10 @@ class TableCalendarState extends State> oldWidget.daysOfWeekHeight != widget.daysOfWeekHeight) { _calendarHeight.value = _getPageHeight(baseDay: _baseDay); } + + if (oldWidget.startingDayOfWeek != widget.startingDayOfWeek) { + _calendarHeight.value = _getPageHeight(baseDay: _baseDay); + } } @override From 5c1535644562d1a6f134da9b536d8017ddb26bb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Wed, 6 Jan 2021 16:38:14 +0100 Subject: [PATCH 019/164] Normalize headers --- lib/src/calendar.dart | 4 ++-- lib/src/customization/calendar_builders.dart | 4 ++-- lib/src/customization/calendar_style.dart | 4 ++-- lib/src/customization/days_of_week_style.dart | 4 ++-- lib/src/customization/header_style.dart | 4 ++-- lib/src/widgets/cell_widget.dart | 4 ++-- lib/src/widgets/custom_icon_button.dart | 4 ++-- lib/table_calendar.dart | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/src/calendar.dart b/lib/src/calendar.dart index 8f194f29..3ca3e2ad 100644 --- a/lib/src/calendar.dart +++ b/lib/src/calendar.dart @@ -1,5 +1,5 @@ -// Copyright (c) 2019 Aleksander Woźniak -// Licensed under Apache License v2.0 +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 part of table_calendar; diff --git a/lib/src/customization/calendar_builders.dart b/lib/src/customization/calendar_builders.dart index 28dcd235..648ed459 100644 --- a/lib/src/customization/calendar_builders.dart +++ b/lib/src/customization/calendar_builders.dart @@ -1,5 +1,5 @@ -// Copyright (c) 2019 Aleksander Woźniak -// Licensed under Apache License v2.0 +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 part of table_calendar; diff --git a/lib/src/customization/calendar_style.dart b/lib/src/customization/calendar_style.dart index 99538c33..08e37444 100644 --- a/lib/src/customization/calendar_style.dart +++ b/lib/src/customization/calendar_style.dart @@ -1,5 +1,5 @@ -// Copyright (c) 2019 Aleksander Woźniak -// Licensed under Apache License v2.0 +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 part of table_calendar; diff --git a/lib/src/customization/days_of_week_style.dart b/lib/src/customization/days_of_week_style.dart index 7ec03eeb..86bc8a17 100644 --- a/lib/src/customization/days_of_week_style.dart +++ b/lib/src/customization/days_of_week_style.dart @@ -1,5 +1,5 @@ -// Copyright (c) 2019 Aleksander Woźniak -// Licensed under Apache License v2.0 +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 part of table_calendar; diff --git a/lib/src/customization/header_style.dart b/lib/src/customization/header_style.dart index 03b10359..ac0ef660 100644 --- a/lib/src/customization/header_style.dart +++ b/lib/src/customization/header_style.dart @@ -1,5 +1,5 @@ -// Copyright (c) 2019 Aleksander Woźniak -// Licensed under Apache License v2.0 +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 part of table_calendar; diff --git a/lib/src/widgets/cell_widget.dart b/lib/src/widgets/cell_widget.dart index 6260e576..7c68a022 100644 --- a/lib/src/widgets/cell_widget.dart +++ b/lib/src/widgets/cell_widget.dart @@ -1,5 +1,5 @@ -// Copyright (c) 2019 Aleksander Woźniak -// Licensed under Apache License v2.0 +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 part of table_calendar; diff --git a/lib/src/widgets/custom_icon_button.dart b/lib/src/widgets/custom_icon_button.dart index 06be1983..111a79fc 100644 --- a/lib/src/widgets/custom_icon_button.dart +++ b/lib/src/widgets/custom_icon_button.dart @@ -1,5 +1,5 @@ -// Copyright (c) 2019 Aleksander Woźniak -// Licensed under Apache License v2.0 +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 part of table_calendar; diff --git a/lib/table_calendar.dart b/lib/table_calendar.dart index 889038f9..43145b15 100644 --- a/lib/table_calendar.dart +++ b/lib/table_calendar.dart @@ -1,5 +1,5 @@ -// Copyright (c) 2019 Aleksander Woźniak -// Licensed under Apache License v2.0 +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 library table_calendar; From 15b46bda784a72812be7fd220c49399b6cb8eaad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Wed, 6 Jan 2021 16:41:54 +0100 Subject: [PATCH 020/164] Update .gitignore --- .gitignore | 2 +- .idea/libraries/Dart_SDK.xml | 19 ------------------- .idea/modules.xml | 8 -------- .idea/workspace.xml | 36 ------------------------------------ .vscode/settings.json | 3 +++ 5 files changed, 4 insertions(+), 64 deletions(-) delete mode 100644 .idea/libraries/Dart_SDK.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/workspace.xml create mode 100644 .vscode/settings.json diff --git a/.gitignore b/.gitignore index d5ccb7e2..e9f86be8 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,7 @@ .idea/ # Visual Studio Code related -.vscode/ +vscode/ # Flutter/Dart/Pub related **/doc/api/ diff --git a/.idea/libraries/Dart_SDK.xml b/.idea/libraries/Dart_SDK.xml deleted file mode 100644 index 4b939a0b..00000000 --- a/.idea/libraries/Dart_SDK.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index dcbe982e..00000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/.idea/workspace.xml b/.idea/workspace.xml deleted file mode 100644 index 5b3388cc..00000000 --- a/.idea/workspace.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..8d4d525e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "dart.lineLength": 80, +} \ No newline at end of file From 325f864df2be5b42716a4c3911915634b0b31648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Wed, 6 Jan 2021 16:43:44 +0100 Subject: [PATCH 021/164] Update dependencies --- example/pubspec.lock | 2 +- pubspec.lock | 2 +- pubspec.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index a637b40b..0c1dbfcb 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -101,7 +101,7 @@ packages: name: simple_gesture_detector url: "https://pub.dartlang.org" source: hosted - version: "0.1.5" + version: "0.1.6" sky_engine: dependency: transitive description: flutter diff --git a/pubspec.lock b/pubspec.lock index 107c2c3a..e1823b82 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -94,7 +94,7 @@ packages: name: simple_gesture_detector url: "https://pub.dartlang.org" source: hosted - version: "0.1.5" + version: "0.1.6" sky_engine: dependency: transitive description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index af2bd55d..79188081 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,7 +13,7 @@ dependencies: sdk: flutter intl: ">=0.15.0 <0.18.0" - simple_gesture_detector: ^0.1.5 + simple_gesture_detector: ^0.1.6 dev_dependencies: flutter_test: From 71ae233f8ffa1b94ee27dc1584788664d38c4365 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Wed, 6 Jan 2021 16:56:48 +0100 Subject: [PATCH 022/164] Update docs --- lib/src/calendar.dart | 2 +- lib/src/customization/calendar_style.dart | 2 +- lib/src/customization/days_of_week_style.dart | 4 ++-- lib/src/customization/header_style.dart | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/src/calendar.dart b/lib/src/calendar.dart index 3ca3e2ad..f32b1f13 100644 --- a/lib/src/calendar.dart +++ b/lib/src/calendar.dart @@ -425,7 +425,7 @@ class TableCalendarState extends State> int id = formats.indexOf(_calendarFormat); // Order of CalendarFormats must be from biggest to smallest, - // eg.: [month, twoWeeks, week] + // e.g.: [month, twoWeeks, week] if (isSwipeUp) { id = min(formats.length - 1, id + 1); } else { diff --git a/lib/src/customization/calendar_style.dart b/lib/src/customization/calendar_style.dart index 08e37444..3e3ab23b 100644 --- a/lib/src/customization/calendar_style.dart +++ b/lib/src/customization/calendar_style.dart @@ -70,7 +70,7 @@ class CalendarStyle { /// Specifies whether or not days outside of current month should be displayed. /// /// Sometimes a fragment of previous month's last week (or next month's first week) appears in current month's view. - /// This property defines if those should be visible (eg. with custom style) or hidden. + /// This property defines if those should be visible (e.g. with custom style) or hidden. final bool outsideDaysVisible; /// Determines rendering priority for SelectedDay and Today. diff --git a/lib/src/customization/days_of_week_style.dart b/lib/src/customization/days_of_week_style.dart index 86bc8a17..ba811781 100644 --- a/lib/src/customization/days_of_week_style.dart +++ b/lib/src/customization/days_of_week_style.dart @@ -5,9 +5,9 @@ part of table_calendar; /// Class containing styling for `TableCalendar`'s days of week panel. class DaysOfWeekStyle { - /// Use to customize days of week panel text (eg. with different `DateFormat`). + /// Use to customize days of week panel text (e.g. with different `DateFormat`). /// You can use `String` transformations to further customize the text. - /// Defaults to simple `'E'` format (eg. Mon, Tue, Wed, etc.). + /// Defaults to simple `'E'` format (i.e. Mon, Tue, Wed, etc.). /// /// Example usage: /// ```dart diff --git a/lib/src/customization/header_style.dart b/lib/src/customization/header_style.dart index ac0ef660..2214031a 100644 --- a/lib/src/customization/header_style.dart +++ b/lib/src/customization/header_style.dart @@ -16,9 +16,9 @@ class HeaderStyle { /// * `false` - the button will show current CalendarFormat final bool formatButtonShowsNext; - /// Use to customize header's title text (eg. with different `DateFormat`). + /// Use to customize header's title text (e.g. with different `DateFormat`). /// You can use `String` transformations to further customize the text. - /// Defaults to simple `'yMMMM'` format (eg. January 2019, February 2019, March 2019, etc.). + /// Defaults to simple `'yMMMM'` format (i.e. January 2019, February 2019, March 2019, etc.). /// /// Example usage: /// ```dart From ef77562f5c803187f81be2d2b454545a6b4ffb4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Thu, 7 Jan 2021 12:47:03 +0100 Subject: [PATCH 023/164] Allow null selectedDay --- lib/src/calendar.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/src/calendar.dart b/lib/src/calendar.dart index f32b1f13..21936fec 100644 --- a/lib/src/calendar.dart +++ b/lib/src/calendar.dart @@ -46,7 +46,7 @@ enum StartingDayOfWeek { thursday, friday, saturday, - sunday + sunday, } int _getWeekdayNumber(StartingDayOfWeek weekday) { @@ -288,7 +288,9 @@ class TableCalendarState extends State> void didUpdateWidget(TableCalendar oldWidget) { super.didUpdateWidget(oldWidget); - if (_selectedDay != widget.selectedDay && widget.selectedDay != null) { + if (widget.selectedDay == null) { + _selectedDay = null; + } else if (_selectedDay != widget.selectedDay) { final day = widget.selectedDay; _selectedDay = day; From e7d4f34783e47e89d86bc4b289f39ddbefc66c14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Thu, 7 Jan 2021 12:58:15 +0100 Subject: [PATCH 024/164] Add updated examples --- example/lib/main.dart | 386 +++------------------ example/lib/pages/complex_example.dart | 321 +++++++++++++++++ example/lib/pages/events_example.dart | 121 +++++++ example/lib/pages/persistence_example.dart | 70 ++++ example/lib/pages/simple_example.dart | 61 ++++ example/lib/utils.dart | 43 +++ 6 files changed, 656 insertions(+), 346 deletions(-) create mode 100644 example/lib/pages/complex_example.dart create mode 100644 example/lib/pages/events_example.dart create mode 100644 example/lib/pages/persistence_example.dart create mode 100644 example/lib/pages/simple_example.dart create mode 100644 example/lib/utils.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index c8e93738..14199417 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,18 +1,12 @@ -// Copyright (c) 2019 Aleksander Woźniak -// Licensed under Apache License v2.0 +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 import 'package:flutter/material.dart'; import 'package:intl/date_symbol_data_local.dart'; -import 'package:table_calendar/table_calendar.dart'; - -// Example holidays -final Map _holidays = { - DateTime(2020, 1, 1): ['New Year\'s Day'], - DateTime(2020, 1, 6): ['Epiphany'], - DateTime(2020, 2, 14): ['Valentine\'s Day'], - DateTime(2020, 4, 21): ['Easter Sunday'], - DateTime(2020, 4, 22): ['Easter Monday'], -}; +import 'package:table_calendar_example/pages/complex_example.dart'; +import 'package:table_calendar_example/pages/events_example.dart'; +import 'package:table_calendar_example/pages/persistence_example.dart'; +import 'package:table_calendar_example/pages/simple_example.dart'; void main() { initializeDateFormatting().then((_) => runApp(MyApp())); @@ -22,365 +16,65 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( - title: 'Table Calendar Demo', + title: 'TableCalendar Example', theme: ThemeData( primarySwatch: Colors.blue, ), - home: MyHomePage(title: 'Table Calendar Demo'), + home: StartPage(), ); } } -class MyHomePage extends StatefulWidget { - MyHomePage({Key key, this.title}) : super(key: key); - - final String title; - +class StartPage extends StatefulWidget { @override - _MyHomePageState createState() => _MyHomePageState(); + _StartPageState createState() => _StartPageState(); } -class _MyHomePageState extends State with TickerProviderStateMixin { - Map _events; - List _selectedEvents; - AnimationController _animationController; - CalendarController _calendarController; - - @override - void initState() { - super.initState(); - final _selectedDay = DateTime.now(); - - _events = { - _selectedDay.subtract(Duration(days: 30)): [ - 'Event A0', - 'Event B0', - 'Event C0' - ], - _selectedDay.subtract(Duration(days: 27)): ['Event A1'], - _selectedDay.subtract(Duration(days: 20)): [ - 'Event A2', - 'Event B2', - 'Event C2', - 'Event D2' - ], - _selectedDay.subtract(Duration(days: 16)): ['Event A3', 'Event B3'], - _selectedDay.subtract(Duration(days: 10)): [ - 'Event A4', - 'Event B4', - 'Event C4' - ], - _selectedDay.subtract(Duration(days: 4)): [ - 'Event A5', - 'Event B5', - 'Event C5' - ], - _selectedDay.subtract(Duration(days: 2)): ['Event A6', 'Event B6'], - _selectedDay: ['Event A7', 'Event B7', 'Event C7', 'Event D7'], - _selectedDay.add(Duration(days: 1)): [ - 'Event A8', - 'Event B8', - 'Event C8', - 'Event D8' - ], - _selectedDay.add(Duration(days: 3)): - Set.from(['Event A9', 'Event A9', 'Event B9']).toList(), - _selectedDay.add(Duration(days: 7)): [ - 'Event A10', - 'Event B10', - 'Event C10' - ], - _selectedDay.add(Duration(days: 11)): ['Event A11', 'Event B11'], - _selectedDay.add(Duration(days: 17)): [ - 'Event A12', - 'Event B12', - 'Event C12', - 'Event D12' - ], - _selectedDay.add(Duration(days: 22)): ['Event A13', 'Event B13'], - _selectedDay.add(Duration(days: 26)): [ - 'Event A14', - 'Event B14', - 'Event C14' - ], - }; - - _selectedEvents = _events[_selectedDay] ?? []; - _calendarController = CalendarController(); - - _animationController = AnimationController( - vsync: this, - duration: const Duration(milliseconds: 400), - ); - - _animationController.forward(); - } - - @override - void dispose() { - _animationController.dispose(); - _calendarController.dispose(); - super.dispose(); - } - - void _onDaySelected(DateTime day, List events, List holidays) { - print('CALLBACK: _onDaySelected'); - setState(() { - _selectedEvents = events; - }); - } - - void _onVisibleDaysChanged( - DateTime first, DateTime last, CalendarFormat format) { - print('CALLBACK: _onVisibleDaysChanged'); - } - - void _onCalendarCreated( - DateTime first, DateTime last, CalendarFormat format) { - print('CALLBACK: _onCalendarCreated'); - } - +class _StartPageState extends State { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text(widget.title), - ), - body: Column( - mainAxisSize: MainAxisSize.max, - children: [ - // Switch out 2 lines below to play with TableCalendar's settings - //----------------------- - _buildTableCalendar(), - // _buildTableCalendarWithBuilders(), - const SizedBox(height: 8.0), - _buildButtons(), - const SizedBox(height: 8.0), - Expanded(child: _buildEventList()), - ], - ), - ); - } - - // Simple TableCalendar configuration (using Styles) - Widget _buildTableCalendar() { - return TableCalendar( - calendarController: _calendarController, - events: _events, - holidays: _holidays, - startingDayOfWeek: StartingDayOfWeek.monday, - calendarStyle: CalendarStyle( - selectedColor: Colors.deepOrange[400], - todayColor: Colors.deepOrange[200], - markersColor: Colors.brown[700], - outsideDaysVisible: false, - ), - headerStyle: HeaderStyle( - formatButtonTextStyle: - TextStyle().copyWith(color: Colors.white, fontSize: 15.0), - formatButtonDecoration: BoxDecoration( - color: Colors.deepOrange[400], - borderRadius: BorderRadius.circular(16.0), - ), - ), - onDaySelected: _onDaySelected, - onVisibleDaysChanged: _onVisibleDaysChanged, - onCalendarCreated: _onCalendarCreated, - ); - } - - // More advanced TableCalendar configuration (using Builders & Styles) - Widget _buildTableCalendarWithBuilders() { - return TableCalendar( - locale: 'pl_PL', - calendarController: _calendarController, - events: _events, - holidays: _holidays, - initialCalendarFormat: CalendarFormat.month, - formatAnimation: FormatAnimation.slide, - startingDayOfWeek: StartingDayOfWeek.sunday, - availableGestures: AvailableGestures.all, - availableCalendarFormats: const { - CalendarFormat.month: '', - CalendarFormat.week: '', - }, - calendarStyle: CalendarStyle( - outsideDaysVisible: false, - weekendStyle: TextStyle().copyWith(color: Colors.blue[800]), - holidayStyle: TextStyle().copyWith(color: Colors.blue[800]), + title: Text('TableCalendar Example'), ), - daysOfWeekStyle: DaysOfWeekStyle( - weekendStyle: TextStyle().copyWith(color: Colors.blue[600]), - ), - headerStyle: HeaderStyle( - centerHeaderTitle: true, - formatButtonVisible: false, - ), - builders: CalendarBuilders( - selectedDayBuilder: (context, date, _) { - return FadeTransition( - opacity: Tween(begin: 0.0, end: 1.0).animate(_animationController), - child: Container( - margin: const EdgeInsets.all(4.0), - padding: const EdgeInsets.only(top: 5.0, left: 6.0), - color: Colors.deepOrange[300], - width: 100, - height: 100, - child: Text( - '${date.day}', - style: TextStyle().copyWith(fontSize: 16.0), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + RaisedButton( + child: Text('Simple'), + onPressed: () => Navigator.push( + context, + MaterialPageRoute(builder: (_) => SimpleExamplePage()), ), ), - ); - }, - todayDayBuilder: (context, date, _) { - return Container( - margin: const EdgeInsets.all(4.0), - padding: const EdgeInsets.only(top: 5.0, left: 6.0), - color: Colors.amber[400], - width: 100, - height: 100, - child: Text( - '${date.day}', - style: TextStyle().copyWith(fontSize: 16.0), - ), - ); - }, - markersBuilder: (context, date, events, holidays) { - final children = []; - - if (events.isNotEmpty) { - children.add( - Positioned( - right: 1, - bottom: 1, - child: _buildEventsMarker(date, events), - ), - ); - } - - if (holidays.isNotEmpty) { - children.add( - Positioned( - right: -2, - top: -2, - child: _buildHolidaysMarker(), - ), - ); - } - - return children; - }, - ), - onDaySelected: (date, events, holidays) { - _onDaySelected(date, events, holidays); - _animationController.forward(from: 0.0); - }, - onVisibleDaysChanged: _onVisibleDaysChanged, - onCalendarCreated: _onCalendarCreated, - ); - } - - Widget _buildEventsMarker(DateTime date, List events) { - return AnimatedContainer( - duration: const Duration(milliseconds: 300), - decoration: BoxDecoration( - shape: BoxShape.rectangle, - color: _calendarController.isSelected(date) - ? Colors.brown[500] - : _calendarController.isToday(date) - ? Colors.brown[300] - : Colors.blue[400], - ), - width: 16.0, - height: 16.0, - child: Center( - child: Text( - '${events.length}', - style: TextStyle().copyWith( - color: Colors.white, - fontSize: 12.0, - ), - ), - ), - ); - } - - Widget _buildHolidaysMarker() { - return Icon( - Icons.add_box, - size: 20.0, - color: Colors.blueGrey[800], - ); - } - - Widget _buildButtons() { - final dateTime = _events.keys.elementAt(_events.length - 2); - - return Column( - children: [ - Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ + const SizedBox(height: 12.0), RaisedButton( - child: Text('Month'), - onPressed: () { - setState(() { - _calendarController.setCalendarFormat(CalendarFormat.month); - }); - }, + child: Text('Persistence'), + onPressed: () => Navigator.push( + context, + MaterialPageRoute(builder: (_) => PersistenceExamplePage()), + ), ), + const SizedBox(height: 12.0), RaisedButton( - child: Text('2 weeks'), - onPressed: () { - setState(() { - _calendarController - .setCalendarFormat(CalendarFormat.twoWeeks); - }); - }, + child: Text('Events'), + onPressed: () => Navigator.push( + context, + MaterialPageRoute(builder: (_) => EventsExamplePage()), + ), ), + const SizedBox(height: 12.0), RaisedButton( - child: Text('Week'), - onPressed: () { - setState(() { - _calendarController.setCalendarFormat(CalendarFormat.week); - }); - }, + child: Text('Complex'), + onPressed: () => Navigator.push( + context, + MaterialPageRoute(builder: (_) => ComplexExamplePage()), + ), ), ], ), - const SizedBox(height: 8.0), - RaisedButton( - child: Text( - 'Set day ${dateTime.day}-${dateTime.month}-${dateTime.year}'), - onPressed: () { - _calendarController.setSelectedDay( - DateTime(dateTime.year, dateTime.month, dateTime.day), - runCallback: true, - ); - }, - ), - ], - ); - } - - Widget _buildEventList() { - return ListView( - children: _selectedEvents - .map((event) => Container( - decoration: BoxDecoration( - border: Border.all(width: 0.8), - borderRadius: BorderRadius.circular(12.0), - ), - margin: - const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0), - child: ListTile( - title: Text(event.toString()), - onTap: () => print('$event tapped!'), - ), - )) - .toList(), + ), ); } } diff --git a/example/lib/pages/complex_example.dart b/example/lib/pages/complex_example.dart new file mode 100644 index 00000000..a5cdb33d --- /dev/null +++ b/example/lib/pages/complex_example.dart @@ -0,0 +1,321 @@ +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 + +import 'dart:collection'; + +import 'package:flutter/material.dart'; +import 'package:table_calendar/table_calendar.dart'; +import 'package:table_calendar_example/utils.dart'; + +class ComplexExamplePage extends StatefulWidget { + @override + _ComplexExamplePageState createState() => _ComplexExamplePageState(); +} + +class _ComplexExamplePageState extends State + with SingleTickerProviderStateMixin { + Map> _events; + ValueNotifier> _selectedEvents; + AnimationController _animationController; + CalendarFormat _calendarFormat; + DateTime _selectedDay; + DateTime _focusedDay; + + @override + void initState() { + super.initState(); + final now = DateTime.now().toUtc(); + + // Using a [LinkedHashMap] is highly recommended + _events = LinkedHashMap( + equals: isSameDay, + hashCode: getHashCode, + )..addAll(kEvents); + + _selectedDay = now; + _focusedDay = now; + _selectedEvents = ValueNotifier(_events[_selectedDay] ?? []); + _calendarFormat = CalendarFormat.month; + + _animationController = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 400), + ); + + _animationController.forward(); + } + + @override + void dispose() { + _animationController.dispose(); + _selectedEvents.dispose(); + super.dispose(); + } + + void _onDaySelected(DateTime day, List events, List holidays) { + print('==============================='); + print('Callback: _onDaySelected'); + print('SelectedDay: $day'); + print('Events: $events'); + print('Holidays: $holidays'); + + if (_selectedDay != day) { + _selectedDay = day; + _focusedDay = day; + } + + // This rebuilds just the [eventList], not the whole page + _selectedEvents.value = [ + ...holidays, + ...events, + ]; + + _animationController.forward(from: 0.0); + } + + void _onPageChanged(DateTime focusedDay, DateTime first, DateTime last) { + print('==============================='); + print('Callback: _onPageChanged'); + print('FocusedDay: $focusedDay'); + print('First: $first'); + print('Last: $last'); + + if (_focusedDay != focusedDay) { + _focusedDay = focusedDay; + } + } + + void _onFormatChanged(CalendarFormat format, DateTime first, DateTime last) { + print('==============================='); + print('Callback: _onFormatChanged'); + print('CalendarFormat: $format'); + print('First: $first'); + print('Last: $last'); + + if (_calendarFormat != format) { + _calendarFormat = format; + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('TableCalendar-Complex'), + ), + body: Column( + mainAxisSize: MainAxisSize.max, + children: [ + _buildTableCalendar(), + const SizedBox(height: 8.0), + _buildButtons(), + const SizedBox(height: 8.0), + Expanded(child: _buildEventList()), + ], + ), + ); + } + + Widget _buildTableCalendar() { + return TableCalendar( + locale: 'pl_PL', + events: _events, + holidays: kHolidays, + selectedDay: _selectedDay, + focusedDay: _focusedDay, + calendarFormat: _calendarFormat, + startingDayOfWeek: StartingDayOfWeek.monday, + calendarStyle: CalendarStyle( + outsideDaysVisible: false, + weekendStyle: TextStyle(color: Colors.blue[800]), + holidayStyle: TextStyle(color: Colors.blue[800]), + ), + daysOfWeekStyle: DaysOfWeekStyle( + weekendStyle: TextStyle(color: Colors.blue[600]), + ), + headerStyle: HeaderStyle( + centerHeaderTitle: true, + formatButtonVisible: false, + ), + builders: CalendarBuilders( + selectedDayBuilder: (context, date, _) { + return FadeTransition( + opacity: Tween(begin: 0.0, end: 1.0).animate(_animationController), + child: Container( + margin: const EdgeInsets.all(4.0), + padding: const EdgeInsets.only(top: 5.0, left: 6.0), + color: Colors.deepOrange[300], + width: 100, + height: 100, + child: Text( + '${date.day}', + style: TextStyle(fontSize: 16.0), + ), + ), + ); + }, + todayDayBuilder: (context, date, _) { + return Container( + margin: const EdgeInsets.all(4.0), + padding: const EdgeInsets.only(top: 5.0, left: 6.0), + color: Colors.amber[400], + width: 100, + height: 100, + child: Text( + '${date.day}', + style: TextStyle(fontSize: 16.0), + ), + ); + }, + markersBuilder: (context, date, events, holidays) { + final children = []; + + if (events.isNotEmpty) { + children.add( + Positioned( + right: 1, + bottom: 1, + child: _buildEventsMarker(date, events), + ), + ); + } + + if (holidays.isNotEmpty) { + children.add( + Positioned( + right: -2, + top: -2, + child: _buildHolidaysMarker(), + ), + ); + } + + return children; + }, + ), + onDaySelected: _onDaySelected, + onPageChanged: _onPageChanged, + onFormatChanged: _onFormatChanged, + ); + } + + Widget _buildEventsMarker(DateTime date, List events) { + return AnimatedContainer( + duration: const Duration(milliseconds: 300), + decoration: BoxDecoration( + shape: BoxShape.rectangle, + color: isSameDay(date, _selectedDay) + ? Colors.brown[500] + : isSameDay(date, DateTime.now()) + ? Colors.brown[300] + : Colors.blue[400], + ), + width: 16.0, + height: 16.0, + child: Center( + child: Text( + '${events.length}', + style: TextStyle( + color: Colors.white, + fontSize: 12.0, + ), + ), + ), + ); + } + + Widget _buildHolidaysMarker() { + return Icon( + Icons.add_box, + size: 20.0, + color: Colors.blueGrey[800], + ); + } + + Widget _buildButtons() { + final dateTime = _events.keys.elementAt(_events.length - 2); + + return Column( + children: [ + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + RaisedButton( + child: Text('Month'), + onPressed: () { + setState(() { + _calendarFormat = CalendarFormat.month; + }); + }, + ), + RaisedButton( + child: Text('2 weeks'), + onPressed: () { + setState(() { + _calendarFormat = CalendarFormat.twoWeeks; + }); + }, + ), + RaisedButton( + child: Text('Week'), + onPressed: () { + setState(() { + _calendarFormat = CalendarFormat.week; + }); + }, + ), + ], + ), + const SizedBox(height: 8.0), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + RaisedButton( + child: Text('Select today'), + onPressed: () { + setState(() { + _selectedDay = DateTime.now().toUtc(); + _focusedDay = _selectedDay; + }); + }, + ), + RaisedButton( + child: Text( + 'Select ${dateTime.day}/${dateTime.month}/${dateTime.year}', + ), + onPressed: () { + setState(() { + _selectedDay = dateTime; + _focusedDay = dateTime; + }); + }, + ), + ], + ), + ], + ); + } + + Widget _buildEventList() { + return ValueListenableBuilder>( + valueListenable: _selectedEvents, + builder: (context, value, child) => ListView( + children: value + .map((event) => Container( + decoration: BoxDecoration( + border: Border.all(width: 0.8), + borderRadius: BorderRadius.circular(12.0), + ), + margin: const EdgeInsets.symmetric( + horizontal: 8.0, vertical: 4.0), + child: ListTile( + title: Text(event.toString()), + onTap: () => print('$event tapped!'), + ), + )) + .toList(), + ), + ); + } +} diff --git a/example/lib/pages/events_example.dart b/example/lib/pages/events_example.dart new file mode 100644 index 00000000..0e6665af --- /dev/null +++ b/example/lib/pages/events_example.dart @@ -0,0 +1,121 @@ +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 + +import 'dart:collection'; + +import 'package:flutter/material.dart'; +import 'package:table_calendar/table_calendar.dart'; +import 'package:table_calendar_example/utils.dart'; + +class EventsExamplePage extends StatefulWidget { + @override + _EventsExamplePageState createState() => _EventsExamplePageState(); +} + +class _EventsExamplePageState extends State { + Map> _events; + List _selectedEvents; + DateTime _selectedDay; + CalendarFormat _calendarFormat; + + @override + void initState() { + super.initState(); + final selectedDay = DateTime.now().toUtc(); + + // Using a [LinkedHashMap] is highly recommended + _events = LinkedHashMap( + equals: isSameDay, + hashCode: getHashCode, + )..addAll(kEvents); + + _selectedEvents = _events[selectedDay] ?? []; + _selectedDay = selectedDay; + _calendarFormat = CalendarFormat.month; + } + + void _onDaySelected(DateTime day, List events, List holidays) { + print('==============================='); + print('Callback: _onDaySelected'); + print('SelectedDay: $day'); + print('Events: $events'); + print('Holidays: $holidays'); + + if (_selectedDay != day) { + _selectedDay = day; + } + + // Only [eventList] requires rebuilding, so this can be improved + // -> check out "complex_example.dart" file + setState(() { + _selectedEvents = [ + ...holidays, + ...events, + ]; + }); + } + + void _onFormatChanged(CalendarFormat format, DateTime first, DateTime last) { + print('==============================='); + print('Callback: _onFormatChanged'); + print('Format: $format'); + print('First: $first'); + print('Last: $last'); + + if (_calendarFormat != format) { + _calendarFormat = format; + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('TableCalendar-Events'), + ), + body: Column( + mainAxisSize: MainAxisSize.max, + children: [ + TableCalendar( + events: _events, + holidays: kHolidays, + selectedDay: _selectedDay, + calendarFormat: _calendarFormat, + onDaySelected: _onDaySelected, + onFormatChanged: _onFormatChanged, + ), + const SizedBox(height: 8.0), + RaisedButton( + child: Text('Clear selection'), + onPressed: () { + setState(() { + _selectedDay = null; + _selectedEvents = []; + }); + }, + ), + Expanded(child: _buildEventList()), + ], + ), + ); + } + + Widget _buildEventList() { + return ListView( + children: _selectedEvents + .map((event) => Container( + decoration: BoxDecoration( + border: Border.all(width: 0.8), + borderRadius: BorderRadius.circular(12.0), + ), + margin: + const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0), + child: ListTile( + title: Text(event.toString()), + onTap: () => print('$event tapped!'), + ), + )) + .toList(), + ); + } +} diff --git a/example/lib/pages/persistence_example.dart b/example/lib/pages/persistence_example.dart new file mode 100644 index 00000000..a0b9ee9a --- /dev/null +++ b/example/lib/pages/persistence_example.dart @@ -0,0 +1,70 @@ +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 + +import 'package:flutter/material.dart'; +import 'package:table_calendar/table_calendar.dart'; + +class PersistenceExamplePage extends StatefulWidget { + @override + _PersistenceExamplePageState createState() => _PersistenceExamplePageState(); +} + +class _PersistenceExamplePageState extends State { + CalendarFormat _calendarFormat = CalendarFormat.month; + bool _isVisible = true; + DateTime _selectedDay; + DateTime _focusedDay; + + @override + Widget build(BuildContext context) { + final orientation = MediaQuery.of(context).orientation; + + return Scaffold( + appBar: AppBar(title: Text('TableCalendar-Persistence')), + body: Column( + children: [ + Visibility( + visible: _isVisible, + child: TableCalendar( + rowHeight: orientation == Orientation.portrait ? 52.0 : 30.0, + selectedDay: _selectedDay, + focusedDay: _focusedDay, + calendarFormat: _calendarFormat, + onDaySelected: (day, _, __) { + if (_selectedDay != day) { + // Note that this does not rebuild the widget + // Values are updated only in case widget gets rebuilt in future + // (for example by toggling [_isVisible]) + _selectedDay = day; + _focusedDay = day; + } + }, + onPageChanged: (focusedDay, _, __) { + if (_focusedDay != focusedDay) { + _focusedDay = focusedDay; + } + }, + onFormatChanged: (format, _, __) { + if (_calendarFormat != format) { + _calendarFormat = format; + } + }, + ), + ), + Spacer(), + Center( + child: RaisedButton( + child: Text('Toggle visibility'), + onPressed: () { + setState(() { + _isVisible = !_isVisible; + }); + }, + ), + ), + const SizedBox(height: 20.0), + ], + ), + ); + } +} diff --git a/example/lib/pages/simple_example.dart b/example/lib/pages/simple_example.dart new file mode 100644 index 00000000..e94c6502 --- /dev/null +++ b/example/lib/pages/simple_example.dart @@ -0,0 +1,61 @@ +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 + +import 'dart:collection'; + +import 'package:flutter/material.dart'; +import 'package:table_calendar/table_calendar.dart'; +import 'package:table_calendar_example/utils.dart'; + +class SimpleExamplePage extends StatefulWidget { + @override + _SimpleExamplePageState createState() => _SimpleExamplePageState(); +} + +class _SimpleExamplePageState extends State { + Map> _events; + + @override + void initState() { + super.initState(); + + // Using a [LinkedHashMap] is highly recommended + _events = LinkedHashMap( + equals: isSameDay, + hashCode: getHashCode, + )..addAll(kEvents); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('TableCalendar-Simple'), + ), + body: TableCalendar( + events: _events, + // Use to customize calendar's body style + calendarStyle: CalendarStyle( + selectedColor: Colors.deepOrange[400], + todayColor: Colors.deepOrange[200], + markersColor: Colors.brown[700], + outsideDaysVisible: false, + ), + // Use to customize calendar's header style + headerStyle: HeaderStyle( + formatButtonTextStyle: + const TextStyle(color: Colors.white, fontSize: 15.0), + formatButtonDecoration: BoxDecoration( + color: Colors.deepOrange[400], + borderRadius: BorderRadius.circular(16.0), + ), + ), + onDaySelected: (day, events, holidays) { + print('Callback: onDaySelected'); + print('SelectedDay: $day'); + print('Events: $events'); + }, + ), + ); + } +} diff --git a/example/lib/utils.dart b/example/lib/utils.dart new file mode 100644 index 00000000..8c10d9aa --- /dev/null +++ b/example/lib/utils.dart @@ -0,0 +1,43 @@ +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 + +/// Example Event class +class Event { + final String title; + + const Event(this.title); + + @override + String toString() => title; +} + +/// Example events +final kEvents = Map.fromIterable(List.generate(50, (index) => index), + key: (item) => DateTime.utc(2020, 10, item * 5), + value: (item) => List.generate( + item % 4 + 1, (index) => Event('Event $item | ${index + 1}'))) + ..addAll({ + DateTime.now().toUtc(): [ + Event('Today\'s Event 1'), + Event('Today\'s Event 2'), + ], + }); + +/// Example holidays +final kHolidays = { + DateTime.utc(2021, 1, 1): [Event('New Year\'s Day')], + DateTime.utc(2021, 2, 14): [Event('Valentine\'s Day')], + DateTime.utc(2021, 3, 8): [Event('International Women\'s Day')], + DateTime.utc(2021, 4, 1): [Event('April Fools\' Day')], + DateTime.utc(2021, 5, 4): [Event('Star Wars Day')], + DateTime.utc(2021, 10, 31): [Event('Halloween')], + DateTime.utc(2021, 12, 31): [Event('New Year\s Eve')], +}; + +bool isSameDay(DateTime a, DateTime b) { + return a.year == b.year && a.month == b.month && a.day == b.day; +} + +int getHashCode(DateTime key) { + return key.day * 1000000 + key.month * 10000 + key.year; +} From fea582cfcc7adac1f2b3e57f5cf78a1c6c6b20a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Mon, 11 Jan 2021 17:22:53 +0100 Subject: [PATCH 025/164] Format fixes and improvements --- lib/src/calendar.dart | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/src/calendar.dart b/lib/src/calendar.dart index 21936fec..a7f02ebb 100644 --- a/lib/src/calendar.dart +++ b/lib/src/calendar.dart @@ -868,8 +868,9 @@ class TableCalendarState extends State> DateTime _getPageBaseDay(int index, CalendarFormat format) { if (index == _pageIndex) { - if (format == CalendarFormat.month) { - return _focusedDay.value; + if (format != CalendarFormat.twoWeeks) { + _baseDay = _focusedDay.value; + return _baseDay; } return _baseDay; @@ -878,8 +879,7 @@ class TableCalendarState extends State> final pageDifference = index - _pageIndex; if (format == CalendarFormat.month) { - return DateTime.utc( - _baseDay.year, _baseDay.month + pageDifference, _baseDay.day); + return DateTime.utc(_baseDay.year, _baseDay.month + pageDifference, 1); } else if (format == CalendarFormat.twoWeeks) { return DateTime.utc( _baseDay.year, @@ -900,8 +900,8 @@ class TableCalendarState extends State> final pageDifference = index - _pageIndex; if (format == CalendarFormat.month) { - return DateTime.utc(_focusedDay.value.year, - _focusedDay.value.month + pageDifference, _focusedDay.value.day); + return DateTime.utc( + _focusedDay.value.year, _focusedDay.value.month + pageDifference, 1); } else if (format == CalendarFormat.twoWeeks) { return DateTime.utc( _focusedDay.value.year, From deb557ee51806f37c50597daf3ca9c3b7c567868 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Mon, 11 Jan 2021 18:07:34 +0100 Subject: [PATCH 026/164] Prevent month navigation on selecting outside days --- lib/src/calendar.dart | 67 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 14 deletions(-) diff --git a/lib/src/calendar.dart b/lib/src/calendar.dart index a7f02ebb..6e8cfa43 100644 --- a/lib/src/calendar.dart +++ b/lib/src/calendar.dart @@ -4,7 +4,8 @@ part of table_calendar; /// Callback exposing currently selected day. -typedef void OnDaySelected(DateTime day, List events, List holidays); +typedef void OnDaySelected(DateTime selectedDay, DateTime focusedDay, + List events, List holidays); /// Callback exposing currently focused day along with first and last visible day. typedef void OnPageChanged(DateTime focusedDay, DateTime first, DateTime last); @@ -288,6 +289,14 @@ class TableCalendarState extends State> void didUpdateWidget(TableCalendar oldWidget) { super.didUpdateWidget(oldWidget); + if (_focusedDay.value != widget.focusedDay && widget.focusedDay != null) { + final day = widget.focusedDay; + + _focusedDay.value = day; + _baseDay = day; + _calendarHeight.value = _getPageHeight(baseDay: _baseDay); + } + if (widget.selectedDay == null) { _selectedDay = null; } else if (_selectedDay != widget.selectedDay) { @@ -297,20 +306,13 @@ class TableCalendarState extends State> if (widget.onDaySelected != null) { widget.onDaySelected( day, + _focusedDay.value, widget.events[day] ?? [], widget.holidays[day] ?? [], ); } } - if (_focusedDay.value != widget.focusedDay && widget.focusedDay != null) { - final day = widget.focusedDay; - - _focusedDay.value = day; - _baseDay = day; - _calendarHeight.value = _getPageHeight(baseDay: _baseDay); - } - if (_calendarFormat != widget.calendarFormat) { _calendarFormat = widget.calendarFormat; @@ -467,23 +469,44 @@ class TableCalendarState extends State> void _onDaySelected(DateTime day, List events, List holidays) { setState(() { - _focusedDay.value = day; _selectedDay = day; + // TODO: + // * adjust name + // * add as a widget's property + final preventTapNavigation = true; + if (_calendarFormat == CalendarFormat.month) { - _baseDay = day; - _calendarHeight.value = _getPageHeight(baseDay: _baseDay); + if (preventTapNavigation) { + if (_isBeforeMonth(day, _focusedDay.value)) { + _baseDay = _firstDayOfMonth(_focusedDay.value); + _focusedDay.value = _baseDay; + } else if (_isAfterMonth(day, _focusedDay.value)) { + _baseDay = _lastDayOfMonth(_focusedDay.value); + _focusedDay.value = _baseDay; + } else { + _baseDay = day; + _focusedDay.value = day; + } + } else { + // default implementation with jumping to previous/next month + _baseDay = day; + _focusedDay.value = day; + _calendarHeight.value = _getPageHeight(baseDay: _baseDay); + } + } else { + _focusedDay.value = day; } }); if (widget.onDaySelected != null) { - widget.onDaySelected(day, events, holidays); + widget.onDaySelected(day, _focusedDay.value, events, holidays); } } void _onDayLongPressed(DateTime day, List events, List holidays) { if (widget.onDayLongPressed != null) { - widget.onDayLongPressed(day, events, holidays); + widget.onDayLongPressed(day, _focusedDay.value, events, holidays); } } @@ -1015,6 +1038,22 @@ class TableCalendarState extends State> : widget.enabledDayPredicate(day); } + bool _isBeforeMonth(DateTime day, DateTime month) { + if (day.year == month.year) { + return day.month < month.month; + } else { + return day.isBefore(month); + } + } + + bool _isAfterMonth(DateTime day, DateTime month) { + if (day.year == month.year) { + return day.month > month.month; + } else { + return day.isAfter(month); + } + } + bool _isWeekend(DateTime day, List weekendDays) { return weekendDays.contains(day.weekday); } From 5202ee1e668b1ea5ae6c4ff0f968990eda4d63b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Mon, 11 Jan 2021 18:08:04 +0100 Subject: [PATCH 027/164] Update examples --- example/lib/pages/complex_example.dart | 14 +++++++++----- example/lib/pages/events_example.dart | 9 +++++---- example/lib/pages/persistence_example.dart | 17 ++++++++++------- example/lib/pages/simple_example.dart | 5 ++--- 4 files changed, 26 insertions(+), 19 deletions(-) diff --git a/example/lib/pages/complex_example.dart b/example/lib/pages/complex_example.dart index a5cdb33d..7090dbb1 100644 --- a/example/lib/pages/complex_example.dart +++ b/example/lib/pages/complex_example.dart @@ -52,16 +52,20 @@ class _ComplexExamplePageState extends State super.dispose(); } - void _onDaySelected(DateTime day, List events, List holidays) { + void _onDaySelected(DateTime selectedDay, DateTime focusedDay, + List events, List holidays) { print('==============================='); print('Callback: _onDaySelected'); - print('SelectedDay: $day'); + print('SelectedDay: $selectedDay'); print('Events: $events'); print('Holidays: $holidays'); - if (_selectedDay != day) { - _selectedDay = day; - _focusedDay = day; + if (_selectedDay != selectedDay) { + _selectedDay = selectedDay; + } + + if (_focusedDay != focusedDay) { + _focusedDay = focusedDay; } // This rebuilds just the [eventList], not the whole page diff --git a/example/lib/pages/events_example.dart b/example/lib/pages/events_example.dart index 0e6665af..87490163 100644 --- a/example/lib/pages/events_example.dart +++ b/example/lib/pages/events_example.dart @@ -34,15 +34,16 @@ class _EventsExamplePageState extends State { _calendarFormat = CalendarFormat.month; } - void _onDaySelected(DateTime day, List events, List holidays) { + void _onDaySelected(DateTime selectedDay, DateTime focusedDay, + List events, List holidays) { print('==============================='); print('Callback: _onDaySelected'); - print('SelectedDay: $day'); + print('SelectedDay: $selectedDay'); print('Events: $events'); print('Holidays: $holidays'); - if (_selectedDay != day) { - _selectedDay = day; + if (_selectedDay != selectedDay) { + _selectedDay = selectedDay; } // Only [eventList] requires rebuilding, so this can be improved diff --git a/example/lib/pages/persistence_example.dart b/example/lib/pages/persistence_example.dart index a0b9ee9a..415873e9 100644 --- a/example/lib/pages/persistence_example.dart +++ b/example/lib/pages/persistence_example.dart @@ -30,13 +30,16 @@ class _PersistenceExamplePageState extends State { selectedDay: _selectedDay, focusedDay: _focusedDay, calendarFormat: _calendarFormat, - onDaySelected: (day, _, __) { - if (_selectedDay != day) { - // Note that this does not rebuild the widget - // Values are updated only in case widget gets rebuilt in future - // (for example by toggling [_isVisible]) - _selectedDay = day; - _focusedDay = day; + onDaySelected: (selectedDay, focusedDay, _, __) { + // Note that this does not rebuild the widget + // Values are updated only in case widget gets rebuilt in future + // (for example by toggling [_isVisible]) + if (_selectedDay != selectedDay) { + _selectedDay = selectedDay; + } + + if (_focusedDay != focusedDay) { + _focusedDay = focusedDay; } }, onPageChanged: (focusedDay, _, __) { diff --git a/example/lib/pages/simple_example.dart b/example/lib/pages/simple_example.dart index e94c6502..a29239a4 100644 --- a/example/lib/pages/simple_example.dart +++ b/example/lib/pages/simple_example.dart @@ -39,7 +39,6 @@ class _SimpleExamplePageState extends State { selectedColor: Colors.deepOrange[400], todayColor: Colors.deepOrange[200], markersColor: Colors.brown[700], - outsideDaysVisible: false, ), // Use to customize calendar's header style headerStyle: HeaderStyle( @@ -50,9 +49,9 @@ class _SimpleExamplePageState extends State { borderRadius: BorderRadius.circular(16.0), ), ), - onDaySelected: (day, events, holidays) { + onDaySelected: (selectedDay, _, events, __) { print('Callback: onDaySelected'); - print('SelectedDay: $day'); + print('SelectedDay: $selectedDay'); print('Events: $events'); }, ), From ea67e52ec283db682c4aeee268c8db8b62886dfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Wed, 13 Jan 2021 19:41:03 +0100 Subject: [PATCH 028/164] Use correct base for toggling formats --- lib/src/calendar.dart | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/lib/src/calendar.dart b/lib/src/calendar.dart index 6e8cfa43..c22c378e 100644 --- a/lib/src/calendar.dart +++ b/lib/src/calendar.dart @@ -390,15 +390,19 @@ class TableCalendarState extends State> return; } + final base = _calendarFormat == CalendarFormat.twoWeeks + ? _focusedDay.value + : _baseDay; + setState(() { _calendarFormat = _nextFormat(); }); - final days = _getVisibleDays(_baseDay); + final days = _getVisibleDays(base); if (_calendarFormat == CalendarFormat.month) { - _firstActiveDay = _firstDayOfMonth(_baseDay); - _lastActiveDay = _lastDayOfMonth(_baseDay); + _firstActiveDay = _firstDayOfMonth(base); + _lastActiveDay = _lastDayOfMonth(base); } else { _firstActiveDay = days.first; _lastActiveDay = days.last; @@ -409,8 +413,8 @@ class TableCalendarState extends State> if (widget.onFormatChanged != null) { if (!widget.calendarStyle.outsideDaysVisible && _calendarFormat == CalendarFormat.month) { - widget.onFormatChanged(_calendarFormat, _firstDayOfMonth(_baseDay), - _lastDayOfMonth(_baseDay)); + widget.onFormatChanged( + _calendarFormat, _firstDayOfMonth(base), _lastDayOfMonth(base)); } else { widget.onFormatChanged(_calendarFormat, days.first, days.last); } @@ -440,15 +444,19 @@ class TableCalendarState extends State> return; } + final base = _calendarFormat == CalendarFormat.twoWeeks + ? _focusedDay.value + : _baseDay; + setState(() { _calendarFormat = formats[id]; }); - final days = _getVisibleDays(_baseDay); + final days = _getVisibleDays(base); if (_calendarFormat == CalendarFormat.month) { - _firstActiveDay = _firstDayOfMonth(_baseDay); - _lastActiveDay = _lastDayOfMonth(_baseDay); + _firstActiveDay = _firstDayOfMonth(base); + _lastActiveDay = _lastDayOfMonth(base); } else { _firstActiveDay = days.first; _lastActiveDay = days.last; @@ -459,8 +467,8 @@ class TableCalendarState extends State> if (widget.onFormatChanged != null) { if (!widget.calendarStyle.outsideDaysVisible && _calendarFormat == CalendarFormat.month) { - widget.onFormatChanged(_calendarFormat, _firstDayOfMonth(_baseDay), - _lastDayOfMonth(_baseDay)); + widget.onFormatChanged( + _calendarFormat, _firstDayOfMonth(base), _lastDayOfMonth(base)); } else { widget.onFormatChanged(_calendarFormat, days.first, days.last); } From 91fc3a3655ea8fcce468004f66f157c510fa273a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 31 Jan 2021 18:31:29 +0100 Subject: [PATCH 029/164] Improve internal code --- lib/src/calendar.dart | 1095 ----------------- lib/src/customization/calendar_builders.dart | 107 +- lib/src/customization/calendar_style.dart | 190 ++- lib/src/customization/days_of_week_style.dart | 16 +- lib/src/customization/header_style.dart | 13 +- lib/src/table_calendar.dart | 485 ++++++++ lib/src/table_calendar_base.dart | 335 +++++ lib/src/table_calendar_lite.dart | 293 +++++ lib/src/widgets/calendar_core.dart | 270 ++++ lib/src/widgets/calendar_header.dart | 90 ++ lib/src/widgets/calendar_page.dart | 60 + lib/src/widgets/cell_content.dart | 164 +++ lib/src/widgets/cell_widget.dart | 87 -- lib/src/widgets/custom_icon_button.dart | 4 +- lib/src/widgets/format_button.dart | 52 + lib/table_calendar.dart | 10 +- 16 files changed, 1886 insertions(+), 1385 deletions(-) delete mode 100644 lib/src/calendar.dart create mode 100644 lib/src/table_calendar.dart create mode 100644 lib/src/table_calendar_base.dart create mode 100644 lib/src/table_calendar_lite.dart create mode 100644 lib/src/widgets/calendar_core.dart create mode 100644 lib/src/widgets/calendar_header.dart create mode 100644 lib/src/widgets/calendar_page.dart create mode 100644 lib/src/widgets/cell_content.dart delete mode 100644 lib/src/widgets/cell_widget.dart create mode 100644 lib/src/widgets/format_button.dart diff --git a/lib/src/calendar.dart b/lib/src/calendar.dart deleted file mode 100644 index c22c378e..00000000 --- a/lib/src/calendar.dart +++ /dev/null @@ -1,1095 +0,0 @@ -// Copyright 2019 Aleksander Woźniak -// SPDX-License-Identifier: Apache-2.0 - -part of table_calendar; - -/// Callback exposing currently selected day. -typedef void OnDaySelected(DateTime selectedDay, DateTime focusedDay, - List events, List holidays); - -/// Callback exposing currently focused day along with first and last visible day. -typedef void OnPageChanged(DateTime focusedDay, DateTime first, DateTime last); - -/// Callback exposing currently used `CalendarFormat` along with first and last visible day. -typedef void OnFormatChanged( - CalendarFormat format, DateTime first, DateTime last); - -/// Callback exposing first and last initially visible day. -typedef void OnCalendarCreated(DateTime first, DateTime last); - -/// Signature for reacting to header gestures. Exposes current month and year as a `DateTime` object. -typedef void HeaderGestureCallback(DateTime focusedDay); - -/// Builder signature for any text that can be localized and formatted with `DateFormat`. -typedef String TextBuilder(DateTime date, dynamic locale); - -/// Signature for enabling days. -typedef bool EnabledDayPredicate(DateTime day); - -/// Format to display the `TableCalendar` with. -enum CalendarFormat { month, twoWeeks, week } - -/// Gestures available to `TableCalendar`. -enum AvailableGestures { none, verticalSwipe, horizontalSwipe, all } - -/// Available day of week formats. `TableCalendar` will start the week with chosen day. -/// * `StartingDayOfWeek.monday`: Monday - Sunday -/// * `StartingDayOfWeek.tuesday`: Tuesday - Monday -/// * `StartingDayOfWeek.wednesday`: Wednesday - Tuesday -/// * `StartingDayOfWeek.thursday`: Thursday - Wednesday -/// * `StartingDayOfWeek.friday`: Friday - Thursday -/// * `StartingDayOfWeek.saturday`: Saturday - Friday -/// * `StartingDayOfWeek.sunday`: Sunday - Saturday -enum StartingDayOfWeek { - monday, - tuesday, - wednesday, - thursday, - friday, - saturday, - sunday, -} - -int _getWeekdayNumber(StartingDayOfWeek weekday) { - return StartingDayOfWeek.values.indexOf(weekday) + 1; -} - -const int _initialPage = 10000; - -/// Highly customizable, feature-packed Flutter Calendar with gestures, animations and multiple formats. -class TableCalendar extends StatefulWidget { - /// Locale to format `TableCalendar` dates with, for example: `'en_US'`. - /// - /// If nothing is provided, a default locale will be used. - final dynamic locale; - - /// `Map` of events. - /// Each `DateTime` inside this `Map` should get its own `List` of objects (i.e. events). - /// - /// Use of `LinkedHashMap` is highly encouraged. - final Map> events; - - /// `Map` of holidays. - /// This property allows you to provide custom holiday rules. - /// - /// Use of `LinkedHashMap` is highly encouraged. - final Map> holidays; - - /// Called whenever any day gets tapped. - final OnDaySelected onDaySelected; - - /// Called whenever any day gets long pressed. - final OnDaySelected onDayLongPressed; - - /// Called whenever any unavailable day gets tapped. - /// Replaces `onDaySelected` for those days. - final Function(DateTime) onUnavailableDaySelected; - - /// Called whenever any unavailable day gets long pressed. - /// Replaces `onDaySelected` for those days. - final Function(DateTime) onUnavailableDayLongPressed; - - /// Called whenever header gets tapped. - final HeaderGestureCallback onHeaderTapped; - - /// Called whenever header gets long pressed. - final HeaderGestureCallback onHeaderLongPressed; - - /// Called whenever currently visible calendar page is changed. - final OnPageChanged onPageChanged; - - /// Called whenever `calendarFormat` is changed. - final OnFormatChanged onFormatChanged; - - /// Called once when the `TableCalendar` widget gets initialized. - final OnCalendarCreated onCalendarCreated; - - /// DateTime to highlight as currently selected. - final DateTime selectedDay; - - /// DateTime that determines which days are currently visible and focused. - final DateTime focusedDay; - - /// The first day of `TableCalendar`. - /// Days before it will use `unavailableStyle` and run `onUnavailableDaySelected` callback. - final DateTime startDay; - - /// The last day of `TableCalendar`. - /// Days after it will use `unavailableStyle` and run `onUnavailableDaySelected` callback. - final DateTime endDay; - - /// List of days treated as weekend days. - /// Use built-in `DateTime` weekday constants (e.g. `DateTime.monday`) instead of `int` literals (e.g. `1`). - final List weekendDays; - - /// Specifies `TableCalendar`'s format. - final CalendarFormat calendarFormat; - - /// `Map` of `CalendarFormat`s and `String` names associated with them. - /// Those `CalendarFormat`s will be used by internal logic to manage displayed format. - /// - /// To ensure proper vertical Swipe behavior, `CalendarFormat`s should be in descending order (i.e. from biggest to smallest). - /// - /// For example: - /// ```dart - /// availableCalendarFormats: const { - /// CalendarFormat.month: 'Month', - /// CalendarFormat.week: 'Week', - /// } - /// ``` - final Map availableCalendarFormats; - - /// Determines whether calendar header should be visible. - final bool headerVisible; - - /// Determines whether the row of days of the week should be visible. - final bool daysOfWeekVisible; - - /// Function deciding whether given day should be enabled or not. - /// If `false` is returned, this day will be unavailable. - final EnabledDayPredicate enabledDayPredicate; - - /// Used for setting the height of `TableCalendar`'s rows. - final double rowHeight; - - /// Used for setting the height of `TableCalendar`'s days of week row. - final double daysOfWeekHeight; - - /// `TableCalendar` will start weeks with provided day. - /// Use `StartingDayOfWeek.monday` for Monday - Sunday week format. - /// Use `StartingDayOfWeek.sunday` for Sunday - Saturday week format. - final StartingDayOfWeek startingDayOfWeek; - - /// `HitTestBehavior` for every day cell inside `TableCalendar`. - final HitTestBehavior dayHitTestBehavior; - - /// Specifies gestures available to `TableCalendar`. - /// If `AvailableGestures.none` is used, the calendar will only be interactive via buttons. - final AvailableGestures availableGestures; - - /// Configuration for vertical Swipe detector. - final SimpleSwipeConfig simpleSwipeConfig; - - /// Style for `TableCalendar`'s content. - final CalendarStyle calendarStyle; - - /// Style for DaysOfWeek displayed between `TableCalendar`'s Header and content. - final DaysOfWeekStyle daysOfWeekStyle; - - /// Style for `TableCalendar`'s Header. - final HeaderStyle headerStyle; - - /// Set of Builders for `TableCalendar` to work with. - final CalendarBuilders builders; - - TableCalendar({ - Key key, - this.locale, - this.events = const {}, - this.holidays = const {}, - this.onDaySelected, - this.onDayLongPressed, - this.onUnavailableDaySelected, - this.onUnavailableDayLongPressed, - this.onHeaderTapped, - this.onHeaderLongPressed, - this.onPageChanged, - this.onFormatChanged, - this.onCalendarCreated, - this.selectedDay, - this.focusedDay, - this.startDay, - this.endDay, - this.weekendDays = const [DateTime.saturday, DateTime.sunday], - this.calendarFormat = CalendarFormat.month, - this.availableCalendarFormats = const { - CalendarFormat.month: 'Month', - CalendarFormat.twoWeeks: '2 weeks', - CalendarFormat.week: 'Week', - }, - this.headerVisible = true, - this.daysOfWeekVisible = true, - this.enabledDayPredicate, - this.rowHeight = 52.0, - this.daysOfWeekHeight = 16.0, - this.startingDayOfWeek = StartingDayOfWeek.sunday, - this.dayHitTestBehavior = HitTestBehavior.deferToChild, - this.availableGestures = AvailableGestures.all, - this.simpleSwipeConfig = const SimpleSwipeConfig( - verticalThreshold: 25.0, - swipeDetectionBehavior: SwipeDetectionBehavior.continuousDistinct, - ), - this.calendarStyle = const CalendarStyle(), - this.daysOfWeekStyle = const DaysOfWeekStyle(), - this.headerStyle = const HeaderStyle(), - this.builders = const CalendarBuilders(), - }) : assert(calendarFormat != null), - assert(availableCalendarFormats.keys.contains(calendarFormat)), - assert(availableCalendarFormats.length <= CalendarFormat.values.length), - assert(weekendDays != null), - assert(weekendDays.isNotEmpty - ? weekendDays.every( - (day) => day >= DateTime.monday && day <= DateTime.sunday) - : true), - super(key: key); - - @override - TableCalendarState createState() => TableCalendarState(); -} - -class TableCalendarState extends State> - with SingleTickerProviderStateMixin { - PageController _pageController; - CalendarFormat _calendarFormat; - ValueNotifier _calendarHeight; - ValueNotifier _focusedDay; - DateTime _selectedDay; - DateTime _baseDay; - DateTime _firstActiveDay; - DateTime _lastActiveDay; - int _pageIndex; - - @override - void initState() { - super.initState(); - _pageController = PageController(initialPage: _initialPage); - - final now = DateTime.now(); - _baseDay = widget.focusedDay ?? DateTime.utc(now.year, now.month, now.day); - _focusedDay = ValueNotifier(_baseDay); - _selectedDay = widget.selectedDay; - - _pageIndex = _initialPage; - _calendarFormat = widget.calendarFormat; - - final days = _getVisibleDays(_baseDay); - - if (_calendarFormat == CalendarFormat.month) { - _firstActiveDay = _firstDayOfMonth(_baseDay); - _lastActiveDay = _lastDayOfMonth(_baseDay); - } else { - _firstActiveDay = days.first; - _lastActiveDay = days.last; - } - - _calendarHeight = ValueNotifier(_getPageHeight(dayCount: days.length)); - - if (widget.onCalendarCreated != null) { - if (!widget.calendarStyle.outsideDaysVisible && - _calendarFormat == CalendarFormat.month) { - widget.onCalendarCreated( - _firstDayOfMonth(_baseDay), _lastDayOfMonth(_baseDay)); - } else { - widget.onCalendarCreated(days.first, days.last); - } - } - } - - @override - void didUpdateWidget(TableCalendar oldWidget) { - super.didUpdateWidget(oldWidget); - - if (_focusedDay.value != widget.focusedDay && widget.focusedDay != null) { - final day = widget.focusedDay; - - _focusedDay.value = day; - _baseDay = day; - _calendarHeight.value = _getPageHeight(baseDay: _baseDay); - } - - if (widget.selectedDay == null) { - _selectedDay = null; - } else if (_selectedDay != widget.selectedDay) { - final day = widget.selectedDay; - _selectedDay = day; - - if (widget.onDaySelected != null) { - widget.onDaySelected( - day, - _focusedDay.value, - widget.events[day] ?? [], - widget.holidays[day] ?? [], - ); - } - } - - if (_calendarFormat != widget.calendarFormat) { - _calendarFormat = widget.calendarFormat; - - final days = _getVisibleDays(_baseDay); - - if (_calendarFormat == CalendarFormat.month) { - _firstActiveDay = _firstDayOfMonth(_baseDay); - _lastActiveDay = _lastDayOfMonth(_baseDay); - } else { - _firstActiveDay = days.first; - _lastActiveDay = days.last; - } - - _calendarHeight.value = _getPageHeight(dayCount: days.length); - - if (widget.onFormatChanged != null) { - if (!widget.calendarStyle.outsideDaysVisible && - _calendarFormat == CalendarFormat.month) { - widget.onFormatChanged(_calendarFormat, _firstDayOfMonth(_baseDay), - _lastDayOfMonth(_baseDay)); - } else { - widget.onFormatChanged(_calendarFormat, days.first, days.last); - } - } - } - - if (oldWidget.rowHeight != widget.rowHeight || - oldWidget.daysOfWeekHeight != widget.daysOfWeekHeight) { - _calendarHeight.value = _getPageHeight(baseDay: _baseDay); - } - - if (oldWidget.startingDayOfWeek != widget.startingDayOfWeek) { - _calendarHeight.value = _getPageHeight(baseDay: _baseDay); - } - } - - @override - void dispose() { - _focusedDay.dispose(); - _calendarHeight.dispose(); - _pageController.dispose(); - super.dispose(); - } - - void previousPage({ - Duration duration = const Duration(milliseconds: 350), - Curve curve = Curves.easeOut, - }) { - if (widget.startDay != null) { - if (widget.startDay.isAfter(_firstActiveDay) || - _isSameDay(widget.startDay, _firstActiveDay)) { - return; - } - } - - _pageController.previousPage(duration: duration, curve: curve); - } - - void nextPage({ - Duration duration = const Duration(milliseconds: 350), - Curve curve = Curves.easeOut, - }) { - if (widget.endDay != null) { - if (widget.endDay.isBefore(_lastActiveDay) || - _isSameDay(widget.endDay, _lastActiveDay)) { - return; - } - } - - _pageController.nextPage(duration: duration, curve: curve); - } - - void toggleCalendarFormat() { - if (widget.availableCalendarFormats.keys.length <= 1) { - return; - } - - final base = _calendarFormat == CalendarFormat.twoWeeks - ? _focusedDay.value - : _baseDay; - - setState(() { - _calendarFormat = _nextFormat(); - }); - - final days = _getVisibleDays(base); - - if (_calendarFormat == CalendarFormat.month) { - _firstActiveDay = _firstDayOfMonth(base); - _lastActiveDay = _lastDayOfMonth(base); - } else { - _firstActiveDay = days.first; - _lastActiveDay = days.last; - } - - _calendarHeight.value = _getPageHeight(dayCount: days.length); - - if (widget.onFormatChanged != null) { - if (!widget.calendarStyle.outsideDaysVisible && - _calendarFormat == CalendarFormat.month) { - widget.onFormatChanged( - _calendarFormat, _firstDayOfMonth(base), _lastDayOfMonth(base)); - } else { - widget.onFormatChanged(_calendarFormat, days.first, days.last); - } - } - } - - void swipeCalendarFormat({@required bool isSwipeUp}) { - assert(isSwipeUp != null); - - final formats = widget.availableCalendarFormats.keys.toList(); - - if (formats.length <= 1) { - return; - } - - int id = formats.indexOf(_calendarFormat); - - // Order of CalendarFormats must be from biggest to smallest, - // e.g.: [month, twoWeeks, week] - if (isSwipeUp) { - id = min(formats.length - 1, id + 1); - } else { - id = max(0, id - 1); - } - - if (_calendarFormat == formats[id]) { - return; - } - - final base = _calendarFormat == CalendarFormat.twoWeeks - ? _focusedDay.value - : _baseDay; - - setState(() { - _calendarFormat = formats[id]; - }); - - final days = _getVisibleDays(base); - - if (_calendarFormat == CalendarFormat.month) { - _firstActiveDay = _firstDayOfMonth(base); - _lastActiveDay = _lastDayOfMonth(base); - } else { - _firstActiveDay = days.first; - _lastActiveDay = days.last; - } - - _calendarHeight.value = _getPageHeight(dayCount: days.length); - - if (widget.onFormatChanged != null) { - if (!widget.calendarStyle.outsideDaysVisible && - _calendarFormat == CalendarFormat.month) { - widget.onFormatChanged( - _calendarFormat, _firstDayOfMonth(base), _lastDayOfMonth(base)); - } else { - widget.onFormatChanged(_calendarFormat, days.first, days.last); - } - } - } - - void _onDaySelected(DateTime day, List events, List holidays) { - setState(() { - _selectedDay = day; - - // TODO: - // * adjust name - // * add as a widget's property - final preventTapNavigation = true; - - if (_calendarFormat == CalendarFormat.month) { - if (preventTapNavigation) { - if (_isBeforeMonth(day, _focusedDay.value)) { - _baseDay = _firstDayOfMonth(_focusedDay.value); - _focusedDay.value = _baseDay; - } else if (_isAfterMonth(day, _focusedDay.value)) { - _baseDay = _lastDayOfMonth(_focusedDay.value); - _focusedDay.value = _baseDay; - } else { - _baseDay = day; - _focusedDay.value = day; - } - } else { - // default implementation with jumping to previous/next month - _baseDay = day; - _focusedDay.value = day; - _calendarHeight.value = _getPageHeight(baseDay: _baseDay); - } - } else { - _focusedDay.value = day; - } - }); - - if (widget.onDaySelected != null) { - widget.onDaySelected(day, _focusedDay.value, events, holidays); - } - } - - void _onDayLongPressed(DateTime day, List events, List holidays) { - if (widget.onDayLongPressed != null) { - widget.onDayLongPressed(day, _focusedDay.value, events, holidays); - } - } - - void _onUnavailableDaySelected(DateTime day) { - if (widget.onUnavailableDaySelected != null) { - widget.onUnavailableDaySelected(day); - } - } - - void _onUnavailableDayLongPressed(DateTime day) { - if (widget.onUnavailableDayLongPressed != null) { - widget.onUnavailableDayLongPressed(day); - } - } - - void _onHeaderTapped() { - if (widget.onHeaderTapped != null) { - widget.onHeaderTapped(_focusedDay.value); - } - } - - void _onHeaderLongPressed() { - if (widget.onHeaderLongPressed != null) { - widget.onHeaderLongPressed(_focusedDay.value); - } - } - - @override - Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - if (widget.headerVisible) _buildHeader(), - Padding( - padding: widget.calendarStyle.contentPadding, - child: _buildPageView(), - ), - ], - ); - } - - Widget _buildPageView() { - return SimpleGestureDetector( - onVerticalSwipe: widget.availableGestures == AvailableGestures.all || - widget.availableGestures == AvailableGestures.verticalSwipe - ? (swipeDirection) => swipeCalendarFormat( - isSwipeUp: swipeDirection == SwipeDirection.up) - : null, - swipeConfig: widget.simpleSwipeConfig, - child: ValueListenableBuilder( - valueListenable: _calendarHeight, - builder: (context, value, child) { - return AnimatedSize( - vsync: this, - alignment: Alignment.topCenter, - duration: const Duration(milliseconds: 200), - child: SizedBox(height: value, child: child), - ); - }, - child: PageView.custom( - controller: _pageController, - physics: widget.availableGestures == AvailableGestures.all || - widget.availableGestures == AvailableGestures.horizontalSwipe - ? PageScrollPhysics() - : NeverScrollableScrollPhysics(), - childrenDelegate: SliverChildBuilderDelegate( - (context, index) { - final baseDay = _getPageBaseDay(index, _calendarFormat); - final days = _getVisibleDays(baseDay); - - final focusedDay = _getPageFocusedDay(index, _calendarFormat); - - final daysInWeek = 7; - - final children = [ - if (widget.daysOfWeekVisible) - _buildDaysOfWeek(days.take(daysInWeek).toList()), - ]; - - int x = 0; - while (x < days.length) { - children.add(_buildTableRow( - days.skip(x).take(daysInWeek).toList(), focusedDay)); - x += daysInWeek; - } - - return Table( - defaultColumnWidth: FractionColumnWidth(1.0 / daysInWeek), - children: children, - ); - }, - addAutomaticKeepAlives: false, - addRepaintBoundaries: true, - ), - onPageChanged: (index) { - _baseDay = _getPageBaseDay(index, _calendarFormat); - _focusedDay.value = _baseDay; - _pageIndex = index; - - final days = _getVisibleDays(_baseDay); - _calendarHeight.value = _getPageHeight(dayCount: days.length); - - if (_calendarFormat == CalendarFormat.month) { - _firstActiveDay = _firstDayOfMonth(_baseDay); - _lastActiveDay = _lastDayOfMonth(_baseDay); - } else { - _firstActiveDay = days.first; - _lastActiveDay = days.last; - } - - if (widget.onPageChanged != null) { - if (!widget.calendarStyle.outsideDaysVisible && - _calendarFormat == CalendarFormat.month) { - widget.onPageChanged(_focusedDay.value, - _firstDayOfMonth(_baseDay), _lastDayOfMonth(_baseDay)); - } else { - widget.onPageChanged(_focusedDay.value, days.first, days.last); - } - } - }, - ), - ), - ); - } - - TableRow _buildDaysOfWeek(List days) { - return TableRow( - decoration: widget.daysOfWeekStyle.decoration, - children: days.map((date) { - final weekdayString = widget.daysOfWeekStyle.dowTextBuilder != null - ? widget.daysOfWeekStyle.dowTextBuilder(date, widget.locale) - : DateFormat.E(widget.locale).format(date); - final isWeekend = _isWeekend(date, widget.weekendDays); - - Widget child; - - if (isWeekend && widget.builders.dowWeekendBuilder != null) { - child = widget.builders.dowWeekendBuilder(context, weekdayString); - } else if (widget.builders.dowWeekdayBuilder != null) { - child = widget.builders.dowWeekdayBuilder(context, weekdayString); - } else { - child = Center( - child: Text( - weekdayString, - style: isWeekend - ? widget.daysOfWeekStyle.weekendStyle - : widget.daysOfWeekStyle.weekdayStyle, - ), - ); - } - - return SizedBox(height: widget.daysOfWeekHeight, child: child); - }).toList(), - ); - } - - TableRow _buildTableRow(List days, DateTime focusedDay) { - return TableRow( - decoration: widget.calendarStyle.contentDecoration, - children: days.map( - (date) { - return SizedBox( - height: widget.rowHeight, - child: _buildCell(date, focusedDay), - ); - }, - ).toList(), - ); - } - - Widget _buildHeader() { - return Container( - decoration: widget.headerStyle.decoration, - margin: widget.headerStyle.headerMargin, - padding: widget.headerStyle.headerPadding, - child: Row( - mainAxisSize: MainAxisSize.max, - children: [ - if (widget.headerStyle.leftChevronVisible) - _CustomIconButton( - icon: widget.headerStyle.leftChevronIcon, - onTap: previousPage, - margin: widget.headerStyle.leftChevronMargin, - padding: widget.headerStyle.leftChevronPadding, - ), - Expanded( - child: GestureDetector( - onTap: _onHeaderTapped, - onLongPress: _onHeaderLongPressed, - child: ValueListenableBuilder( - valueListenable: _focusedDay, - builder: (context, value, child) => Text( - widget.headerStyle.titleTextBuilder != null - ? widget.headerStyle - .titleTextBuilder(value, widget.locale) - : DateFormat.yMMMM(widget.locale).format(value), - style: widget.headerStyle.titleTextStyle, - textAlign: widget.headerStyle.centerHeaderTitle - ? TextAlign.center - : TextAlign.start, - ), - ), - ), - ), - if (widget.headerStyle.formatButtonVisible && - widget.availableCalendarFormats.length > 1) - Padding( - padding: const EdgeInsets.only(left: 8.0), - child: _buildFormatButton(), - ), - if (widget.headerStyle.rightChevronVisible) - _CustomIconButton( - icon: widget.headerStyle.rightChevronIcon, - onTap: nextPage, - margin: widget.headerStyle.rightChevronMargin, - padding: widget.headerStyle.rightChevronPadding, - ), - ], - ), - ); - } - - Widget _buildFormatButton() { - return GestureDetector( - onTap: toggleCalendarFormat, - child: Container( - decoration: widget.headerStyle.formatButtonDecoration, - padding: widget.headerStyle.formatButtonPadding, - child: Text( - _getFormatButtonText(), - style: widget.headerStyle.formatButtonTextStyle, - ), - ), - ); - } - - Widget _buildCell(DateTime date, DateTime focusedDay) { - if (!widget.calendarStyle.outsideDaysVisible && - date.month != focusedDay.month && - _calendarFormat == CalendarFormat.month) { - return Container(); - } - - final events = widget.events[date] ?? []; - final holidays = widget.holidays[date] ?? []; - - Widget content = _buildCellContent(date, focusedDay, events); - final children = [content]; - - if (!_isDayUnavailable(date)) { - if (widget.builders.markersBuilder != null) { - children.addAll( - widget.builders.markersBuilder( - context, - date, - events, - holidays, - ), - ); - } else if (events.isNotEmpty) { - children.add( - Positioned( - top: widget.calendarStyle.markersPositionTop, - bottom: widget.calendarStyle.markersPositionBottom, - left: widget.calendarStyle.markersPositionLeft, - right: widget.calendarStyle.markersPositionRight, - child: Row( - mainAxisSize: MainAxisSize.min, - children: events - .take(widget.calendarStyle.markersMaxAmount) - .map((event) => _buildMarker(date, event)) - .toList(), - ), - ), - ); - } - } - - if (children.length > 1) { - content = Stack( - alignment: widget.calendarStyle.markersAlignment, - children: children, - clipBehavior: widget.calendarStyle.canEventMarkersOverflow - ? Clip.none - : Clip.hardEdge, - ); - } - - return GestureDetector( - behavior: widget.dayHitTestBehavior, - onTap: () => _isDayUnavailable(date) - ? _onUnavailableDaySelected(date) - : _onDaySelected(date, events, holidays), - onLongPress: () => _isDayUnavailable(date) - ? _onUnavailableDayLongPressed(date) - : _onDayLongPressed(date, events, holidays), - child: content, - ); - } - - Widget _buildCellContent(DateTime date, DateTime focusedDay, List events) { - final tIsUnavailable = _isDayUnavailable(date); - final tIsSelected = _isSameDay(date, _selectedDay); - final tIsToday = _isSameDay(date, DateTime.now().toUtc()); - final tIsOutside = date.month != focusedDay.month; - final tIsHoliday = widget.holidays.containsKey(date); - final tIsWeekend = _isWeekend(date, widget.weekendDays); - - final isUnavailable = - widget.builders.unavailableDayBuilder != null && tIsUnavailable; - final isSelected = - widget.builders.selectedDayBuilder != null && tIsSelected; - final isToday = widget.builders.todayDayBuilder != null && tIsToday; - final isOutsideHoliday = widget.builders.outsideHolidayDayBuilder != null && - tIsOutside && - tIsHoliday; - final isHoliday = - widget.builders.holidayDayBuilder != null && !tIsOutside && tIsHoliday; - final isOutsideWeekend = widget.builders.outsideWeekendDayBuilder != null && - tIsOutside && - tIsWeekend && - !tIsHoliday; - final isOutside = widget.builders.outsideDayBuilder != null && - tIsOutside && - !tIsWeekend && - !tIsHoliday; - final isWeekend = widget.builders.weekendDayBuilder != null && - !tIsOutside && - tIsWeekend && - !tIsHoliday; - - if (isUnavailable) { - return widget.builders.unavailableDayBuilder(context, date, events); - } else if (isSelected && widget.calendarStyle.renderSelectedFirst) { - return widget.builders.selectedDayBuilder(context, date, events); - } else if (isToday) { - return widget.builders.todayDayBuilder(context, date, events); - } else if (isSelected) { - return widget.builders.selectedDayBuilder(context, date, events); - } else if (isOutsideHoliday) { - return widget.builders.outsideHolidayDayBuilder(context, date, events); - } else if (isHoliday) { - return widget.builders.holidayDayBuilder(context, date, events); - } else if (isOutsideWeekend) { - return widget.builders.outsideWeekendDayBuilder(context, date, events); - } else if (isOutside) { - return widget.builders.outsideDayBuilder(context, date, events); - } else if (isWeekend) { - return widget.builders.weekendDayBuilder(context, date, events); - } else if (widget.builders.dayBuilder != null) { - return widget.builders.dayBuilder(context, date, events); - } else { - return _CellWidget( - text: '${date.day}', - isUnavailable: tIsUnavailable, - isSelected: tIsSelected, - isToday: tIsToday, - isWeekend: tIsWeekend, - isOutsideMonth: tIsOutside, - isHoliday: tIsHoliday, - isEventDay: events.isNotEmpty, - calendarStyle: widget.calendarStyle, - ); - } - } - - Widget _buildMarker(DateTime date, T event) { - if (widget.builders.singleMarkerBuilder != null) { - return widget.builders.singleMarkerBuilder(context, date, event); - } else { - return Container( - width: 8.0, - height: 8.0, - margin: const EdgeInsets.symmetric(horizontal: 0.3), - decoration: BoxDecoration( - shape: BoxShape.circle, - color: widget.calendarStyle.markersColor, - ), - ); - } - } - - DateTime _getPageBaseDay(int index, CalendarFormat format) { - if (index == _pageIndex) { - if (format != CalendarFormat.twoWeeks) { - _baseDay = _focusedDay.value; - return _baseDay; - } - - return _baseDay; - } - - final pageDifference = index - _pageIndex; - - if (format == CalendarFormat.month) { - return DateTime.utc(_baseDay.year, _baseDay.month + pageDifference, 1); - } else if (format == CalendarFormat.twoWeeks) { - return DateTime.utc( - _baseDay.year, - _baseDay.month, - _baseDay.day + pageDifference * 14, - ); - } else { - return DateTime.utc( - _baseDay.year, _baseDay.month, _baseDay.day + pageDifference * 7); - } - } - - DateTime _getPageFocusedDay(int index, CalendarFormat format) { - if (index == _pageIndex) { - return _focusedDay.value; - } - - final pageDifference = index - _pageIndex; - - if (format == CalendarFormat.month) { - return DateTime.utc( - _focusedDay.value.year, _focusedDay.value.month + pageDifference, 1); - } else if (format == CalendarFormat.twoWeeks) { - return DateTime.utc( - _focusedDay.value.year, - _focusedDay.value.month, - _focusedDay.value.day + pageDifference * 14, - ); - } else { - return DateTime.utc(_focusedDay.value.year, _focusedDay.value.month, - _focusedDay.value.day + pageDifference * 7); - } - } - - List _getVisibleDays(DateTime baseDay) { - if (_calendarFormat == CalendarFormat.month) { - return _daysInMonth(baseDay); - } else if (_calendarFormat == CalendarFormat.twoWeeks) { - return _daysInWeek(baseDay) - ..addAll(_daysInWeek( - baseDay.add(const Duration(days: 7)), - )); - } else { - return _daysInWeek(baseDay); - } - } - - List _daysInMonth(DateTime month) { - final first = _firstDayOfMonth(month); - final daysBefore = _getDaysBefore(first); - final firstToDisplay = first.subtract(Duration(days: daysBefore)); - - final last = _lastDayOfMonth(month); - final daysAfter = _getDaysAfter(last); - - final lastToDisplay = last.add(Duration(days: daysAfter)); - return _daysInRange(firstToDisplay, lastToDisplay).toList(); - } - - int _getDaysBefore(DateTime firstDay) { - return (firstDay.weekday + - 7 - - _getWeekdayNumber(widget.startingDayOfWeek)) % - 7; - } - - int _getDaysAfter(DateTime lastDay) { - int invertedStartingWeekday = - 8 - _getWeekdayNumber(widget.startingDayOfWeek); - - int daysAfter = 7 - ((lastDay.weekday + invertedStartingWeekday) % 7) + 1; - if (daysAfter == 8) { - daysAfter = 1; - } - - return daysAfter; - } - - List _daysInWeek(DateTime week) { - final first = _firstDayOfWeek(week); - final last = _lastDayOfWeek(week); - - return _daysInRange(first, last).toList(); - } - - DateTime _firstDayOfWeek(DateTime day) { - final decreaseNum = _getDaysBefore(day); - return day.subtract(Duration(days: decreaseNum)); - } - - DateTime _lastDayOfWeek(DateTime day) { - final increaseNum = _getDaysBefore(day); - return day.add(Duration(days: 7 - increaseNum)); - } - - DateTime _firstDayOfMonth(DateTime month) { - return DateTime.utc(month.year, month.month, 1); - } - - DateTime _lastDayOfMonth(DateTime month) { - final date = month.month < 12 - ? DateTime.utc(month.year, month.month + 1, 1) - : DateTime.utc(month.year + 1, 1, 1); - return date.subtract(const Duration(days: 1)); - } - - Iterable _daysInRange(DateTime firstDay, DateTime lastDay) sync* { - var temp = firstDay; - - while (temp.isBefore(lastDay)) { - yield temp; - temp = temp.add(const Duration(days: 1)); - } - } - - bool _isSameDay(DateTime a, DateTime b) { - if (a == null || b == null) { - return false; - } - - return a.year == b.year && a.month == b.month && a.day == b.day; - } - - bool _isDayUnavailable(DateTime day) { - return (widget.startDay != null && day.isBefore(widget.startDay)) || - (widget.endDay != null && day.isAfter(widget.endDay)) || - (!_isDayEnabled(day)); - } - - bool _isDayEnabled(DateTime day) { - return widget.enabledDayPredicate == null - ? true - : widget.enabledDayPredicate(day); - } - - bool _isBeforeMonth(DateTime day, DateTime month) { - if (day.year == month.year) { - return day.month < month.month; - } else { - return day.isBefore(month); - } - } - - bool _isAfterMonth(DateTime day, DateTime month) { - if (day.year == month.year) { - return day.month > month.month; - } else { - return day.isAfter(month); - } - } - - bool _isWeekend(DateTime day, List weekendDays) { - return weekendDays.contains(day.weekday); - } - - CalendarFormat _nextFormat() { - final formats = widget.availableCalendarFormats.keys.toList(); - int id = formats.indexOf(_calendarFormat); - id = (id + 1) % formats.length; - - return formats[id]; - } - - String _getFormatButtonText() => widget.headerStyle.formatButtonShowsNext - ? widget.availableCalendarFormats[_nextFormat()] - : widget.availableCalendarFormats[_calendarFormat]; - - double _getPageHeight({int dayCount, DateTime baseDay}) { - var rowCount; - - if (dayCount != null) { - rowCount = dayCount ~/ 7; - } else { - final base = baseDay ?? _getPageBaseDay(_pageIndex, _calendarFormat); - final days = _getVisibleDays(base); - rowCount = days.length ~/ 7; - } - - final dowHeight = widget.daysOfWeekVisible ? widget.daysOfWeekHeight : 0.0; - return rowCount * widget.rowHeight + dowHeight; - } -} diff --git a/lib/src/customization/calendar_builders.dart b/lib/src/customization/calendar_builders.dart index 648ed459..b59db200 100644 --- a/lib/src/customization/calendar_builders.dart +++ b/lib/src/customization/calendar_builders.dart @@ -3,92 +3,45 @@ part of table_calendar; -/// Main Builder signature for `TableCalendar`. Contains `date` and list of all `events` associated with that `date`. -/// Note that most of the time, `events` param will be ommited, however it is there if needed. -/// `events` param can be null. -typedef FullBuilder = Widget Function( - BuildContext context, DateTime date, List events); - -/// Builder signature for a list of event markers. Contains `date` and list of all `events` associated with that `date`. -/// Both `events` and `holidays` params can be null. -typedef FullListBuilder = List Function( - BuildContext context, DateTime date, List events, List holidays); - -/// Builder signature for weekday names row. Contains `weekday` string, which is formatted by `dowTextBuilder` -/// or by default function (DateFormat.E(widget.locale).format(date)), if `dowTextBuilder` is null. -typedef DowBuilder = Widget Function(BuildContext context, String weekday); - /// Builder signature for a single event marker. Contains `date` and a single `event` associated with that `date`. typedef SingleMarkerBuilder = Widget Function( BuildContext context, DateTime date, T event); -/// Class containing all custom Builders for `TableCalendar`. -class CalendarBuilders { - /// The most general custom Builder. Use to provide your own UI for every day cell. - /// If `dayBuilder` is not specified, a default day cell will be displayed. - /// Default day cells are customizable with `CalendarStyle`. - final FullBuilder dayBuilder; - - /// Custom Builder for currently selected day. Will overwrite `dayBuilder` on selected day. - final FullBuilder selectedDayBuilder; - - /// Custom Builder for today. Will overwrite `dayBuilder` on today. - final FullBuilder todayDayBuilder; - - /// Custom Builder for holidays. Will overwrite `dayBuilder` on holidays. - final FullBuilder holidayDayBuilder; - - /// Custom Builder for weekends. Will overwrite `dayBuilder` on weekends. - final FullBuilder weekendDayBuilder; - - /// Custom Builder for days outside of current month. Will overwrite `dayBuilder` on days outside of current month. - final FullBuilder outsideDayBuilder; - - /// Custom Builder for weekends outside of current month. Will overwrite `dayBuilder`on weekends outside of current month. - final FullBuilder outsideWeekendDayBuilder; - - /// Custom Builder for holidays outside of current month. Will overwrite `dayBuilder` on holidays outside of current month. - final FullBuilder outsideHolidayDayBuilder; - - /// Custom Builder for days outside of `startDay` - `endDay` Date range. Will overwrite `dayBuilder` for aforementioned days. - final FullBuilder unavailableDayBuilder; +typedef MarkerBuilder = Widget Function( + BuildContext context, DateTime date, List events); - /// Custom Builder for a whole group of event markers. Use to provide your own marker UI for each day cell. - /// Every `Widget` passed here will be placed in a `Stack`, above the cell content. - /// Wrap them with `Positioned` to gain more control over their placement. - /// - /// If `markersBuilder` is not specified, `TableCalendar` will try to use `singleMarkerBuilder` or default markers (customizable with `CalendarStyle`). - /// Mutually exclusive with `singleMarkerBuilder`. - final FullListBuilder markersBuilder; +class CalendarBuilders { + final FocusedDayBuilder prioritizedBuilder; + final FocusedDayBuilder todayBuilder; + final FocusedDayBuilder selectedBuilder; + final FocusedDayBuilder rangeStartBuilder; + final FocusedDayBuilder rangeEndBuilder; + final FocusedDayBuilder withinRangeBuilder; + final FocusedDayBuilder outsideBuilder; + final FocusedDayBuilder disabledBuilder; + final FocusedDayBuilder holidayBuilder; + final FocusedDayBuilder weekendBuilder; + final FocusedDayBuilder defaultBuilder; - /// Custom Builder for a single event marker. Each of those will be displayed in a `Row` above of the day cell. - /// You can adjust markers' position with `CalendarStyle` properties. - /// - /// If `singleMarkerBuilder` is not specified, a default event marker will be displayed (customizable with `CalendarStyle`). - /// Mutually exclusive with `markersBuilder`. final SingleMarkerBuilder singleMarkerBuilder; + final MarkerBuilder markerBuilder; - /// Custom builder for dow weekday names (displayed between `HeaderRow` and calendar days). - /// Will overwrite `weekdayStyle` and `weekendStyle` from `DaysOfWeekStyle`. - final DowBuilder dowWeekdayBuilder; - - /// Custom builder for dow weekend names (displayed between `HeaderRow` and calendar days). - /// Will overwrite `weekendStyle` from `DaysOfWeekStyle` and `dowWeekdayBuilder` for weekends, if it also exists. - final DowBuilder dowWeekendBuilder; + final DayBuilder dowBuilder; const CalendarBuilders({ - this.dayBuilder, - this.selectedDayBuilder, - this.todayDayBuilder, - this.holidayDayBuilder, - this.weekendDayBuilder, - this.outsideDayBuilder, - this.outsideWeekendDayBuilder, - this.outsideHolidayDayBuilder, - this.unavailableDayBuilder, - this.markersBuilder, + this.prioritizedBuilder, + this.todayBuilder, + this.selectedBuilder, + this.rangeStartBuilder, + this.rangeEndBuilder, + this.withinRangeBuilder, + this.outsideBuilder, + this.disabledBuilder, + this.holidayBuilder, + this.weekendBuilder, + this.defaultBuilder, this.singleMarkerBuilder, - this.dowWeekdayBuilder, - this.dowWeekendBuilder, - }) : assert(!(singleMarkerBuilder != null && markersBuilder != null)); + this.markerBuilder, + this.dowBuilder, + }); } diff --git a/lib/src/customization/calendar_style.dart b/lib/src/customization/calendar_style.dart index 3e3ab23b..e34f22a5 100644 --- a/lib/src/customization/calendar_style.dart +++ b/lib/src/customization/calendar_style.dart @@ -3,133 +3,107 @@ part of table_calendar; -/// Class containing styling for `TableCalendar`'s content. -class CalendarStyle { - /// BoxDecoration for each interior row of the table - final BoxDecoration contentDecoration; - - /// Style of foreground Text for regular weekdays. - final TextStyle weekdayStyle; - - /// Style of foreground Text for regular weekends. - final TextStyle weekendStyle; - - /// Style of foreground Text for holidays. - final TextStyle holidayStyle; - - /// Style of foreground Text for selected day. - final TextStyle selectedStyle; - - /// Style of foreground Text for today. - final TextStyle todayStyle; - - /// Style of foreground Text for weekdays outside of current month. - final TextStyle outsideStyle; - - /// Style of foreground Text for weekends outside of current month. - final TextStyle outsideWeekendStyle; - - /// Style of foreground Text for holidays outside of current month. - final TextStyle outsideHolidayStyle; - - /// Style of foreground Text for days outside of `startDay` - `endDay` Date range. - final TextStyle unavailableStyle; - - /// Style of foreground Text for days that contain events. - final TextStyle eventDayStyle; - - /// Background Color of selected day. - final Color selectedColor; +class PositionedOffset { + final double top; + final double bottom; + final double start; + final double end; - /// Background Color of today. - final Color todayColor; + const PositionedOffset({this.top, this.bottom, this.start, this.end}); +} - /// Color of event markers placed on the bottom of every day containing events. - final Color markersColor; +class CalendarStyle { + /// Maximum amount of event markers to be displayed. + final int markersMaxAmount; + final bool isTodayHighlighted; + final AlignmentGeometry markersAlignment; + final bool canMarkersOverflow; + final bool outsideDaysVisible; - /// General `Alignment` for event markers. - /// NOTE: `markersPositionBottom` defaults to `5.0`, so you might want to set it to `null` when using `markersAlignment`. - final Alignment markersAlignment; + final EdgeInsets cellMargin; - /// `top` property of `Positioned` widget used for event markers. - final double markersPositionTop; + final PositionedOffset rangeFillOffset; + final PositionedOffset markersOffset; + final Color rangeFillColor; + final Decoration markerDecoration; - /// `bottom` property of `Positioned` widget used for event markers. - /// NOTE: This defaults to `5.0`, so you might occasionally want to set it to `null`. - final double markersPositionBottom; + final TextStyle todayTextStyle; + final Decoration todayDecoration; - /// `left` property of `Positioned` widget used for event markers. - final double markersPositionLeft; + final TextStyle selectedTextStyle; + final Decoration selectedDecoration; - /// `right` property of `Positioned` widget used for event markers. - final double markersPositionRight; + final TextStyle rangeStartTextStyle; + final Decoration rangeStartDecoration; - /// Maximum amount of event markers to be displayed. - final int markersMaxAmount; + final TextStyle rangeEndTextStyle; + final Decoration rangeEndDecoration; - /// Specifies whether or not days outside of current month should be displayed. - /// - /// Sometimes a fragment of previous month's last week (or next month's first week) appears in current month's view. - /// This property defines if those should be visible (e.g. with custom style) or hidden. - final bool outsideDaysVisible; + final TextStyle withinRangeTextStyle; + final Decoration withinRangeDecoration; - /// Determines rendering priority for SelectedDay and Today. - /// * `true` - SelectedDay will have higher priority than Today - /// * `false` - Today will have higher priority than SelectedDay - final bool renderSelectedFirst; + final TextStyle outsideTextStyle; + final Decoration outsideDecoration; - /// Padding of `TableCalendar`'s content. - final EdgeInsets contentPadding; + final TextStyle disabledTextStyle; + final Decoration disabledDecoration; - /// Margin of each individual cell. - final EdgeInsets cellMargin; + final TextStyle holidayTextStyle; + final Decoration holidayDecoration; - /// Specifies if event markers rendered for a day cell can overflow cell's boundaries. - /// * `true` - Event markers will be drawn over the cell boundaries - /// * `false` - Event markers will not be drawn over the cell boundaries and will be clipped if they are too big - final bool canEventMarkersOverflow; + final TextStyle weekendTextStyle; + final Decoration weekendDecoration; - /// Specifies whether or not SelectedDay should be highlighted. - final bool highlightSelected; + final TextStyle defaultTextStyle; + final Decoration defaultDecoration; - /// Specifies whether or not Today should be highlighted. - final bool highlightToday; + final Decoration rowDecoration; const CalendarStyle({ - this.contentDecoration = const BoxDecoration(), - this.weekdayStyle = const TextStyle(), - this.weekendStyle = - const TextStyle(color: const Color(0xFFF44336)), // Material red[500] - this.holidayStyle = - const TextStyle(color: const Color(0xFFF44336)), // Material red[500] - this.selectedStyle = const TextStyle( - color: const Color(0xFFFAFAFA), fontSize: 16.0), // Material grey[50] - this.todayStyle = const TextStyle( - color: const Color(0xFFFAFAFA), fontSize: 16.0), // Material grey[50] - this.outsideStyle = - const TextStyle(color: const Color(0xFF9E9E9E)), // Material grey[500] - this.outsideWeekendStyle = - const TextStyle(color: const Color(0xFFEF9A9A)), // Material red[200] - this.outsideHolidayStyle = - const TextStyle(color: const Color(0xFFEF9A9A)), // Material red[200] - this.unavailableStyle = const TextStyle(color: const Color(0xFFBFBFBF)), - this.eventDayStyle = const TextStyle(), - this.selectedColor = const Color(0xFF5C6BC0), // Material indigo[400] - this.todayColor = const Color(0xFF9FA8DA), // Material indigo[200] - this.markersColor = const Color(0xFF263238), // Material blueGrey[900] + this.isTodayHighlighted = true, + this.canMarkersOverflow = true, + this.outsideDaysVisible = true, this.markersAlignment = Alignment.bottomCenter, - this.markersPositionTop, - this.markersPositionBottom = 5.0, - this.markersPositionLeft, - this.markersPositionRight, this.markersMaxAmount = 4, - this.outsideDaysVisible = true, - this.renderSelectedFirst = true, - this.contentPadding = - const EdgeInsets.only(bottom: 4.0, left: 8.0, right: 8.0), this.cellMargin = const EdgeInsets.all(6.0), - this.canEventMarkersOverflow = false, - this.highlightSelected = true, - this.highlightToday = true, + this.rangeFillOffset = const PositionedOffset(top: 6.0, bottom: 6.0), + this.markersOffset = const PositionedOffset(bottom: 5.0), + this.rangeFillColor = const Color(0xFFBBDDFF), + this.markerDecoration = const BoxDecoration( + color: const Color(0xFF263238), shape: BoxShape.circle), + this.todayTextStyle = + const TextStyle(color: const Color(0xFFFAFAFA), fontSize: 16.0), // + this.todayDecoration = const BoxDecoration( + color: const Color(0xFF9FA8DA), shape: BoxShape.circle), + this.selectedTextStyle = + const TextStyle(color: const Color(0xFFFAFAFA), fontSize: 16.0), + this.selectedDecoration = const BoxDecoration( + color: const Color(0xFF5C6BC0), shape: BoxShape.circle), + this.rangeStartTextStyle = + const TextStyle(color: const Color(0xFFFAFAFA), fontSize: 16.0), + this.rangeStartDecoration = const BoxDecoration( + color: const Color(0xFF6699FF), shape: BoxShape.circle), + this.rangeEndTextStyle = + const TextStyle(color: const Color(0xFFFAFAFA), fontSize: 16.0), + this.rangeEndDecoration = const BoxDecoration( + color: const Color(0xFF6699FF), shape: BoxShape.circle), + this.withinRangeTextStyle = const TextStyle(), + this.withinRangeDecoration = const BoxDecoration(shape: BoxShape.circle), + this.outsideTextStyle = const TextStyle(color: const Color(0xFFAEAEAE)), + this.outsideDecoration = const BoxDecoration(shape: BoxShape.circle), + this.disabledTextStyle = const TextStyle(color: const Color(0xFFBFBFBF)), + this.disabledDecoration = const BoxDecoration(shape: BoxShape.circle), + this.holidayTextStyle = const TextStyle(color: const Color(0xFF5C6BC0)), + this.holidayDecoration = const BoxDecoration( + border: const Border.fromBorderSide( + const BorderSide(color: const Color(0xFF9FA8DA), width: 1.4), + ), + shape: BoxShape.circle, + ), + this.weekendTextStyle = const TextStyle(color: const Color(0xFF5A5A5A)), + this.weekendDecoration = const BoxDecoration(shape: BoxShape.circle), + this.defaultTextStyle = const TextStyle(), + this.defaultDecoration = const BoxDecoration(shape: BoxShape.circle), + this.rowDecoration = const BoxDecoration(), }); } diff --git a/lib/src/customization/days_of_week_style.dart b/lib/src/customization/days_of_week_style.dart index ba811781..d96be3b0 100644 --- a/lib/src/customization/days_of_week_style.dart +++ b/lib/src/customization/days_of_week_style.dart @@ -11,25 +11,23 @@ class DaysOfWeekStyle { /// /// Example usage: /// ```dart - /// dowTextBuilder: (date, locale) => DateFormat.E(locale).format(date)[0], + /// dowTextFormatter: (date, locale) => DateFormat.E(locale).format(date)[0], /// ``` - final TextBuilder dowTextBuilder; + final TextFormatter dowTextFormatter; /// BoxDecoration for the top row of the table final BoxDecoration decoration; - /// Style for weekdays on the top of Calendar. + /// Style for weekdays on the top of calendar. final TextStyle weekdayStyle; - /// Style for weekend days on the top of Calendar. + /// Style for weekend days on the top of calendar. final TextStyle weekendStyle; const DaysOfWeekStyle({ - this.dowTextBuilder, + this.dowTextFormatter, this.decoration = const BoxDecoration(), - this.weekdayStyle = - const TextStyle(color: const Color(0xFF616161)), // Material grey[700] - this.weekendStyle = - const TextStyle(color: const Color(0xFFF44336)), // Material red[500] + this.weekdayStyle = const TextStyle(color: const Color(0xFF4F4F4F)), + this.weekendStyle = const TextStyle(color: const Color(0xFF6A6A6A)), }); } diff --git a/lib/src/customization/header_style.dart b/lib/src/customization/header_style.dart index 2214031a..ff71c86a 100644 --- a/lib/src/customization/header_style.dart +++ b/lib/src/customization/header_style.dart @@ -3,10 +3,13 @@ part of table_calendar; +/// Signature for function returning text that can be localized and formatted with `DateFormat`. +typedef TextFormatter = String Function(DateTime date, dynamic locale); + /// Class containing styling and configuration of `TableCalendar`'s header. class HeaderStyle { /// Responsible for making title Text centered. - final bool centerHeaderTitle; + final bool titleCentered; /// Responsible for FormatButton visibility. final bool formatButtonVisible; @@ -22,9 +25,9 @@ class HeaderStyle { /// /// Example usage: /// ```dart - /// titleTextBuilder: (date, locale) => DateFormat.yM(locale).format(date), + /// titleTextFormatter: (date, locale) => DateFormat.yM(locale).format(date), /// ``` - final TextBuilder titleTextBuilder; + final TextFormatter titleTextFormatter; /// Style for title Text (month-year) displayed in header. final TextStyle titleTextStyle; @@ -77,10 +80,10 @@ class HeaderStyle { final BoxDecoration decoration; const HeaderStyle({ - this.centerHeaderTitle = false, + this.titleCentered = false, this.formatButtonVisible = true, this.formatButtonShowsNext = true, - this.titleTextBuilder, + this.titleTextFormatter, this.titleTextStyle = const TextStyle(fontSize: 17.0), this.formatButtonTextStyle = const TextStyle(), this.formatButtonDecoration = const BoxDecoration( diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart new file mode 100644 index 00000000..df88bea7 --- /dev/null +++ b/lib/src/table_calendar.dart @@ -0,0 +1,485 @@ +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 + +part of table_calendar; + +/// Highly customizable, feature-packed Flutter Calendar with gestures, animations and multiple formats. +class TableCalendar extends StatefulWidget { + /// Locale to format `TableCalendar` dates with, for example: `'en_US'`. + /// + /// If nothing is provided, a default locale will be used. + final dynamic locale; + + /// DateTime to highlight as currently selected. + final DateTime selectedDay; + + /// The start of the selected day range. + final DateTime rangeStartDay; + + /// The end of the selected day range. + final DateTime rangeEndDay; + + /// DateTime that determines which days are currently visible and focused. + final DateTime focusedDay; + + /// The first active day of `TableCalendar`. + /// Blocks swiping to days before it. + /// + /// Days before it will use `disabledStyle` and trigger `onDisabledDayTapped` callback. + final DateTime firstDay; + + /// The last active day of `TableCalendar`. + /// Blocks swiping to days after it. + /// + /// Days after it will use `disabledStyle` and trigger `onDisabledDayTapped` callback. + final DateTime lastDay; + + /// List of days treated as weekend days. + /// Use built-in `DateTime` weekday constants (e.g. `DateTime.monday`) instead of `int` literals (e.g. `1`). + final List weekendDays; + + /// Specifies `TableCalendar`'s current format. + final CalendarFormat calendarFormat; + + /// `Map` of `CalendarFormat`s and `String` names associated with them. + /// Those `CalendarFormat`s will be used by internal logic to manage displayed format. + /// + /// To ensure proper vertical swipe behavior, `CalendarFormat`s should be in descending order (i.e. from biggest to smallest). + /// + /// For example: + /// ```dart + /// availableCalendarFormats: const { + /// CalendarFormat.month: 'Month', + /// CalendarFormat.week: 'Week', + /// } + /// ``` + final Map availableCalendarFormats; + + /// Determines the visibility of calendar header. + final bool headerVisible; + + /// Determines the visibility of the row of days of the week. + final bool daysOfWeekVisible; + + /// When set to true, tapping on an outside day in `CalendarFormat.month` format + /// will jump to a page related to the tapped month. + final bool pageJumpingEnabled; + + /// Used for setting the height of `TableCalendar`'s rows. + final double rowHeight; + + /// Used for setting the height of `TableCalendar`'s days of week row. + final double daysOfWeekHeight; + + /// `TableCalendar` will start weeks with provided day. + /// Use `StartingDayOfWeek.monday` for Monday - Sunday week format. + /// Use `StartingDayOfWeek.sunday` for Sunday - Saturday week format. + final StartingDayOfWeek startingDayOfWeek; + + /// `HitTestBehavior` for every day cell inside `TableCalendar`. + final HitTestBehavior dayHitTestBehavior; + + /// Specifies swipe gestures available to `TableCalendar`. + /// If `AvailableGestures.none` is used, the calendar will only be interactive via buttons. + final AvailableGestures availableGestures; + + /// Configuration for vertical swipe detector. + final SimpleSwipeConfig simpleSwipeConfig; + + /// Style for `TableCalendar`'s header. + final HeaderStyle headerStyle; + + /// Style for days of week displayed between `TableCalendar`'s header and content. + final DaysOfWeekStyle daysOfWeekStyle; + + /// Style for `TableCalendar`'s content. + final CalendarStyle calendarStyle; + + /// Set of custom builders for `TableCalendar` to work with. + /// Use those to fully tailor the UI. + final CalendarBuilders calendarBuilders; + + /// Current mode of range selection. + final RangeSelectionMode rangeSelectionMode; + + /// Called whenever a day range gets selected. + final OnRangeSelected onRangeSelected; + + /// Function that assigns a list of events to a specified day. + final List Function(DateTime day) eventLoader; + + /// Function deciding whether given day should be enabled or not. + /// If `false` is returned, this day will be disabled. + final bool Function(DateTime day) enabledDayPredicate; + + /// Function deciding whether given day is treated as a holiday. + final bool Function(DateTime day) holidayPredicate; + + /// Called whenever any day gets tapped. + final OnDaySelected onDaySelected; + + /// Called whenever any disabled day gets tapped. + final void Function(DateTime day) onDisabledDayTapped; + + /// Called whenever any disabled day gets long pressed. + final void Function(DateTime day) onDisabledDayLongPressed; + + /// Called whenever header gets tapped. + final void Function(DateTime focusedDay) onHeaderTapped; + + /// Called whenever header gets long pressed. + final void Function(DateTime focusedDay) onHeaderLongPressed; + + /// Called whenever currently visible calendar page is changed. + final void Function(DateTime focusedDay) onPageChanged; + + /// Called whenever `calendarFormat` is changed. + final void Function(CalendarFormat format) onFormatChanged; + + /// Called when the calendar is created. Exposes its PageController. + final void Function(PageController pageController) onCalendarCreated; + + TableCalendar({ + Key key, + this.locale, + this.selectedDay, + this.rangeStartDay, + this.rangeEndDay, + @required this.focusedDay, + @required this.firstDay, + @required this.lastDay, + this.weekendDays = const [DateTime.saturday, DateTime.sunday], + this.calendarFormat = CalendarFormat.month, + this.availableCalendarFormats = const { + CalendarFormat.month: 'Month', + CalendarFormat.twoWeeks: '2 weeks', + CalendarFormat.week: 'Week', + }, + this.headerVisible = true, + this.daysOfWeekVisible = true, + this.pageJumpingEnabled = false, + this.rowHeight = 52.0, + this.daysOfWeekHeight = 16.0, + this.startingDayOfWeek = StartingDayOfWeek.sunday, + this.dayHitTestBehavior = HitTestBehavior.deferToChild, + this.availableGestures = AvailableGestures.all, + this.simpleSwipeConfig = const SimpleSwipeConfig( + verticalThreshold: 25.0, + swipeDetectionBehavior: SwipeDetectionBehavior.continuousDistinct, + ), + this.headerStyle = const HeaderStyle(), + this.daysOfWeekStyle = const DaysOfWeekStyle(), + this.calendarStyle = const CalendarStyle(), + this.calendarBuilders = const CalendarBuilders(), + this.rangeSelectionMode, + this.onRangeSelected, + this.eventLoader, + this.enabledDayPredicate, + this.holidayPredicate, + this.onDaySelected, + this.onDisabledDayTapped, + this.onDisabledDayLongPressed, + this.onHeaderTapped, + this.onHeaderLongPressed, + this.onPageChanged, + this.onFormatChanged, + this.onCalendarCreated, + }) : assert(calendarFormat != null), + assert(rowHeight != null), + assert(firstDay != null), + assert(lastDay != null), + assert(focusedDay != null), + assert(availableCalendarFormats.keys.contains(calendarFormat)), + assert(availableCalendarFormats.length <= CalendarFormat.values.length), + assert(weekendDays != null), + assert(weekendDays.isNotEmpty + ? weekendDays.every( + (day) => day >= DateTime.monday && day <= DateTime.sunday) + : true), + super(key: key); + + @override + _TableCalendarState createState() => _TableCalendarState(); +} + +class _TableCalendarState extends State> { + ValueNotifier _focusedDay; + PageController _pageController; + + @override + void initState() { + super.initState(); + _focusedDay = ValueNotifier(widget.focusedDay); + } + + @override + void didUpdateWidget(TableCalendar oldWidget) { + super.didUpdateWidget(oldWidget); + + if (_focusedDay.value != widget.focusedDay) { + _focusedDay.value = widget.focusedDay; + } + } + + @override + void dispose() { + _focusedDay.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Column( + children: [ + if (widget.headerVisible) + ValueListenableBuilder( + valueListenable: _focusedDay, + builder: (context, value, _) { + return _CalendarHeader( + focusedMonth: value, + onLeftChevronTap: () { + _pageController.previousPage( + duration: const Duration(milliseconds: 300), + curve: Curves.easeOut, + ); + }, + onRightChevronTap: () { + _pageController.nextPage( + duration: const Duration(milliseconds: 300), + curve: Curves.easeOut, + ); + }, + onHeaderTap: () => widget.onHeaderTapped(value), + onHeaderLongPress: () => widget.onHeaderLongPressed(value), + headerStyle: widget.headerStyle, + availableCalendarFormats: widget.availableCalendarFormats, + calendarFormat: widget.calendarFormat, + locale: widget.locale, + onFormatButtonTap: (format) { + widget.onFormatChanged(format); + }, + ); + }, + ), + TableCalendarLite( + onCalendarCreated: (pageController) { + _pageController = pageController; + widget.onCalendarCreated?.call(pageController); + }, + focusedDay: _focusedDay.value, + calendarFormat: widget.calendarFormat, + availableGestures: widget.availableGestures, + firstDay: widget.firstDay, + lastDay: widget.lastDay, + startingDayOfWeek: widget.startingDayOfWeek, + dowDecoration: widget.daysOfWeekStyle.decoration, + rowDecoration: widget.calendarStyle.rowDecoration, + dowVisible: widget.daysOfWeekVisible, + dowHeight: widget.daysOfWeekHeight, + rowHeight: widget.rowHeight, + availableCalendarFormats: widget.availableCalendarFormats, + simpleSwipeConfig: widget.simpleSwipeConfig, + dayHitTestBehavior: widget.dayHitTestBehavior, + enabledDayPredicate: widget.enabledDayPredicate, + rangeSelectionMode: widget.rangeSelectionMode, + pageJumpingEnabled: widget.pageJumpingEnabled, + onDisabledDayTapped: widget.onDisabledDayTapped, + onDisabledDayLongPressed: widget.onDisabledDayLongPressed, + onDaySelected: (selectedDay, focusedDay) { + _focusedDay.value = focusedDay; + widget.onDaySelected?.call(selectedDay, focusedDay); + }, + onRangeSelected: widget.onRangeSelected != null + ? (start, end, focusedDay) { + _focusedDay.value = focusedDay; + widget.onRangeSelected(start, end, focusedDay); + } + : null, + onFormatChanged: (format) { + widget.onFormatChanged?.call(format); + }, + onPageChanged: (focusedDay) { + _focusedDay.value = focusedDay; + widget.onPageChanged?.call(focusedDay); + }, + dowBuilder: (BuildContext context, DateTime day) { + Widget dowCell = + widget.calendarBuilders.dowBuilder?.call(context, day); + + if (dowCell == null) { + final weekdayString = widget.daysOfWeekStyle.dowTextFormatter + ?.call(day, widget.locale) ?? + DateFormat.E(widget.locale).format(day); + + final isWeekend = + _isWeekend(day, weekendDays: widget.weekendDays); + + dowCell = Center( + child: Text( + weekdayString, + style: isWeekend + ? widget.daysOfWeekStyle.weekendStyle + : widget.daysOfWeekStyle.weekdayStyle, + ), + ); + } + + return dowCell; + }, + dayBuilder: (context, day, focusedMonth) { + return _buildCell(day, focusedMonth); + }, + ), + ], + ); + } + + Widget _buildCell(DateTime date, DateTime focusedDay) { + final isOutside = date.month != focusedDay.month; + + if (isOutside && + !widget.calendarStyle.outsideDaysVisible && + widget.calendarFormat == CalendarFormat.month) { + return Container(); + } + + final children = []; + + final isWithinRange = widget.rangeStartDay != null && + widget.rangeEndDay != null && + _isWithinRange(date, widget.rangeStartDay, widget.rangeEndDay); + + final isRangeStart = isSameDay(date, widget.rangeStartDay); + final isRangeEnd = isSameDay(date, widget.rangeEndDay); + + final startOffset = + widget.calendarStyle.rangeFillOffset.start ?? widget.rowHeight / 2; + + final endOffset = + widget.calendarStyle.rangeFillOffset.end ?? widget.rowHeight / 2; + + final rangeColor = isWithinRange + ? widget.calendarStyle.rangeFillColor + : Colors.transparent; + + children.add( + PositionedDirectional( + top: widget.calendarStyle.rangeFillOffset.top, + bottom: widget.calendarStyle.rangeFillOffset.bottom, + start: isRangeStart ? startOffset : 0.0, + end: isRangeEnd ? endOffset : 0.0, + child: Container(color: rangeColor), + ), + ); + + final isToday = isSameDay(date, DateTime.now()); + final isSelected = isSameDay(date, widget.selectedDay); + final isDisabled = _isDayDisabled(date); + final isWeekend = _isWeekend(date, weekendDays: widget.weekendDays); + final isHoliday = _isHoliday(date); + + Widget content = _CellContent( + day: date, + focusedDay: focusedDay, + calendarStyle: widget.calendarStyle, + calendarBuilders: widget.calendarBuilders, + isTodayHighlighted: widget.calendarStyle.isTodayHighlighted, + isToday: isToday, + isSelected: isSelected, + isRangeStart: isRangeStart, + isRangeEnd: isRangeEnd, + isWithinRange: isWithinRange, + isOutside: isOutside, + isDisabled: isDisabled, + isWeekend: isWeekend, + isHoliday: isHoliday, + ); + + children.add(content); + + if (!isDisabled) { + final events = widget.eventLoader?.call(date) ?? []; + Widget markerWidget = + widget.calendarBuilders.markerBuilder?.call(context, date, events); + + if (events.isNotEmpty && markerWidget == null) { + markerWidget = PositionedDirectional( + top: widget.calendarStyle.markersOffset.top, + bottom: widget.calendarStyle.markersOffset.bottom, + start: widget.calendarStyle.markersOffset.start, + end: widget.calendarStyle.markersOffset.end, + child: Row( + mainAxisSize: MainAxisSize.min, + children: events + .take(widget.calendarStyle.markersMaxAmount) + .map((event) => _buildSingleMarker(date, event)) + .toList(), + ), + ); + } + + if (markerWidget != null) { + children.add(markerWidget); + } + } + + if (children.length > 1) { + content = Stack( + alignment: widget.calendarStyle.markersAlignment, + children: children, + clipBehavior: + widget.calendarStyle.canMarkersOverflow ? Clip.none : Clip.hardEdge, + ); + } + + return content; + } + + Widget _buildSingleMarker(DateTime date, T event) { + return widget.calendarBuilders.singleMarkerBuilder + ?.call(context, date, event) ?? + Container( + width: 8.0, + height: 8.0, + margin: const EdgeInsets.symmetric(horizontal: 0.3), + decoration: widget.calendarStyle.markerDecoration, + ); + } + + bool _isWithinRange(DateTime date, DateTime start, DateTime end) { + if (isSameDay(date, start) || isSameDay(date, end)) { + return true; + } + + if (date.isAfter(start) && date.isBefore(end)) { + return true; + } + + return false; + } + + bool _isDayDisabled(DateTime day) { + return day.isBefore(widget.firstDay) || + day.isAfter(widget.lastDay) || + !_isDayAvailable(day); + } + + bool _isDayAvailable(DateTime day) { + return widget.enabledDayPredicate == null + ? true + : widget.enabledDayPredicate(day); + } + + bool _isHoliday(DateTime day) { + return widget.holidayPredicate == null + ? false + : widget.holidayPredicate(day); + } + + bool _isWeekend( + DateTime day, { + List weekendDays = const [DateTime.saturday, DateTime.sunday], + }) { + return weekendDays.contains(day.weekday); + } +} diff --git a/lib/src/table_calendar_base.dart b/lib/src/table_calendar_base.dart new file mode 100644 index 00000000..3362abec --- /dev/null +++ b/lib/src/table_calendar_base.dart @@ -0,0 +1,335 @@ +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 + +part of table_calendar; + +typedef DayBuilder = Widget Function(BuildContext context, DateTime day); + +typedef FocusedDayBuilder = Widget Function( + BuildContext context, DateTime day, DateTime focusedMonth); + +/// Gestures available to `TableCalendar`. +enum AvailableGestures { none, verticalSwipe, horizontalSwipe, all } + +enum CalendarFormat { month, twoWeeks, week } + +enum StartingDayOfWeek { + monday, + tuesday, + wednesday, + thursday, + friday, + saturday, + sunday, +} + +int _getWeekdayNumber(StartingDayOfWeek weekday) { + return StartingDayOfWeek.values.indexOf(weekday) + 1; +} + +bool isSameDay(DateTime a, DateTime b) { + if (a == null || b == null) { + return false; + } + + return a.year == b.year && a.month == b.month && a.day == b.day; +} + +class TableCalendarBase extends StatefulWidget { + final DateTime firstDay; + final DateTime lastDay; + final DateTime focusedDay; + final CalendarFormat calendarFormat; + final DayBuilder dowBuilder; + final FocusedDayBuilder dayBuilder; + final double dowHeight; + final double rowHeight; + final bool dowVisible; + final Decoration dowDecoration; + final Decoration rowDecoration; + final StartingDayOfWeek startingDayOfWeek; + final AvailableGestures availableGestures; + final SimpleSwipeConfig simpleSwipeConfig; + final Map availableCalendarFormats; + final SwipeCallback onVerticalSwipe; + final void Function(DateTime focusedDay) onPageChanged; + final void Function(PageController pageController) onCalendarCreated; + + TableCalendarBase({ + Key key, + @required this.firstDay, + @required this.lastDay, + @required this.focusedDay, + this.calendarFormat = CalendarFormat.month, + this.dowBuilder, + @required this.dayBuilder, + this.dowHeight, + @required this.rowHeight, + this.dowVisible = true, + this.dowDecoration, + this.rowDecoration, + this.startingDayOfWeek = StartingDayOfWeek.sunday, + this.availableGestures = AvailableGestures.all, + this.simpleSwipeConfig = const SimpleSwipeConfig( + verticalThreshold: 25.0, + swipeDetectionBehavior: SwipeDetectionBehavior.continuousDistinct, + ), + this.availableCalendarFormats = const { + CalendarFormat.month: 'Month', + CalendarFormat.twoWeeks: '2 weeks', + CalendarFormat.week: 'Week', + }, + this.onVerticalSwipe, + this.onPageChanged, + this.onCalendarCreated, + }) : assert(!dowVisible || (dowHeight != null && dowBuilder != null)), + assert(dayBuilder != null), + assert(rowHeight != null), + assert(firstDay != null), + assert(lastDay != null), + assert(focusedDay != null), + assert(isSameDay(focusedDay, firstDay) || focusedDay.isAfter(firstDay)), + assert(isSameDay(focusedDay, lastDay) || focusedDay.isBefore(lastDay)), + super(key: key); + + @override + _TableCalendarBaseState createState() => _TableCalendarBaseState(); +} + +class _TableCalendarBaseState extends State + with SingleTickerProviderStateMixin { + ValueNotifier _pageHeight; + PageController _pageController; + DateTime _focusedDay; + int _previousIndex; + bool _pageCallbackDisabled; + + @override + void initState() { + super.initState(); + _focusedDay = widget.focusedDay; + + final rowCount = _getRowCount(widget.calendarFormat, _focusedDay); + _pageHeight = ValueNotifier(_getPageHeight(rowCount)); + + final initialPage = _calculateFocusedPage( + widget.calendarFormat, widget.firstDay, _focusedDay); + + _pageController = PageController(initialPage: initialPage); + widget.onCalendarCreated?.call(_pageController); + + _previousIndex = initialPage; + _pageCallbackDisabled = false; + } + + @override + void didUpdateWidget(TableCalendarBase oldWidget) { + super.didUpdateWidget(oldWidget); + + if (widget.calendarFormat != oldWidget.calendarFormat) { + _updatePage(); + } + + if (_focusedDay != widget.focusedDay) { + _focusedDay = widget.focusedDay; + _updatePage(); + } + + if (widget.rowHeight != oldWidget.rowHeight || + widget.dowHeight != oldWidget.dowHeight) { + final rowCount = _getRowCount(widget.calendarFormat, _focusedDay); + _pageHeight.value = _getPageHeight(rowCount); + } + + if (widget.startingDayOfWeek != oldWidget.startingDayOfWeek) { + final rowCount = _getRowCount(widget.calendarFormat, _focusedDay); + _pageHeight.value = _getPageHeight(rowCount); + } + } + + @override + void dispose() { + _pageController.dispose(); + _pageHeight.dispose(); + super.dispose(); + } + + bool get _canScrollHorizontally => + widget.availableGestures == AvailableGestures.all || + widget.availableGestures == AvailableGestures.horizontalSwipe; + + bool get _canScrollVertically => + widget.availableGestures == AvailableGestures.all || + widget.availableGestures == AvailableGestures.verticalSwipe; + + void _updatePage() { + final currentIndex = _calculateFocusedPage( + widget.calendarFormat, widget.firstDay, _focusedDay); + + final endIndex = _calculateFocusedPage( + widget.calendarFormat, widget.firstDay, widget.lastDay); + + if (currentIndex != _previousIndex || + currentIndex == 0 || + currentIndex == endIndex) { + _pageCallbackDisabled = true; + } + + _previousIndex = currentIndex; + final rowCount = _getRowCount(widget.calendarFormat, _focusedDay); + _pageHeight.value = _getPageHeight(rowCount); + + _pageController.jumpToPage(currentIndex); + _pageCallbackDisabled = false; + } + + @override + Widget build(BuildContext context) { + return SimpleGestureDetector( + onVerticalSwipe: _canScrollVertically ? widget.onVerticalSwipe : null, + swipeConfig: widget.simpleSwipeConfig, + child: ValueListenableBuilder( + valueListenable: _pageHeight, + builder: (context, value, child) { + return AnimatedSize( + duration: const Duration(milliseconds: 200), + vsync: this, + alignment: Alignment.topCenter, + child: SizedBox( + height: _pageHeight.value, + child: child, + ), + ); + }, + child: _CalendarCore( + pageController: _pageController, + scrollPhysics: _canScrollHorizontally + ? PageScrollPhysics() + : NeverScrollableScrollPhysics(), + firstDay: widget.firstDay, + lastDay: widget.lastDay, + startingDayOfWeek: widget.startingDayOfWeek, + calendarFormat: widget.calendarFormat, + previousIndex: _previousIndex, + focusedDay: _focusedDay, + dowVisible: widget.dowVisible, + dowDecoration: widget.dowDecoration, + rowDecoration: widget.rowDecoration, + onPageChanged: (index, focusedMonth, rowCount) { + if (!isSameDay(_focusedDay, focusedMonth)) { + _focusedDay = focusedMonth; + } + + if (!_pageCallbackDisabled) { + _previousIndex = index; + _pageHeight.value = _getPageHeight(rowCount); + + widget.onPageChanged?.call(focusedMonth); + } + + _pageCallbackDisabled = false; + }, + dowBuilder: (context, day) { + return SizedBox( + height: widget.dowHeight, + child: widget.dowBuilder(context, day), + ); + }, + dayBuilder: (context, day, focusedMonth) { + return SizedBox( + height: widget.rowHeight, + child: widget.dayBuilder(context, day, focusedMonth), + ); + }, + ), + ), + ); + } + + double _getPageHeight(int rowCount) { + final dowHeight = widget.dowVisible ? widget.dowHeight : 0.0; + return dowHeight + rowCount * widget.rowHeight; + } + + int _calculateFocusedPage( + CalendarFormat format, DateTime startDay, DateTime focusedDay) { + switch (format) { + case CalendarFormat.month: + return _getMonthCount(startDay, focusedDay); + case CalendarFormat.twoWeeks: + return _getTwoWeekCount(startDay, focusedDay); + case CalendarFormat.week: + return _getWeekCount(startDay, focusedDay); + default: + return _getMonthCount(startDay, focusedDay); + } + } + + int _getMonthCount(DateTime first, DateTime last) { + final yearDif = last.year - first.year; + final monthDif = last.month - first.month; + + return yearDif * 12 + monthDif; + } + + int _getWeekCount(DateTime first, DateTime last) { + return last.difference(_firstDayOfWeek(first)).inDays ~/ 7; + } + + int _getTwoWeekCount(DateTime first, DateTime last) { + return last.difference(_firstDayOfWeek(first)).inDays ~/ 14; + } + + int _getRowCount(CalendarFormat format, DateTime focusedDay) { + if (format == CalendarFormat.twoWeeks) { + return 2; + } else if (format == CalendarFormat.week) { + return 1; + } + + final first = _firstDayOfMonth(focusedDay); + final daysBefore = _getDaysBefore(first); + final firstToDisplay = first.subtract(Duration(days: daysBefore)); + + final last = _lastDayOfMonth(focusedDay); + final daysAfter = _getDaysAfter(last); + final lastToDisplay = last.add(Duration(days: daysAfter)); + + return lastToDisplay.difference(firstToDisplay).inDays ~/ 7; + } + + int _getDaysBefore(DateTime firstDay) { + return (firstDay.weekday + + 7 - + _getWeekdayNumber(widget.startingDayOfWeek)) % + 7; + } + + int _getDaysAfter(DateTime lastDay) { + int invertedStartingWeekday = + 8 - _getWeekdayNumber(widget.startingDayOfWeek); + + int daysAfter = 7 - ((lastDay.weekday + invertedStartingWeekday) % 7) + 1; + if (daysAfter == 8) { + daysAfter = 1; + } + + return daysAfter; + } + + DateTime _firstDayOfWeek(DateTime week) { + final daysBefore = _getDaysBefore(week); + return week.subtract(Duration(days: daysBefore)); + } + + DateTime _firstDayOfMonth(DateTime month) { + return DateTime.utc(month.year, month.month, 1); + } + + DateTime _lastDayOfMonth(DateTime month) { + final date = month.month < 12 + ? DateTime.utc(month.year, month.month + 1, 1) + : DateTime.utc(month.year + 1, 1, 1); + return date.subtract(const Duration(days: 1)); + } +} diff --git a/lib/src/table_calendar_lite.dart b/lib/src/table_calendar_lite.dart new file mode 100644 index 00000000..4cc57cff --- /dev/null +++ b/lib/src/table_calendar_lite.dart @@ -0,0 +1,293 @@ +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 + +part of table_calendar; + +typedef OnDaySelected = void Function( + DateTime selectedDay, DateTime focusedDay); + +typedef OnRangeSelected = void Function( + DateTime start, DateTime end, DateTime focusedDay); + +enum RangeSelectionMode { + disable, // always off + toggledOff, // currently off, can be toggled + toggledOn, // currently on, can be toggled + enforced, // always on +} + +class TableCalendarLite extends StatefulWidget { + final DateTime firstDay; + final DateTime lastDay; + final DateTime focusedDay; + final CalendarFormat calendarFormat; + final DayBuilder dowBuilder; + final FocusedDayBuilder dayBuilder; + final double dowHeight; + final double rowHeight; + final bool dowVisible; + final bool pageJumpingEnabled; + final Decoration dowDecoration; + final Decoration rowDecoration; + final StartingDayOfWeek startingDayOfWeek; + final AvailableGestures availableGestures; + final SimpleSwipeConfig simpleSwipeConfig; + final HitTestBehavior dayHitTestBehavior; + final Map availableCalendarFormats; + final RangeSelectionMode rangeSelectionMode; + final OnDaySelected onDaySelected; + final OnRangeSelected onRangeSelected; + final bool Function(DateTime day) enabledDayPredicate; + final void Function(CalendarFormat format) onFormatChanged; + final void Function(DateTime focusedDay) onPageChanged; + final void Function(DateTime day) onDisabledDayTapped; + final void Function(DateTime day) onDisabledDayLongPressed; + final void Function(PageController pageController) onCalendarCreated; + + const TableCalendarLite({ + Key key, + @required this.firstDay, + @required this.lastDay, + @required this.focusedDay, + this.calendarFormat = CalendarFormat.month, + this.dowBuilder, + @required this.dayBuilder, + this.dowHeight, + @required this.rowHeight, + this.dowVisible = true, + this.pageJumpingEnabled = false, + this.dowDecoration, + this.rowDecoration, + this.startingDayOfWeek = StartingDayOfWeek.sunday, + this.availableGestures = AvailableGestures.all, + this.simpleSwipeConfig = const SimpleSwipeConfig( + verticalThreshold: 25.0, + swipeDetectionBehavior: SwipeDetectionBehavior.continuousDistinct, + ), + this.dayHitTestBehavior = HitTestBehavior.deferToChild, + this.availableCalendarFormats = const { + CalendarFormat.month: 'Month', + CalendarFormat.twoWeeks: '2 weeks', + CalendarFormat.week: 'Week', + }, + this.rangeSelectionMode, + this.onDaySelected, + this.onRangeSelected, + this.enabledDayPredicate, + this.onFormatChanged, + this.onPageChanged, + this.onDisabledDayTapped, + this.onDisabledDayLongPressed, + this.onCalendarCreated, + }) : assert(!dowVisible || (dowHeight != null && dowBuilder != null)), + assert(dayBuilder != null), + assert(rowHeight != null), + assert(firstDay != null), + assert(lastDay != null), + assert(focusedDay != null), + super(key: key); + + @override + _TableCalendarLiteState createState() => _TableCalendarLiteState(); +} + +class _TableCalendarLiteState extends State { + DateTime _focusedDay; + DateTime _firstSelectedDay; + RangeSelectionMode _rangeSelectionMode; + + @override + void initState() { + super.initState(); + _focusedDay = widget.focusedDay; + _rangeSelectionMode = + widget.rangeSelectionMode ?? RangeSelectionMode.toggledOff; + } + + @override + void didUpdateWidget(TableCalendarLite oldWidget) { + super.didUpdateWidget(oldWidget); + + if (_focusedDay != widget.focusedDay) { + _focusedDay = widget.focusedDay; + } + + if (widget.rangeSelectionMode != null && + _rangeSelectionMode != widget.rangeSelectionMode) { + _rangeSelectionMode = widget.rangeSelectionMode; + } + } + + bool get _isRangeSelectionToggleable => + _rangeSelectionMode == RangeSelectionMode.toggledOn || + _rangeSelectionMode == RangeSelectionMode.toggledOff; + + bool get _isRangeSelectionOn => + _rangeSelectionMode == RangeSelectionMode.toggledOn || + _rangeSelectionMode == RangeSelectionMode.enforced; + + void _swipeCalendarFormat(SwipeDirection direction) { + if (widget.onFormatChanged != null) { + final formats = widget.availableCalendarFormats.keys.toList(); + + final isSwipeUp = direction == SwipeDirection.up; + int id = formats.indexOf(widget.calendarFormat); + + // Order of CalendarFormats must be from biggest to smallest, + // e.g.: [month, twoWeeks, week] + if (isSwipeUp) { + id = min(formats.length - 1, id + 1); + } else { + id = max(0, id - 1); + } + + widget.onFormatChanged(formats[id]); + } + } + + void _onDaySelected(DateTime day) { + if (_isDayDisabled(day)) { + return widget.onDisabledDayTapped?.call(day); + } + + _updateFocusOnTap(day); + + if (_isRangeSelectionOn) { + if (_firstSelectedDay == null) { + _firstSelectedDay = day; + widget.onRangeSelected?.call(_firstSelectedDay, null, _focusedDay); + } else { + if (day.isAfter(_firstSelectedDay)) { + widget.onRangeSelected?.call(_firstSelectedDay, day, _focusedDay); + _firstSelectedDay = null; + } else if (day.isBefore(_firstSelectedDay)) { + widget.onRangeSelected?.call(day, _firstSelectedDay, _focusedDay); + _firstSelectedDay = null; + } + } + } else { + widget.onDaySelected?.call(day, _focusedDay); + } + } + + void _onLongPress(DateTime day) { + if (_isDayDisabled(day)) { + return widget.onDisabledDayLongPressed?.call(day); + } + + if (widget.onRangeSelected != null) { + if (_isRangeSelectionToggleable) { + _updateFocusOnTap(day); + _toggleRangeSelection(); + + if (_isRangeSelectionOn) { + _firstSelectedDay = day; + widget.onRangeSelected(_firstSelectedDay, null, _focusedDay); + } else { + _firstSelectedDay = null; + widget.onDaySelected?.call(day, _focusedDay); + } + } + } + } + + void _updateFocusOnTap(DateTime day) { + if (widget.pageJumpingEnabled) { + _focusedDay = day; + return; + } + + if (widget.calendarFormat == CalendarFormat.month) { + if (_isBeforeMonth(day, _focusedDay)) { + _focusedDay = _firstDayOfMonth(_focusedDay); + } else if (_isAfterMonth(day, _focusedDay)) { + _focusedDay = _lastDayOfMonth(_focusedDay); + } else { + _focusedDay = day; + } + } else { + _focusedDay = day; + } + } + + void _toggleRangeSelection() { + if (_rangeSelectionMode == RangeSelectionMode.toggledOn) { + _rangeSelectionMode = RangeSelectionMode.toggledOff; + } else { + _rangeSelectionMode = RangeSelectionMode.toggledOn; + } + } + + @override + Widget build(BuildContext context) { + return TableCalendarBase( + availableCalendarFormats: widget.availableCalendarFormats, + simpleSwipeConfig: widget.simpleSwipeConfig, + onCalendarCreated: widget.onCalendarCreated, + focusedDay: _focusedDay, + firstDay: widget.firstDay, + lastDay: widget.lastDay, + startingDayOfWeek: widget.startingDayOfWeek, + availableGestures: widget.availableGestures, + calendarFormat: widget.calendarFormat, + onVerticalSwipe: _swipeCalendarFormat, + onPageChanged: (focusedMonth) { + _focusedDay = focusedMonth; + widget.onPageChanged?.call(focusedMonth); + }, + dowVisible: widget.dowVisible, + dowDecoration: widget.dowDecoration, + rowDecoration: widget.rowDecoration, + dowHeight: widget.dowHeight, + dowBuilder: widget.dowBuilder, + rowHeight: widget.rowHeight, + dayBuilder: (context, day, focusedMonth) { + return GestureDetector( + behavior: widget.dayHitTestBehavior, + onTap: () => _onDaySelected(day), + onLongPress: () => _onLongPress(day), + child: widget.dayBuilder(context, day, focusedMonth), + ); + }, + ); + } + + DateTime _firstDayOfMonth(DateTime month) { + return DateTime.utc(month.year, month.month, 1); + } + + DateTime _lastDayOfMonth(DateTime month) { + final date = month.month < 12 + ? DateTime.utc(month.year, month.month + 1, 1) + : DateTime.utc(month.year + 1, 1, 1); + return date.subtract(const Duration(days: 1)); + } + + bool _isDayDisabled(DateTime day) { + return day.isBefore(widget.firstDay) || + day.isAfter(widget.lastDay) || + !_isDayAvailable(day); + } + + bool _isDayAvailable(DateTime day) { + return widget.enabledDayPredicate == null + ? true + : widget.enabledDayPredicate(day); + } + + bool _isBeforeMonth(DateTime day, DateTime month) { + if (day.year == month.year) { + return day.month < month.month; + } else { + return day.isBefore(month); + } + } + + bool _isAfterMonth(DateTime day, DateTime month) { + if (day.year == month.year) { + return day.month > month.month; + } else { + return day.isAfter(month); + } + } +} diff --git a/lib/src/widgets/calendar_core.dart b/lib/src/widgets/calendar_core.dart new file mode 100644 index 00000000..6c9fd205 --- /dev/null +++ b/lib/src/widgets/calendar_core.dart @@ -0,0 +1,270 @@ +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 + +part of table_calendar; + +typedef _OnCalendarPageChanged = void Function( + int pageIndex, DateTime focusedDay, int rowCount); + +class _CalendarCore extends StatelessWidget { + final DateTime focusedDay; + final DateTime firstDay; + final DateTime lastDay; + final CalendarFormat calendarFormat; + final DayBuilder dowBuilder; + final FocusedDayBuilder dayBuilder; + final bool dowVisible; + final Decoration dowDecoration; + final Decoration rowDecoration; + final int previousIndex; + final StartingDayOfWeek startingDayOfWeek; + final PageController pageController; + final ScrollPhysics scrollPhysics; + final _OnCalendarPageChanged onPageChanged; + + const _CalendarCore({ + Key key, + this.dowBuilder, + @required this.dayBuilder, + @required this.onPageChanged, + @required this.firstDay, + @required this.lastDay, + this.startingDayOfWeek = StartingDayOfWeek.sunday, + this.calendarFormat = CalendarFormat.month, + this.pageController, + this.focusedDay, + this.previousIndex, + this.dowVisible = true, + this.dowDecoration, + this.rowDecoration, + this.scrollPhysics, + }) : assert(firstDay != null), + assert(lastDay != null), + assert(onPageChanged != null), + super(key: key); + + @override + Widget build(BuildContext context) { + return PageView.builder( + controller: pageController, + physics: scrollPhysics, + itemCount: _getPageCount(calendarFormat, firstDay, lastDay), + itemBuilder: (context, index) { + final baseDay = _getBaseDay(calendarFormat, index); + final visibleRange = _getVisibleRange(calendarFormat, baseDay); + final visibleDays = _daysInRange(visibleRange.start, visibleRange.end); + + return _CalendarPage( + visibleDays: visibleDays, + dowVisible: dowVisible, + dowDecoration: dowDecoration, + rowDecoration: rowDecoration, + dowBuilder: dowBuilder, + dayBuilder: (context, day) { + DateTime baseDay; + if (focusedDay == null || previousIndex == null) { + baseDay = _getBaseDay(calendarFormat, index); + } else { + baseDay = _getFocusedDay(calendarFormat, index); + } + + return dayBuilder(context, day, baseDay); + }, + ); + }, + onPageChanged: (index) { + DateTime baseDay; + if (focusedDay == null || previousIndex == null) { + baseDay = _getBaseDay(calendarFormat, index); + } else { + baseDay = _getFocusedDay(calendarFormat, index); + } + + final rowCount = _getRowCount(calendarFormat, baseDay); + return onPageChanged(index, baseDay, rowCount); + }, + ); + } + + int _getPageCount(CalendarFormat format, DateTime first, DateTime last) { + switch (format) { + case CalendarFormat.month: + return _getMonthCount(first, last) + 1; + case CalendarFormat.twoWeeks: + return _getTwoWeekCount(first, last) + 1; + case CalendarFormat.week: + return _getWeekCount(first, last) + 1; + default: + return _getMonthCount(first, last) + 1; + } + } + + int _getMonthCount(DateTime first, DateTime last) { + final yearDif = last.year - first.year; + final monthDif = last.month - first.month; + + return yearDif * 12 + monthDif; + } + + int _getWeekCount(DateTime first, DateTime last) { + return last.difference(_firstDayOfWeek(first)).inDays ~/ 7; + } + + int _getTwoWeekCount(DateTime first, DateTime last) { + return last.difference(_firstDayOfWeek(first)).inDays ~/ 14; + } + + DateTime _getFocusedDay(CalendarFormat format, int pageIndex) { + if (pageIndex == previousIndex) { + return focusedDay; + } + + final pageDif = pageIndex - previousIndex; + DateTime day; + + switch (format) { + case CalendarFormat.month: + day = DateTime.utc(focusedDay.year, focusedDay.month + pageDif); + break; + case CalendarFormat.twoWeeks: + day = DateTime.utc( + focusedDay.year, focusedDay.month, focusedDay.day + pageDif * 14); + break; + case CalendarFormat.week: + day = DateTime.utc( + focusedDay.year, focusedDay.month, focusedDay.day + pageDif * 7); + break; + } + + if (day.isBefore(firstDay)) { + day = firstDay; + } else if (day.isAfter(lastDay)) { + day = lastDay; + } + + return day; + } + + DateTime _getBaseDay(CalendarFormat format, int pageIndex) { + DateTime day; + + switch (format) { + case CalendarFormat.month: + day = DateTime.utc( + firstDay.year, firstDay.month + pageIndex, firstDay.day); + break; + case CalendarFormat.twoWeeks: + day = DateTime.utc( + firstDay.year, firstDay.month, firstDay.day + pageIndex * 14); + break; + case CalendarFormat.week: + day = DateTime.utc( + firstDay.year, firstDay.month, firstDay.day + pageIndex * 7); + break; + } + + if (day.isBefore(firstDay)) { + day = firstDay; + } else if (day.isAfter(lastDay)) { + day = lastDay; + } + + return day; + } + + DateTimeRange _getVisibleRange(CalendarFormat format, DateTime focusedDay) { + switch (format) { + case CalendarFormat.month: + return _daysInMonth(focusedDay); + case CalendarFormat.twoWeeks: + return _daysInTwoWeeks(focusedDay); + case CalendarFormat.week: + return _daysInWeek(focusedDay); + default: + return _daysInMonth(focusedDay); + } + } + + DateTimeRange _daysInWeek(DateTime focusedDay) { + final daysBefore = _getDaysBefore(focusedDay); + final firstToDisplay = focusedDay.subtract(Duration(days: daysBefore)); + final lastToDisplay = firstToDisplay.add(const Duration(days: 7)); + return DateTimeRange(start: firstToDisplay, end: lastToDisplay); + } + + DateTimeRange _daysInTwoWeeks(DateTime focusedDay) { + final daysBefore = _getDaysBefore(focusedDay); + final firstToDisplay = focusedDay.subtract(Duration(days: daysBefore)); + final lastToDisplay = firstToDisplay.add(const Duration(days: 14)); + return DateTimeRange(start: firstToDisplay, end: lastToDisplay); + } + + DateTimeRange _daysInMonth(DateTime focusedDay) { + final first = _firstDayOfMonth(focusedDay); + final daysBefore = _getDaysBefore(first); + final firstToDisplay = first.subtract(Duration(days: daysBefore)); + + final last = _lastDayOfMonth(focusedDay); + final daysAfter = _getDaysAfter(last); + final lastToDisplay = last.add(Duration(days: daysAfter)); + + return DateTimeRange(start: firstToDisplay, end: lastToDisplay); + } + + List _daysInRange(DateTime first, DateTime last) { + final dayCount = last.difference(first).inDays + 1; + return List.generate( + dayCount, + (index) => DateTime.utc(first.year, first.month, first.day + index), + ); + } + + DateTime _firstDayOfWeek(DateTime week) { + final daysBefore = _getDaysBefore(week); + return week.subtract(Duration(days: daysBefore)); + } + + DateTime _firstDayOfMonth(DateTime month) { + return DateTime.utc(month.year, month.month, 1); + } + + DateTime _lastDayOfMonth(DateTime month) { + final date = month.month < 12 + ? DateTime.utc(month.year, month.month + 1, 1) + : DateTime.utc(month.year + 1, 1, 1); + return date.subtract(const Duration(days: 1)); + } + + int _getRowCount(CalendarFormat format, DateTime focusedDay) { + if (format == CalendarFormat.twoWeeks) { + return 2; + } else if (format == CalendarFormat.week) { + return 1; + } + + final first = _firstDayOfMonth(focusedDay); + final daysBefore = _getDaysBefore(first); + final firstToDisplay = first.subtract(Duration(days: daysBefore)); + + final last = _lastDayOfMonth(focusedDay); + final daysAfter = _getDaysAfter(last); + final lastToDisplay = last.add(Duration(days: daysAfter)); + + return (lastToDisplay.difference(firstToDisplay).inDays + 1) ~/ 7; + } + + int _getDaysBefore(DateTime firstDay) { + return (firstDay.weekday + 7 - _getWeekdayNumber(startingDayOfWeek)) % 7; + } + + int _getDaysAfter(DateTime lastDay) { + int invertedStartingWeekday = 8 - _getWeekdayNumber(startingDayOfWeek); + + int daysAfter = 7 - ((lastDay.weekday + invertedStartingWeekday) % 7); + if (daysAfter == 7) { + daysAfter = 0; + } + + return daysAfter; + } +} diff --git a/lib/src/widgets/calendar_header.dart b/lib/src/widgets/calendar_header.dart new file mode 100644 index 00000000..1a6a8d8b --- /dev/null +++ b/lib/src/widgets/calendar_header.dart @@ -0,0 +1,90 @@ +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 + +part of table_calendar; + +class _CalendarHeader extends StatelessWidget { + final dynamic locale; + final DateTime focusedMonth; + final CalendarFormat calendarFormat; + final HeaderStyle headerStyle; + final VoidCallback onLeftChevronTap; + final VoidCallback onRightChevronTap; + final VoidCallback onHeaderTap; + final VoidCallback onHeaderLongPress; + final ValueChanged onFormatButtonTap; + final Map availableCalendarFormats; + + const _CalendarHeader({ + Key key, + this.locale, + @required this.focusedMonth, + @required this.calendarFormat, + @required this.headerStyle, + @required this.onLeftChevronTap, + @required this.onRightChevronTap, + @required this.onHeaderTap, + @required this.onHeaderLongPress, + @required this.onFormatButtonTap, + @required this.availableCalendarFormats, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final text = headerStyle.titleTextFormatter != null + ? headerStyle.titleTextFormatter(focusedMonth, locale) + : DateFormat.yMMMM(locale).format(focusedMonth); + + return Container( + decoration: headerStyle.decoration, + margin: headerStyle.headerMargin, + padding: headerStyle.headerPadding, + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + if (headerStyle.leftChevronVisible) + _CustomIconButton( + icon: headerStyle.leftChevronIcon, + onTap: onLeftChevronTap, + margin: headerStyle.leftChevronMargin, + padding: headerStyle.leftChevronPadding, + ), + Expanded( + child: GestureDetector( + onTap: onHeaderTap, + onLongPress: onHeaderLongPress, + child: Text( + text, + style: headerStyle.titleTextStyle, + textAlign: headerStyle.titleCentered + ? TextAlign.center + : TextAlign.start, + ), + ), + ), + if (headerStyle.formatButtonVisible && + availableCalendarFormats.length > 1) + Padding( + padding: const EdgeInsets.only(left: 8.0), + child: _FormatButton( + onTap: onFormatButtonTap, + availableCalendarFormats: availableCalendarFormats, + calendarFormat: calendarFormat, + decoration: headerStyle.formatButtonDecoration, + padding: headerStyle.formatButtonPadding, + textStyle: headerStyle.formatButtonTextStyle, + showsNextFormat: headerStyle.formatButtonShowsNext, + ), + ), + if (headerStyle.rightChevronVisible) + _CustomIconButton( + icon: headerStyle.rightChevronIcon, + onTap: onRightChevronTap, + margin: headerStyle.rightChevronMargin, + padding: headerStyle.rightChevronPadding, + ), + ], + ), + ); + } +} diff --git a/lib/src/widgets/calendar_page.dart b/lib/src/widgets/calendar_page.dart new file mode 100644 index 00000000..ba9bb111 --- /dev/null +++ b/lib/src/widgets/calendar_page.dart @@ -0,0 +1,60 @@ +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 + +part of table_calendar; + +class _CalendarPage extends StatelessWidget { + final List visibleDays; + final DayBuilder dowBuilder; + final DayBuilder dayBuilder; + final Decoration dowDecoration; + final Decoration rowDecoration; + final bool dowVisible; + + const _CalendarPage({ + Key key, + @required this.visibleDays, + this.dowBuilder, + @required this.dayBuilder, + this.dowDecoration, + this.rowDecoration, + this.dowVisible = true, + }) : assert(!dowVisible || dowBuilder != null), + assert(dayBuilder != null), + assert(visibleDays != null), + super(key: key); + + @override + Widget build(BuildContext context) { + return Table( + children: [ + if (dowVisible) _buildDaysOfWeek(context), + ..._buildCalendarDays(context), + ], + ); + } + + TableRow _buildDaysOfWeek(BuildContext context) { + return TableRow( + decoration: dowDecoration, + children: List.generate( + 7, + (index) => dowBuilder(context, visibleDays[index]), + ).toList(), + ); + } + + List _buildCalendarDays(BuildContext context) { + final rowAmount = visibleDays.length ~/ 7; + + return List.generate(rowAmount, (index) => index * 7) + .map((index) => TableRow( + decoration: rowDecoration, + children: List.generate( + 7, + (id) => dayBuilder(context, visibleDays[index + id]), + ), + )) + .toList(); + } +} diff --git a/lib/src/widgets/cell_content.dart b/lib/src/widgets/cell_content.dart new file mode 100644 index 00000000..3798c90d --- /dev/null +++ b/lib/src/widgets/cell_content.dart @@ -0,0 +1,164 @@ +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 + +part of table_calendar; + +class _CellContent extends StatelessWidget { + final DateTime day; + final DateTime focusedDay; + final bool isTodayHighlighted; + final bool isToday; + final bool isSelected; + final bool isRangeStart; + final bool isRangeEnd; + final bool isWithinRange; + final bool isOutside; + final bool isDisabled; + final bool isHoliday; + final bool isWeekend; + final CalendarStyle calendarStyle; + final CalendarBuilders calendarBuilders; + + const _CellContent({ + Key key, + @required this.day, + @required this.focusedDay, + @required this.calendarStyle, + @required this.calendarBuilders, + @required this.isTodayHighlighted, + @required this.isToday, + @required this.isSelected, + @required this.isRangeStart, + @required this.isRangeEnd, + @required this.isWithinRange, + @required this.isOutside, + @required this.isDisabled, + @required this.isHoliday, + @required this.isWeekend, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + Widget cell = + calendarBuilders.prioritizedBuilder?.call(context, day, focusedDay); + + if (cell != null) { + return cell; + } + + final text = '${day.day}'; + final margin = calendarStyle.cellMargin; + + if (isDisabled) { + cell = calendarBuilders.disabledBuilder?.call(context, day, focusedDay) ?? + _DefaultCellWidget( + text: text, + textStyle: calendarStyle.disabledTextStyle, + decoration: calendarStyle.disabledDecoration, + margin: margin, + ); + } else if (isSelected) { + cell = calendarBuilders.selectedBuilder?.call(context, day, focusedDay) ?? + _DefaultCellWidget( + text: text, + textStyle: calendarStyle.selectedTextStyle, + decoration: calendarStyle.selectedDecoration, + margin: margin, + ); + } else if (isRangeStart) { + cell = + calendarBuilders.rangeStartBuilder?.call(context, day, focusedDay) ?? + _DefaultCellWidget( + text: text, + textStyle: calendarStyle.rangeStartTextStyle, + decoration: calendarStyle.rangeStartDecoration, + margin: margin, + ); + } else if (isRangeEnd) { + cell = calendarBuilders.rangeEndBuilder?.call(context, day, focusedDay) ?? + _DefaultCellWidget( + text: text, + textStyle: calendarStyle.rangeEndTextStyle, + decoration: calendarStyle.rangeEndDecoration, + margin: margin, + ); + } else if (isToday && isTodayHighlighted) { + cell = calendarBuilders.todayBuilder?.call(context, day, focusedDay) ?? + _DefaultCellWidget( + text: text, + textStyle: calendarStyle.todayTextStyle, + decoration: calendarStyle.todayDecoration, + margin: margin, + ); + } else if (isHoliday) { + cell = calendarBuilders.holidayBuilder?.call(context, day, focusedDay) ?? + _DefaultCellWidget( + text: text, + textStyle: calendarStyle.holidayTextStyle, + decoration: calendarStyle.holidayDecoration, + margin: margin, + ); + } else if (isWithinRange) { + cell = + calendarBuilders.withinRangeBuilder?.call(context, day, focusedDay) ?? + _DefaultCellWidget( + text: text, + textStyle: calendarStyle.withinRangeTextStyle, + decoration: calendarStyle.withinRangeDecoration, + margin: margin, + ); + } else if (isOutside) { + cell = calendarBuilders.outsideBuilder?.call(context, day, focusedDay) ?? + _DefaultCellWidget( + text: text, + textStyle: calendarStyle.outsideTextStyle, + decoration: calendarStyle.outsideDecoration, + margin: margin, + ); + } else if (isWeekend) { + cell = calendarBuilders.weekendBuilder?.call(context, day, focusedDay) ?? + _DefaultCellWidget( + text: text, + textStyle: calendarStyle.weekendTextStyle, + decoration: calendarStyle.weekendDecoration, + margin: margin, + ); + } else { + cell = calendarBuilders.defaultBuilder?.call(context, day, focusedDay) ?? + _DefaultCellWidget( + text: text, + textStyle: calendarStyle.defaultTextStyle, + decoration: calendarStyle.defaultDecoration, + margin: margin, + ); + } + + return cell; + } +} + +class _DefaultCellWidget extends StatelessWidget { + final String text; + final TextStyle textStyle; + final Decoration decoration; + final EdgeInsetsGeometry margin; + + const _DefaultCellWidget({ + Key key, + @required this.text, + @required this.textStyle, + @required this.decoration, + @required this.margin, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return AnimatedContainer( + duration: const Duration(milliseconds: 250), + margin: margin, + decoration: decoration, + alignment: Alignment.center, + child: Text(text, style: textStyle), + ); + } +} diff --git a/lib/src/widgets/cell_widget.dart b/lib/src/widgets/cell_widget.dart deleted file mode 100644 index 7c68a022..00000000 --- a/lib/src/widgets/cell_widget.dart +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2019 Aleksander Woźniak -// SPDX-License-Identifier: Apache-2.0 - -part of table_calendar; - -class _CellWidget extends StatelessWidget { - final String text; - final bool isUnavailable; - final bool isSelected; - final bool isToday; - final bool isWeekend; - final bool isOutsideMonth; - final bool isHoliday; - final bool isEventDay; - final CalendarStyle calendarStyle; - - const _CellWidget({ - Key key, - @required this.text, - this.isUnavailable = false, - this.isSelected = false, - this.isToday = false, - this.isWeekend = false, - this.isOutsideMonth = false, - this.isHoliday = false, - this.isEventDay = false, - @required this.calendarStyle, - }) : assert(text != null), - assert(calendarStyle != null), - super(key: key); - - @override - Widget build(BuildContext context) { - return AnimatedContainer( - duration: const Duration(milliseconds: 250), - decoration: _buildCellDecoration(), - margin: calendarStyle.cellMargin, - alignment: Alignment.center, - child: Text(text, style: _buildCellTextStyle()), - ); - } - - Decoration _buildCellDecoration() { - if (isSelected && - calendarStyle.renderSelectedFirst && - calendarStyle.highlightSelected) { - return BoxDecoration( - shape: BoxShape.circle, color: calendarStyle.selectedColor); - } else if (isToday && calendarStyle.highlightToday) { - return BoxDecoration( - shape: BoxShape.circle, color: calendarStyle.todayColor); - } else if (isSelected && calendarStyle.highlightSelected) { - return BoxDecoration( - shape: BoxShape.circle, color: calendarStyle.selectedColor); - } else { - return BoxDecoration(shape: BoxShape.circle); - } - } - - TextStyle _buildCellTextStyle() { - if (isUnavailable) { - return calendarStyle.unavailableStyle; - } else if (isSelected && - calendarStyle.renderSelectedFirst && - calendarStyle.highlightSelected) { - return calendarStyle.selectedStyle; - } else if (isToday && calendarStyle.highlightToday) { - return calendarStyle.todayStyle; - } else if (isSelected && calendarStyle.highlightSelected) { - return calendarStyle.selectedStyle; - } else if (isOutsideMonth && isHoliday) { - return calendarStyle.outsideHolidayStyle; - } else if (isHoliday) { - return calendarStyle.holidayStyle; - } else if (isOutsideMonth && isWeekend) { - return calendarStyle.outsideWeekendStyle; - } else if (isOutsideMonth) { - return calendarStyle.outsideStyle; - } else if (isWeekend) { - return calendarStyle.weekendStyle; - } else if (isEventDay) { - return calendarStyle.eventDayStyle; - } else { - return calendarStyle.weekdayStyle; - } - } -} diff --git a/lib/src/widgets/custom_icon_button.dart b/lib/src/widgets/custom_icon_button.dart index 111a79fc..eae02312 100644 --- a/lib/src/widgets/custom_icon_button.dart +++ b/lib/src/widgets/custom_icon_button.dart @@ -13,8 +13,8 @@ class _CustomIconButton extends StatelessWidget { Key key, @required this.icon, @required this.onTap, - this.margin, - this.padding, + this.margin = const EdgeInsets.all(0.0), + this.padding = const EdgeInsets.all(8.0), }) : assert(icon != null), assert(onTap != null), super(key: key); diff --git a/lib/src/widgets/format_button.dart b/lib/src/widgets/format_button.dart new file mode 100644 index 00000000..51fccd14 --- /dev/null +++ b/lib/src/widgets/format_button.dart @@ -0,0 +1,52 @@ +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 + +part of table_calendar; + +class _FormatButton extends StatelessWidget { + final CalendarFormat calendarFormat; + final ValueChanged onTap; + final TextStyle textStyle; + final BoxDecoration decoration; + final EdgeInsets padding; + final bool showsNextFormat; + final Map availableCalendarFormats; + + const _FormatButton({ + Key key, + @required this.calendarFormat, + @required this.onTap, + @required this.textStyle, + @required this.decoration, + @required this.padding, + @required this.showsNextFormat, + @required this.availableCalendarFormats, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () => onTap(_nextFormat()), + child: Container( + decoration: decoration, + padding: padding, + child: Text( + _formatButtonText, + style: textStyle, + ), + ), + ); + } + + String get _formatButtonText => showsNextFormat + ? availableCalendarFormats[_nextFormat()] + : availableCalendarFormats[calendarFormat]; + + CalendarFormat _nextFormat() { + final formats = availableCalendarFormats.keys.toList(); + int id = formats.indexOf(calendarFormat); + id = (id + 1) % formats.length; + + return formats[id]; + } +} diff --git a/lib/table_calendar.dart b/lib/table_calendar.dart index 43145b15..c2b96360 100644 --- a/lib/table_calendar.dart +++ b/lib/table_calendar.dart @@ -9,10 +9,16 @@ import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:simple_gesture_detector/simple_gesture_detector.dart'; -part 'src/calendar.dart'; part 'src/customization/calendar_builders.dart'; part 'src/customization/calendar_style.dart'; part 'src/customization/days_of_week_style.dart'; part 'src/customization/header_style.dart'; -part 'src/widgets/cell_widget.dart'; +part 'src/table_calendar.dart'; +part 'src/table_calendar_base.dart'; +part 'src/table_calendar_lite.dart'; +part 'src/widgets/calendar_core.dart'; +part 'src/widgets/calendar_header.dart'; +part 'src/widgets/calendar_page.dart'; +part 'src/widgets/cell_content.dart'; part 'src/widgets/custom_icon_button.dart'; +part 'src/widgets/format_button.dart'; From 30a22f733e904701fb1be58c060cbd87cee16327 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 31 Jan 2021 18:35:58 +0100 Subject: [PATCH 030/164] Update examples --- example/lib/main.dart | 29 +- example/lib/pages/complex_example.dart | 325 -------------------- example/lib/pages/events_example.dart | 122 -------- example/lib/pages/lite_example.dart | 112 +++++++ example/lib/pages/persistence_example.dart | 73 ----- example/lib/pages/simple_example.dart | 60 ---- example/lib/pages/table_basics_example.dart | 55 ++++ example/lib/pages/table_events_example.dart | 154 ++++++++++ example/lib/pages/table_range_example.dart | 71 +++++ example/lib/utils.dart | 41 +-- 10 files changed, 431 insertions(+), 611 deletions(-) delete mode 100644 example/lib/pages/complex_example.dart delete mode 100644 example/lib/pages/events_example.dart create mode 100644 example/lib/pages/lite_example.dart delete mode 100644 example/lib/pages/persistence_example.dart delete mode 100644 example/lib/pages/simple_example.dart create mode 100644 example/lib/pages/table_basics_example.dart create mode 100644 example/lib/pages/table_events_example.dart create mode 100644 example/lib/pages/table_range_example.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index 14199417..f9449a94 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -3,10 +3,11 @@ import 'package:flutter/material.dart'; import 'package:intl/date_symbol_data_local.dart'; -import 'package:table_calendar_example/pages/complex_example.dart'; -import 'package:table_calendar_example/pages/events_example.dart'; -import 'package:table_calendar_example/pages/persistence_example.dart'; -import 'package:table_calendar_example/pages/simple_example.dart'; + +import 'pages/lite_example.dart'; +import 'pages/table_basics_example.dart'; +import 'pages/table_events_example.dart'; +import 'pages/table_range_example.dart'; void main() { initializeDateFormatting().then((_) => runApp(MyApp())); @@ -41,37 +42,39 @@ class _StartPageState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ + const SizedBox(height: 20.0), RaisedButton( - child: Text('Simple'), + child: Text('TableCalendar - Basics'), onPressed: () => Navigator.push( context, - MaterialPageRoute(builder: (_) => SimpleExamplePage()), + MaterialPageRoute(builder: (_) => TableBasicsExample()), ), ), const SizedBox(height: 12.0), RaisedButton( - child: Text('Persistence'), + child: Text('TableCalendar - Range'), onPressed: () => Navigator.push( context, - MaterialPageRoute(builder: (_) => PersistenceExamplePage()), + MaterialPageRoute(builder: (_) => TableRangeExample()), ), ), const SizedBox(height: 12.0), RaisedButton( - child: Text('Events'), + child: Text('TableCalendar - Events'), onPressed: () => Navigator.push( context, - MaterialPageRoute(builder: (_) => EventsExamplePage()), + MaterialPageRoute(builder: (_) => TableEventsExample()), ), ), - const SizedBox(height: 12.0), + const SizedBox(height: 48.0), RaisedButton( - child: Text('Complex'), + child: Text('TableCalendarLite'), onPressed: () => Navigator.push( context, - MaterialPageRoute(builder: (_) => ComplexExamplePage()), + MaterialPageRoute(builder: (_) => LiteExample()), ), ), + const SizedBox(height: 20.0), ], ), ), diff --git a/example/lib/pages/complex_example.dart b/example/lib/pages/complex_example.dart deleted file mode 100644 index 7090dbb1..00000000 --- a/example/lib/pages/complex_example.dart +++ /dev/null @@ -1,325 +0,0 @@ -// Copyright 2019 Aleksander Woźniak -// SPDX-License-Identifier: Apache-2.0 - -import 'dart:collection'; - -import 'package:flutter/material.dart'; -import 'package:table_calendar/table_calendar.dart'; -import 'package:table_calendar_example/utils.dart'; - -class ComplexExamplePage extends StatefulWidget { - @override - _ComplexExamplePageState createState() => _ComplexExamplePageState(); -} - -class _ComplexExamplePageState extends State - with SingleTickerProviderStateMixin { - Map> _events; - ValueNotifier> _selectedEvents; - AnimationController _animationController; - CalendarFormat _calendarFormat; - DateTime _selectedDay; - DateTime _focusedDay; - - @override - void initState() { - super.initState(); - final now = DateTime.now().toUtc(); - - // Using a [LinkedHashMap] is highly recommended - _events = LinkedHashMap( - equals: isSameDay, - hashCode: getHashCode, - )..addAll(kEvents); - - _selectedDay = now; - _focusedDay = now; - _selectedEvents = ValueNotifier(_events[_selectedDay] ?? []); - _calendarFormat = CalendarFormat.month; - - _animationController = AnimationController( - vsync: this, - duration: const Duration(milliseconds: 400), - ); - - _animationController.forward(); - } - - @override - void dispose() { - _animationController.dispose(); - _selectedEvents.dispose(); - super.dispose(); - } - - void _onDaySelected(DateTime selectedDay, DateTime focusedDay, - List events, List holidays) { - print('==============================='); - print('Callback: _onDaySelected'); - print('SelectedDay: $selectedDay'); - print('Events: $events'); - print('Holidays: $holidays'); - - if (_selectedDay != selectedDay) { - _selectedDay = selectedDay; - } - - if (_focusedDay != focusedDay) { - _focusedDay = focusedDay; - } - - // This rebuilds just the [eventList], not the whole page - _selectedEvents.value = [ - ...holidays, - ...events, - ]; - - _animationController.forward(from: 0.0); - } - - void _onPageChanged(DateTime focusedDay, DateTime first, DateTime last) { - print('==============================='); - print('Callback: _onPageChanged'); - print('FocusedDay: $focusedDay'); - print('First: $first'); - print('Last: $last'); - - if (_focusedDay != focusedDay) { - _focusedDay = focusedDay; - } - } - - void _onFormatChanged(CalendarFormat format, DateTime first, DateTime last) { - print('==============================='); - print('Callback: _onFormatChanged'); - print('CalendarFormat: $format'); - print('First: $first'); - print('Last: $last'); - - if (_calendarFormat != format) { - _calendarFormat = format; - } - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text('TableCalendar-Complex'), - ), - body: Column( - mainAxisSize: MainAxisSize.max, - children: [ - _buildTableCalendar(), - const SizedBox(height: 8.0), - _buildButtons(), - const SizedBox(height: 8.0), - Expanded(child: _buildEventList()), - ], - ), - ); - } - - Widget _buildTableCalendar() { - return TableCalendar( - locale: 'pl_PL', - events: _events, - holidays: kHolidays, - selectedDay: _selectedDay, - focusedDay: _focusedDay, - calendarFormat: _calendarFormat, - startingDayOfWeek: StartingDayOfWeek.monday, - calendarStyle: CalendarStyle( - outsideDaysVisible: false, - weekendStyle: TextStyle(color: Colors.blue[800]), - holidayStyle: TextStyle(color: Colors.blue[800]), - ), - daysOfWeekStyle: DaysOfWeekStyle( - weekendStyle: TextStyle(color: Colors.blue[600]), - ), - headerStyle: HeaderStyle( - centerHeaderTitle: true, - formatButtonVisible: false, - ), - builders: CalendarBuilders( - selectedDayBuilder: (context, date, _) { - return FadeTransition( - opacity: Tween(begin: 0.0, end: 1.0).animate(_animationController), - child: Container( - margin: const EdgeInsets.all(4.0), - padding: const EdgeInsets.only(top: 5.0, left: 6.0), - color: Colors.deepOrange[300], - width: 100, - height: 100, - child: Text( - '${date.day}', - style: TextStyle(fontSize: 16.0), - ), - ), - ); - }, - todayDayBuilder: (context, date, _) { - return Container( - margin: const EdgeInsets.all(4.0), - padding: const EdgeInsets.only(top: 5.0, left: 6.0), - color: Colors.amber[400], - width: 100, - height: 100, - child: Text( - '${date.day}', - style: TextStyle(fontSize: 16.0), - ), - ); - }, - markersBuilder: (context, date, events, holidays) { - final children = []; - - if (events.isNotEmpty) { - children.add( - Positioned( - right: 1, - bottom: 1, - child: _buildEventsMarker(date, events), - ), - ); - } - - if (holidays.isNotEmpty) { - children.add( - Positioned( - right: -2, - top: -2, - child: _buildHolidaysMarker(), - ), - ); - } - - return children; - }, - ), - onDaySelected: _onDaySelected, - onPageChanged: _onPageChanged, - onFormatChanged: _onFormatChanged, - ); - } - - Widget _buildEventsMarker(DateTime date, List events) { - return AnimatedContainer( - duration: const Duration(milliseconds: 300), - decoration: BoxDecoration( - shape: BoxShape.rectangle, - color: isSameDay(date, _selectedDay) - ? Colors.brown[500] - : isSameDay(date, DateTime.now()) - ? Colors.brown[300] - : Colors.blue[400], - ), - width: 16.0, - height: 16.0, - child: Center( - child: Text( - '${events.length}', - style: TextStyle( - color: Colors.white, - fontSize: 12.0, - ), - ), - ), - ); - } - - Widget _buildHolidaysMarker() { - return Icon( - Icons.add_box, - size: 20.0, - color: Colors.blueGrey[800], - ); - } - - Widget _buildButtons() { - final dateTime = _events.keys.elementAt(_events.length - 2); - - return Column( - children: [ - Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - RaisedButton( - child: Text('Month'), - onPressed: () { - setState(() { - _calendarFormat = CalendarFormat.month; - }); - }, - ), - RaisedButton( - child: Text('2 weeks'), - onPressed: () { - setState(() { - _calendarFormat = CalendarFormat.twoWeeks; - }); - }, - ), - RaisedButton( - child: Text('Week'), - onPressed: () { - setState(() { - _calendarFormat = CalendarFormat.week; - }); - }, - ), - ], - ), - const SizedBox(height: 8.0), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - RaisedButton( - child: Text('Select today'), - onPressed: () { - setState(() { - _selectedDay = DateTime.now().toUtc(); - _focusedDay = _selectedDay; - }); - }, - ), - RaisedButton( - child: Text( - 'Select ${dateTime.day}/${dateTime.month}/${dateTime.year}', - ), - onPressed: () { - setState(() { - _selectedDay = dateTime; - _focusedDay = dateTime; - }); - }, - ), - ], - ), - ], - ); - } - - Widget _buildEventList() { - return ValueListenableBuilder>( - valueListenable: _selectedEvents, - builder: (context, value, child) => ListView( - children: value - .map((event) => Container( - decoration: BoxDecoration( - border: Border.all(width: 0.8), - borderRadius: BorderRadius.circular(12.0), - ), - margin: const EdgeInsets.symmetric( - horizontal: 8.0, vertical: 4.0), - child: ListTile( - title: Text(event.toString()), - onTap: () => print('$event tapped!'), - ), - )) - .toList(), - ), - ); - } -} diff --git a/example/lib/pages/events_example.dart b/example/lib/pages/events_example.dart deleted file mode 100644 index 87490163..00000000 --- a/example/lib/pages/events_example.dart +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2019 Aleksander Woźniak -// SPDX-License-Identifier: Apache-2.0 - -import 'dart:collection'; - -import 'package:flutter/material.dart'; -import 'package:table_calendar/table_calendar.dart'; -import 'package:table_calendar_example/utils.dart'; - -class EventsExamplePage extends StatefulWidget { - @override - _EventsExamplePageState createState() => _EventsExamplePageState(); -} - -class _EventsExamplePageState extends State { - Map> _events; - List _selectedEvents; - DateTime _selectedDay; - CalendarFormat _calendarFormat; - - @override - void initState() { - super.initState(); - final selectedDay = DateTime.now().toUtc(); - - // Using a [LinkedHashMap] is highly recommended - _events = LinkedHashMap( - equals: isSameDay, - hashCode: getHashCode, - )..addAll(kEvents); - - _selectedEvents = _events[selectedDay] ?? []; - _selectedDay = selectedDay; - _calendarFormat = CalendarFormat.month; - } - - void _onDaySelected(DateTime selectedDay, DateTime focusedDay, - List events, List holidays) { - print('==============================='); - print('Callback: _onDaySelected'); - print('SelectedDay: $selectedDay'); - print('Events: $events'); - print('Holidays: $holidays'); - - if (_selectedDay != selectedDay) { - _selectedDay = selectedDay; - } - - // Only [eventList] requires rebuilding, so this can be improved - // -> check out "complex_example.dart" file - setState(() { - _selectedEvents = [ - ...holidays, - ...events, - ]; - }); - } - - void _onFormatChanged(CalendarFormat format, DateTime first, DateTime last) { - print('==============================='); - print('Callback: _onFormatChanged'); - print('Format: $format'); - print('First: $first'); - print('Last: $last'); - - if (_calendarFormat != format) { - _calendarFormat = format; - } - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text('TableCalendar-Events'), - ), - body: Column( - mainAxisSize: MainAxisSize.max, - children: [ - TableCalendar( - events: _events, - holidays: kHolidays, - selectedDay: _selectedDay, - calendarFormat: _calendarFormat, - onDaySelected: _onDaySelected, - onFormatChanged: _onFormatChanged, - ), - const SizedBox(height: 8.0), - RaisedButton( - child: Text('Clear selection'), - onPressed: () { - setState(() { - _selectedDay = null; - _selectedEvents = []; - }); - }, - ), - Expanded(child: _buildEventList()), - ], - ), - ); - } - - Widget _buildEventList() { - return ListView( - children: _selectedEvents - .map((event) => Container( - decoration: BoxDecoration( - border: Border.all(width: 0.8), - borderRadius: BorderRadius.circular(12.0), - ), - margin: - const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0), - child: ListTile( - title: Text(event.toString()), - onTap: () => print('$event tapped!'), - ), - )) - .toList(), - ); - } -} diff --git a/example/lib/pages/lite_example.dart b/example/lib/pages/lite_example.dart new file mode 100644 index 00000000..7e49d772 --- /dev/null +++ b/example/lib/pages/lite_example.dart @@ -0,0 +1,112 @@ +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 + +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:table_calendar/table_calendar.dart'; + +class LiteExample extends StatefulWidget { + @override + _LiteExampleState createState() => _LiteExampleState(); +} + +class _LiteExampleState extends State { + final _firstDay = DateTime.utc(2020, 10, 10); + final _lastDay = DateTime.utc(2021, 3, 14); + ValueNotifier _focusedDay; + DateTime _selectedDay; + CalendarFormat _calendarFormat; + + @override + void initState() { + super.initState(); + _focusedDay = ValueNotifier(DateTime.now()); + _calendarFormat = CalendarFormat.month; + } + + @override + void dispose() { + _focusedDay.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('TableCalendarLite'), + ), + body: Column( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: ValueListenableBuilder( + valueListenable: _focusedDay, + builder: (context, value, _) { + final headerText = DateFormat.yMMMM().format(value); + return Text(headerText, style: TextStyle(fontSize: 24.0)); + }, + ), + ), + TableCalendarLite( + dowDecoration: const BoxDecoration(color: Colors.tealAccent), + dowBuilder: (context, day) { + final text = DateFormat.E().format(day); + return Center(child: Text(text)); + }, + dayBuilder: (context, day, focusedDay) { + var decoration = const BoxDecoration(); + var textStyle = const TextStyle(); + + if (day.isBefore(_firstDay) || day.isAfter((_lastDay))) { + textStyle = TextStyle(color: Colors.grey[300]); + } else if (_selectedDay == day) { + decoration = const BoxDecoration( + color: Colors.tealAccent, + ); + } else if (day.month != focusedDay.month) { + textStyle = const TextStyle(color: Colors.grey); + } + + return Center( + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + width: 36.0, + height: 36.0, + decoration: decoration, + child: Center( + child: Text('${day.day}', style: textStyle), + ), + ), + ); + }, + dayHitTestBehavior: HitTestBehavior.opaque, + firstDay: _firstDay, + lastDay: _lastDay, + focusedDay: _focusedDay.value, + calendarFormat: _calendarFormat, + dowHeight: 24.0, + rowHeight: 48.0, + onDaySelected: (selectedDay, focusedDay) { + if (_selectedDay != selectedDay) { + setState(() { + _selectedDay = selectedDay; + }); + } + + _focusedDay.value = focusedDay; + }, + onFormatChanged: (format) { + setState(() { + _calendarFormat = format; + }); + }, + onPageChanged: (focusedDay) { + _focusedDay.value = focusedDay; + }, + ), + ], + ), + ); + } +} diff --git a/example/lib/pages/persistence_example.dart b/example/lib/pages/persistence_example.dart deleted file mode 100644 index 415873e9..00000000 --- a/example/lib/pages/persistence_example.dart +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2019 Aleksander Woźniak -// SPDX-License-Identifier: Apache-2.0 - -import 'package:flutter/material.dart'; -import 'package:table_calendar/table_calendar.dart'; - -class PersistenceExamplePage extends StatefulWidget { - @override - _PersistenceExamplePageState createState() => _PersistenceExamplePageState(); -} - -class _PersistenceExamplePageState extends State { - CalendarFormat _calendarFormat = CalendarFormat.month; - bool _isVisible = true; - DateTime _selectedDay; - DateTime _focusedDay; - - @override - Widget build(BuildContext context) { - final orientation = MediaQuery.of(context).orientation; - - return Scaffold( - appBar: AppBar(title: Text('TableCalendar-Persistence')), - body: Column( - children: [ - Visibility( - visible: _isVisible, - child: TableCalendar( - rowHeight: orientation == Orientation.portrait ? 52.0 : 30.0, - selectedDay: _selectedDay, - focusedDay: _focusedDay, - calendarFormat: _calendarFormat, - onDaySelected: (selectedDay, focusedDay, _, __) { - // Note that this does not rebuild the widget - // Values are updated only in case widget gets rebuilt in future - // (for example by toggling [_isVisible]) - if (_selectedDay != selectedDay) { - _selectedDay = selectedDay; - } - - if (_focusedDay != focusedDay) { - _focusedDay = focusedDay; - } - }, - onPageChanged: (focusedDay, _, __) { - if (_focusedDay != focusedDay) { - _focusedDay = focusedDay; - } - }, - onFormatChanged: (format, _, __) { - if (_calendarFormat != format) { - _calendarFormat = format; - } - }, - ), - ), - Spacer(), - Center( - child: RaisedButton( - child: Text('Toggle visibility'), - onPressed: () { - setState(() { - _isVisible = !_isVisible; - }); - }, - ), - ), - const SizedBox(height: 20.0), - ], - ), - ); - } -} diff --git a/example/lib/pages/simple_example.dart b/example/lib/pages/simple_example.dart deleted file mode 100644 index a29239a4..00000000 --- a/example/lib/pages/simple_example.dart +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2019 Aleksander Woźniak -// SPDX-License-Identifier: Apache-2.0 - -import 'dart:collection'; - -import 'package:flutter/material.dart'; -import 'package:table_calendar/table_calendar.dart'; -import 'package:table_calendar_example/utils.dart'; - -class SimpleExamplePage extends StatefulWidget { - @override - _SimpleExamplePageState createState() => _SimpleExamplePageState(); -} - -class _SimpleExamplePageState extends State { - Map> _events; - - @override - void initState() { - super.initState(); - - // Using a [LinkedHashMap] is highly recommended - _events = LinkedHashMap( - equals: isSameDay, - hashCode: getHashCode, - )..addAll(kEvents); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text('TableCalendar-Simple'), - ), - body: TableCalendar( - events: _events, - // Use to customize calendar's body style - calendarStyle: CalendarStyle( - selectedColor: Colors.deepOrange[400], - todayColor: Colors.deepOrange[200], - markersColor: Colors.brown[700], - ), - // Use to customize calendar's header style - headerStyle: HeaderStyle( - formatButtonTextStyle: - const TextStyle(color: Colors.white, fontSize: 15.0), - formatButtonDecoration: BoxDecoration( - color: Colors.deepOrange[400], - borderRadius: BorderRadius.circular(16.0), - ), - ), - onDaySelected: (selectedDay, _, events, __) { - print('Callback: onDaySelected'); - print('SelectedDay: $selectedDay'); - print('Events: $events'); - }, - ), - ); - } -} diff --git a/example/lib/pages/table_basics_example.dart b/example/lib/pages/table_basics_example.dart new file mode 100644 index 00000000..adab5941 --- /dev/null +++ b/example/lib/pages/table_basics_example.dart @@ -0,0 +1,55 @@ +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 + +import 'package:flutter/material.dart'; +import 'package:table_calendar/table_calendar.dart'; + +class TableBasicsExample extends StatefulWidget { + @override + _TableBasicsExampleState createState() => _TableBasicsExampleState(); +} + +class _TableBasicsExampleState extends State { + CalendarFormat _calendarFormat = CalendarFormat.month; + DateTime _focusedDay = DateTime.now(); + DateTime _selectedDay; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('TableCalendar - Basics'), + ), + body: TableCalendar( + firstDay: DateTime.utc(2020, 10, 16), + lastDay: DateTime.utc(2021, 3, 14), + focusedDay: _focusedDay, + selectedDay: _selectedDay, + calendarFormat: _calendarFormat, + onDaySelected: (selectedDay, focusedDay) { + if (_selectedDay != selectedDay) { + // Call `setState()` to update the selected day + setState(() { + _selectedDay = selectedDay; + }); + } + + // Don't call `setState()` just to update the focused day + _focusedDay = focusedDay; + }, + onFormatChanged: (format) { + if (_calendarFormat != format) { + // Call `setState()` to update calendar format + setState(() { + _calendarFormat = format; + }); + } + }, + onPageChanged: (focusedDay) { + // Don't call `setState()` just to update the focused day + _focusedDay = focusedDay; + }, + ), + ); + } +} diff --git a/example/lib/pages/table_events_example.dart b/example/lib/pages/table_events_example.dart new file mode 100644 index 00000000..2414fe23 --- /dev/null +++ b/example/lib/pages/table_events_example.dart @@ -0,0 +1,154 @@ +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 + +import 'package:flutter/material.dart'; +import 'package:table_calendar/table_calendar.dart'; + +import '../utils.dart'; + +class TableEventsExample extends StatefulWidget { + @override + _TableEventsExampleState createState() => _TableEventsExampleState(); +} + +class _TableEventsExampleState extends State { + CalendarFormat _calendarFormat = CalendarFormat.month; + RangeSelectionMode _rangeSelectionMode = RangeSelectionMode + .toggledOff; // Can be toggled on/off by longpressing a date + DateTime _focusedDay = DateTime.now(); + DateTime _selectedDay; + DateTime _rangeStart; + DateTime _rangeEnd; + + ValueNotifier> _selectedEvents; + + @override + void initState() { + super.initState(); + + _selectedDay = _focusedDay; + _selectedEvents = ValueNotifier(_getEventsForDay(_selectedDay)); + } + + @override + void dispose() { + _selectedEvents.dispose(); + super.dispose(); + } + + List _getEventsForDay(DateTime day) { + // Implementation example + return kEvents[day] ?? []; + } + + List _getEventsForRange(DateTime start, DateTime end) { + // Implementation example + final days = daysInRange(start, end); + + return [ + for (final d in days) ..._getEventsForDay(d), + ]; + } + + void _onDaySelected(selectedDay, focusedDay) { + if (_selectedDay != selectedDay) { + setState(() { + _selectedDay = selectedDay; + _rangeStart = null; // Important to clean those + _rangeEnd = null; + _rangeSelectionMode = RangeSelectionMode.toggledOff; + }); + + _selectedEvents.value = _getEventsForDay(selectedDay); + } + + _focusedDay = focusedDay; + } + + void _onRangeSelected(start, end, focusedDay) { + setState(() { + _selectedDay = null; + _rangeStart = start; + _rangeEnd = end; + _rangeSelectionMode = RangeSelectionMode.toggledOn; + }); + + _focusedDay = focusedDay; + + // `start` or `end` could be null + if (start != null && end != null) { + _selectedEvents.value = _getEventsForRange(start, end); + } else if (start != null) { + _selectedEvents.value = _getEventsForDay(start); + } else { + _selectedEvents.value = _getEventsForDay(end); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('TableCalendar - Events'), + ), + body: Column( + children: [ + TableCalendar( + firstDay: DateTime.utc(2020, 10, 16), + lastDay: DateTime.utc(2021, 3, 14), + focusedDay: _focusedDay, + selectedDay: _selectedDay, + rangeStartDay: _rangeStart, + rangeEndDay: _rangeEnd, + calendarFormat: _calendarFormat, + rangeSelectionMode: _rangeSelectionMode, + eventLoader: _getEventsForDay, + startingDayOfWeek: StartingDayOfWeek.monday, + calendarStyle: CalendarStyle( + // Use `CalendarStyle` to customize the UI + outsideDaysVisible: false, + ), + onDaySelected: _onDaySelected, + onRangeSelected: _onRangeSelected, + onFormatChanged: (format) { + if (_calendarFormat != format) { + setState(() { + _calendarFormat = format; + }); + } + }, + onPageChanged: (focusedDay) { + _focusedDay = focusedDay; + }, + ), + Expanded( + child: ValueListenableBuilder>( + valueListenable: _selectedEvents, + builder: (context, value, _) { + return ListView.builder( + itemCount: value.length, + itemBuilder: (context, index) { + return Container( + margin: const EdgeInsets.symmetric( + horizontal: 12.0, + vertical: 4.0, + ), + decoration: BoxDecoration( + border: Border.all(), + borderRadius: BorderRadius.circular(12.0), + ), + child: ListTile( + onTap: () => print('${value[index]}'), + title: Text('${value[index]}'), + ), + ); + }, + ); + }, + ), + ), + ], + ), + ); + } +} diff --git a/example/lib/pages/table_range_example.dart b/example/lib/pages/table_range_example.dart new file mode 100644 index 00000000..0cc2da42 --- /dev/null +++ b/example/lib/pages/table_range_example.dart @@ -0,0 +1,71 @@ +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 + +import 'package:flutter/material.dart'; +import 'package:table_calendar/table_calendar.dart'; + +class TableRangeExample extends StatefulWidget { + @override + _TableRangeExampleState createState() => _TableRangeExampleState(); +} + +class _TableRangeExampleState extends State { + CalendarFormat _calendarFormat = CalendarFormat.month; + RangeSelectionMode _rangeSelectionMode = RangeSelectionMode + .toggledOn; // Can be toggled on/off by longpressing a date + DateTime _focusedDay = DateTime.now(); + DateTime _selectedDay; + DateTime _rangeStart; + DateTime _rangeEnd; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('TableCalendar - Range'), + ), + body: TableCalendar( + firstDay: DateTime.utc(2020, 10, 16), + lastDay: DateTime.utc(2021, 3, 14), + focusedDay: _focusedDay, + selectedDay: _selectedDay, + rangeStartDay: _rangeStart, + rangeEndDay: _rangeEnd, + calendarFormat: _calendarFormat, + rangeSelectionMode: _rangeSelectionMode, + onDaySelected: (selectedDay, focusedDay) { + if (_selectedDay != selectedDay) { + setState(() { + _selectedDay = selectedDay; + _rangeStart = null; // Important to clean those + _rangeEnd = null; + _rangeSelectionMode = RangeSelectionMode.toggledOff; + }); + } + + _focusedDay = focusedDay; + }, + onRangeSelected: (start, end, focusedDay) { + setState(() { + _selectedDay = null; + _rangeStart = start; + _rangeEnd = end; + _rangeSelectionMode = RangeSelectionMode.toggledOn; + }); + + _focusedDay = focusedDay; + }, + onFormatChanged: (format) { + if (_calendarFormat != format) { + setState(() { + _calendarFormat = format; + }); + } + }, + onPageChanged: (focusedDay) { + _focusedDay = focusedDay; + }, + ), + ); + } +} diff --git a/example/lib/utils.dart b/example/lib/utils.dart index 8c10d9aa..2577679d 100644 --- a/example/lib/utils.dart +++ b/example/lib/utils.dart @@ -1,7 +1,11 @@ // Copyright 2019 Aleksander Woźniak // SPDX-License-Identifier: Apache-2.0 -/// Example Event class +import 'dart:collection'; + +import 'package:table_calendar/table_calendar.dart'; + +/// Example event class. class Event { final String title; @@ -11,8 +15,15 @@ class Event { String toString() => title; } -/// Example events -final kEvents = Map.fromIterable(List.generate(50, (index) => index), +/// Example events. +/// +/// Using a [LinkedHashMap] is highly recommended if you decide to use a map. +final kEvents = LinkedHashMap( + equals: isSameDay, + hashCode: _getHashCode, +)..addAll(_kEventSource); + +final _kEventSource = Map.fromIterable(List.generate(50, (index) => index), key: (item) => DateTime.utc(2020, 10, item * 5), value: (item) => List.generate( item % 4 + 1, (index) => Event('Event $item | ${index + 1}'))) @@ -23,21 +34,15 @@ final kEvents = Map.fromIterable(List.generate(50, (index) => index), ], }); -/// Example holidays -final kHolidays = { - DateTime.utc(2021, 1, 1): [Event('New Year\'s Day')], - DateTime.utc(2021, 2, 14): [Event('Valentine\'s Day')], - DateTime.utc(2021, 3, 8): [Event('International Women\'s Day')], - DateTime.utc(2021, 4, 1): [Event('April Fools\' Day')], - DateTime.utc(2021, 5, 4): [Event('Star Wars Day')], - DateTime.utc(2021, 10, 31): [Event('Halloween')], - DateTime.utc(2021, 12, 31): [Event('New Year\s Eve')], -}; - -bool isSameDay(DateTime a, DateTime b) { - return a.year == b.year && a.month == b.month && a.day == b.day; +int _getHashCode(DateTime key) { + return key.day * 1000000 + key.month * 10000 + key.year; } -int getHashCode(DateTime key) { - return key.day * 1000000 + key.month * 10000 + key.year; +/// Returns a list of [DateTime] objects from [first] to [last], inclusive. +List daysInRange(DateTime first, DateTime last) { + final dayCount = last.difference(first).inDays + 1; + return List.generate( + dayCount, + (index) => DateTime.utc(first.year, first.month, first.day + index), + ); } From 280f07f479a9d3f517cf0e1430394a83b5e3c71b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 31 Jan 2021 18:38:37 +0100 Subject: [PATCH 031/164] Fix naming --- lib/src/table_calendar_lite.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/table_calendar_lite.dart b/lib/src/table_calendar_lite.dart index 4cc57cff..72b0d439 100644 --- a/lib/src/table_calendar_lite.dart +++ b/lib/src/table_calendar_lite.dart @@ -10,7 +10,7 @@ typedef OnRangeSelected = void Function( DateTime start, DateTime end, DateTime focusedDay); enum RangeSelectionMode { - disable, // always off + disabled, // always off toggledOff, // currently off, can be toggled toggledOn, // currently on, can be toggled enforced, // always on From 236362ea4173b3a231bec7458313ce6363778489 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 31 Jan 2021 20:37:22 +0100 Subject: [PATCH 032/164] Improve AnimatedContainer behavior inside builders --- lib/src/widgets/cell_content.dart | 117 +++++++++++++----------------- 1 file changed, 51 insertions(+), 66 deletions(-) diff --git a/lib/src/widgets/cell_content.dart b/lib/src/widgets/cell_content.dart index 3798c90d..1518b4b2 100644 --- a/lib/src/widgets/cell_content.dart +++ b/lib/src/widgets/cell_content.dart @@ -48,117 +48,102 @@ class _CellContent extends StatelessWidget { final text = '${day.day}'; final margin = calendarStyle.cellMargin; + final duration = const Duration(milliseconds: 250); if (isDisabled) { cell = calendarBuilders.disabledBuilder?.call(context, day, focusedDay) ?? - _DefaultCellWidget( - text: text, - textStyle: calendarStyle.disabledTextStyle, - decoration: calendarStyle.disabledDecoration, + AnimatedContainer( + duration: duration, margin: margin, + decoration: calendarStyle.disabledDecoration, + alignment: Alignment.center, + child: Text(text, style: calendarStyle.disabledTextStyle), ); } else if (isSelected) { cell = calendarBuilders.selectedBuilder?.call(context, day, focusedDay) ?? - _DefaultCellWidget( - text: text, - textStyle: calendarStyle.selectedTextStyle, - decoration: calendarStyle.selectedDecoration, + AnimatedContainer( + duration: duration, margin: margin, + decoration: calendarStyle.selectedDecoration, + alignment: Alignment.center, + child: Text(text, style: calendarStyle.selectedTextStyle), ); } else if (isRangeStart) { cell = calendarBuilders.rangeStartBuilder?.call(context, day, focusedDay) ?? - _DefaultCellWidget( - text: text, - textStyle: calendarStyle.rangeStartTextStyle, - decoration: calendarStyle.rangeStartDecoration, + AnimatedContainer( + duration: duration, margin: margin, + decoration: calendarStyle.rangeStartDecoration, + alignment: Alignment.center, + child: Text(text, style: calendarStyle.rangeStartTextStyle), ); } else if (isRangeEnd) { cell = calendarBuilders.rangeEndBuilder?.call(context, day, focusedDay) ?? - _DefaultCellWidget( - text: text, - textStyle: calendarStyle.rangeEndTextStyle, - decoration: calendarStyle.rangeEndDecoration, + AnimatedContainer( + duration: duration, margin: margin, + decoration: calendarStyle.rangeEndDecoration, + alignment: Alignment.center, + child: Text(text, style: calendarStyle.rangeEndTextStyle), ); } else if (isToday && isTodayHighlighted) { cell = calendarBuilders.todayBuilder?.call(context, day, focusedDay) ?? - _DefaultCellWidget( - text: text, - textStyle: calendarStyle.todayTextStyle, - decoration: calendarStyle.todayDecoration, + AnimatedContainer( + duration: duration, margin: margin, + decoration: calendarStyle.todayDecoration, + alignment: Alignment.center, + child: Text(text, style: calendarStyle.todayTextStyle), ); } else if (isHoliday) { cell = calendarBuilders.holidayBuilder?.call(context, day, focusedDay) ?? - _DefaultCellWidget( - text: text, - textStyle: calendarStyle.holidayTextStyle, - decoration: calendarStyle.holidayDecoration, + AnimatedContainer( + duration: duration, margin: margin, + decoration: calendarStyle.holidayDecoration, + alignment: Alignment.center, + child: Text(text, style: calendarStyle.holidayTextStyle), ); } else if (isWithinRange) { cell = calendarBuilders.withinRangeBuilder?.call(context, day, focusedDay) ?? - _DefaultCellWidget( - text: text, - textStyle: calendarStyle.withinRangeTextStyle, - decoration: calendarStyle.withinRangeDecoration, + AnimatedContainer( + duration: duration, margin: margin, + decoration: calendarStyle.withinRangeDecoration, + alignment: Alignment.center, + child: Text(text, style: calendarStyle.withinRangeTextStyle), ); } else if (isOutside) { cell = calendarBuilders.outsideBuilder?.call(context, day, focusedDay) ?? - _DefaultCellWidget( - text: text, - textStyle: calendarStyle.outsideTextStyle, - decoration: calendarStyle.outsideDecoration, + AnimatedContainer( + duration: duration, margin: margin, + decoration: calendarStyle.outsideDecoration, + alignment: Alignment.center, + child: Text(text, style: calendarStyle.outsideTextStyle), ); } else if (isWeekend) { cell = calendarBuilders.weekendBuilder?.call(context, day, focusedDay) ?? - _DefaultCellWidget( - text: text, - textStyle: calendarStyle.weekendTextStyle, - decoration: calendarStyle.weekendDecoration, + AnimatedContainer( + duration: duration, margin: margin, + decoration: calendarStyle.weekendDecoration, + alignment: Alignment.center, + child: Text(text, style: calendarStyle.weekendTextStyle), ); } else { cell = calendarBuilders.defaultBuilder?.call(context, day, focusedDay) ?? - _DefaultCellWidget( - text: text, - textStyle: calendarStyle.defaultTextStyle, - decoration: calendarStyle.defaultDecoration, + AnimatedContainer( + duration: duration, margin: margin, + decoration: calendarStyle.defaultDecoration, + alignment: Alignment.center, + child: Text(text, style: calendarStyle.defaultTextStyle), ); } return cell; } } - -class _DefaultCellWidget extends StatelessWidget { - final String text; - final TextStyle textStyle; - final Decoration decoration; - final EdgeInsetsGeometry margin; - - const _DefaultCellWidget({ - Key key, - @required this.text, - @required this.textStyle, - @required this.decoration, - @required this.margin, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return AnimatedContainer( - duration: const Duration(milliseconds: 250), - margin: margin, - decoration: decoration, - alignment: Alignment.center, - child: Text(text, style: textStyle), - ); - } -} From 3404f9096d7b6f60d4e0e76224c45144d88462e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 31 Jan 2021 20:48:29 +0100 Subject: [PATCH 033/164] Replace selectedDay with a predicate --- lib/src/table_calendar.dart | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index df88bea7..ebbcf5df 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -10,9 +10,6 @@ class TableCalendar extends StatefulWidget { /// If nothing is provided, a default locale will be used. final dynamic locale; - /// DateTime to highlight as currently selected. - final DateTime selectedDay; - /// The start of the selected day range. final DateTime rangeStartDay; @@ -112,6 +109,9 @@ class TableCalendar extends StatefulWidget { /// If `false` is returned, this day will be disabled. final bool Function(DateTime day) enabledDayPredicate; + /// Function deciding whether given day should be marked as selected. + final bool Function(DateTime day) selectedDayPredicate; + /// Function deciding whether given day is treated as a holiday. final bool Function(DateTime day) holidayPredicate; @@ -142,7 +142,6 @@ class TableCalendar extends StatefulWidget { TableCalendar({ Key key, this.locale, - this.selectedDay, this.rangeStartDay, this.rangeEndDay, @required this.focusedDay, @@ -175,6 +174,7 @@ class TableCalendar extends StatefulWidget { this.onRangeSelected, this.eventLoader, this.enabledDayPredicate, + this.selectedDayPredicate, this.holidayPredicate, this.onDaySelected, this.onDisabledDayTapped, @@ -373,10 +373,8 @@ class _TableCalendarState extends State> { ); final isToday = isSameDay(date, DateTime.now()); - final isSelected = isSameDay(date, widget.selectedDay); final isDisabled = _isDayDisabled(date); final isWeekend = _isWeekend(date, weekendDays: widget.weekendDays); - final isHoliday = _isHoliday(date); Widget content = _CellContent( day: date, @@ -385,14 +383,14 @@ class _TableCalendarState extends State> { calendarBuilders: widget.calendarBuilders, isTodayHighlighted: widget.calendarStyle.isTodayHighlighted, isToday: isToday, - isSelected: isSelected, + isSelected: widget.selectedDayPredicate?.call(date) ?? false, isRangeStart: isRangeStart, isRangeEnd: isRangeEnd, isWithinRange: isWithinRange, isOutside: isOutside, isDisabled: isDisabled, isWeekend: isWeekend, - isHoliday: isHoliday, + isHoliday: widget.holidayPredicate?.call(date) ?? false, ); children.add(content); @@ -470,12 +468,6 @@ class _TableCalendarState extends State> { : widget.enabledDayPredicate(day); } - bool _isHoliday(DateTime day) { - return widget.holidayPredicate == null - ? false - : widget.holidayPredicate(day); - } - bool _isWeekend( DateTime day, { List weekendDays = const [DateTime.saturday, DateTime.sunday], From 1f1ed07d68e49163e3154fe391d3edc3d378069f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 31 Jan 2021 20:53:22 +0100 Subject: [PATCH 034/164] Update examples --- example/lib/pages/table_basics_example.dart | 6 +++++- example/lib/pages/table_events_example.dart | 2 +- example/lib/pages/table_range_example.dart | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/example/lib/pages/table_basics_example.dart b/example/lib/pages/table_basics_example.dart index adab5941..1d12f511 100644 --- a/example/lib/pages/table_basics_example.dart +++ b/example/lib/pages/table_basics_example.dart @@ -24,7 +24,11 @@ class _TableBasicsExampleState extends State { firstDay: DateTime.utc(2020, 10, 16), lastDay: DateTime.utc(2021, 3, 14), focusedDay: _focusedDay, - selectedDay: _selectedDay, + selectedDayPredicate: (day) { + // Use `selectedDayPredicate` to determine which day is currently selected. + // If this returns true, then `day` will be marked as selected. + return _selectedDay == day; + }, calendarFormat: _calendarFormat, onDaySelected: (selectedDay, focusedDay) { if (_selectedDay != selectedDay) { diff --git a/example/lib/pages/table_events_example.dart b/example/lib/pages/table_events_example.dart index 2414fe23..65cb4964 100644 --- a/example/lib/pages/table_events_example.dart +++ b/example/lib/pages/table_events_example.dart @@ -97,7 +97,7 @@ class _TableEventsExampleState extends State { firstDay: DateTime.utc(2020, 10, 16), lastDay: DateTime.utc(2021, 3, 14), focusedDay: _focusedDay, - selectedDay: _selectedDay, + selectedDayPredicate: (day) => _selectedDay == day, rangeStartDay: _rangeStart, rangeEndDay: _rangeEnd, calendarFormat: _calendarFormat, diff --git a/example/lib/pages/table_range_example.dart b/example/lib/pages/table_range_example.dart index 0cc2da42..3771a06c 100644 --- a/example/lib/pages/table_range_example.dart +++ b/example/lib/pages/table_range_example.dart @@ -28,7 +28,7 @@ class _TableRangeExampleState extends State { firstDay: DateTime.utc(2020, 10, 16), lastDay: DateTime.utc(2021, 3, 14), focusedDay: _focusedDay, - selectedDay: _selectedDay, + selectedDayPredicate: (day) => _selectedDay == day, rangeStartDay: _rangeStart, rangeEndDay: _rangeEnd, calendarFormat: _calendarFormat, From f63a9b8748d524816a1efbbd0189b408b2f28c5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 31 Jan 2021 20:54:27 +0100 Subject: [PATCH 035/164] Add multiple selection example --- example/lib/main.dart | 9 ++ example/lib/pages/table_multi_example.dart | 126 +++++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 example/lib/pages/table_multi_example.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index f9449a94..3ae54d00 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -7,6 +7,7 @@ import 'package:intl/date_symbol_data_local.dart'; import 'pages/lite_example.dart'; import 'pages/table_basics_example.dart'; import 'pages/table_events_example.dart'; +import 'pages/table_multi_example.dart'; import 'pages/table_range_example.dart'; void main() { @@ -66,6 +67,14 @@ class _StartPageState extends State { MaterialPageRoute(builder: (_) => TableEventsExample()), ), ), + const SizedBox(height: 12.0), + RaisedButton( + child: Text('TableCalendar - Multi'), + onPressed: () => Navigator.push( + context, + MaterialPageRoute(builder: (_) => TableMultiExample()), + ), + ), const SizedBox(height: 48.0), RaisedButton( child: Text('TableCalendarLite'), diff --git a/example/lib/pages/table_multi_example.dart b/example/lib/pages/table_multi_example.dart new file mode 100644 index 00000000..210a815a --- /dev/null +++ b/example/lib/pages/table_multi_example.dart @@ -0,0 +1,126 @@ +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 + +import 'package:flutter/material.dart'; +import 'package:table_calendar/table_calendar.dart'; + +import '../utils.dart'; + +class TableMultiExample extends StatefulWidget { + @override + _TableMultiExampleState createState() => _TableMultiExampleState(); +} + +class _TableMultiExampleState extends State { + CalendarFormat _calendarFormat = CalendarFormat.month; + DateTime _focusedDay = DateTime.now(); + Set _selectedDays = {}; + + ValueNotifier> _selectedEvents; + + @override + void initState() { + super.initState(); + _selectedEvents = ValueNotifier(_getEventsForDay(_focusedDay)); + } + + @override + void dispose() { + _selectedEvents.dispose(); + super.dispose(); + } + + List _getEventsForDay(DateTime day) { + // Implementation example + return kEvents[day] ?? []; + } + + List _getEventsForDays(Set days) { + // Implementation example + // Note that days are in selection order (same applies to events) + return [ + for (final d in days) ..._getEventsForDay(d), + ]; + } + + void _onDaySelected(selectedDay, focusedDay) { + // Update values in a Set + setState(() { + if (_selectedDays.contains(selectedDay)) { + _selectedDays.remove(selectedDay); + } else { + _selectedDays.add(selectedDay); + } + }); + + _selectedEvents.value = _getEventsForDays(_selectedDays); + _focusedDay = focusedDay; + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('TableCalendar - Multi'), + ), + body: Column( + children: [ + TableCalendar( + firstDay: DateTime.utc(2020, 10, 16), + lastDay: DateTime.utc(2021, 3, 14), + focusedDay: _focusedDay, + calendarFormat: _calendarFormat, + eventLoader: _getEventsForDay, + startingDayOfWeek: StartingDayOfWeek.monday, + selectedDayPredicate: (day) => _selectedDays.contains(day), + onDaySelected: _onDaySelected, + onFormatChanged: (format) { + if (_calendarFormat != format) { + setState(() { + _calendarFormat = format; + }); + } + }, + onPageChanged: (focusedDay) { + _focusedDay = focusedDay; + }, + ), + RaisedButton( + child: Text('Clear selection'), + onPressed: () { + setState(() {}); + _selectedDays.clear(); + _selectedEvents.value.clear(); + }, + ), + Expanded( + child: ValueListenableBuilder>( + valueListenable: _selectedEvents, + builder: (context, value, _) { + return ListView.builder( + itemCount: value.length, + itemBuilder: (context, index) { + return Container( + margin: const EdgeInsets.symmetric( + horizontal: 12.0, + vertical: 4.0, + ), + decoration: BoxDecoration( + border: Border.all(), + borderRadius: BorderRadius.circular(12.0), + ), + child: ListTile( + onTap: () => print('${value[index]}'), + title: Text('${value[index]}'), + ), + ); + }, + ); + }, + ), + ), + ], + ), + ); + } +} From 8750f00c8ec1123eb7c62264e625b7fc47a59111 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 31 Jan 2021 20:56:24 +0100 Subject: [PATCH 036/164] Update multi example --- example/lib/pages/table_multi_example.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/example/lib/pages/table_multi_example.dart b/example/lib/pages/table_multi_example.dart index 210a815a..49c69f46 100644 --- a/example/lib/pages/table_multi_example.dart +++ b/example/lib/pages/table_multi_example.dart @@ -72,7 +72,10 @@ class _TableMultiExampleState extends State { calendarFormat: _calendarFormat, eventLoader: _getEventsForDay, startingDayOfWeek: StartingDayOfWeek.monday, - selectedDayPredicate: (day) => _selectedDays.contains(day), + selectedDayPredicate: (day) { + // Use values from Set to mark multiple days as selected + return _selectedDays.contains(day); + }, onDaySelected: _onDaySelected, onFormatChanged: (format) { if (_calendarFormat != format) { From f3818de21e38840f76d47c640c0c2bad861ee47f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 31 Jan 2021 21:12:17 +0100 Subject: [PATCH 037/164] Use isSameDay to compare dates --- example/lib/pages/table_basics_example.dart | 7 +++++-- example/lib/pages/table_events_example.dart | 8 ++++---- example/lib/pages/table_range_example.dart | 4 ++-- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/example/lib/pages/table_basics_example.dart b/example/lib/pages/table_basics_example.dart index 1d12f511..5612bde2 100644 --- a/example/lib/pages/table_basics_example.dart +++ b/example/lib/pages/table_basics_example.dart @@ -27,11 +27,14 @@ class _TableBasicsExampleState extends State { selectedDayPredicate: (day) { // Use `selectedDayPredicate` to determine which day is currently selected. // If this returns true, then `day` will be marked as selected. - return _selectedDay == day; + + // Using `isSameDay` is recommended to disregard + // the time-part of compared DateTime objects. + return isSameDay(_selectedDay, day); }, calendarFormat: _calendarFormat, onDaySelected: (selectedDay, focusedDay) { - if (_selectedDay != selectedDay) { + if (!isSameDay(_selectedDay, selectedDay)) { // Call `setState()` to update the selected day setState(() { _selectedDay = selectedDay; diff --git a/example/lib/pages/table_events_example.dart b/example/lib/pages/table_events_example.dart index 65cb4964..68b8a8cc 100644 --- a/example/lib/pages/table_events_example.dart +++ b/example/lib/pages/table_events_example.dart @@ -50,8 +50,8 @@ class _TableEventsExampleState extends State { ]; } - void _onDaySelected(selectedDay, focusedDay) { - if (_selectedDay != selectedDay) { + void _onDaySelected(DateTime selectedDay, DateTime focusedDay) { + if (!isSameDay(_selectedDay, selectedDay)) { setState(() { _selectedDay = selectedDay; _rangeStart = null; // Important to clean those @@ -65,7 +65,7 @@ class _TableEventsExampleState extends State { _focusedDay = focusedDay; } - void _onRangeSelected(start, end, focusedDay) { + void _onRangeSelected(DateTime start, DateTime end, DateTime focusedDay) { setState(() { _selectedDay = null; _rangeStart = start; @@ -97,7 +97,7 @@ class _TableEventsExampleState extends State { firstDay: DateTime.utc(2020, 10, 16), lastDay: DateTime.utc(2021, 3, 14), focusedDay: _focusedDay, - selectedDayPredicate: (day) => _selectedDay == day, + selectedDayPredicate: (day) => isSameDay(_selectedDay, day), rangeStartDay: _rangeStart, rangeEndDay: _rangeEnd, calendarFormat: _calendarFormat, diff --git a/example/lib/pages/table_range_example.dart b/example/lib/pages/table_range_example.dart index 3771a06c..51ccec1f 100644 --- a/example/lib/pages/table_range_example.dart +++ b/example/lib/pages/table_range_example.dart @@ -28,13 +28,13 @@ class _TableRangeExampleState extends State { firstDay: DateTime.utc(2020, 10, 16), lastDay: DateTime.utc(2021, 3, 14), focusedDay: _focusedDay, - selectedDayPredicate: (day) => _selectedDay == day, + selectedDayPredicate: (day) => isSameDay(_selectedDay, day), rangeStartDay: _rangeStart, rangeEndDay: _rangeEnd, calendarFormat: _calendarFormat, rangeSelectionMode: _rangeSelectionMode, onDaySelected: (selectedDay, focusedDay) { - if (_selectedDay != selectedDay) { + if (!isSameDay(_selectedDay, selectedDay)) { setState(() { _selectedDay = selectedDay; _rangeStart = null; // Important to clean those From 32368f61babeb761ba2c6f87187b8ffbf1470156 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 31 Jan 2021 21:12:34 +0100 Subject: [PATCH 038/164] Add docs --- lib/src/table_calendar_base.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/src/table_calendar_base.dart b/lib/src/table_calendar_base.dart index 3362abec..5887ea27 100644 --- a/lib/src/table_calendar_base.dart +++ b/lib/src/table_calendar_base.dart @@ -27,6 +27,8 @@ int _getWeekdayNumber(StartingDayOfWeek weekday) { return StartingDayOfWeek.values.indexOf(weekday) + 1; } +/// Checks if two DateTime objects are the same day. +/// Returns `false` if either of them is null. bool isSameDay(DateTime a, DateTime b) { if (a == null || b == null) { return false; From 5d89f8703cc8f46c212173caab4692aabd7652fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 31 Jan 2021 21:26:36 +0100 Subject: [PATCH 039/164] Use LinkedHashSet in example --- example/lib/pages/table_multi_example.dart | 12 ++++++++++-- example/lib/utils.dart | 4 ++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/example/lib/pages/table_multi_example.dart b/example/lib/pages/table_multi_example.dart index 49c69f46..903569d6 100644 --- a/example/lib/pages/table_multi_example.dart +++ b/example/lib/pages/table_multi_example.dart @@ -1,6 +1,8 @@ // Copyright 2019 Aleksander Woźniak // SPDX-License-Identifier: Apache-2.0 +import 'dart:collection'; + import 'package:flutter/material.dart'; import 'package:table_calendar/table_calendar.dart'; @@ -14,7 +16,7 @@ class TableMultiExample extends StatefulWidget { class _TableMultiExampleState extends State { CalendarFormat _calendarFormat = CalendarFormat.month; DateTime _focusedDay = DateTime.now(); - Set _selectedDays = {}; + Set _selectedDays; ValueNotifier> _selectedEvents; @@ -22,6 +24,12 @@ class _TableMultiExampleState extends State { void initState() { super.initState(); _selectedEvents = ValueNotifier(_getEventsForDay(_focusedDay)); + + // Using a `LinkedHashSet` is recommended due to equality comparison override + _selectedDays = LinkedHashSet( + equals: isSameDay, + hashCode: getHashCode, + ); } @override @@ -43,7 +51,7 @@ class _TableMultiExampleState extends State { ]; } - void _onDaySelected(selectedDay, focusedDay) { + void _onDaySelected(DateTime selectedDay, DateTime focusedDay) { // Update values in a Set setState(() { if (_selectedDays.contains(selectedDay)) { diff --git a/example/lib/utils.dart b/example/lib/utils.dart index 2577679d..cdb23c5c 100644 --- a/example/lib/utils.dart +++ b/example/lib/utils.dart @@ -20,7 +20,7 @@ class Event { /// Using a [LinkedHashMap] is highly recommended if you decide to use a map. final kEvents = LinkedHashMap( equals: isSameDay, - hashCode: _getHashCode, + hashCode: getHashCode, )..addAll(_kEventSource); final _kEventSource = Map.fromIterable(List.generate(50, (index) => index), @@ -34,7 +34,7 @@ final _kEventSource = Map.fromIterable(List.generate(50, (index) => index), ], }); -int _getHashCode(DateTime key) { +int getHashCode(DateTime key) { return key.day * 1000000 + key.month * 10000 + key.year; } From 99b52e733c57813b2e02f0973646f7afbb9a2857 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Tue, 2 Feb 2021 16:56:41 +0100 Subject: [PATCH 040/164] Code improvements --- lib/src/table_calendar.dart | 6 ++++++ lib/src/table_calendar_base.dart | 31 +++++++++++++++++++++++------- lib/src/table_calendar_lite.dart | 3 +++ lib/src/widgets/calendar_core.dart | 30 +++++++++-------------------- 4 files changed, 42 insertions(+), 28 deletions(-) diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index ebbcf5df..8de8c4a3 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -62,6 +62,10 @@ class TableCalendar extends StatefulWidget { /// will jump to a page related to the tapped month. final bool pageJumpingEnabled; + /// When set to true, `CalendarFormat.month` will always display six weeks, + /// even if the content would fit in less. + final bool sixWeekMonthsEnforced; + /// Used for setting the height of `TableCalendar`'s rows. final double rowHeight; @@ -157,6 +161,7 @@ class TableCalendar extends StatefulWidget { this.headerVisible = true, this.daysOfWeekVisible = true, this.pageJumpingEnabled = false, + this.sixWeekMonthsEnforced = false, this.rowHeight = 52.0, this.daysOfWeekHeight = 16.0, this.startingDayOfWeek = StartingDayOfWeek.sunday, @@ -282,6 +287,7 @@ class _TableCalendarState extends State> { dayHitTestBehavior: widget.dayHitTestBehavior, enabledDayPredicate: widget.enabledDayPredicate, rangeSelectionMode: widget.rangeSelectionMode, + sixWeekMonthsEnforced: widget.sixWeekMonthsEnforced, pageJumpingEnabled: widget.pageJumpingEnabled, onDisabledDayTapped: widget.onDisabledDayTapped, onDisabledDayLongPressed: widget.onDisabledDayLongPressed, diff --git a/lib/src/table_calendar_base.dart b/lib/src/table_calendar_base.dart index 5887ea27..d1f92a24 100644 --- a/lib/src/table_calendar_base.dart +++ b/lib/src/table_calendar_base.dart @@ -46,6 +46,7 @@ class TableCalendarBase extends StatefulWidget { final FocusedDayBuilder dayBuilder; final double dowHeight; final double rowHeight; + final bool sixWeekMonthsEnforced; final bool dowVisible; final Decoration dowDecoration; final Decoration rowDecoration; @@ -67,6 +68,7 @@ class TableCalendarBase extends StatefulWidget { @required this.dayBuilder, this.dowHeight, @required this.rowHeight, + this.sixWeekMonthsEnforced = false, this.dowVisible = true, this.dowDecoration, this.rowDecoration, @@ -147,6 +149,11 @@ class _TableCalendarBaseState extends State final rowCount = _getRowCount(widget.calendarFormat, _focusedDay); _pageHeight.value = _getPageHeight(rowCount); } + + if (widget.sixWeekMonthsEnforced != oldWidget.sixWeekMonthsEnforced) { + final rowCount = _getRowCount(widget.calendarFormat, _focusedDay); + _pageHeight.value = _getPageHeight(rowCount); + } } @override @@ -214,18 +221,26 @@ class _TableCalendarBaseState extends State calendarFormat: widget.calendarFormat, previousIndex: _previousIndex, focusedDay: _focusedDay, + sixWeekMonthsEnforced: widget.sixWeekMonthsEnforced, dowVisible: widget.dowVisible, dowDecoration: widget.dowDecoration, rowDecoration: widget.rowDecoration, - onPageChanged: (index, focusedMonth, rowCount) { + onPageChanged: (index, focusedMonth) { if (!isSameDay(_focusedDay, focusedMonth)) { _focusedDay = focusedMonth; } if (!_pageCallbackDisabled) { - _previousIndex = index; - _pageHeight.value = _getPageHeight(rowCount); + if (widget.calendarFormat == CalendarFormat.month && + !widget.sixWeekMonthsEnforced) { + final rowCount = _getRowCount( + widget.calendarFormat, + focusedMonth, + ); + _pageHeight.value = _getPageHeight(rowCount); + } + _previousIndex = index; widget.onPageChanged?.call(focusedMonth); } @@ -287,6 +302,8 @@ class _TableCalendarBaseState extends State return 2; } else if (format == CalendarFormat.week) { return 1; + } else if (widget.sixWeekMonthsEnforced) { + return 6; } final first = _firstDayOfMonth(focusedDay); @@ -297,7 +314,7 @@ class _TableCalendarBaseState extends State final daysAfter = _getDaysAfter(last); final lastToDisplay = last.add(Duration(days: daysAfter)); - return lastToDisplay.difference(firstToDisplay).inDays ~/ 7; + return (lastToDisplay.difference(firstToDisplay).inDays + 1) ~/ 7; } int _getDaysBefore(DateTime firstDay) { @@ -311,9 +328,9 @@ class _TableCalendarBaseState extends State int invertedStartingWeekday = 8 - _getWeekdayNumber(widget.startingDayOfWeek); - int daysAfter = 7 - ((lastDay.weekday + invertedStartingWeekday) % 7) + 1; - if (daysAfter == 8) { - daysAfter = 1; + int daysAfter = 7 - ((lastDay.weekday + invertedStartingWeekday) % 7); + if (daysAfter == 7) { + daysAfter = 0; } return daysAfter; diff --git a/lib/src/table_calendar_lite.dart b/lib/src/table_calendar_lite.dart index 72b0d439..7a94a209 100644 --- a/lib/src/table_calendar_lite.dart +++ b/lib/src/table_calendar_lite.dart @@ -27,6 +27,7 @@ class TableCalendarLite extends StatefulWidget { final double rowHeight; final bool dowVisible; final bool pageJumpingEnabled; + final bool sixWeekMonthsEnforced; final Decoration dowDecoration; final Decoration rowDecoration; final StartingDayOfWeek startingDayOfWeek; @@ -56,6 +57,7 @@ class TableCalendarLite extends StatefulWidget { @required this.rowHeight, this.dowVisible = true, this.pageJumpingEnabled = false, + this.sixWeekMonthsEnforced = false, this.dowDecoration, this.rowDecoration, this.startingDayOfWeek = StartingDayOfWeek.sunday, @@ -235,6 +237,7 @@ class _TableCalendarLiteState extends State { _focusedDay = focusedMonth; widget.onPageChanged?.call(focusedMonth); }, + sixWeekMonthsEnforced: widget.sixWeekMonthsEnforced, dowVisible: widget.dowVisible, dowDecoration: widget.dowDecoration, rowDecoration: widget.rowDecoration, diff --git a/lib/src/widgets/calendar_core.dart b/lib/src/widgets/calendar_core.dart index 6c9fd205..ed30fd67 100644 --- a/lib/src/widgets/calendar_core.dart +++ b/lib/src/widgets/calendar_core.dart @@ -4,7 +4,7 @@ part of table_calendar; typedef _OnCalendarPageChanged = void Function( - int pageIndex, DateTime focusedDay, int rowCount); + int pageIndex, DateTime focusedDay); class _CalendarCore extends StatelessWidget { final DateTime focusedDay; @@ -13,6 +13,7 @@ class _CalendarCore extends StatelessWidget { final CalendarFormat calendarFormat; final DayBuilder dowBuilder; final FocusedDayBuilder dayBuilder; + final bool sixWeekMonthsEnforced; final bool dowVisible; final Decoration dowDecoration; final Decoration rowDecoration; @@ -34,6 +35,7 @@ class _CalendarCore extends StatelessWidget { this.pageController, this.focusedDay, this.previousIndex, + this.sixWeekMonthsEnforced = false, this.dowVisible = true, this.dowDecoration, this.rowDecoration, @@ -80,8 +82,7 @@ class _CalendarCore extends StatelessWidget { baseDay = _getFocusedDay(calendarFormat, index); } - final rowCount = _getRowCount(calendarFormat, baseDay); - return onPageChanged(index, baseDay, rowCount); + return onPageChanged(index, baseDay); }, ); } @@ -204,6 +205,11 @@ class _CalendarCore extends StatelessWidget { final daysBefore = _getDaysBefore(first); final firstToDisplay = first.subtract(Duration(days: daysBefore)); + if (sixWeekMonthsEnforced) { + final end = firstToDisplay.add(const Duration(days: 42)); + return DateTimeRange(start: firstToDisplay, end: end); + } + final last = _lastDayOfMonth(focusedDay); final daysAfter = _getDaysAfter(last); final lastToDisplay = last.add(Duration(days: daysAfter)); @@ -235,24 +241,6 @@ class _CalendarCore extends StatelessWidget { return date.subtract(const Duration(days: 1)); } - int _getRowCount(CalendarFormat format, DateTime focusedDay) { - if (format == CalendarFormat.twoWeeks) { - return 2; - } else if (format == CalendarFormat.week) { - return 1; - } - - final first = _firstDayOfMonth(focusedDay); - final daysBefore = _getDaysBefore(first); - final firstToDisplay = first.subtract(Duration(days: daysBefore)); - - final last = _lastDayOfMonth(focusedDay); - final daysAfter = _getDaysAfter(last); - final lastToDisplay = last.add(Duration(days: daysAfter)); - - return (lastToDisplay.difference(firstToDisplay).inDays + 1) ~/ 7; - } - int _getDaysBefore(DateTime firstDay) { return (firstDay.weekday + 7 - _getWeekdayNumber(startingDayOfWeek)) % 7; } From 5860c05b3e21f640d941cc0affeea7c6297c4dab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Tue, 2 Feb 2021 18:06:25 +0100 Subject: [PATCH 041/164] Add shouldFillViewport property --- lib/src/table_calendar.dart | 145 +++++++++++++++-------------- lib/src/table_calendar_base.dart | 136 ++++++++++++++------------- lib/src/widgets/calendar_core.dart | 44 ++++++++- 3 files changed, 189 insertions(+), 136 deletions(-) diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index 8de8c4a3..3943ca91 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -66,6 +66,9 @@ class TableCalendar extends StatefulWidget { /// even if the content would fit in less. final bool sixWeekMonthsEnforced; + /// When set to true, `TableCalendar` will fill available height. + final bool shouldFillViewport; + /// Used for setting the height of `TableCalendar`'s rows. final double rowHeight; @@ -162,6 +165,7 @@ class TableCalendar extends StatefulWidget { this.daysOfWeekVisible = true, this.pageJumpingEnabled = false, this.sixWeekMonthsEnforced = false, + this.shouldFillViewport = false, this.rowHeight = 52.0, this.daysOfWeekHeight = 16.0, this.startingDayOfWeek = StartingDayOfWeek.sunday, @@ -266,75 +270,78 @@ class _TableCalendarState extends State> { ); }, ), - TableCalendarLite( - onCalendarCreated: (pageController) { - _pageController = pageController; - widget.onCalendarCreated?.call(pageController); - }, - focusedDay: _focusedDay.value, - calendarFormat: widget.calendarFormat, - availableGestures: widget.availableGestures, - firstDay: widget.firstDay, - lastDay: widget.lastDay, - startingDayOfWeek: widget.startingDayOfWeek, - dowDecoration: widget.daysOfWeekStyle.decoration, - rowDecoration: widget.calendarStyle.rowDecoration, - dowVisible: widget.daysOfWeekVisible, - dowHeight: widget.daysOfWeekHeight, - rowHeight: widget.rowHeight, - availableCalendarFormats: widget.availableCalendarFormats, - simpleSwipeConfig: widget.simpleSwipeConfig, - dayHitTestBehavior: widget.dayHitTestBehavior, - enabledDayPredicate: widget.enabledDayPredicate, - rangeSelectionMode: widget.rangeSelectionMode, - sixWeekMonthsEnforced: widget.sixWeekMonthsEnforced, - pageJumpingEnabled: widget.pageJumpingEnabled, - onDisabledDayTapped: widget.onDisabledDayTapped, - onDisabledDayLongPressed: widget.onDisabledDayLongPressed, - onDaySelected: (selectedDay, focusedDay) { - _focusedDay.value = focusedDay; - widget.onDaySelected?.call(selectedDay, focusedDay); - }, - onRangeSelected: widget.onRangeSelected != null - ? (start, end, focusedDay) { - _focusedDay.value = focusedDay; - widget.onRangeSelected(start, end, focusedDay); - } - : null, - onFormatChanged: (format) { - widget.onFormatChanged?.call(format); - }, - onPageChanged: (focusedDay) { - _focusedDay.value = focusedDay; - widget.onPageChanged?.call(focusedDay); - }, - dowBuilder: (BuildContext context, DateTime day) { - Widget dowCell = - widget.calendarBuilders.dowBuilder?.call(context, day); - - if (dowCell == null) { - final weekdayString = widget.daysOfWeekStyle.dowTextFormatter - ?.call(day, widget.locale) ?? - DateFormat.E(widget.locale).format(day); - - final isWeekend = - _isWeekend(day, weekendDays: widget.weekendDays); - - dowCell = Center( - child: Text( - weekdayString, - style: isWeekend - ? widget.daysOfWeekStyle.weekendStyle - : widget.daysOfWeekStyle.weekdayStyle, - ), - ); - } - - return dowCell; - }, - dayBuilder: (context, day, focusedMonth) { - return _buildCell(day, focusedMonth); - }, + Flexible( + flex: widget.shouldFillViewport ? 1 : 0, + child: TableCalendarLite( + onCalendarCreated: (pageController) { + _pageController = pageController; + widget.onCalendarCreated?.call(pageController); + }, + focusedDay: _focusedDay.value, + calendarFormat: widget.calendarFormat, + availableGestures: widget.availableGestures, + firstDay: widget.firstDay, + lastDay: widget.lastDay, + startingDayOfWeek: widget.startingDayOfWeek, + dowDecoration: widget.daysOfWeekStyle.decoration, + rowDecoration: widget.calendarStyle.rowDecoration, + dowVisible: widget.daysOfWeekVisible, + dowHeight: widget.daysOfWeekHeight, + rowHeight: widget.rowHeight, + availableCalendarFormats: widget.availableCalendarFormats, + simpleSwipeConfig: widget.simpleSwipeConfig, + dayHitTestBehavior: widget.dayHitTestBehavior, + enabledDayPredicate: widget.enabledDayPredicate, + rangeSelectionMode: widget.rangeSelectionMode, + sixWeekMonthsEnforced: widget.sixWeekMonthsEnforced, + pageJumpingEnabled: widget.pageJumpingEnabled, + onDisabledDayTapped: widget.onDisabledDayTapped, + onDisabledDayLongPressed: widget.onDisabledDayLongPressed, + onDaySelected: (selectedDay, focusedDay) { + _focusedDay.value = focusedDay; + widget.onDaySelected?.call(selectedDay, focusedDay); + }, + onRangeSelected: widget.onRangeSelected != null + ? (start, end, focusedDay) { + _focusedDay.value = focusedDay; + widget.onRangeSelected(start, end, focusedDay); + } + : null, + onFormatChanged: (format) { + widget.onFormatChanged?.call(format); + }, + onPageChanged: (focusedDay) { + _focusedDay.value = focusedDay; + widget.onPageChanged?.call(focusedDay); + }, + dowBuilder: (BuildContext context, DateTime day) { + Widget dowCell = + widget.calendarBuilders.dowBuilder?.call(context, day); + + if (dowCell == null) { + final weekdayString = widget.daysOfWeekStyle.dowTextFormatter + ?.call(day, widget.locale) ?? + DateFormat.E(widget.locale).format(day); + + final isWeekend = + _isWeekend(day, weekendDays: widget.weekendDays); + + dowCell = Center( + child: Text( + weekdayString, + style: isWeekend + ? widget.daysOfWeekStyle.weekendStyle + : widget.daysOfWeekStyle.weekdayStyle, + ), + ); + } + + return dowCell; + }, + dayBuilder: (context, day, focusedMonth) { + return _buildCell(day, focusedMonth); + }, + ), ), ], ); diff --git a/lib/src/table_calendar_base.dart b/lib/src/table_calendar_base.dart index d1f92a24..a3fa1c8d 100644 --- a/lib/src/table_calendar_base.dart +++ b/lib/src/table_calendar_base.dart @@ -145,6 +145,11 @@ class _TableCalendarBaseState extends State _pageHeight.value = _getPageHeight(rowCount); } + if (widget.dowVisible != oldWidget.dowVisible) { + final rowCount = _getRowCount(widget.calendarFormat, _focusedDay); + _pageHeight.value = _getPageHeight(rowCount); + } + if (widget.startingDayOfWeek != oldWidget.startingDayOfWeek) { final rowCount = _getRowCount(widget.calendarFormat, _focusedDay); _pageHeight.value = _getPageHeight(rowCount); @@ -194,72 +199,73 @@ class _TableCalendarBaseState extends State @override Widget build(BuildContext context) { - return SimpleGestureDetector( - onVerticalSwipe: _canScrollVertically ? widget.onVerticalSwipe : null, - swipeConfig: widget.simpleSwipeConfig, - child: ValueListenableBuilder( - valueListenable: _pageHeight, - builder: (context, value, child) { - return AnimatedSize( - duration: const Duration(milliseconds: 200), - vsync: this, - alignment: Alignment.topCenter, - child: SizedBox( - height: _pageHeight.value, - child: child, + return LayoutBuilder( + builder: (context, constraints) { + return SimpleGestureDetector( + onVerticalSwipe: _canScrollVertically ? widget.onVerticalSwipe : null, + swipeConfig: widget.simpleSwipeConfig, + child: ValueListenableBuilder( + valueListenable: _pageHeight, + builder: (context, value, child) { + final height = + constraints.hasBoundedHeight ? constraints.maxHeight : value; + + return AnimatedSize( + duration: const Duration(milliseconds: 200), + vsync: this, + alignment: Alignment.topCenter, + child: SizedBox( + height: height, + child: child, + ), + ); + }, + child: _CalendarCore( + constraints: constraints, + pageController: _pageController, + scrollPhysics: _canScrollHorizontally + ? PageScrollPhysics() + : NeverScrollableScrollPhysics(), + firstDay: widget.firstDay, + lastDay: widget.lastDay, + startingDayOfWeek: widget.startingDayOfWeek, + calendarFormat: widget.calendarFormat, + previousIndex: _previousIndex, + focusedDay: _focusedDay, + sixWeekMonthsEnforced: widget.sixWeekMonthsEnforced, + dowVisible: widget.dowVisible, + dowHeight: widget.dowHeight, + rowHeight: widget.rowHeight, + dowDecoration: widget.dowDecoration, + rowDecoration: widget.rowDecoration, + onPageChanged: (index, focusedMonth) { + if (!isSameDay(_focusedDay, focusedMonth)) { + _focusedDay = focusedMonth; + } + + if (!_pageCallbackDisabled) { + if (widget.calendarFormat == CalendarFormat.month && + !widget.sixWeekMonthsEnforced && + !constraints.hasBoundedHeight) { + final rowCount = _getRowCount( + widget.calendarFormat, + focusedMonth, + ); + _pageHeight.value = _getPageHeight(rowCount); + } + + _previousIndex = index; + widget.onPageChanged?.call(focusedMonth); + } + + _pageCallbackDisabled = false; + }, + dowBuilder: widget.dowBuilder, + dayBuilder: widget.dayBuilder, ), - ); - }, - child: _CalendarCore( - pageController: _pageController, - scrollPhysics: _canScrollHorizontally - ? PageScrollPhysics() - : NeverScrollableScrollPhysics(), - firstDay: widget.firstDay, - lastDay: widget.lastDay, - startingDayOfWeek: widget.startingDayOfWeek, - calendarFormat: widget.calendarFormat, - previousIndex: _previousIndex, - focusedDay: _focusedDay, - sixWeekMonthsEnforced: widget.sixWeekMonthsEnforced, - dowVisible: widget.dowVisible, - dowDecoration: widget.dowDecoration, - rowDecoration: widget.rowDecoration, - onPageChanged: (index, focusedMonth) { - if (!isSameDay(_focusedDay, focusedMonth)) { - _focusedDay = focusedMonth; - } - - if (!_pageCallbackDisabled) { - if (widget.calendarFormat == CalendarFormat.month && - !widget.sixWeekMonthsEnforced) { - final rowCount = _getRowCount( - widget.calendarFormat, - focusedMonth, - ); - _pageHeight.value = _getPageHeight(rowCount); - } - - _previousIndex = index; - widget.onPageChanged?.call(focusedMonth); - } - - _pageCallbackDisabled = false; - }, - dowBuilder: (context, day) { - return SizedBox( - height: widget.dowHeight, - child: widget.dowBuilder(context, day), - ); - }, - dayBuilder: (context, day, focusedMonth) { - return SizedBox( - height: widget.rowHeight, - child: widget.dayBuilder(context, day, focusedMonth), - ); - }, - ), - ), + ), + ); + }, ); } diff --git a/lib/src/widgets/calendar_core.dart b/lib/src/widgets/calendar_core.dart index ed30fd67..241a6666 100644 --- a/lib/src/widgets/calendar_core.dart +++ b/lib/src/widgets/calendar_core.dart @@ -17,6 +17,9 @@ class _CalendarCore extends StatelessWidget { final bool dowVisible; final Decoration dowDecoration; final Decoration rowDecoration; + final double dowHeight; + final double rowHeight; + final BoxConstraints constraints; final int previousIndex; final StartingDayOfWeek startingDayOfWeek; final PageController pageController; @@ -30,6 +33,9 @@ class _CalendarCore extends StatelessWidget { @required this.onPageChanged, @required this.firstDay, @required this.lastDay, + @required this.constraints, + this.dowHeight, + this.rowHeight, this.startingDayOfWeek = StartingDayOfWeek.sunday, this.calendarFormat = CalendarFormat.month, this.pageController, @@ -56,12 +62,23 @@ class _CalendarCore extends StatelessWidget { final visibleRange = _getVisibleRange(calendarFormat, baseDay); final visibleDays = _daysInRange(visibleRange.start, visibleRange.end); + final actualDowHeight = dowVisible ? dowHeight : 0.0; + final constrainedRowHeight = constraints.hasBoundedHeight + ? (constraints.maxHeight - actualDowHeight) / + _getRowCount(calendarFormat, baseDay) + : null; + return _CalendarPage( visibleDays: visibleDays, dowVisible: dowVisible, dowDecoration: dowDecoration, rowDecoration: rowDecoration, - dowBuilder: dowBuilder, + dowBuilder: (context, day) { + return SizedBox( + height: dowHeight, + child: dowBuilder(context, day), + ); + }, dayBuilder: (context, day) { DateTime baseDay; if (focusedDay == null || previousIndex == null) { @@ -70,7 +87,10 @@ class _CalendarCore extends StatelessWidget { baseDay = _getFocusedDay(calendarFormat, index); } - return dayBuilder(context, day, baseDay); + return SizedBox( + height: constrainedRowHeight ?? rowHeight, + child: dayBuilder(context, day, baseDay), + ); }, ); }, @@ -241,6 +261,26 @@ class _CalendarCore extends StatelessWidget { return date.subtract(const Duration(days: 1)); } + int _getRowCount(CalendarFormat format, DateTime focusedDay) { + if (format == CalendarFormat.twoWeeks) { + return 2; + } else if (format == CalendarFormat.week) { + return 1; + } else if (sixWeekMonthsEnforced) { + return 6; + } + + final first = _firstDayOfMonth(focusedDay); + final daysBefore = _getDaysBefore(first); + final firstToDisplay = first.subtract(Duration(days: daysBefore)); + + final last = _lastDayOfMonth(focusedDay); + final daysAfter = _getDaysAfter(last); + final lastToDisplay = last.add(Duration(days: daysAfter)); + + return (lastToDisplay.difference(firstToDisplay).inDays + 1) ~/ 7; + } + int _getDaysBefore(DateTime firstDay) { return (firstDay.weekday + 7 - _getWeekdayNumber(startingDayOfWeek)) % 7; } From 9fddbbb609d36ef52ea30f4de27c3a5753529221 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Wed, 3 Feb 2021 13:52:34 +0100 Subject: [PATCH 042/164] WeekendBuilder removed --- lib/src/widgets/cell_content.dart | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/lib/src/widgets/cell_content.dart b/lib/src/widgets/cell_content.dart index 1518b4b2..a314d0bb 100644 --- a/lib/src/widgets/cell_content.dart +++ b/lib/src/widgets/cell_content.dart @@ -124,23 +124,21 @@ class _CellContent extends StatelessWidget { alignment: Alignment.center, child: Text(text, style: calendarStyle.outsideTextStyle), ); - } else if (isWeekend) { - cell = calendarBuilders.weekendBuilder?.call(context, day, focusedDay) ?? - AnimatedContainer( - duration: duration, - margin: margin, - decoration: calendarStyle.weekendDecoration, - alignment: Alignment.center, - child: Text(text, style: calendarStyle.weekendTextStyle), - ); } else { cell = calendarBuilders.defaultBuilder?.call(context, day, focusedDay) ?? AnimatedContainer( duration: duration, margin: margin, - decoration: calendarStyle.defaultDecoration, + decoration: isWeekend + ? calendarStyle.weekendDecoration + : calendarStyle.defaultDecoration, alignment: Alignment.center, - child: Text(text, style: calendarStyle.defaultTextStyle), + child: Text( + text, + style: isWeekend + ? calendarStyle.weekendTextStyle + : calendarStyle.defaultTextStyle, + ), ); } From 30f303fca0b9b1be08c2a931fd01e1bdb4b2ee35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Wed, 3 Feb 2021 13:55:08 +0100 Subject: [PATCH 043/164] Added rangeHighlightBuilder --- lib/src/customization/calendar_builders.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/src/customization/calendar_builders.dart b/lib/src/customization/calendar_builders.dart index b59db200..93611c93 100644 --- a/lib/src/customization/calendar_builders.dart +++ b/lib/src/customization/calendar_builders.dart @@ -10,6 +10,9 @@ typedef SingleMarkerBuilder = Widget Function( typedef MarkerBuilder = Widget Function( BuildContext context, DateTime date, List events); +typedef HighlightBuilder = Widget Function( + BuildContext context, DateTime date, bool isWithinRange); + class CalendarBuilders { final FocusedDayBuilder prioritizedBuilder; final FocusedDayBuilder todayBuilder; @@ -20,9 +23,10 @@ class CalendarBuilders { final FocusedDayBuilder outsideBuilder; final FocusedDayBuilder disabledBuilder; final FocusedDayBuilder holidayBuilder; - final FocusedDayBuilder weekendBuilder; final FocusedDayBuilder defaultBuilder; + final HighlightBuilder rangeHighlightBuilder; + final SingleMarkerBuilder singleMarkerBuilder; final MarkerBuilder markerBuilder; @@ -38,8 +42,8 @@ class CalendarBuilders { this.outsideBuilder, this.disabledBuilder, this.holidayBuilder, - this.weekendBuilder, this.defaultBuilder, + this.rangeHighlightBuilder, this.singleMarkerBuilder, this.markerBuilder, this.dowBuilder, From 356440c7be104c9d29952ca9a8ce23aea6e557c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Wed, 3 Feb 2021 13:55:57 +0100 Subject: [PATCH 044/164] Improved marker and highlight positioning --- lib/src/customization/calendar_style.dart | 43 +++-- lib/src/table_calendar.dart | 207 ++++++++++++---------- 2 files changed, 144 insertions(+), 106 deletions(-) diff --git a/lib/src/customization/calendar_style.dart b/lib/src/customization/calendar_style.dart index e34f22a5..190b5073 100644 --- a/lib/src/customization/calendar_style.dart +++ b/lib/src/customization/calendar_style.dart @@ -3,29 +3,26 @@ part of table_calendar; -class PositionedOffset { - final double top; - final double bottom; - final double start; - final double end; - - const PositionedOffset({this.top, this.bottom, this.start, this.end}); -} - class CalendarStyle { /// Maximum amount of event markers to be displayed. final int markersMaxAmount; - final bool isTodayHighlighted; final AlignmentGeometry markersAlignment; + final bool markersAutoAligned; final bool canMarkersOverflow; - final bool outsideDaysVisible; + final double markerSize; + final double markerSizeScale; + final double markerPosition; + final PositionedOffset markersOffset; + final Decoration markerDecoration; + final EdgeInsets markerMargin; final EdgeInsets cellMargin; - final PositionedOffset rangeFillOffset; - final PositionedOffset markersOffset; + final double rangeFillScale; final Color rangeFillColor; - final Decoration markerDecoration; + + final bool outsideDaysVisible; + final bool isTodayHighlighted; final TextStyle todayTextStyle; final Decoration todayDecoration; @@ -63,11 +60,16 @@ class CalendarStyle { this.isTodayHighlighted = true, this.canMarkersOverflow = true, this.outsideDaysVisible = true, + this.markersAutoAligned = true, + this.markerSize, + this.markerSizeScale = 0.2, + this.markerPosition = 0.7, + this.rangeFillScale = 1.0, + this.markerMargin = const EdgeInsets.symmetric(horizontal: 0.3), this.markersAlignment = Alignment.bottomCenter, this.markersMaxAmount = 4, this.cellMargin = const EdgeInsets.all(6.0), - this.rangeFillOffset = const PositionedOffset(top: 6.0, bottom: 6.0), - this.markersOffset = const PositionedOffset(bottom: 5.0), + this.markersOffset = const PositionedOffset(), this.rangeFillColor = const Color(0xFFBBDDFF), this.markerDecoration = const BoxDecoration( color: const Color(0xFF263238), shape: BoxShape.circle), @@ -107,3 +109,12 @@ class CalendarStyle { this.rowDecoration = const BoxDecoration(), }); } + +class PositionedOffset { + final double top; + final double bottom; + final double start; + final double end; + + const PositionedOffset({this.top, this.bottom, this.start, this.end}); +} diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index 3943ca91..70747f14 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -356,103 +356,130 @@ class _TableCalendarState extends State> { return Container(); } - final children = []; - - final isWithinRange = widget.rangeStartDay != null && - widget.rangeEndDay != null && - _isWithinRange(date, widget.rangeStartDay, widget.rangeEndDay); - - final isRangeStart = isSameDay(date, widget.rangeStartDay); - final isRangeEnd = isSameDay(date, widget.rangeEndDay); - - final startOffset = - widget.calendarStyle.rangeFillOffset.start ?? widget.rowHeight / 2; - - final endOffset = - widget.calendarStyle.rangeFillOffset.end ?? widget.rowHeight / 2; - - final rangeColor = isWithinRange - ? widget.calendarStyle.rangeFillColor - : Colors.transparent; - - children.add( - PositionedDirectional( - top: widget.calendarStyle.rangeFillOffset.top, - bottom: widget.calendarStyle.rangeFillOffset.bottom, - start: isRangeStart ? startOffset : 0.0, - end: isRangeEnd ? endOffset : 0.0, - child: Container(color: rangeColor), - ), - ); - - final isToday = isSameDay(date, DateTime.now()); - final isDisabled = _isDayDisabled(date); - final isWeekend = _isWeekend(date, weekendDays: widget.weekendDays); - - Widget content = _CellContent( - day: date, - focusedDay: focusedDay, - calendarStyle: widget.calendarStyle, - calendarBuilders: widget.calendarBuilders, - isTodayHighlighted: widget.calendarStyle.isTodayHighlighted, - isToday: isToday, - isSelected: widget.selectedDayPredicate?.call(date) ?? false, - isRangeStart: isRangeStart, - isRangeEnd: isRangeEnd, - isWithinRange: isWithinRange, - isOutside: isOutside, - isDisabled: isDisabled, - isWeekend: isWeekend, - isHoliday: widget.holidayPredicate?.call(date) ?? false, - ); - - children.add(content); - - if (!isDisabled) { - final events = widget.eventLoader?.call(date) ?? []; - Widget markerWidget = - widget.calendarBuilders.markerBuilder?.call(context, date, events); - - if (events.isNotEmpty && markerWidget == null) { - markerWidget = PositionedDirectional( - top: widget.calendarStyle.markersOffset.top, - bottom: widget.calendarStyle.markersOffset.bottom, - start: widget.calendarStyle.markersOffset.start, - end: widget.calendarStyle.markersOffset.end, - child: Row( - mainAxisSize: MainAxisSize.min, - children: events - .take(widget.calendarStyle.markersMaxAmount) - .map((event) => _buildSingleMarker(date, event)) - .toList(), - ), + return LayoutBuilder( + builder: (context, constraints) { + final shorterSide = constraints.maxHeight > constraints.maxWidth + ? constraints.maxWidth + : constraints.maxHeight; + + final children = []; + + final isWithinRange = widget.rangeStartDay != null && + widget.rangeEndDay != null && + _isWithinRange(date, widget.rangeStartDay, widget.rangeEndDay); + + final isRangeStart = isSameDay(date, widget.rangeStartDay); + final isRangeEnd = isSameDay(date, widget.rangeEndDay); + + Widget rangeHighlight = widget.calendarBuilders.rangeHighlightBuilder + ?.call(context, date, isWithinRange); + + if (rangeHighlight == null) { + if (isWithinRange) { + rangeHighlight = Center( + child: Container( + margin: EdgeInsetsDirectional.only( + start: isRangeStart ? constraints.maxWidth * 0.5 : 0.0, + end: isRangeEnd ? constraints.maxWidth * 0.5 : 0.0, + ), + height: + (shorterSide - widget.calendarStyle.cellMargin.vertical) * + widget.calendarStyle.rangeFillScale, + color: widget.calendarStyle.rangeFillColor, + ), + ); + } + } + + if (rangeHighlight != null) { + children.add(rangeHighlight); + } + + final isToday = isSameDay(date, DateTime.now()); + final isDisabled = _isDayDisabled(date); + final isWeekend = _isWeekend(date, weekendDays: widget.weekendDays); + + Widget content = _CellContent( + day: date, + focusedDay: focusedDay, + calendarStyle: widget.calendarStyle, + calendarBuilders: widget.calendarBuilders, + isTodayHighlighted: widget.calendarStyle.isTodayHighlighted, + isToday: isToday, + isSelected: widget.selectedDayPredicate?.call(date) ?? false, + isRangeStart: isRangeStart, + isRangeEnd: isRangeEnd, + isWithinRange: isWithinRange, + isOutside: isOutside, + isDisabled: isDisabled, + isWeekend: isWeekend, + isHoliday: widget.holidayPredicate?.call(date) ?? false, ); - } - - if (markerWidget != null) { - children.add(markerWidget); - } - } - if (children.length > 1) { - content = Stack( - alignment: widget.calendarStyle.markersAlignment, - children: children, - clipBehavior: - widget.calendarStyle.canMarkersOverflow ? Clip.none : Clip.hardEdge, - ); - } - - return content; + children.add(content); + + if (!isDisabled) { + final events = widget.eventLoader?.call(date) ?? []; + Widget markerWidget = widget.calendarBuilders.markerBuilder + ?.call(context, date, events); + + if (events.isNotEmpty && markerWidget == null) { + final center = constraints.maxHeight / 2; + + final markerSize = widget.calendarStyle.markerSize ?? + (shorterSide - widget.calendarStyle.cellMargin.vertical) * + widget.calendarStyle.markerSizeScale; + + final markerAutoAlignmentTop = center + + (shorterSide - widget.calendarStyle.cellMargin.vertical) / 2 - + (markerSize * widget.calendarStyle.markerPosition); + + markerWidget = PositionedDirectional( + top: widget.calendarStyle.markersAutoAligned + ? markerAutoAlignmentTop + : widget.calendarStyle.markersOffset.top, + bottom: widget.calendarStyle.markersAutoAligned + ? null + : widget.calendarStyle.markersOffset.bottom, + start: widget.calendarStyle.markersAutoAligned + ? null + : widget.calendarStyle.markersOffset.start, + end: widget.calendarStyle.markersAutoAligned + ? null + : widget.calendarStyle.markersOffset.end, + child: Row( + mainAxisSize: MainAxisSize.min, + children: events + .take(widget.calendarStyle.markersMaxAmount) + .map((event) => _buildSingleMarker(date, event, markerSize)) + .toList(), + ), + ); + } + + if (markerWidget != null) { + children.add(markerWidget); + } + } + + return Stack( + alignment: widget.calendarStyle.markersAlignment, + children: children, + clipBehavior: widget.calendarStyle.canMarkersOverflow + ? Clip.none + : Clip.hardEdge, + ); + }, + ); } - Widget _buildSingleMarker(DateTime date, T event) { + Widget _buildSingleMarker(DateTime date, T event, double markerSize) { return widget.calendarBuilders.singleMarkerBuilder ?.call(context, date, event) ?? Container( - width: 8.0, - height: 8.0, - margin: const EdgeInsets.symmetric(horizontal: 0.3), + width: markerSize, + height: markerSize, + margin: widget.calendarStyle.markerMargin, decoration: widget.calendarStyle.markerDecoration, ); } From f86573402c6ac73b27413118238025a471fbd51f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Wed, 3 Feb 2021 13:59:14 +0100 Subject: [PATCH 045/164] Rename rangeFill to rangeHighlight --- lib/src/customization/calendar_style.dart | 8 ++++---- lib/src/table_calendar.dart | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/src/customization/calendar_style.dart b/lib/src/customization/calendar_style.dart index 190b5073..6ef56bc8 100644 --- a/lib/src/customization/calendar_style.dart +++ b/lib/src/customization/calendar_style.dart @@ -18,8 +18,8 @@ class CalendarStyle { final EdgeInsets markerMargin; final EdgeInsets cellMargin; - final double rangeFillScale; - final Color rangeFillColor; + final double rangeHighlightScale; + final Color rangeHighlightColor; final bool outsideDaysVisible; final bool isTodayHighlighted; @@ -64,13 +64,13 @@ class CalendarStyle { this.markerSize, this.markerSizeScale = 0.2, this.markerPosition = 0.7, - this.rangeFillScale = 1.0, + this.rangeHighlightScale = 1.0, this.markerMargin = const EdgeInsets.symmetric(horizontal: 0.3), this.markersAlignment = Alignment.bottomCenter, this.markersMaxAmount = 4, this.cellMargin = const EdgeInsets.all(6.0), this.markersOffset = const PositionedOffset(), - this.rangeFillColor = const Color(0xFFBBDDFF), + this.rangeHighlightColor = const Color(0xFFBBDDFF), this.markerDecoration = const BoxDecoration( color: const Color(0xFF263238), shape: BoxShape.circle), this.todayTextStyle = diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index 70747f14..c2dca3f9 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -384,8 +384,8 @@ class _TableCalendarState extends State> { ), height: (shorterSide - widget.calendarStyle.cellMargin.vertical) * - widget.calendarStyle.rangeFillScale, - color: widget.calendarStyle.rangeFillColor, + widget.calendarStyle.rangeHighlightScale, + color: widget.calendarStyle.rangeHighlightColor, ), ); } From 8f6f47c6006105c8f2e53c615847f136c2d0abb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Wed, 3 Feb 2021 14:19:01 +0100 Subject: [PATCH 046/164] Rename markerPosition to markersAnchor --- lib/src/customization/calendar_style.dart | 4 ++-- lib/src/table_calendar.dart | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/src/customization/calendar_style.dart b/lib/src/customization/calendar_style.dart index 6ef56bc8..35880b50 100644 --- a/lib/src/customization/calendar_style.dart +++ b/lib/src/customization/calendar_style.dart @@ -11,7 +11,7 @@ class CalendarStyle { final bool canMarkersOverflow; final double markerSize; final double markerSizeScale; - final double markerPosition; + final double markersAnchor; final PositionedOffset markersOffset; final Decoration markerDecoration; @@ -63,7 +63,7 @@ class CalendarStyle { this.markersAutoAligned = true, this.markerSize, this.markerSizeScale = 0.2, - this.markerPosition = 0.7, + this.markersAnchor = 0.7, this.rangeHighlightScale = 1.0, this.markerMargin = const EdgeInsets.symmetric(horizontal: 0.3), this.markersAlignment = Alignment.bottomCenter, diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index c2dca3f9..c1109da6 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -432,7 +432,7 @@ class _TableCalendarState extends State> { final markerAutoAlignmentTop = center + (shorterSide - widget.calendarStyle.cellMargin.vertical) / 2 - - (markerSize * widget.calendarStyle.markerPosition); + (markerSize * widget.calendarStyle.markersAnchor); markerWidget = PositionedDirectional( top: widget.calendarStyle.markersAutoAligned From 8d2472b2806e9917310e690743d072d648e42952 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Thu, 4 Feb 2021 17:16:55 +0100 Subject: [PATCH 047/164] Update examples --- example/lib/pages/table_multi_example.dart | 2 +- example/lib/utils.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/example/lib/pages/table_multi_example.dart b/example/lib/pages/table_multi_example.dart index 903569d6..21f0c1de 100644 --- a/example/lib/pages/table_multi_example.dart +++ b/example/lib/pages/table_multi_example.dart @@ -23,7 +23,7 @@ class _TableMultiExampleState extends State { @override void initState() { super.initState(); - _selectedEvents = ValueNotifier(_getEventsForDay(_focusedDay)); + _selectedEvents = ValueNotifier([]); // Using a `LinkedHashSet` is recommended due to equality comparison override _selectedDays = LinkedHashSet( diff --git a/example/lib/utils.dart b/example/lib/utils.dart index cdb23c5c..4027d5db 100644 --- a/example/lib/utils.dart +++ b/example/lib/utils.dart @@ -28,7 +28,7 @@ final _kEventSource = Map.fromIterable(List.generate(50, (index) => index), value: (item) => List.generate( item % 4 + 1, (index) => Event('Event $item | ${index + 1}'))) ..addAll({ - DateTime.now().toUtc(): [ + DateTime.now(): [ Event('Today\'s Event 1'), Event('Today\'s Event 2'), ], From c6bc17cc2c501f71dbb72ff05cb0b8cac3dd1a89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Thu, 4 Feb 2021 17:27:20 +0100 Subject: [PATCH 048/164] Update docs --- lib/src/customization/days_of_week_style.dart | 1 + lib/src/customization/header_style.dart | 5 +++-- lib/src/table_calendar.dart | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/src/customization/days_of_week_style.dart b/lib/src/customization/days_of_week_style.dart index d96be3b0..272710a3 100644 --- a/lib/src/customization/days_of_week_style.dart +++ b/lib/src/customization/days_of_week_style.dart @@ -24,6 +24,7 @@ class DaysOfWeekStyle { /// Style for weekend days on the top of calendar. final TextStyle weekendStyle; + /// Creates `DaysOfWeekStyle` used by `TableCalendar` widget. const DaysOfWeekStyle({ this.dowTextFormatter, this.decoration = const BoxDecoration(), diff --git a/lib/src/customization/header_style.dart b/lib/src/customization/header_style.dart index ff71c86a..e10144c4 100644 --- a/lib/src/customization/header_style.dart +++ b/lib/src/customization/header_style.dart @@ -60,11 +60,11 @@ class HeaderStyle { final EdgeInsets rightChevronMargin; /// Icon used for left chevron. - /// Defaults to `Icons.chevron_left`. + /// Defaults to `Icon(Icons.chevron_left)`. final Icon leftChevronIcon; /// Icon used for right chevron. - /// Defaults to `Icons.chevron_right`. + /// Defaults to `Icon(Icons.chevron_right)`. final Icon rightChevronIcon; /// Determines left chevron's visibility. @@ -79,6 +79,7 @@ class HeaderStyle { /// Defaults to empty BoxDecoration. final BoxDecoration decoration; + /// Creates a `HeaderStyle` used by `TableCalendar` widget. const HeaderStyle({ this.titleCentered = false, this.formatButtonVisible = true, diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index c1109da6..c19804a1 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -3,7 +3,7 @@ part of table_calendar; -/// Highly customizable, feature-packed Flutter Calendar with gestures, animations and multiple formats. +/// Highly customizable, feature-packed Flutter calendar with gestures, animations and multiple formats. class TableCalendar extends StatefulWidget { /// Locale to format `TableCalendar` dates with, for example: `'en_US'`. /// @@ -146,6 +146,7 @@ class TableCalendar extends StatefulWidget { /// Called when the calendar is created. Exposes its PageController. final void Function(PageController pageController) onCalendarCreated; + /// Creates a horizontal calendar widget. TableCalendar({ Key key, this.locale, From b65dffbd3c95839f1ecfaa5ee57bc259f1905487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Thu, 4 Feb 2021 18:21:44 +0100 Subject: [PATCH 049/164] Add CalendarBuilders docs --- lib/src/customization/calendar_builders.dart | 40 +++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/lib/src/customization/calendar_builders.dart b/lib/src/customization/calendar_builders.dart index 93611c93..7b5e113c 100644 --- a/lib/src/customization/calendar_builders.dart +++ b/lib/src/customization/calendar_builders.dart @@ -3,35 +3,73 @@ part of table_calendar; -/// Builder signature for a single event marker. Contains `date` and a single `event` associated with that `date`. +/// Builder signature for a single event marker. Contains a `date` and a single `event` associated with that `date`. typedef SingleMarkerBuilder = Widget Function( BuildContext context, DateTime date, T event); +/// Builder signature for event markers. Contains a `date` and a list of `events` associated with that `date`. typedef MarkerBuilder = Widget Function( BuildContext context, DateTime date, List events); +/// Builder signature for background highlight of range selection. +/// Contains a `date` and a value determining if that `date` falls within the selected range. typedef HighlightBuilder = Widget Function( BuildContext context, DateTime date, bool isWithinRange); +/// Class containing all custom builders for `TableCalendar`. class CalendarBuilders { + /// Custom builder for day cells, with a priority over any other builder. final FocusedDayBuilder prioritizedBuilder; + + /// Custom builder for a day cell, of which the `date` is equal to `DateTime.now()`. final FocusedDayBuilder todayBuilder; + + /// Custom builder for day cells that are currently marked as selected by `selectedDayPredicate`. final FocusedDayBuilder selectedBuilder; + + /// Custom builder for a day cell that is the start of current range selection. final FocusedDayBuilder rangeStartBuilder; + + /// Custom builder for a day cell that is the end of current range selection. final FocusedDayBuilder rangeEndBuilder; + + /// Custom builder for day cells that fall within the currently selected range. final FocusedDayBuilder withinRangeBuilder; + + /// Custom builder for day cells, of which the `date.month` is different than `focusedDay.month`. + /// This will affect day cells that do not match the currently focused month. final FocusedDayBuilder outsideBuilder; + + /// Custom builder for day cells that have been disabled. + /// + /// This refers to dates disabled by returning false in `enabledDayPredicate`, + /// as well as dates that are outside of the bounds set up by `firstDay` and `lastDay`. final FocusedDayBuilder disabledBuilder; + + /// Custom builder for day cells that are marked as holidays by `holidayPredicate`. final FocusedDayBuilder holidayBuilder; + + /// Custom builder for day cells that do not match any other builder. final FocusedDayBuilder defaultBuilder; + /// Custom builder for background highlight of range selection. + /// If `isWithinRange` is true, then `date` is within the selected range. final HighlightBuilder rangeHighlightBuilder; + /// Custom builder for a single event marker. Each of those will be displayed in a `Row` above of the day cell. + /// You can adjust markers' position with `CalendarStyle` properties. + /// + /// If `singleMarkerBuilder` is not specified, a default event marker will be displayed (customizable with `CalendarStyle`). final SingleMarkerBuilder singleMarkerBuilder; + + /// Custom builder for event markers. Use to provide your own marker UI for each day cell. + /// Using `markerBuilder` will override `singleMarkerBuilder` and default event markers. final MarkerBuilder markerBuilder; + /// Custom builder for days of the week labels (Mon, Tue, Wed, etc.). final DayBuilder dowBuilder; + /// Creates `CalendarBuilders` for `TableCalendar` widget. const CalendarBuilders({ this.prioritizedBuilder, this.todayBuilder, From e63b09533582e8bbaf037215c62f9c6aeea83654 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Thu, 4 Feb 2021 20:53:10 +0100 Subject: [PATCH 050/164] Replace BoxDecoration with Decoration --- lib/src/customization/days_of_week_style.dart | 6 +++--- lib/src/customization/header_style.dart | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/src/customization/days_of_week_style.dart b/lib/src/customization/days_of_week_style.dart index 272710a3..f0ffc544 100644 --- a/lib/src/customization/days_of_week_style.dart +++ b/lib/src/customization/days_of_week_style.dart @@ -15,8 +15,8 @@ class DaysOfWeekStyle { /// ``` final TextFormatter dowTextFormatter; - /// BoxDecoration for the top row of the table - final BoxDecoration decoration; + /// Decoration for the top row of the table + final Decoration decoration; /// Style for weekdays on the top of calendar. final TextStyle weekdayStyle; @@ -24,7 +24,7 @@ class DaysOfWeekStyle { /// Style for weekend days on the top of calendar. final TextStyle weekendStyle; - /// Creates `DaysOfWeekStyle` used by `TableCalendar` widget. + /// Creates a `DaysOfWeekStyle` used by `TableCalendar` widget. const DaysOfWeekStyle({ this.dowTextFormatter, this.decoration = const BoxDecoration(), diff --git a/lib/src/customization/header_style.dart b/lib/src/customization/header_style.dart index e10144c4..5604c2eb 100644 --- a/lib/src/customization/header_style.dart +++ b/lib/src/customization/header_style.dart @@ -76,8 +76,7 @@ class HeaderStyle { final bool rightChevronVisible; /// Header decoration, used to draw border or shadow or change color of the header - /// Defaults to empty BoxDecoration. - final BoxDecoration decoration; + final Decoration decoration; /// Creates a `HeaderStyle` used by `TableCalendar` widget. const HeaderStyle({ From 8429b306045052f13be872b353f0bbeaee05a26b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Thu, 4 Feb 2021 22:08:06 +0100 Subject: [PATCH 051/164] Update docs --- lib/src/customization/calendar_builders.dart | 2 +- lib/src/customization/calendar_style.dart | 146 ++++++++++++++++--- lib/src/table_calendar.dart | 4 +- 3 files changed, 130 insertions(+), 22 deletions(-) diff --git a/lib/src/customization/calendar_builders.dart b/lib/src/customization/calendar_builders.dart index 7b5e113c..421bfec5 100644 --- a/lib/src/customization/calendar_builders.dart +++ b/lib/src/customization/calendar_builders.dart @@ -21,7 +21,7 @@ class CalendarBuilders { /// Custom builder for day cells, with a priority over any other builder. final FocusedDayBuilder prioritizedBuilder; - /// Custom builder for a day cell, of which the `date` is equal to `DateTime.now()`. + /// Custom builder for a day cell that matches `DateTime.now()`. final FocusedDayBuilder todayBuilder; /// Custom builder for day cells that are currently marked as selected by `selectedDayPredicate`. diff --git a/lib/src/customization/calendar_style.dart b/lib/src/customization/calendar_style.dart index 35880b50..1d2ce36c 100644 --- a/lib/src/customization/calendar_style.dart +++ b/lib/src/customization/calendar_style.dart @@ -3,59 +3,140 @@ part of table_calendar; +/// Class containing styling and configuration for `TableCalendar`'s content. class CalendarStyle { - /// Maximum amount of event markers to be displayed. - final int markersMaxAmount; - final AlignmentGeometry markersAlignment; - final bool markersAutoAligned; + /// Maximum amount of single event marker dots to be displayed. + final int markersMaxCount; + + /// Specifies if event markers rendered for a day cell can overflow cell's boundaries. + /// * `true` - Event markers will be drawn over the cell boundaries + /// * `false` - Event markers will be clipped if they are too big final bool canMarkersOverflow; + + /// Determines if single event marker dots should be aligned automatically with `markersAnchor`. + /// If `false`, `markersOffset` will be used instead. + final bool markersAutoAligned; + + /// Specifies the anchor point of single event markers if `markersAutoAligned` is `true`. + /// A value of `0.5` will center the markers at the bottom edge of day cell's decoration. + /// + /// Includes `cellMargin` for calculations. + final double markersAnchor; + + /// The size of single event marker dot. + /// + /// By default `markerSizeScale` is used. To use `markerSize` instead, simply provide a non-null value. final double markerSize; + + /// Proportion of single event marker dot size in relation to day cell size. + /// + /// Includes `cellMargin` for calculations. final double markerSizeScale; - final double markersAnchor; + + /// `PositionedOffset` for event markers. Allows to specify `top`, `bottom`, `start` and `end`. final PositionedOffset markersOffset; + + /// General `Alignment` for event markers. + /// Will have no effect on markers if `markersAutoAligned` or `markersOffset` is used. + final AlignmentGeometry markersAlignment; + + /// Decoration of single event markers. Affects each marker dot. final Decoration markerDecoration; + /// Margin of single event markers. Affects each marker dot. final EdgeInsets markerMargin; + + /// Margin of each individual day cell. final EdgeInsets cellMargin; + /// Proportion of range selection highlight size in relation to day cell size. + /// + /// Includes `cellMargin` for calculations. final double rangeHighlightScale; + + /// Color of range selection highlight. final Color rangeHighlightColor; + /// Determines if day cells that do not match the currently focused month should be visible. + /// + /// Affects only `CalendarFormat.month`. final bool outsideDaysVisible; + + /// Determines if a day cell that matches `DateTime.now()` should be highlighted. final bool isTodayHighlighted; + /// TextStyle for a day cell that matches `DateTime.now()`. final TextStyle todayTextStyle; + + /// Decoration for a day cell that matches `DateTime.now()`. final Decoration todayDecoration; + /// TextStyle for day cells that are currently marked as selected by `selectedDayPredicate`. final TextStyle selectedTextStyle; + + /// Decoration for day cells that are currently marked as selected by `selectedDayPredicate`. final Decoration selectedDecoration; + /// TextStyle for a day cell that is the start of current range selection. final TextStyle rangeStartTextStyle; + + /// Decoration for a day cell that is the start of current range selection. final Decoration rangeStartDecoration; + /// TextStyle for a day cell that is the end of current range selection. final TextStyle rangeEndTextStyle; + + /// Decoration for a day cell that is the end of current range selection. final Decoration rangeEndDecoration; + /// TextStyle for day cells that fall within the currently selected range. final TextStyle withinRangeTextStyle; + + /// Decoration for day cells that fall within the currently selected range. final Decoration withinRangeDecoration; + /// TextStyle for day cells, of which the `date.month` is different than `focusedDay.month`. + /// This will affect day cells that do not match the currently focused month. final TextStyle outsideTextStyle; + + /// Decoration for day cells, of which the `date.month` is different than `focusedDay.month`. + /// This will affect day cells that do not match the currently focused month. final Decoration outsideDecoration; + /// TextStyle for day cells that have been disabled. + /// + /// This refers to dates disabled by returning false in `enabledDayPredicate`, + /// as well as dates that are outside of the bounds set up by `firstDay` and `lastDay`. final TextStyle disabledTextStyle; + + /// Decoration for day cells that have been disabled. + /// + /// This refers to dates disabled by returning false in `enabledDayPredicate`, + /// as well as dates that are outside of the bounds set up by `firstDay` and `lastDay`. final Decoration disabledDecoration; + /// TextStyle for day cells that are marked as holidays by `holidayPredicate`. final TextStyle holidayTextStyle; + + /// Decoration for day cells that are marked as holidays by `holidayPredicate`. final Decoration holidayDecoration; + /// TextStyle for day cells that match `weekendDay` list. final TextStyle weekendTextStyle; + + /// Decoration for day cells that match `weekendDay` list. final Decoration weekendDecoration; + /// TextStyle for day cells that do not match any other styles. final TextStyle defaultTextStyle; + + /// Decoration for day cells that do not match any other styles. final Decoration defaultDecoration; + /// Decoration for each interior row of day cells. final Decoration rowDecoration; + /// Creates a `CalendarStyle` used by `TableCalendar` widget. const CalendarStyle({ this.isTodayHighlighted = true, this.canMarkersOverflow = true, @@ -67,28 +148,46 @@ class CalendarStyle { this.rangeHighlightScale = 1.0, this.markerMargin = const EdgeInsets.symmetric(horizontal: 0.3), this.markersAlignment = Alignment.bottomCenter, - this.markersMaxAmount = 4, + this.markersMaxCount = 4, this.cellMargin = const EdgeInsets.all(6.0), this.markersOffset = const PositionedOffset(), this.rangeHighlightColor = const Color(0xFFBBDDFF), this.markerDecoration = const BoxDecoration( - color: const Color(0xFF263238), shape: BoxShape.circle), - this.todayTextStyle = - const TextStyle(color: const Color(0xFFFAFAFA), fontSize: 16.0), // + color: const Color(0xFF263238), + shape: BoxShape.circle, + ), + this.todayTextStyle = const TextStyle( + color: const Color(0xFFFAFAFA), + fontSize: 16.0, + ), // this.todayDecoration = const BoxDecoration( - color: const Color(0xFF9FA8DA), shape: BoxShape.circle), - this.selectedTextStyle = - const TextStyle(color: const Color(0xFFFAFAFA), fontSize: 16.0), + color: const Color(0xFF9FA8DA), + shape: BoxShape.circle, + ), + this.selectedTextStyle = const TextStyle( + color: const Color(0xFFFAFAFA), + fontSize: 16.0, + ), this.selectedDecoration = const BoxDecoration( - color: const Color(0xFF5C6BC0), shape: BoxShape.circle), - this.rangeStartTextStyle = - const TextStyle(color: const Color(0xFFFAFAFA), fontSize: 16.0), + color: const Color(0xFF5C6BC0), + shape: BoxShape.circle, + ), + this.rangeStartTextStyle = const TextStyle( + color: const Color(0xFFFAFAFA), + fontSize: 16.0, + ), this.rangeStartDecoration = const BoxDecoration( - color: const Color(0xFF6699FF), shape: BoxShape.circle), - this.rangeEndTextStyle = - const TextStyle(color: const Color(0xFFFAFAFA), fontSize: 16.0), + color: const Color(0xFF6699FF), + shape: BoxShape.circle, + ), + this.rangeEndTextStyle = const TextStyle( + color: const Color(0xFFFAFAFA), + fontSize: 16.0, + ), this.rangeEndDecoration = const BoxDecoration( - color: const Color(0xFF6699FF), shape: BoxShape.circle), + color: const Color(0xFF6699FF), + shape: BoxShape.circle, + ), this.withinRangeTextStyle = const TextStyle(), this.withinRangeDecoration = const BoxDecoration(shape: BoxShape.circle), this.outsideTextStyle = const TextStyle(color: const Color(0xFFAEAEAE)), @@ -110,11 +209,20 @@ class CalendarStyle { }); } +/// Helper class containing data for internal `Positioned` widget. class PositionedOffset { + /// Distance from the top edge. final double top; + + /// Distance from the bottom edge. final double bottom; + + /// Distance from the leading edge. final double start; + + /// Distance from the trailing edge. final double end; + /// Creates a `PositionedOffset`. Values are set to `null` by default. const PositionedOffset({this.top, this.bottom, this.start, this.end}); } diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index c19804a1..c2df3d56 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -146,7 +146,7 @@ class TableCalendar extends StatefulWidget { /// Called when the calendar is created. Exposes its PageController. final void Function(PageController pageController) onCalendarCreated; - /// Creates a horizontal calendar widget. + /// Creates a `TableCalendar` widget. TableCalendar({ Key key, this.locale, @@ -451,7 +451,7 @@ class _TableCalendarState extends State> { child: Row( mainAxisSize: MainAxisSize.min, children: events - .take(widget.calendarStyle.markersMaxAmount) + .take(widget.calendarStyle.markersMaxCount) .map((event) => _buildSingleMarker(date, event, markerSize)) .toList(), ), From f1d0fc182559981cd6066a45d3f78cee014d155b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 7 Feb 2021 21:33:18 +0100 Subject: [PATCH 052/164] Update basic example --- example/lib/pages/table_basics_example.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/example/lib/pages/table_basics_example.dart b/example/lib/pages/table_basics_example.dart index 5612bde2..0e5fc90f 100644 --- a/example/lib/pages/table_basics_example.dart +++ b/example/lib/pages/table_basics_example.dart @@ -24,6 +24,7 @@ class _TableBasicsExampleState extends State { firstDay: DateTime.utc(2020, 10, 16), lastDay: DateTime.utc(2021, 3, 14), focusedDay: _focusedDay, + calendarFormat: _calendarFormat, selectedDayPredicate: (day) { // Use `selectedDayPredicate` to determine which day is currently selected. // If this returns true, then `day` will be marked as selected. @@ -32,28 +33,27 @@ class _TableBasicsExampleState extends State { // the time-part of compared DateTime objects. return isSameDay(_selectedDay, day); }, - calendarFormat: _calendarFormat, onDaySelected: (selectedDay, focusedDay) { if (!isSameDay(_selectedDay, selectedDay)) { - // Call `setState()` to update the selected day + // Call `setState()` when updating the selected day setState(() { _selectedDay = selectedDay; }); } - // Don't call `setState()` just to update the focused day + // Don't call `setState()` when updating just the focused day _focusedDay = focusedDay; }, onFormatChanged: (format) { if (_calendarFormat != format) { - // Call `setState()` to update calendar format + // Call `setState()` when updating calendar format setState(() { _calendarFormat = format; }); } }, onPageChanged: (focusedDay) { - // Don't call `setState()` just to update the focused day + // Don't call `setState()` when updating just the focused day _focusedDay = focusedDay; }, ), From 4afefe2a2e481edd201db109944d6d00c7179126 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 7 Feb 2021 21:33:49 +0100 Subject: [PATCH 053/164] Add onFormatChanged assertion --- lib/src/table_calendar.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index c2df3d56..9a892a42 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -266,7 +266,12 @@ class _TableCalendarState extends State> { calendarFormat: widget.calendarFormat, locale: widget.locale, onFormatButtonTap: (format) { - widget.onFormatChanged(format); + assert( + widget.onFormatChanged != null, + 'Using `FormatButton` without providing `onFormatChanged` will have no effect.', + ); + + widget.onFormatChanged?.call(format); }, ); }, From 37f3252bfb0e0077fb48e0f2b40543b39e476337 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 7 Feb 2021 21:54:50 +0100 Subject: [PATCH 054/164] Update README.md --- README.md | 228 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 137 insertions(+), 91 deletions(-) diff --git a/README.md b/README.md index 1f5e6099..16d7a12a 100644 --- a/README.md +++ b/README.md @@ -1,84 +1,180 @@ -# Table Calendar +# TableCalendar [![Pub Package](https://img.shields.io/pub/v/table_calendar.svg?style=flat-square)](https://pub.dartlang.org/packages/table_calendar) [![Awesome Flutter](https://img.shields.io/badge/Awesome-Flutter-52bdeb.svg?longCache=true&style=flat-square)](https://github.com/Solido/awesome-flutter) -Highly customizable, feature-packed Flutter Calendar with gestures, animations and multiple formats. +Highly customizable, feature-packed calendar widget for Flutter. | ![Image](https://raw.githubusercontent.com/aleksanderwozniak/table_calendar/assets/table_calendar_styles.gif) | ![Image](https://raw.githubusercontent.com/aleksanderwozniak/table_calendar/assets/table_calendar_builders.gif) | | :------------: | :------------: | -| **Table Calendar** with custom styles | **Table Calendar** with Builders | +| **TableCalendar** with custom styles | **TableCalendar** with custom builders | ## Features * Extensive, yet easy to use API -* Custom Builders for truly flexible UI -* Complete programmatic control with CalendarController -* Dynamic events -* Interface for holidays +* Preconfigured UI with customizable styling +* Custom selective builders for unlimited UI design * Locale support -* Vertical autosizing -* Beautiful animations -* Gesture handling -* Multiple Calendar formats -* Multiple days of the week formats -* Specifying available date range -* Nice, configurable UI out of the box +* Range selection support +* Multiple selection support +* Dynamic events and holidays +* Vertical autosizing - fit the content, or fill the viewport +* Multiple calendar formats (month, two weeks, week) +* Horizontal swipe boundaries (firstDay, lastDay) ## Usage -Make sure to check out [example project](https://github.com/aleksanderwozniak/table_calendar/blob/master/example). -For additional info please refer to [API docs](https://pub.dartlang.org/documentation/table_calendar/latest/table_calendar/table_calendar-library.html). +Make sure to check out [examples](https://github.com/aleksanderwozniak/table_calendar/tree/3.0.0-beta/example/lib/pages) for more details. ### Installation -Add to pubspec.yaml: +To use `3.0.0-beta` branch, add this to `pubspec.yaml`: ```yaml -dependencies: - table_calendar: ^2.3.3 + table_calendar: + git: + url: git://github.com/aleksanderwozniak/table_calendar.git + ref: 3.0.0-beta ``` -Then import it to your project: +### Basic setup + +*The complete example is available [here](https://github.com/aleksanderwozniak/table_calendar/blob/3.0.0-beta/example/lib/pages/table_basics_example.dart).* + +**TableCalendar** requires you to provide `firstDay`, `lastDay` and `focusedDay`: +* `firstDay` is the first available day for the calendar. Users will not be able to access days before it. +* `lastDay` is the last available day for the calendar. Users will not be able to access days after it. +* `focusedDay` is the currently targeted day. Use this property to determine which month should be currently visible. ```dart -import 'package:table_calendar/table_calendar.dart'; +TableCalendar( + firstDay: DateTime.utc(2020, 10, 16), + lastDay: DateTime.utc(2021, 3, 14), + focusedDay: DateTime.now(), +); ``` -And finally create the **TableCalendar** with a `CalendarController`: +#### Adding interactivity + +You will surely notice that previously set up calendar widget isn't quite interactive - you can only swipe it horizontally, to change the currently visible month. While it may be sufficient in certain situations, you can easily bring it to life by specifying a couple of callbacks. + +Adding the following code to the calendar widget will allow it to respond to user's taps, marking the tapped day as selected: ```dart -@override -void initState() { - super.initState(); - _calendarController = CalendarController(); -} +selectedDayPredicate: (day) { + return _selectedDay == day; +}, +onDaySelected: (selectedDay, focusedDay) { + setState(() { + _selectedDay = selectedDay; + }); + + _focusedDay = focusedDay; // update `_focusedDay` here as well +}, +``` + +In order to dynamically update visible calendar format, add those lines to the widget: + +```dart +calendarFormat: _calendarFormat, +onFormatChanged: (format) { + setState(() { + _calendarFormat = format; + }); +}, +``` + +Those two changes will make the calendar interactive and responsive to user's input. + +#### Updating focusedDay + +Setting `focusedDay` to a static value means that whenever **TableCalendar** widget rebuilds, it will use that specific `focusedDay`. You can quickly test it by using hot reload: set `focusedDay` to `DateTime.now()`, swipe to next month and trigger a hot reload - the calendar will "reset" to its initial state. To prevent this from happening, you should store and update `focusedDay` whenever any callback exposes it. + +Add this one callback to complete the basic setup: -@override -void dispose() { - _calendarController.dispose(); - super.dispose(); +```dart +onPageChanged: (focusedDay) { + _focusedDay = focusedDay; +}, +``` + +It is worth noting that you don't need to rebuild **TableCalendar** widget whenever `focusedDay` changes. You should just update the stored value, so that if the widget gets rebuilt, it will use the proper `focusedDay`. Don't call `setState()` just to trigger a rebuild with updated `focusedDay`. + +*The complete example is available [here](https://github.com/aleksanderwozniak/table_calendar/blob/3.0.0-beta/example/lib/pages/table_basics_example.dart). You can find other examples [here](https://github.com/aleksanderwozniak/table_calendar/tree/3.0.0-beta/example/lib/pages).* + +### Events + +*The complete example is available [here](https://github.com/aleksanderwozniak/table_calendar/blob/3.0.0-beta/example/lib/pages/table_events_example.dart).* + +You can supply custom events to **TableCalendar** widget. To do so, use `eventLoader` property - you will be given a `DateTime` object, to which you need to assign a list of events. + +```dart +eventLoader: (day) { + return _getEventsForDay(day); +}, +``` + +`_getEventsForDay()` can be of any implementation. For example, a `Map>` can be used: + +```dart +List _getEventsForDay(DateTime day) { + return events[day] ?? []; } +``` + +One thing worth remembering is that `DateTime` objects consist of both date and time parts. In many cases this time part is redundant for calendar related aspects. + +If you decide to use a `Map`, I suggest making it a `LinkedHashMap` - this will allow you to override equality comparison for two `DateTime` objects, comparing them just by their date parts: -@override -Widget build(BuildContext context) { - return TableCalendar( - calendarController: _calendarController, - ); +```dart +final events = LinkedHashMap( + equals: isSameDay, + hashCode: getHashCode, +)..addAll(eventSource); +``` + +#### Cyclic events + +`eventLoader` allows you to easily add events that repeat in a pattern. For example, this will add an event to every Monday: + +```dart +eventLoader: (day) { + if (day.weekday == DateTime.monday) { + return [Event('Cyclic event')]; + } + + return []; +}, +``` + +#### Events selected on tap + +Often times having a sublist of events that are selected by tapping on a day is desired. You can achieve that by using the same method you provided to `eventLoader` inside of `onDaySelected` callback: + +```dart +void _onDaySelected(DateTime selectedDay, DateTime focusedDay) { + if (!isSameDay(_selectedDay, selectedDay)) { + setState(() { + _selectedDay = selectedDay; + _selectedEvents = _getEventsForDay(selectedDay); + }); + } + + _focusedDay = focusedDay; } ``` +*The complete example is available [here](https://github.com/aleksanderwozniak/table_calendar/blob/3.0.0-beta/example/lib/pages/table_events_example.dart).* + ### Locale -**Table Calendar** supports locales. To display the Calendar in desired language, use `locale` property. +**TableCalendar** supports locales. To display the calendar in desired language, use `locale` property. If you don't specify it, a default locale will be used. #### Initialization Before you can use a locale, you need to initialize the i18n formatting. -*This is independent of **Table Calendar** package, so I encourage you to do your own research.* - A simple way of doing it is as follows: * First of all, add [intl](https://pub.dartlang.org/packages/intl) package to your pubspec.yaml file * Then make modifications to your `main()`: @@ -91,13 +187,13 @@ void main() { } ``` -After those two steps your app should be ready to use **Table Calendar** with different languages. +After those two steps your app should be ready to use **TableCalendar** with different languages. #### Specifying a language To specify a language, simply pass it as a String code to `locale` property. -For example, this will make **Table Calendar** use Polish language: +For example, this will make **TableCalendar** use Polish language: ```dart TableCalendar( @@ -113,53 +209,3 @@ Note, that if you want to change the language of `FormatButton`'s text, you have Use i18n method of your choice. You can also hide the button altogether by setting `formatButtonVisible` to false. - -### Holidays - -**Table Calendar** provides a simple interface for displaying holidays. Here are a few steps to follow: - -* Fetch a map of holidays tied to dates. You can search for it manually, or perhaps use some online API -* Convert it to a proper format - note that these are lists of holidays, since one date could have a couple of holidays: -```dart -{ - `DateTime A`: [`Holiday A1`, `Holiday A2`, ...], - `DateTime B`: [`Holiday B1`, `Holiday B2`, ...], - ... -} -``` -* Link it to **Table Calendar**. Use `holidays` property - -And that's your basic setup! Now you can add some styling: - -* By using `CalendarStyle` properties: `holidayStyle` and `outsideHolidayStyle` -* By using `CalendarBuilders` for complete UI control over calendar cell - -You can also add custom holiday markers thanks to improved marker API. Check out [example project](https://github.com/aleksanderwozniak/table_calendar/tree/master/example) for more details. - -```dart -markersBuilder: (context, date, events, holidays) { - final children = []; - - if (events.isNotEmpty) { - children.add( - Positioned( - right: 1, - bottom: 1, - child: _buildEventsMarker(date, events), - ), - ); - } - - if (holidays.isNotEmpty) { - children.add( - Positioned( - right: -2, - top: -2, - child: _buildHolidaysMarker(), - ), - ); - } - - return children; -}, -``` From 443c6c238bd265006b4867c3203476ecf2229a4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Tue, 9 Feb 2021 20:06:46 +0100 Subject: [PATCH 055/164] Adapt dates on input --- lib/src/table_calendar.dart | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index 9a892a42..361d789a 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -149,12 +149,12 @@ class TableCalendar extends StatefulWidget { /// Creates a `TableCalendar` widget. TableCalendar({ Key key, + @required DateTime focusedDay, + @required DateTime firstDay, + @required DateTime lastDay, this.locale, this.rangeStartDay, this.rangeEndDay, - @required this.focusedDay, - @required this.firstDay, - @required this.lastDay, this.weekendDays = const [DateTime.saturday, DateTime.sunday], this.calendarFormat = CalendarFormat.month, this.availableCalendarFormats = const { @@ -206,6 +206,9 @@ class TableCalendar extends StatefulWidget { ? weekendDays.every( (day) => day >= DateTime.monday && day <= DateTime.sunday) : true), + focusedDay = _normalizeDate(focusedDay), + firstDay = _normalizeDate(firstDay), + lastDay = _normalizeDate(lastDay), super(key: key); @override @@ -521,3 +524,7 @@ class _TableCalendarState extends State> { return weekendDays.contains(day.weekday); } } + +DateTime _normalizeDate(DateTime date) { + return DateTime.utc(date.year, date.month, date.day); +} From d6a2f0f4387136f11c718230a388182daafd45c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Tue, 9 Feb 2021 20:54:57 +0100 Subject: [PATCH 056/164] Internal improvements --- lib/src/table_calendar_base.dart | 35 +++++++++----------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/lib/src/table_calendar_base.dart b/lib/src/table_calendar_base.dart index a3fa1c8d..67deccea 100644 --- a/lib/src/table_calendar_base.dart +++ b/lib/src/table_calendar_base.dart @@ -130,32 +130,17 @@ class _TableCalendarBaseState extends State void didUpdateWidget(TableCalendarBase oldWidget) { super.didUpdateWidget(oldWidget); - if (widget.calendarFormat != oldWidget.calendarFormat) { - _updatePage(); - } - - if (_focusedDay != widget.focusedDay) { + if (_focusedDay != widget.focusedDay || + widget.calendarFormat != oldWidget.calendarFormat || + widget.startingDayOfWeek != oldWidget.startingDayOfWeek) { _focusedDay = widget.focusedDay; _updatePage(); } if (widget.rowHeight != oldWidget.rowHeight || - widget.dowHeight != oldWidget.dowHeight) { - final rowCount = _getRowCount(widget.calendarFormat, _focusedDay); - _pageHeight.value = _getPageHeight(rowCount); - } - - if (widget.dowVisible != oldWidget.dowVisible) { - final rowCount = _getRowCount(widget.calendarFormat, _focusedDay); - _pageHeight.value = _getPageHeight(rowCount); - } - - if (widget.startingDayOfWeek != oldWidget.startingDayOfWeek) { - final rowCount = _getRowCount(widget.calendarFormat, _focusedDay); - _pageHeight.value = _getPageHeight(rowCount); - } - - if (widget.sixWeekMonthsEnforced != oldWidget.sixWeekMonthsEnforced) { + widget.dowHeight != oldWidget.dowHeight || + widget.dowVisible != oldWidget.dowVisible || + widget.sixWeekMonthsEnforced != oldWidget.sixWeekMonthsEnforced) { final rowCount = _getRowCount(widget.calendarFormat, _focusedDay); _pageHeight.value = _getPageHeight(rowCount); } @@ -239,11 +224,11 @@ class _TableCalendarBaseState extends State dowDecoration: widget.dowDecoration, rowDecoration: widget.rowDecoration, onPageChanged: (index, focusedMonth) { - if (!isSameDay(_focusedDay, focusedMonth)) { - _focusedDay = focusedMonth; - } - if (!_pageCallbackDisabled) { + if (!isSameDay(_focusedDay, focusedMonth)) { + _focusedDay = focusedMonth; + } + if (widget.calendarFormat == CalendarFormat.month && !widget.sixWeekMonthsEnforced && !constraints.hasBoundedHeight) { From b6fa26db2695056c119f235ab6dad40c4612ef58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Thu, 11 Feb 2021 13:44:15 +0100 Subject: [PATCH 057/164] Add formatAnimationDuration and formatAnimationCurve --- lib/src/table_calendar.dart | 7 +++++++ lib/src/table_calendar_base.dart | 7 ++++++- lib/src/table_calendar_lite.dart | 6 ++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index 361d789a..a6430e2d 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -75,6 +75,9 @@ class TableCalendar extends StatefulWidget { /// Used for setting the height of `TableCalendar`'s days of week row. final double daysOfWeekHeight; + final Duration formatAnimationDuration; + final Curve formatAnimationCurve; + /// `TableCalendar` will start weeks with provided day. /// Use `StartingDayOfWeek.monday` for Monday - Sunday week format. /// Use `StartingDayOfWeek.sunday` for Sunday - Saturday week format. @@ -169,6 +172,8 @@ class TableCalendar extends StatefulWidget { this.shouldFillViewport = false, this.rowHeight = 52.0, this.daysOfWeekHeight = 16.0, + this.formatAnimationDuration = const Duration(milliseconds: 200), + this.formatAnimationCurve = Curves.linear, this.startingDayOfWeek = StartingDayOfWeek.sunday, this.dayHitTestBehavior = HitTestBehavior.deferToChild, this.availableGestures = AvailableGestures.all, @@ -297,6 +302,8 @@ class _TableCalendarState extends State> { dowVisible: widget.daysOfWeekVisible, dowHeight: widget.daysOfWeekHeight, rowHeight: widget.rowHeight, + formatAnimationDuration: widget.formatAnimationDuration, + formatAnimationCurve: widget.formatAnimationCurve, availableCalendarFormats: widget.availableCalendarFormats, simpleSwipeConfig: widget.simpleSwipeConfig, dayHitTestBehavior: widget.dayHitTestBehavior, diff --git a/lib/src/table_calendar_base.dart b/lib/src/table_calendar_base.dart index 67deccea..9c7fa2f3 100644 --- a/lib/src/table_calendar_base.dart +++ b/lib/src/table_calendar_base.dart @@ -50,6 +50,8 @@ class TableCalendarBase extends StatefulWidget { final bool dowVisible; final Decoration dowDecoration; final Decoration rowDecoration; + final Duration formatAnimationDuration; + final Curve formatAnimationCurve; final StartingDayOfWeek startingDayOfWeek; final AvailableGestures availableGestures; final SimpleSwipeConfig simpleSwipeConfig; @@ -72,6 +74,8 @@ class TableCalendarBase extends StatefulWidget { this.dowVisible = true, this.dowDecoration, this.rowDecoration, + this.formatAnimationDuration = const Duration(milliseconds: 200), + this.formatAnimationCurve = Curves.linear, this.startingDayOfWeek = StartingDayOfWeek.sunday, this.availableGestures = AvailableGestures.all, this.simpleSwipeConfig = const SimpleSwipeConfig( @@ -196,8 +200,9 @@ class _TableCalendarBaseState extends State constraints.hasBoundedHeight ? constraints.maxHeight : value; return AnimatedSize( - duration: const Duration(milliseconds: 200), vsync: this, + duration: widget.formatAnimationDuration, + curve: widget.formatAnimationCurve, alignment: Alignment.topCenter, child: SizedBox( height: height, diff --git a/lib/src/table_calendar_lite.dart b/lib/src/table_calendar_lite.dart index 7a94a209..12fe834c 100644 --- a/lib/src/table_calendar_lite.dart +++ b/lib/src/table_calendar_lite.dart @@ -30,6 +30,8 @@ class TableCalendarLite extends StatefulWidget { final bool sixWeekMonthsEnforced; final Decoration dowDecoration; final Decoration rowDecoration; + final Duration formatAnimationDuration; + final Curve formatAnimationCurve; final StartingDayOfWeek startingDayOfWeek; final AvailableGestures availableGestures; final SimpleSwipeConfig simpleSwipeConfig; @@ -60,6 +62,8 @@ class TableCalendarLite extends StatefulWidget { this.sixWeekMonthsEnforced = false, this.dowDecoration, this.rowDecoration, + this.formatAnimationDuration = const Duration(milliseconds: 200), + this.formatAnimationCurve = Curves.linear, this.startingDayOfWeek = StartingDayOfWeek.sunday, this.availableGestures = AvailableGestures.all, this.simpleSwipeConfig = const SimpleSwipeConfig( @@ -232,6 +236,8 @@ class _TableCalendarLiteState extends State { startingDayOfWeek: widget.startingDayOfWeek, availableGestures: widget.availableGestures, calendarFormat: widget.calendarFormat, + formatAnimationDuration: widget.formatAnimationDuration, + formatAnimationCurve: widget.formatAnimationCurve, onVerticalSwipe: _swipeCalendarFormat, onPageChanged: (focusedMonth) { _focusedDay = focusedMonth; From ba0c47c5343b020dd3baaa69d5333c99ca6eec34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Thu, 11 Feb 2021 13:49:54 +0100 Subject: [PATCH 058/164] Add docs --- lib/src/table_calendar.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index a6430e2d..4c3b35a5 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -75,7 +75,10 @@ class TableCalendar extends StatefulWidget { /// Used for setting the height of `TableCalendar`'s days of week row. final double daysOfWeekHeight; + /// Specifies the duration of size animation that takes place when `calendarFormat` is changed. final Duration formatAnimationDuration; + + /// Specifies the curve of size animation that takes place when `calendarFormat` is changed. final Curve formatAnimationCurve; /// `TableCalendar` will start weeks with provided day. From d1585f7db50f893ede270e20bafbf655cc0bbae7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Thu, 11 Feb 2021 14:02:05 +0100 Subject: [PATCH 059/164] Add pageAnimationDuration and pageAnimationCurve --- lib/src/table_calendar.dart | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index 4c3b35a5..c8c585e0 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -81,6 +81,12 @@ class TableCalendar extends StatefulWidget { /// Specifies the curve of size animation that takes place when `calendarFormat` is changed. final Curve formatAnimationCurve; + /// Specifies the duration of page change animation that takes place when left or right chevron is tapped. + final Duration pageAnimationDuration; + + /// Specifies the curve of page change animation that takes place when left or right chevron is tapped. + final Curve pageAnimationCurve; + /// `TableCalendar` will start weeks with provided day. /// Use `StartingDayOfWeek.monday` for Monday - Sunday week format. /// Use `StartingDayOfWeek.sunday` for Sunday - Saturday week format. @@ -177,6 +183,8 @@ class TableCalendar extends StatefulWidget { this.daysOfWeekHeight = 16.0, this.formatAnimationDuration = const Duration(milliseconds: 200), this.formatAnimationCurve = Curves.linear, + this.pageAnimationDuration = const Duration(milliseconds: 300), + this.pageAnimationCurve = Curves.easeOut, this.startingDayOfWeek = StartingDayOfWeek.sunday, this.dayHitTestBehavior = HitTestBehavior.deferToChild, this.availableGestures = AvailableGestures.all, @@ -260,14 +268,14 @@ class _TableCalendarState extends State> { focusedMonth: value, onLeftChevronTap: () { _pageController.previousPage( - duration: const Duration(milliseconds: 300), - curve: Curves.easeOut, + duration: widget.pageAnimationDuration, + curve: widget.pageAnimationCurve, ); }, onRightChevronTap: () { _pageController.nextPage( - duration: const Duration(milliseconds: 300), - curve: Curves.easeOut, + duration: widget.pageAnimationDuration, + curve: widget.pageAnimationCurve, ); }, onHeaderTap: () => widget.onHeaderTapped(value), From 27b087eab131217446f2128781e6dbfcbc1abcbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Thu, 11 Feb 2021 22:58:57 +0100 Subject: [PATCH 060/164] Refactor code --- example/lib/main.dart | 9 - example/lib/pages/lite_example.dart | 112 ----------- lib/src/table_calendar.dart | 208 +++++++++++++++---- lib/src/table_calendar_lite.dart | 302 ---------------------------- lib/table_calendar.dart | 1 - 5 files changed, 174 insertions(+), 458 deletions(-) delete mode 100644 example/lib/pages/lite_example.dart delete mode 100644 lib/src/table_calendar_lite.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index 3ae54d00..1f4f1358 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -4,7 +4,6 @@ import 'package:flutter/material.dart'; import 'package:intl/date_symbol_data_local.dart'; -import 'pages/lite_example.dart'; import 'pages/table_basics_example.dart'; import 'pages/table_events_example.dart'; import 'pages/table_multi_example.dart'; @@ -75,14 +74,6 @@ class _StartPageState extends State { MaterialPageRoute(builder: (_) => TableMultiExample()), ), ), - const SizedBox(height: 48.0), - RaisedButton( - child: Text('TableCalendarLite'), - onPressed: () => Navigator.push( - context, - MaterialPageRoute(builder: (_) => LiteExample()), - ), - ), const SizedBox(height: 20.0), ], ), diff --git a/example/lib/pages/lite_example.dart b/example/lib/pages/lite_example.dart deleted file mode 100644 index 7e49d772..00000000 --- a/example/lib/pages/lite_example.dart +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2019 Aleksander Woźniak -// SPDX-License-Identifier: Apache-2.0 - -import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; -import 'package:table_calendar/table_calendar.dart'; - -class LiteExample extends StatefulWidget { - @override - _LiteExampleState createState() => _LiteExampleState(); -} - -class _LiteExampleState extends State { - final _firstDay = DateTime.utc(2020, 10, 10); - final _lastDay = DateTime.utc(2021, 3, 14); - ValueNotifier _focusedDay; - DateTime _selectedDay; - CalendarFormat _calendarFormat; - - @override - void initState() { - super.initState(); - _focusedDay = ValueNotifier(DateTime.now()); - _calendarFormat = CalendarFormat.month; - } - - @override - void dispose() { - _focusedDay.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text('TableCalendarLite'), - ), - body: Column( - children: [ - Padding( - padding: const EdgeInsets.all(8.0), - child: ValueListenableBuilder( - valueListenable: _focusedDay, - builder: (context, value, _) { - final headerText = DateFormat.yMMMM().format(value); - return Text(headerText, style: TextStyle(fontSize: 24.0)); - }, - ), - ), - TableCalendarLite( - dowDecoration: const BoxDecoration(color: Colors.tealAccent), - dowBuilder: (context, day) { - final text = DateFormat.E().format(day); - return Center(child: Text(text)); - }, - dayBuilder: (context, day, focusedDay) { - var decoration = const BoxDecoration(); - var textStyle = const TextStyle(); - - if (day.isBefore(_firstDay) || day.isAfter((_lastDay))) { - textStyle = TextStyle(color: Colors.grey[300]); - } else if (_selectedDay == day) { - decoration = const BoxDecoration( - color: Colors.tealAccent, - ); - } else if (day.month != focusedDay.month) { - textStyle = const TextStyle(color: Colors.grey); - } - - return Center( - child: AnimatedContainer( - duration: const Duration(milliseconds: 200), - width: 36.0, - height: 36.0, - decoration: decoration, - child: Center( - child: Text('${day.day}', style: textStyle), - ), - ), - ); - }, - dayHitTestBehavior: HitTestBehavior.opaque, - firstDay: _firstDay, - lastDay: _lastDay, - focusedDay: _focusedDay.value, - calendarFormat: _calendarFormat, - dowHeight: 24.0, - rowHeight: 48.0, - onDaySelected: (selectedDay, focusedDay) { - if (_selectedDay != selectedDay) { - setState(() { - _selectedDay = selectedDay; - }); - } - - _focusedDay.value = focusedDay; - }, - onFormatChanged: (format) { - setState(() { - _calendarFormat = format; - }); - }, - onPageChanged: (focusedDay) { - _focusedDay.value = focusedDay; - }, - ), - ], - ), - ); - } -} diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index c8c585e0..0be0ad06 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -3,6 +3,19 @@ part of table_calendar; +typedef OnDaySelected = void Function( + DateTime selectedDay, DateTime focusedDay); + +typedef OnRangeSelected = void Function( + DateTime start, DateTime end, DateTime focusedDay); + +enum RangeSelectionMode { + disabled, // always off + toggledOff, // currently off, can be toggled + toggledOn, // currently on, can be toggled + enforced, // always on +} + /// Highly customizable, feature-packed Flutter calendar with gestures, animations and multiple formats. class TableCalendar extends StatefulWidget { /// Locale to format `TableCalendar` dates with, for example: `'en_US'`. @@ -232,13 +245,17 @@ class TableCalendar extends StatefulWidget { } class _TableCalendarState extends State> { - ValueNotifier _focusedDay; PageController _pageController; + ValueNotifier _focusedDay; + DateTime _firstSelectedDay; + RangeSelectionMode _rangeSelectionMode; @override void initState() { super.initState(); _focusedDay = ValueNotifier(widget.focusedDay); + _rangeSelectionMode = + widget.rangeSelectionMode ?? RangeSelectionMode.toggledOff; } @override @@ -248,6 +265,11 @@ class _TableCalendarState extends State> { if (_focusedDay.value != widget.focusedDay) { _focusedDay.value = widget.focusedDay; } + + if (widget.rangeSelectionMode != null && + _rangeSelectionMode != widget.rangeSelectionMode) { + _rangeSelectionMode = widget.rangeSelectionMode; + } } @override @@ -256,6 +278,120 @@ class _TableCalendarState extends State> { super.dispose(); } + bool get _isRangeSelectionToggleable => + _rangeSelectionMode == RangeSelectionMode.toggledOn || + _rangeSelectionMode == RangeSelectionMode.toggledOff; + + bool get _isRangeSelectionOn => + _rangeSelectionMode == RangeSelectionMode.toggledOn || + _rangeSelectionMode == RangeSelectionMode.enforced; + + void _swipeCalendarFormat(SwipeDirection direction) { + if (widget.onFormatChanged != null) { + final formats = widget.availableCalendarFormats.keys.toList(); + + final isSwipeUp = direction == SwipeDirection.up; + int id = formats.indexOf(widget.calendarFormat); + + // Order of CalendarFormats must be from biggest to smallest, + // e.g.: [month, twoWeeks, week] + if (isSwipeUp) { + id = min(formats.length - 1, id + 1); + } else { + id = max(0, id - 1); + } + + widget.onFormatChanged(formats[id]); + } + } + + void _onDayTapped(DateTime day) { + if (_isDayDisabled(day)) { + return widget.onDisabledDayTapped?.call(day); + } + + _updateFocusOnTap(day); + + if (_isRangeSelectionOn && widget.onRangeSelected != null) { + if (_firstSelectedDay == null) { + _firstSelectedDay = day; + widget.onRangeSelected(_firstSelectedDay, null, _focusedDay.value); + } else { + if (day.isAfter(_firstSelectedDay)) { + widget.onRangeSelected(_firstSelectedDay, day, _focusedDay.value); + _firstSelectedDay = null; + } else if (day.isBefore(_firstSelectedDay)) { + widget.onRangeSelected(day, _firstSelectedDay, _focusedDay.value); + _firstSelectedDay = null; + } + } + } else { + widget.onDaySelected?.call(day, _focusedDay.value); + } + } + + void _onDayLongPressed(DateTime day) { + if (_isDayDisabled(day)) { + return widget.onDisabledDayLongPressed?.call(day); + } + + if (widget.onRangeSelected != null) { + if (_isRangeSelectionToggleable) { + _updateFocusOnTap(day); + _toggleRangeSelection(); + + if (_isRangeSelectionOn) { + _firstSelectedDay = day; + widget.onRangeSelected(_firstSelectedDay, null, _focusedDay.value); + } else { + _firstSelectedDay = null; + widget.onDaySelected?.call(day, _focusedDay.value); + } + } + } + } + + void _updateFocusOnTap(DateTime day) { + if (widget.pageJumpingEnabled) { + _focusedDay.value = day; + return; + } + + if (widget.calendarFormat == CalendarFormat.month) { + if (_isBeforeMonth(day, _focusedDay.value)) { + _focusedDay.value = _firstDayOfMonth(_focusedDay.value); + } else if (_isAfterMonth(day, _focusedDay.value)) { + _focusedDay.value = _lastDayOfMonth(_focusedDay.value); + } else { + _focusedDay.value = day; + } + } else { + _focusedDay.value = day; + } + } + + void _toggleRangeSelection() { + if (_rangeSelectionMode == RangeSelectionMode.toggledOn) { + _rangeSelectionMode = RangeSelectionMode.toggledOff; + } else { + _rangeSelectionMode = RangeSelectionMode.toggledOn; + } + } + + void _onLeftChevronTap() { + _pageController.previousPage( + duration: widget.pageAnimationDuration, + curve: widget.pageAnimationCurve, + ); + } + + void _onRightChevronTap() { + _pageController.nextPage( + duration: widget.pageAnimationDuration, + curve: widget.pageAnimationCurve, + ); + } + @override Widget build(BuildContext context) { return Column( @@ -266,18 +402,8 @@ class _TableCalendarState extends State> { builder: (context, value, _) { return _CalendarHeader( focusedMonth: value, - onLeftChevronTap: () { - _pageController.previousPage( - duration: widget.pageAnimationDuration, - curve: widget.pageAnimationCurve, - ); - }, - onRightChevronTap: () { - _pageController.nextPage( - duration: widget.pageAnimationDuration, - curve: widget.pageAnimationCurve, - ); - }, + onLeftChevronTap: _onLeftChevronTap, + onRightChevronTap: _onRightChevronTap, onHeaderTap: () => widget.onHeaderTapped(value), onHeaderLongPress: () => widget.onHeaderLongPressed(value), headerStyle: widget.headerStyle, @@ -297,7 +423,7 @@ class _TableCalendarState extends State> { ), Flexible( flex: widget.shouldFillViewport ? 1 : 0, - child: TableCalendarLite( + child: TableCalendarBase( onCalendarCreated: (pageController) { _pageController = pageController; widget.onCalendarCreated?.call(pageController); @@ -317,26 +443,8 @@ class _TableCalendarState extends State> { formatAnimationCurve: widget.formatAnimationCurve, availableCalendarFormats: widget.availableCalendarFormats, simpleSwipeConfig: widget.simpleSwipeConfig, - dayHitTestBehavior: widget.dayHitTestBehavior, - enabledDayPredicate: widget.enabledDayPredicate, - rangeSelectionMode: widget.rangeSelectionMode, sixWeekMonthsEnforced: widget.sixWeekMonthsEnforced, - pageJumpingEnabled: widget.pageJumpingEnabled, - onDisabledDayTapped: widget.onDisabledDayTapped, - onDisabledDayLongPressed: widget.onDisabledDayLongPressed, - onDaySelected: (selectedDay, focusedDay) { - _focusedDay.value = focusedDay; - widget.onDaySelected?.call(selectedDay, focusedDay); - }, - onRangeSelected: widget.onRangeSelected != null - ? (start, end, focusedDay) { - _focusedDay.value = focusedDay; - widget.onRangeSelected(start, end, focusedDay); - } - : null, - onFormatChanged: (format) { - widget.onFormatChanged?.call(format); - }, + onVerticalSwipe: _swipeCalendarFormat, onPageChanged: (focusedDay) { _focusedDay.value = focusedDay; widget.onPageChanged?.call(focusedDay); @@ -366,7 +474,12 @@ class _TableCalendarState extends State> { return dowCell; }, dayBuilder: (context, day, focusedMonth) { - return _buildCell(day, focusedMonth); + return GestureDetector( + behavior: widget.dayHitTestBehavior, + onTap: () => _onDayTapped(day), + onLongPress: () => _onDayLongPressed(day), + child: _buildCell(day, focusedMonth), + ); }, ), ), @@ -535,6 +648,33 @@ class _TableCalendarState extends State> { : widget.enabledDayPredicate(day); } + DateTime _firstDayOfMonth(DateTime month) { + return DateTime.utc(month.year, month.month, 1); + } + + DateTime _lastDayOfMonth(DateTime month) { + final date = month.month < 12 + ? DateTime.utc(month.year, month.month + 1, 1) + : DateTime.utc(month.year + 1, 1, 1); + return date.subtract(const Duration(days: 1)); + } + + bool _isBeforeMonth(DateTime day, DateTime month) { + if (day.year == month.year) { + return day.month < month.month; + } else { + return day.isBefore(month); + } + } + + bool _isAfterMonth(DateTime day, DateTime month) { + if (day.year == month.year) { + return day.month > month.month; + } else { + return day.isAfter(month); + } + } + bool _isWeekend( DateTime day, { List weekendDays = const [DateTime.saturday, DateTime.sunday], diff --git a/lib/src/table_calendar_lite.dart b/lib/src/table_calendar_lite.dart deleted file mode 100644 index 12fe834c..00000000 --- a/lib/src/table_calendar_lite.dart +++ /dev/null @@ -1,302 +0,0 @@ -// Copyright 2019 Aleksander Woźniak -// SPDX-License-Identifier: Apache-2.0 - -part of table_calendar; - -typedef OnDaySelected = void Function( - DateTime selectedDay, DateTime focusedDay); - -typedef OnRangeSelected = void Function( - DateTime start, DateTime end, DateTime focusedDay); - -enum RangeSelectionMode { - disabled, // always off - toggledOff, // currently off, can be toggled - toggledOn, // currently on, can be toggled - enforced, // always on -} - -class TableCalendarLite extends StatefulWidget { - final DateTime firstDay; - final DateTime lastDay; - final DateTime focusedDay; - final CalendarFormat calendarFormat; - final DayBuilder dowBuilder; - final FocusedDayBuilder dayBuilder; - final double dowHeight; - final double rowHeight; - final bool dowVisible; - final bool pageJumpingEnabled; - final bool sixWeekMonthsEnforced; - final Decoration dowDecoration; - final Decoration rowDecoration; - final Duration formatAnimationDuration; - final Curve formatAnimationCurve; - final StartingDayOfWeek startingDayOfWeek; - final AvailableGestures availableGestures; - final SimpleSwipeConfig simpleSwipeConfig; - final HitTestBehavior dayHitTestBehavior; - final Map availableCalendarFormats; - final RangeSelectionMode rangeSelectionMode; - final OnDaySelected onDaySelected; - final OnRangeSelected onRangeSelected; - final bool Function(DateTime day) enabledDayPredicate; - final void Function(CalendarFormat format) onFormatChanged; - final void Function(DateTime focusedDay) onPageChanged; - final void Function(DateTime day) onDisabledDayTapped; - final void Function(DateTime day) onDisabledDayLongPressed; - final void Function(PageController pageController) onCalendarCreated; - - const TableCalendarLite({ - Key key, - @required this.firstDay, - @required this.lastDay, - @required this.focusedDay, - this.calendarFormat = CalendarFormat.month, - this.dowBuilder, - @required this.dayBuilder, - this.dowHeight, - @required this.rowHeight, - this.dowVisible = true, - this.pageJumpingEnabled = false, - this.sixWeekMonthsEnforced = false, - this.dowDecoration, - this.rowDecoration, - this.formatAnimationDuration = const Duration(milliseconds: 200), - this.formatAnimationCurve = Curves.linear, - this.startingDayOfWeek = StartingDayOfWeek.sunday, - this.availableGestures = AvailableGestures.all, - this.simpleSwipeConfig = const SimpleSwipeConfig( - verticalThreshold: 25.0, - swipeDetectionBehavior: SwipeDetectionBehavior.continuousDistinct, - ), - this.dayHitTestBehavior = HitTestBehavior.deferToChild, - this.availableCalendarFormats = const { - CalendarFormat.month: 'Month', - CalendarFormat.twoWeeks: '2 weeks', - CalendarFormat.week: 'Week', - }, - this.rangeSelectionMode, - this.onDaySelected, - this.onRangeSelected, - this.enabledDayPredicate, - this.onFormatChanged, - this.onPageChanged, - this.onDisabledDayTapped, - this.onDisabledDayLongPressed, - this.onCalendarCreated, - }) : assert(!dowVisible || (dowHeight != null && dowBuilder != null)), - assert(dayBuilder != null), - assert(rowHeight != null), - assert(firstDay != null), - assert(lastDay != null), - assert(focusedDay != null), - super(key: key); - - @override - _TableCalendarLiteState createState() => _TableCalendarLiteState(); -} - -class _TableCalendarLiteState extends State { - DateTime _focusedDay; - DateTime _firstSelectedDay; - RangeSelectionMode _rangeSelectionMode; - - @override - void initState() { - super.initState(); - _focusedDay = widget.focusedDay; - _rangeSelectionMode = - widget.rangeSelectionMode ?? RangeSelectionMode.toggledOff; - } - - @override - void didUpdateWidget(TableCalendarLite oldWidget) { - super.didUpdateWidget(oldWidget); - - if (_focusedDay != widget.focusedDay) { - _focusedDay = widget.focusedDay; - } - - if (widget.rangeSelectionMode != null && - _rangeSelectionMode != widget.rangeSelectionMode) { - _rangeSelectionMode = widget.rangeSelectionMode; - } - } - - bool get _isRangeSelectionToggleable => - _rangeSelectionMode == RangeSelectionMode.toggledOn || - _rangeSelectionMode == RangeSelectionMode.toggledOff; - - bool get _isRangeSelectionOn => - _rangeSelectionMode == RangeSelectionMode.toggledOn || - _rangeSelectionMode == RangeSelectionMode.enforced; - - void _swipeCalendarFormat(SwipeDirection direction) { - if (widget.onFormatChanged != null) { - final formats = widget.availableCalendarFormats.keys.toList(); - - final isSwipeUp = direction == SwipeDirection.up; - int id = formats.indexOf(widget.calendarFormat); - - // Order of CalendarFormats must be from biggest to smallest, - // e.g.: [month, twoWeeks, week] - if (isSwipeUp) { - id = min(formats.length - 1, id + 1); - } else { - id = max(0, id - 1); - } - - widget.onFormatChanged(formats[id]); - } - } - - void _onDaySelected(DateTime day) { - if (_isDayDisabled(day)) { - return widget.onDisabledDayTapped?.call(day); - } - - _updateFocusOnTap(day); - - if (_isRangeSelectionOn) { - if (_firstSelectedDay == null) { - _firstSelectedDay = day; - widget.onRangeSelected?.call(_firstSelectedDay, null, _focusedDay); - } else { - if (day.isAfter(_firstSelectedDay)) { - widget.onRangeSelected?.call(_firstSelectedDay, day, _focusedDay); - _firstSelectedDay = null; - } else if (day.isBefore(_firstSelectedDay)) { - widget.onRangeSelected?.call(day, _firstSelectedDay, _focusedDay); - _firstSelectedDay = null; - } - } - } else { - widget.onDaySelected?.call(day, _focusedDay); - } - } - - void _onLongPress(DateTime day) { - if (_isDayDisabled(day)) { - return widget.onDisabledDayLongPressed?.call(day); - } - - if (widget.onRangeSelected != null) { - if (_isRangeSelectionToggleable) { - _updateFocusOnTap(day); - _toggleRangeSelection(); - - if (_isRangeSelectionOn) { - _firstSelectedDay = day; - widget.onRangeSelected(_firstSelectedDay, null, _focusedDay); - } else { - _firstSelectedDay = null; - widget.onDaySelected?.call(day, _focusedDay); - } - } - } - } - - void _updateFocusOnTap(DateTime day) { - if (widget.pageJumpingEnabled) { - _focusedDay = day; - return; - } - - if (widget.calendarFormat == CalendarFormat.month) { - if (_isBeforeMonth(day, _focusedDay)) { - _focusedDay = _firstDayOfMonth(_focusedDay); - } else if (_isAfterMonth(day, _focusedDay)) { - _focusedDay = _lastDayOfMonth(_focusedDay); - } else { - _focusedDay = day; - } - } else { - _focusedDay = day; - } - } - - void _toggleRangeSelection() { - if (_rangeSelectionMode == RangeSelectionMode.toggledOn) { - _rangeSelectionMode = RangeSelectionMode.toggledOff; - } else { - _rangeSelectionMode = RangeSelectionMode.toggledOn; - } - } - - @override - Widget build(BuildContext context) { - return TableCalendarBase( - availableCalendarFormats: widget.availableCalendarFormats, - simpleSwipeConfig: widget.simpleSwipeConfig, - onCalendarCreated: widget.onCalendarCreated, - focusedDay: _focusedDay, - firstDay: widget.firstDay, - lastDay: widget.lastDay, - startingDayOfWeek: widget.startingDayOfWeek, - availableGestures: widget.availableGestures, - calendarFormat: widget.calendarFormat, - formatAnimationDuration: widget.formatAnimationDuration, - formatAnimationCurve: widget.formatAnimationCurve, - onVerticalSwipe: _swipeCalendarFormat, - onPageChanged: (focusedMonth) { - _focusedDay = focusedMonth; - widget.onPageChanged?.call(focusedMonth); - }, - sixWeekMonthsEnforced: widget.sixWeekMonthsEnforced, - dowVisible: widget.dowVisible, - dowDecoration: widget.dowDecoration, - rowDecoration: widget.rowDecoration, - dowHeight: widget.dowHeight, - dowBuilder: widget.dowBuilder, - rowHeight: widget.rowHeight, - dayBuilder: (context, day, focusedMonth) { - return GestureDetector( - behavior: widget.dayHitTestBehavior, - onTap: () => _onDaySelected(day), - onLongPress: () => _onLongPress(day), - child: widget.dayBuilder(context, day, focusedMonth), - ); - }, - ); - } - - DateTime _firstDayOfMonth(DateTime month) { - return DateTime.utc(month.year, month.month, 1); - } - - DateTime _lastDayOfMonth(DateTime month) { - final date = month.month < 12 - ? DateTime.utc(month.year, month.month + 1, 1) - : DateTime.utc(month.year + 1, 1, 1); - return date.subtract(const Duration(days: 1)); - } - - bool _isDayDisabled(DateTime day) { - return day.isBefore(widget.firstDay) || - day.isAfter(widget.lastDay) || - !_isDayAvailable(day); - } - - bool _isDayAvailable(DateTime day) { - return widget.enabledDayPredicate == null - ? true - : widget.enabledDayPredicate(day); - } - - bool _isBeforeMonth(DateTime day, DateTime month) { - if (day.year == month.year) { - return day.month < month.month; - } else { - return day.isBefore(month); - } - } - - bool _isAfterMonth(DateTime day, DateTime month) { - if (day.year == month.year) { - return day.month > month.month; - } else { - return day.isAfter(month); - } - } -} diff --git a/lib/table_calendar.dart b/lib/table_calendar.dart index c2b96360..03ebfc9f 100644 --- a/lib/table_calendar.dart +++ b/lib/table_calendar.dart @@ -15,7 +15,6 @@ part 'src/customization/days_of_week_style.dart'; part 'src/customization/header_style.dart'; part 'src/table_calendar.dart'; part 'src/table_calendar_base.dart'; -part 'src/table_calendar_lite.dart'; part 'src/widgets/calendar_core.dart'; part 'src/widgets/calendar_header.dart'; part 'src/widgets/calendar_page.dart'; From 3300ea74a95525e9b6c53d22b69bb83eee95655f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Fri, 12 Feb 2021 00:48:07 +0100 Subject: [PATCH 061/164] Change default dayHitTestBehavior to opaque --- lib/src/table_calendar.dart | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index 0be0ad06..1f05d0b8 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -199,7 +199,7 @@ class TableCalendar extends StatefulWidget { this.pageAnimationDuration = const Duration(milliseconds: 300), this.pageAnimationCurve = Curves.easeOut, this.startingDayOfWeek = StartingDayOfWeek.sunday, - this.dayHitTestBehavior = HitTestBehavior.deferToChild, + this.dayHitTestBehavior = HitTestBehavior.opaque, this.availableGestures = AvailableGestures.all, this.simpleSwipeConfig = const SimpleSwipeConfig( verticalThreshold: 25.0, @@ -286,6 +286,10 @@ class _TableCalendarState extends State> { _rangeSelectionMode == RangeSelectionMode.toggledOn || _rangeSelectionMode == RangeSelectionMode.enforced; + bool get _shouldBlockOutsideDays => + !widget.calendarStyle.outsideDaysVisible && + widget.calendarFormat == CalendarFormat.month; + void _swipeCalendarFormat(SwipeDirection direction) { if (widget.onFormatChanged != null) { final formats = widget.availableCalendarFormats.keys.toList(); @@ -306,6 +310,11 @@ class _TableCalendarState extends State> { } void _onDayTapped(DateTime day) { + final isOutside = day.month != _focusedDay.value.month; + if (isOutside && _shouldBlockOutsideDays) { + return; + } + if (_isDayDisabled(day)) { return widget.onDisabledDayTapped?.call(day); } @@ -331,6 +340,11 @@ class _TableCalendarState extends State> { } void _onDayLongPressed(DateTime day) { + final isOutside = day.month != _focusedDay.value.month; + if (isOutside && _shouldBlockOutsideDays) { + return; + } + if (_isDayDisabled(day)) { return widget.onDisabledDayLongPressed?.call(day); } @@ -490,9 +504,7 @@ class _TableCalendarState extends State> { Widget _buildCell(DateTime date, DateTime focusedDay) { final isOutside = date.month != focusedDay.month; - if (isOutside && - !widget.calendarStyle.outsideDaysVisible && - widget.calendarFormat == CalendarFormat.month) { + if (isOutside && _shouldBlockOutsideDays) { return Container(); } From dcd63dafbecb859cb8278d1f8bb2a36462db4f6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Fri, 12 Feb 2021 14:28:27 +0100 Subject: [PATCH 062/164] Add docs --- lib/src/customization/calendar_builders.dart | 18 +++++++++++------- lib/src/customization/header_style.dart | 2 +- lib/src/table_calendar.dart | 17 +++++++++++------ lib/src/table_calendar_base.dart | 9 +++++++-- 4 files changed, 30 insertions(+), 16 deletions(-) diff --git a/lib/src/customization/calendar_builders.dart b/lib/src/customization/calendar_builders.dart index 421bfec5..dbfc7a2b 100644 --- a/lib/src/customization/calendar_builders.dart +++ b/lib/src/customization/calendar_builders.dart @@ -3,18 +3,22 @@ part of table_calendar; -/// Builder signature for a single event marker. Contains a `date` and a single `event` associated with that `date`. +/// Signature for a function that creates a single event marker for a given `day`. +/// Contains a single `event` associated with that `day`. typedef SingleMarkerBuilder = Widget Function( - BuildContext context, DateTime date, T event); + BuildContext context, DateTime day, T event); -/// Builder signature for event markers. Contains a `date` and a list of `events` associated with that `date`. +/// Signature for a function that creates an event marker for a given `day`. +/// Contains a list of `events` associated with that `day`. typedef MarkerBuilder = Widget Function( - BuildContext context, DateTime date, List events); + BuildContext context, DateTime day, List events); -/// Builder signature for background highlight of range selection. -/// Contains a `date` and a value determining if that `date` falls within the selected range. +/// Signature for a function that creates a background highlight for a given `day`. +/// +/// Used for highlighting current range selection. +/// Contains a value determining if the given `day` falls within the selected range. typedef HighlightBuilder = Widget Function( - BuildContext context, DateTime date, bool isWithinRange); + BuildContext context, DateTime day, bool isWithinRange); /// Class containing all custom builders for `TableCalendar`. class CalendarBuilders { diff --git a/lib/src/customization/header_style.dart b/lib/src/customization/header_style.dart index 5604c2eb..b07e6971 100644 --- a/lib/src/customization/header_style.dart +++ b/lib/src/customization/header_style.dart @@ -3,7 +3,7 @@ part of table_calendar; -/// Signature for function returning text that can be localized and formatted with `DateFormat`. +/// Signature for a function returning text that can be localized and formatted with `DateFormat`. typedef TextFormatter = String Function(DateTime date, dynamic locale); /// Class containing styling and configuration of `TableCalendar`'s header. diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index 1f05d0b8..01745e2e 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -3,18 +3,17 @@ part of table_calendar; +/// Signature for `onDaySelected` callback. Contains currently selected day and focused day. typedef OnDaySelected = void Function( DateTime selectedDay, DateTime focusedDay); +/// Signature for `onRangeSelected` callback. +/// Contains start and end of the selected range, as well as currently focused day. typedef OnRangeSelected = void Function( DateTime start, DateTime end, DateTime focusedDay); -enum RangeSelectionMode { - disabled, // always off - toggledOff, // currently off, can be toggled - toggledOn, // currently on, can be toggled - enforced, // always on -} +/// Modes that range selection can operate in. +enum RangeSelectionMode { disabled, toggledOff, toggledOn, enforced } /// Highly customizable, feature-packed Flutter calendar with gestures, animations and multiple formats. class TableCalendar extends StatefulWidget { @@ -101,6 +100,7 @@ class TableCalendar extends StatefulWidget { final Curve pageAnimationCurve; /// `TableCalendar` will start weeks with provided day. + /// /// Use `StartingDayOfWeek.monday` for Monday - Sunday week format. /// Use `StartingDayOfWeek.sunday` for Sunday - Saturday week format. final StartingDayOfWeek startingDayOfWeek; @@ -129,6 +129,11 @@ class TableCalendar extends StatefulWidget { final CalendarBuilders calendarBuilders; /// Current mode of range selection. + /// + /// * `RangeSelectionMode.disabled` - range selection is always off. + /// * `RangeSelectionMode.toggledOff` - range selection is currently off, can be toggled by longpressing a day cell. + /// * `RangeSelectionMode.toggledOn` - range selection is currently on, can be toggled by longpressing a day cell. + /// * `RangeSelectionMode.enforced` - range selection is always on. final RangeSelectionMode rangeSelectionMode; /// Called whenever a day range gets selected. diff --git a/lib/src/table_calendar_base.dart b/lib/src/table_calendar_base.dart index 9c7fa2f3..6e716bc9 100644 --- a/lib/src/table_calendar_base.dart +++ b/lib/src/table_calendar_base.dart @@ -3,16 +3,21 @@ part of table_calendar; +/// Signature for a function that creates a widget for a given `day`. typedef DayBuilder = Widget Function(BuildContext context, DateTime day); +/// Signature for a function that creates a widget for a given `day`. +/// Additionally, contains the currently focused day. typedef FocusedDayBuilder = Widget Function( - BuildContext context, DateTime day, DateTime focusedMonth); + BuildContext context, DateTime day, DateTime focusedDay); -/// Gestures available to `TableCalendar`. +/// Gestures available for the calendar. enum AvailableGestures { none, verticalSwipe, horizontalSwipe, all } +/// Formats that the calendar can display. enum CalendarFormat { month, twoWeeks, week } +/// Days of the week that the calendar can start with. enum StartingDayOfWeek { monday, tuesday, From 66eb0721edf27228fd9b6b852c20add91fd938fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Fri, 12 Feb 2021 14:59:04 +0100 Subject: [PATCH 063/164] Make names uniform --- lib/src/customization/calendar_builders.dart | 4 +- lib/src/customization/calendar_style.dart | 4 +- lib/src/table_calendar.dart | 42 ++++++++++---------- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/lib/src/customization/calendar_builders.dart b/lib/src/customization/calendar_builders.dart index dbfc7a2b..57318297 100644 --- a/lib/src/customization/calendar_builders.dart +++ b/lib/src/customization/calendar_builders.dart @@ -40,7 +40,7 @@ class CalendarBuilders { /// Custom builder for day cells that fall within the currently selected range. final FocusedDayBuilder withinRangeBuilder; - /// Custom builder for day cells, of which the `date.month` is different than `focusedDay.month`. + /// Custom builder for day cells, of which the `day.month` is different than `focusedDay.month`. /// This will affect day cells that do not match the currently focused month. final FocusedDayBuilder outsideBuilder; @@ -57,7 +57,7 @@ class CalendarBuilders { final FocusedDayBuilder defaultBuilder; /// Custom builder for background highlight of range selection. - /// If `isWithinRange` is true, then `date` is within the selected range. + /// If `isWithinRange` is true, then `day` is within the selected range. final HighlightBuilder rangeHighlightBuilder; /// Custom builder for a single event marker. Each of those will be displayed in a `Row` above of the day cell. diff --git a/lib/src/customization/calendar_style.dart b/lib/src/customization/calendar_style.dart index 1d2ce36c..6d54c3d6 100644 --- a/lib/src/customization/calendar_style.dart +++ b/lib/src/customization/calendar_style.dart @@ -95,11 +95,11 @@ class CalendarStyle { /// Decoration for day cells that fall within the currently selected range. final Decoration withinRangeDecoration; - /// TextStyle for day cells, of which the `date.month` is different than `focusedDay.month`. + /// TextStyle for day cells, of which the `day.month` is different than `focusedDay.month`. /// This will affect day cells that do not match the currently focused month. final TextStyle outsideTextStyle; - /// Decoration for day cells, of which the `date.month` is different than `focusedDay.month`. + /// Decoration for day cells, of which the `day.month` is different than `focusedDay.month`. /// This will affect day cells that do not match the currently focused month. final Decoration outsideDecoration; diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index 01745e2e..67fbc582 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -506,8 +506,8 @@ class _TableCalendarState extends State> { ); } - Widget _buildCell(DateTime date, DateTime focusedDay) { - final isOutside = date.month != focusedDay.month; + Widget _buildCell(DateTime day, DateTime focusedDay) { + final isOutside = day.month != focusedDay.month; if (isOutside && _shouldBlockOutsideDays) { return Container(); @@ -523,13 +523,13 @@ class _TableCalendarState extends State> { final isWithinRange = widget.rangeStartDay != null && widget.rangeEndDay != null && - _isWithinRange(date, widget.rangeStartDay, widget.rangeEndDay); + _isWithinRange(day, widget.rangeStartDay, widget.rangeEndDay); - final isRangeStart = isSameDay(date, widget.rangeStartDay); - final isRangeEnd = isSameDay(date, widget.rangeEndDay); + final isRangeStart = isSameDay(day, widget.rangeStartDay); + final isRangeEnd = isSameDay(day, widget.rangeEndDay); Widget rangeHighlight = widget.calendarBuilders.rangeHighlightBuilder - ?.call(context, date, isWithinRange); + ?.call(context, day, isWithinRange); if (rangeHighlight == null) { if (isWithinRange) { @@ -552,33 +552,33 @@ class _TableCalendarState extends State> { children.add(rangeHighlight); } - final isToday = isSameDay(date, DateTime.now()); - final isDisabled = _isDayDisabled(date); - final isWeekend = _isWeekend(date, weekendDays: widget.weekendDays); + final isToday = isSameDay(day, DateTime.now()); + final isDisabled = _isDayDisabled(day); + final isWeekend = _isWeekend(day, weekendDays: widget.weekendDays); Widget content = _CellContent( - day: date, + day: day, focusedDay: focusedDay, calendarStyle: widget.calendarStyle, calendarBuilders: widget.calendarBuilders, isTodayHighlighted: widget.calendarStyle.isTodayHighlighted, isToday: isToday, - isSelected: widget.selectedDayPredicate?.call(date) ?? false, + isSelected: widget.selectedDayPredicate?.call(day) ?? false, isRangeStart: isRangeStart, isRangeEnd: isRangeEnd, isWithinRange: isWithinRange, isOutside: isOutside, isDisabled: isDisabled, isWeekend: isWeekend, - isHoliday: widget.holidayPredicate?.call(date) ?? false, + isHoliday: widget.holidayPredicate?.call(day) ?? false, ); children.add(content); if (!isDisabled) { - final events = widget.eventLoader?.call(date) ?? []; - Widget markerWidget = widget.calendarBuilders.markerBuilder - ?.call(context, date, events); + final events = widget.eventLoader?.call(day) ?? []; + Widget markerWidget = + widget.calendarBuilders.markerBuilder?.call(context, day, events); if (events.isNotEmpty && markerWidget == null) { final center = constraints.maxHeight / 2; @@ -608,7 +608,7 @@ class _TableCalendarState extends State> { mainAxisSize: MainAxisSize.min, children: events .take(widget.calendarStyle.markersMaxCount) - .map((event) => _buildSingleMarker(date, event, markerSize)) + .map((event) => _buildSingleMarker(day, event, markerSize)) .toList(), ), ); @@ -630,9 +630,9 @@ class _TableCalendarState extends State> { ); } - Widget _buildSingleMarker(DateTime date, T event, double markerSize) { + Widget _buildSingleMarker(DateTime day, T event, double markerSize) { return widget.calendarBuilders.singleMarkerBuilder - ?.call(context, date, event) ?? + ?.call(context, day, event) ?? Container( width: markerSize, height: markerSize, @@ -641,12 +641,12 @@ class _TableCalendarState extends State> { ); } - bool _isWithinRange(DateTime date, DateTime start, DateTime end) { - if (isSameDay(date, start) || isSameDay(date, end)) { + bool _isWithinRange(DateTime day, DateTime start, DateTime end) { + if (isSameDay(day, start) || isSameDay(day, end)) { return true; } - if (date.isAfter(start) && date.isBefore(end)) { + if (day.isAfter(start) && day.isBefore(end)) { return true; } From 3695e876cf512a42ede2715a3e3a37fa834be62f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sat, 13 Feb 2021 00:12:54 +0100 Subject: [PATCH 064/164] Replace part directive --- lib/src/customization/calendar_builders.dart | 4 +- lib/src/customization/calendar_style.dart | 2 +- lib/src/customization/days_of_week_style.dart | 4 +- lib/src/customization/header_style.dart | 5 +- lib/src/shared/utils.dart | 51 +++++++++++++++++++ lib/src/table_calendar.dart | 29 +++++++---- lib/src/table_calendar_base.dart | 51 +++---------------- lib/src/widgets/calendar_core.dart | 15 +++--- lib/src/widgets/calendar_header.dart | 18 ++++--- lib/src/widgets/calendar_page.dart | 8 +-- lib/src/widgets/cell_content.dart | 9 ++-- lib/src/widgets/custom_icon_button.dart | 6 +-- lib/src/widgets/format_button.dart | 8 +-- lib/table_calendar.dart | 27 +++------- 14 files changed, 133 insertions(+), 104 deletions(-) create mode 100644 lib/src/shared/utils.dart diff --git a/lib/src/customization/calendar_builders.dart b/lib/src/customization/calendar_builders.dart index 57318297..12317dfd 100644 --- a/lib/src/customization/calendar_builders.dart +++ b/lib/src/customization/calendar_builders.dart @@ -1,7 +1,9 @@ // Copyright 2019 Aleksander Woźniak // SPDX-License-Identifier: Apache-2.0 -part of table_calendar; +import 'package:flutter/widgets.dart'; + +import '../shared/utils.dart' show DayBuilder, FocusedDayBuilder; /// Signature for a function that creates a single event marker for a given `day`. /// Contains a single `event` associated with that `day`. diff --git a/lib/src/customization/calendar_style.dart b/lib/src/customization/calendar_style.dart index 6d54c3d6..ab28a3cd 100644 --- a/lib/src/customization/calendar_style.dart +++ b/lib/src/customization/calendar_style.dart @@ -1,7 +1,7 @@ // Copyright 2019 Aleksander Woźniak // SPDX-License-Identifier: Apache-2.0 -part of table_calendar; +import 'package:flutter/widgets.dart'; /// Class containing styling and configuration for `TableCalendar`'s content. class CalendarStyle { diff --git a/lib/src/customization/days_of_week_style.dart b/lib/src/customization/days_of_week_style.dart index f0ffc544..99785fe9 100644 --- a/lib/src/customization/days_of_week_style.dart +++ b/lib/src/customization/days_of_week_style.dart @@ -1,7 +1,9 @@ // Copyright 2019 Aleksander Woźniak // SPDX-License-Identifier: Apache-2.0 -part of table_calendar; +import 'package:flutter/widgets.dart'; + +import '../shared/utils.dart' show TextFormatter; /// Class containing styling for `TableCalendar`'s days of week panel. class DaysOfWeekStyle { diff --git a/lib/src/customization/header_style.dart b/lib/src/customization/header_style.dart index b07e6971..2f1d1da6 100644 --- a/lib/src/customization/header_style.dart +++ b/lib/src/customization/header_style.dart @@ -1,10 +1,9 @@ // Copyright 2019 Aleksander Woźniak // SPDX-License-Identifier: Apache-2.0 -part of table_calendar; +import 'package:flutter/material.dart'; -/// Signature for a function returning text that can be localized and formatted with `DateFormat`. -typedef TextFormatter = String Function(DateTime date, dynamic locale); +import '../shared/utils.dart' show TextFormatter; /// Class containing styling and configuration of `TableCalendar`'s header. class HeaderStyle { diff --git a/lib/src/shared/utils.dart b/lib/src/shared/utils.dart new file mode 100644 index 00000000..0d861ce6 --- /dev/null +++ b/lib/src/shared/utils.dart @@ -0,0 +1,51 @@ +import 'package:flutter/widgets.dart'; + +/// Signature for a function that creates a widget for a given `day`. +typedef DayBuilder = Widget Function(BuildContext context, DateTime day); + +/// Signature for a function that creates a widget for a given `day`. +/// Additionally, contains the currently focused day. +typedef FocusedDayBuilder = Widget Function( + BuildContext context, DateTime day, DateTime focusedDay); + +/// Signature for a function returning text that can be localized and formatted with `DateFormat`. +typedef TextFormatter = String Function(DateTime date, dynamic locale); + +/// Gestures available for the calendar. +enum AvailableGestures { none, verticalSwipe, horizontalSwipe, all } + +/// Formats that the calendar can display. +enum CalendarFormat { month, twoWeeks, week } + +/// Days of the week that the calendar can start with. +enum StartingDayOfWeek { + monday, + tuesday, + wednesday, + thursday, + friday, + saturday, + sunday, +} + +/// Returns a numerical value associated with given `weekday`. +/// +/// Returns 1 for `StartingDayOfWeek.monday`, all the way to 7 for `StartingDayOfWeek.sunday`. +int getWeekdayNumber(StartingDayOfWeek weekday) { + return StartingDayOfWeek.values.indexOf(weekday) + 1; +} + +/// Returns `date` in UTC format, without its time part. +DateTime normalizeDate(DateTime date) { + return DateTime.utc(date.year, date.month, date.day); +} + +/// Checks if two DateTime objects are the same day. +/// Returns `false` if either of them is null. +bool isSameDay(DateTime a, DateTime b) { + if (a == null || b == null) { + return false; + } + + return a.year == b.year && a.month == b.month && a.day == b.day; +} diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index 67fbc582..7d91f0f7 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -1,7 +1,20 @@ // Copyright 2019 Aleksander Woźniak // SPDX-License-Identifier: Apache-2.0 -part of table_calendar; +import 'dart:math'; + +import 'package:flutter/widgets.dart'; +import 'package:intl/intl.dart'; +import 'package:simple_gesture_detector/simple_gesture_detector.dart'; + +import 'customization/calendar_builders.dart'; +import 'customization/calendar_style.dart'; +import 'customization/days_of_week_style.dart'; +import 'customization/header_style.dart'; +import 'shared/utils.dart'; +import 'table_calendar_base.dart'; +import 'widgets/calendar_header.dart'; +import 'widgets/cell_content.dart'; /// Signature for `onDaySelected` callback. Contains currently selected day and focused day. typedef OnDaySelected = void Function( @@ -240,9 +253,9 @@ class TableCalendar extends StatefulWidget { ? weekendDays.every( (day) => day >= DateTime.monday && day <= DateTime.sunday) : true), - focusedDay = _normalizeDate(focusedDay), - firstDay = _normalizeDate(firstDay), - lastDay = _normalizeDate(lastDay), + focusedDay = normalizeDate(focusedDay), + firstDay = normalizeDate(firstDay), + lastDay = normalizeDate(lastDay), super(key: key); @override @@ -419,7 +432,7 @@ class _TableCalendarState extends State> { ValueListenableBuilder( valueListenable: _focusedDay, builder: (context, value, _) { - return _CalendarHeader( + return CalendarHeader( focusedMonth: value, onLeftChevronTap: _onLeftChevronTap, onRightChevronTap: _onRightChevronTap, @@ -556,7 +569,7 @@ class _TableCalendarState extends State> { final isDisabled = _isDayDisabled(day); final isWeekend = _isWeekend(day, weekendDays: widget.weekendDays); - Widget content = _CellContent( + Widget content = CellContent( day: day, focusedDay: focusedDay, calendarStyle: widget.calendarStyle, @@ -699,7 +712,3 @@ class _TableCalendarState extends State> { return weekendDays.contains(day.weekday); } } - -DateTime _normalizeDate(DateTime date) { - return DateTime.utc(date.year, date.month, date.day); -} diff --git a/lib/src/table_calendar_base.dart b/lib/src/table_calendar_base.dart index 6e716bc9..85e36847 100644 --- a/lib/src/table_calendar_base.dart +++ b/lib/src/table_calendar_base.dart @@ -1,46 +1,11 @@ // Copyright 2019 Aleksander Woźniak // SPDX-License-Identifier: Apache-2.0 -part of table_calendar; - -/// Signature for a function that creates a widget for a given `day`. -typedef DayBuilder = Widget Function(BuildContext context, DateTime day); - -/// Signature for a function that creates a widget for a given `day`. -/// Additionally, contains the currently focused day. -typedef FocusedDayBuilder = Widget Function( - BuildContext context, DateTime day, DateTime focusedDay); - -/// Gestures available for the calendar. -enum AvailableGestures { none, verticalSwipe, horizontalSwipe, all } - -/// Formats that the calendar can display. -enum CalendarFormat { month, twoWeeks, week } - -/// Days of the week that the calendar can start with. -enum StartingDayOfWeek { - monday, - tuesday, - wednesday, - thursday, - friday, - saturday, - sunday, -} - -int _getWeekdayNumber(StartingDayOfWeek weekday) { - return StartingDayOfWeek.values.indexOf(weekday) + 1; -} +import 'package:flutter/material.dart'; +import 'package:simple_gesture_detector/simple_gesture_detector.dart'; -/// Checks if two DateTime objects are the same day. -/// Returns `false` if either of them is null. -bool isSameDay(DateTime a, DateTime b) { - if (a == null || b == null) { - return false; - } - - return a.year == b.year && a.month == b.month && a.day == b.day; -} +import 'shared/utils.dart'; +import 'widgets/calendar_core.dart'; class TableCalendarBase extends StatefulWidget { final DateTime firstDay; @@ -215,7 +180,7 @@ class _TableCalendarBaseState extends State ), ); }, - child: _CalendarCore( + child: CalendarCore( constraints: constraints, pageController: _pageController, scrollPhysics: _canScrollHorizontally @@ -319,15 +284,13 @@ class _TableCalendarBaseState extends State } int _getDaysBefore(DateTime firstDay) { - return (firstDay.weekday + - 7 - - _getWeekdayNumber(widget.startingDayOfWeek)) % + return (firstDay.weekday + 7 - getWeekdayNumber(widget.startingDayOfWeek)) % 7; } int _getDaysAfter(DateTime lastDay) { int invertedStartingWeekday = - 8 - _getWeekdayNumber(widget.startingDayOfWeek); + 8 - getWeekdayNumber(widget.startingDayOfWeek); int daysAfter = 7 - ((lastDay.weekday + invertedStartingWeekday) % 7); if (daysAfter == 7) { diff --git a/lib/src/widgets/calendar_core.dart b/lib/src/widgets/calendar_core.dart index 241a6666..5b4e8c74 100644 --- a/lib/src/widgets/calendar_core.dart +++ b/lib/src/widgets/calendar_core.dart @@ -1,12 +1,15 @@ // Copyright 2019 Aleksander Woźniak // SPDX-License-Identifier: Apache-2.0 -part of table_calendar; +import 'package:flutter/material.dart'; + +import '../shared/utils.dart'; +import 'calendar_page.dart'; typedef _OnCalendarPageChanged = void Function( int pageIndex, DateTime focusedDay); -class _CalendarCore extends StatelessWidget { +class CalendarCore extends StatelessWidget { final DateTime focusedDay; final DateTime firstDay; final DateTime lastDay; @@ -26,7 +29,7 @@ class _CalendarCore extends StatelessWidget { final ScrollPhysics scrollPhysics; final _OnCalendarPageChanged onPageChanged; - const _CalendarCore({ + const CalendarCore({ Key key, this.dowBuilder, @required this.dayBuilder, @@ -68,7 +71,7 @@ class _CalendarCore extends StatelessWidget { _getRowCount(calendarFormat, baseDay) : null; - return _CalendarPage( + return CalendarPage( visibleDays: visibleDays, dowVisible: dowVisible, dowDecoration: dowDecoration, @@ -282,11 +285,11 @@ class _CalendarCore extends StatelessWidget { } int _getDaysBefore(DateTime firstDay) { - return (firstDay.weekday + 7 - _getWeekdayNumber(startingDayOfWeek)) % 7; + return (firstDay.weekday + 7 - getWeekdayNumber(startingDayOfWeek)) % 7; } int _getDaysAfter(DateTime lastDay) { - int invertedStartingWeekday = 8 - _getWeekdayNumber(startingDayOfWeek); + int invertedStartingWeekday = 8 - getWeekdayNumber(startingDayOfWeek); int daysAfter = 7 - ((lastDay.weekday + invertedStartingWeekday) % 7); if (daysAfter == 7) { diff --git a/lib/src/widgets/calendar_header.dart b/lib/src/widgets/calendar_header.dart index 1a6a8d8b..d5712150 100644 --- a/lib/src/widgets/calendar_header.dart +++ b/lib/src/widgets/calendar_header.dart @@ -1,9 +1,15 @@ // Copyright 2019 Aleksander Woźniak // SPDX-License-Identifier: Apache-2.0 -part of table_calendar; +import 'package:flutter/widgets.dart'; +import 'package:intl/intl.dart'; -class _CalendarHeader extends StatelessWidget { +import '../customization/header_style.dart'; +import '../shared/utils.dart' show CalendarFormat; +import 'custom_icon_button.dart'; +import 'format_button.dart'; + +class CalendarHeader extends StatelessWidget { final dynamic locale; final DateTime focusedMonth; final CalendarFormat calendarFormat; @@ -15,7 +21,7 @@ class _CalendarHeader extends StatelessWidget { final ValueChanged onFormatButtonTap; final Map availableCalendarFormats; - const _CalendarHeader({ + const CalendarHeader({ Key key, this.locale, @required this.focusedMonth, @@ -43,7 +49,7 @@ class _CalendarHeader extends StatelessWidget { mainAxisSize: MainAxisSize.max, children: [ if (headerStyle.leftChevronVisible) - _CustomIconButton( + CustomIconButton( icon: headerStyle.leftChevronIcon, onTap: onLeftChevronTap, margin: headerStyle.leftChevronMargin, @@ -66,7 +72,7 @@ class _CalendarHeader extends StatelessWidget { availableCalendarFormats.length > 1) Padding( padding: const EdgeInsets.only(left: 8.0), - child: _FormatButton( + child: FormatButton( onTap: onFormatButtonTap, availableCalendarFormats: availableCalendarFormats, calendarFormat: calendarFormat, @@ -77,7 +83,7 @@ class _CalendarHeader extends StatelessWidget { ), ), if (headerStyle.rightChevronVisible) - _CustomIconButton( + CustomIconButton( icon: headerStyle.rightChevronIcon, onTap: onRightChevronTap, margin: headerStyle.rightChevronMargin, diff --git a/lib/src/widgets/calendar_page.dart b/lib/src/widgets/calendar_page.dart index ba9bb111..d2068e61 100644 --- a/lib/src/widgets/calendar_page.dart +++ b/lib/src/widgets/calendar_page.dart @@ -1,9 +1,11 @@ // Copyright 2019 Aleksander Woźniak // SPDX-License-Identifier: Apache-2.0 -part of table_calendar; +import 'package:flutter/widgets.dart'; -class _CalendarPage extends StatelessWidget { +import '../shared/utils.dart' show DayBuilder; + +class CalendarPage extends StatelessWidget { final List visibleDays; final DayBuilder dowBuilder; final DayBuilder dayBuilder; @@ -11,7 +13,7 @@ class _CalendarPage extends StatelessWidget { final Decoration rowDecoration; final bool dowVisible; - const _CalendarPage({ + const CalendarPage({ Key key, @required this.visibleDays, this.dowBuilder, diff --git a/lib/src/widgets/cell_content.dart b/lib/src/widgets/cell_content.dart index a314d0bb..4b779466 100644 --- a/lib/src/widgets/cell_content.dart +++ b/lib/src/widgets/cell_content.dart @@ -1,9 +1,12 @@ // Copyright 2019 Aleksander Woźniak // SPDX-License-Identifier: Apache-2.0 -part of table_calendar; +import 'package:flutter/widgets.dart'; -class _CellContent extends StatelessWidget { +import '../customization/calendar_builders.dart'; +import '../customization/calendar_style.dart'; + +class CellContent extends StatelessWidget { final DateTime day; final DateTime focusedDay; final bool isTodayHighlighted; @@ -19,7 +22,7 @@ class _CellContent extends StatelessWidget { final CalendarStyle calendarStyle; final CalendarBuilders calendarBuilders; - const _CellContent({ + const CellContent({ Key key, @required this.day, @required this.focusedDay, diff --git a/lib/src/widgets/custom_icon_button.dart b/lib/src/widgets/custom_icon_button.dart index eae02312..59528603 100644 --- a/lib/src/widgets/custom_icon_button.dart +++ b/lib/src/widgets/custom_icon_button.dart @@ -1,15 +1,15 @@ // Copyright 2019 Aleksander Woźniak // SPDX-License-Identifier: Apache-2.0 -part of table_calendar; +import 'package:flutter/material.dart'; -class _CustomIconButton extends StatelessWidget { +class CustomIconButton extends StatelessWidget { final Icon icon; final VoidCallback onTap; final EdgeInsets margin; final EdgeInsets padding; - const _CustomIconButton({ + const CustomIconButton({ Key key, @required this.icon, @required this.onTap, diff --git a/lib/src/widgets/format_button.dart b/lib/src/widgets/format_button.dart index 51fccd14..41298276 100644 --- a/lib/src/widgets/format_button.dart +++ b/lib/src/widgets/format_button.dart @@ -1,9 +1,11 @@ // Copyright 2019 Aleksander Woźniak // SPDX-License-Identifier: Apache-2.0 -part of table_calendar; +import 'package:flutter/material.dart'; -class _FormatButton extends StatelessWidget { +import '../shared/utils.dart' show CalendarFormat; + +class FormatButton extends StatelessWidget { final CalendarFormat calendarFormat; final ValueChanged onTap; final TextStyle textStyle; @@ -12,7 +14,7 @@ class _FormatButton extends StatelessWidget { final bool showsNextFormat; final Map availableCalendarFormats; - const _FormatButton({ + const FormatButton({ Key key, @required this.calendarFormat, @required this.onTap, diff --git a/lib/table_calendar.dart b/lib/table_calendar.dart index 03ebfc9f..4115dc30 100644 --- a/lib/table_calendar.dart +++ b/lib/table_calendar.dart @@ -1,23 +1,10 @@ // Copyright 2019 Aleksander Woźniak // SPDX-License-Identifier: Apache-2.0 -library table_calendar; - -import 'dart:math'; - -import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; -import 'package:simple_gesture_detector/simple_gesture_detector.dart'; - -part 'src/customization/calendar_builders.dart'; -part 'src/customization/calendar_style.dart'; -part 'src/customization/days_of_week_style.dart'; -part 'src/customization/header_style.dart'; -part 'src/table_calendar.dart'; -part 'src/table_calendar_base.dart'; -part 'src/widgets/calendar_core.dart'; -part 'src/widgets/calendar_header.dart'; -part 'src/widgets/calendar_page.dart'; -part 'src/widgets/cell_content.dart'; -part 'src/widgets/custom_icon_button.dart'; -part 'src/widgets/format_button.dart'; +export 'src/customization/calendar_builders.dart'; +export 'src/customization/calendar_style.dart'; +export 'src/customization/days_of_week_style.dart'; +export 'src/customization/header_style.dart'; +export 'src/shared/utils.dart'; +export 'src/table_calendar.dart'; +export 'src/table_calendar_base.dart'; From 80f414ebfdb630d60be7ed867c13f9ebeafb4912 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Fri, 19 Feb 2021 18:22:51 +0545 Subject: [PATCH 065/164] Feat header title builder (#400) Add headerTitleBuilder feature --- lib/src/customization/calendar_builders.dart | 4 ++++ lib/src/table_calendar.dart | 1 + lib/src/widgets/calendar_header.dart | 6 ++++-- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/src/customization/calendar_builders.dart b/lib/src/customization/calendar_builders.dart index 12317dfd..15409f90 100644 --- a/lib/src/customization/calendar_builders.dart +++ b/lib/src/customization/calendar_builders.dart @@ -75,6 +75,9 @@ class CalendarBuilders { /// Custom builder for days of the week labels (Mon, Tue, Wed, etc.). final DayBuilder dowBuilder; + /// Use to customize header's title using different widget + final DayBuilder headerTitleBuilder; + /// Creates `CalendarBuilders` for `TableCalendar` widget. const CalendarBuilders({ this.prioritizedBuilder, @@ -91,5 +94,6 @@ class CalendarBuilders { this.singleMarkerBuilder, this.markerBuilder, this.dowBuilder, + this.headerTitleBuilder, }); } diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index 7d91f0f7..0f9f017a 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -433,6 +433,7 @@ class _TableCalendarState extends State> { valueListenable: _focusedDay, builder: (context, value, _) { return CalendarHeader( + headerTitleBuilder: widget.calendarBuilders.headerTitleBuilder, focusedMonth: value, onLeftChevronTap: _onLeftChevronTap, onRightChevronTap: _onRightChevronTap, diff --git a/lib/src/widgets/calendar_header.dart b/lib/src/widgets/calendar_header.dart index d5712150..e5728b65 100644 --- a/lib/src/widgets/calendar_header.dart +++ b/lib/src/widgets/calendar_header.dart @@ -5,7 +5,7 @@ import 'package:flutter/widgets.dart'; import 'package:intl/intl.dart'; import '../customization/header_style.dart'; -import '../shared/utils.dart' show CalendarFormat; +import '../shared/utils.dart' show CalendarFormat, DayBuilder; import 'custom_icon_button.dart'; import 'format_button.dart'; @@ -20,10 +20,12 @@ class CalendarHeader extends StatelessWidget { final VoidCallback onHeaderLongPress; final ValueChanged onFormatButtonTap; final Map availableCalendarFormats; + final DayBuilder headerTitleBuilder; const CalendarHeader({ Key key, this.locale, + this.headerTitleBuilder, @required this.focusedMonth, @required this.calendarFormat, @required this.headerStyle, @@ -56,7 +58,7 @@ class CalendarHeader extends StatelessWidget { padding: headerStyle.leftChevronPadding, ), Expanded( - child: GestureDetector( + child: headerTitleBuilder?.call(context, focusedMonth) ?? GestureDetector( onTap: onHeaderTap, onLongPress: onHeaderLongPress, child: Text( From f1883b674d6aff189af4653638c3c17ef15ab9d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sat, 20 Feb 2021 16:32:11 +0100 Subject: [PATCH 066/164] Add prioritized onDayLongPressed callback --- lib/src/table_calendar.dart | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index 0f9f017a..6dd18ef1 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -16,7 +16,7 @@ import 'table_calendar_base.dart'; import 'widgets/calendar_header.dart'; import 'widgets/cell_content.dart'; -/// Signature for `onDaySelected` callback. Contains currently selected day and focused day. +/// Signature for `onDaySelected` callback. Contains the selected day and focused day. typedef OnDaySelected = void Function( DateTime selectedDay, DateTime focusedDay); @@ -149,9 +149,6 @@ class TableCalendar extends StatefulWidget { /// * `RangeSelectionMode.enforced` - range selection is always on. final RangeSelectionMode rangeSelectionMode; - /// Called whenever a day range gets selected. - final OnRangeSelected onRangeSelected; - /// Function that assigns a list of events to a specified day. final List Function(DateTime day) eventLoader; @@ -165,9 +162,15 @@ class TableCalendar extends StatefulWidget { /// Function deciding whether given day is treated as a holiday. final bool Function(DateTime day) holidayPredicate; + /// Called whenever a day range gets selected. + final OnRangeSelected onRangeSelected; + /// Called whenever any day gets tapped. final OnDaySelected onDaySelected; + /// Called whenever any day gets long pressed. + final OnDaySelected onDayLongPressed; + /// Called whenever any disabled day gets tapped. final void Function(DateTime day) onDisabledDayTapped; @@ -228,12 +231,13 @@ class TableCalendar extends StatefulWidget { this.calendarStyle = const CalendarStyle(), this.calendarBuilders = const CalendarBuilders(), this.rangeSelectionMode, - this.onRangeSelected, this.eventLoader, this.enabledDayPredicate, this.selectedDayPredicate, this.holidayPredicate, + this.onRangeSelected, this.onDaySelected, + this.onDayLongPressed, this.onDisabledDayTapped, this.onDisabledDayLongPressed, this.onHeaderTapped, @@ -367,6 +371,11 @@ class _TableCalendarState extends State> { return widget.onDisabledDayLongPressed?.call(day); } + if (widget.onDayLongPressed != null) { + _updateFocusOnTap(day); + return widget.onDayLongPressed(day, _focusedDay.value); + } + if (widget.onRangeSelected != null) { if (_isRangeSelectionToggleable) { _updateFocusOnTap(day); From c43c087a8b69a316fe9a2367702e2d0db486deeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 21 Feb 2021 21:37:06 +0100 Subject: [PATCH 067/164] Update basic example --- example/lib/pages/table_basics_example.dart | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/example/lib/pages/table_basics_example.dart b/example/lib/pages/table_basics_example.dart index 0e5fc90f..68f18512 100644 --- a/example/lib/pages/table_basics_example.dart +++ b/example/lib/pages/table_basics_example.dart @@ -38,11 +38,9 @@ class _TableBasicsExampleState extends State { // Call `setState()` when updating the selected day setState(() { _selectedDay = selectedDay; + _focusedDay = focusedDay; }); } - - // Don't call `setState()` when updating just the focused day - _focusedDay = focusedDay; }, onFormatChanged: (format) { if (_calendarFormat != format) { @@ -53,7 +51,7 @@ class _TableBasicsExampleState extends State { } }, onPageChanged: (focusedDay) { - // Don't call `setState()` when updating just the focused day + // No need to call `setState()` here _focusedDay = focusedDay; }, ), From bf57f791ac631c1ce71a4b6bd2a5d7616f4cb586 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 21 Feb 2021 22:12:47 +0100 Subject: [PATCH 068/164] Link firstDay and lastDay to the current date --- example/lib/pages/table_basics_example.dart | 6 ++++-- example/lib/pages/table_events_example.dart | 10 ++++------ example/lib/pages/table_multi_example.dart | 15 ++++++++------- example/lib/pages/table_range_example.dart | 12 ++++++------ example/lib/utils.dart | 4 ++++ 5 files changed, 26 insertions(+), 21 deletions(-) diff --git a/example/lib/pages/table_basics_example.dart b/example/lib/pages/table_basics_example.dart index 68f18512..03165dd3 100644 --- a/example/lib/pages/table_basics_example.dart +++ b/example/lib/pages/table_basics_example.dart @@ -4,6 +4,8 @@ import 'package:flutter/material.dart'; import 'package:table_calendar/table_calendar.dart'; +import '../utils.dart'; + class TableBasicsExample extends StatefulWidget { @override _TableBasicsExampleState createState() => _TableBasicsExampleState(); @@ -21,8 +23,8 @@ class _TableBasicsExampleState extends State { title: Text('TableCalendar - Basics'), ), body: TableCalendar( - firstDay: DateTime.utc(2020, 10, 16), - lastDay: DateTime.utc(2021, 3, 14), + firstDay: kFirstDay, + lastDay: kLastDay, focusedDay: _focusedDay, calendarFormat: _calendarFormat, selectedDayPredicate: (day) { diff --git a/example/lib/pages/table_events_example.dart b/example/lib/pages/table_events_example.dart index 68b8a8cc..f77a9489 100644 --- a/example/lib/pages/table_events_example.dart +++ b/example/lib/pages/table_events_example.dart @@ -54,6 +54,7 @@ class _TableEventsExampleState extends State { if (!isSameDay(_selectedDay, selectedDay)) { setState(() { _selectedDay = selectedDay; + _focusedDay = focusedDay; _rangeStart = null; // Important to clean those _rangeEnd = null; _rangeSelectionMode = RangeSelectionMode.toggledOff; @@ -61,20 +62,17 @@ class _TableEventsExampleState extends State { _selectedEvents.value = _getEventsForDay(selectedDay); } - - _focusedDay = focusedDay; } void _onRangeSelected(DateTime start, DateTime end, DateTime focusedDay) { setState(() { _selectedDay = null; + _focusedDay = focusedDay; _rangeStart = start; _rangeEnd = end; _rangeSelectionMode = RangeSelectionMode.toggledOn; }); - _focusedDay = focusedDay; - // `start` or `end` could be null if (start != null && end != null) { _selectedEvents.value = _getEventsForRange(start, end); @@ -94,8 +92,8 @@ class _TableEventsExampleState extends State { body: Column( children: [ TableCalendar( - firstDay: DateTime.utc(2020, 10, 16), - lastDay: DateTime.utc(2021, 3, 14), + firstDay: kFirstDay, + lastDay: kLastDay, focusedDay: _focusedDay, selectedDayPredicate: (day) => isSameDay(_selectedDay, day), rangeStartDay: _rangeStart, diff --git a/example/lib/pages/table_multi_example.dart b/example/lib/pages/table_multi_example.dart index 21f0c1de..25097a6d 100644 --- a/example/lib/pages/table_multi_example.dart +++ b/example/lib/pages/table_multi_example.dart @@ -52,8 +52,9 @@ class _TableMultiExampleState extends State { } void _onDaySelected(DateTime selectedDay, DateTime focusedDay) { - // Update values in a Set setState(() { + _focusedDay = focusedDay; + // Update values in a Set if (_selectedDays.contains(selectedDay)) { _selectedDays.remove(selectedDay); } else { @@ -62,7 +63,6 @@ class _TableMultiExampleState extends State { }); _selectedEvents.value = _getEventsForDays(_selectedDays); - _focusedDay = focusedDay; } @override @@ -74,8 +74,8 @@ class _TableMultiExampleState extends State { body: Column( children: [ TableCalendar( - firstDay: DateTime.utc(2020, 10, 16), - lastDay: DateTime.utc(2021, 3, 14), + firstDay: kFirstDay, + lastDay: kLastDay, focusedDay: _focusedDay, calendarFormat: _calendarFormat, eventLoader: _getEventsForDay, @@ -99,9 +99,10 @@ class _TableMultiExampleState extends State { RaisedButton( child: Text('Clear selection'), onPressed: () { - setState(() {}); - _selectedDays.clear(); - _selectedEvents.value.clear(); + setState(() { + _selectedDays.clear(); + _selectedEvents.value = []; + }); }, ), Expanded( diff --git a/example/lib/pages/table_range_example.dart b/example/lib/pages/table_range_example.dart index 51ccec1f..79308c2d 100644 --- a/example/lib/pages/table_range_example.dart +++ b/example/lib/pages/table_range_example.dart @@ -4,6 +4,8 @@ import 'package:flutter/material.dart'; import 'package:table_calendar/table_calendar.dart'; +import '../utils.dart'; + class TableRangeExample extends StatefulWidget { @override _TableRangeExampleState createState() => _TableRangeExampleState(); @@ -25,8 +27,8 @@ class _TableRangeExampleState extends State { title: Text('TableCalendar - Range'), ), body: TableCalendar( - firstDay: DateTime.utc(2020, 10, 16), - lastDay: DateTime.utc(2021, 3, 14), + firstDay: kFirstDay, + lastDay: kLastDay, focusedDay: _focusedDay, selectedDayPredicate: (day) => isSameDay(_selectedDay, day), rangeStartDay: _rangeStart, @@ -37,23 +39,21 @@ class _TableRangeExampleState extends State { if (!isSameDay(_selectedDay, selectedDay)) { setState(() { _selectedDay = selectedDay; + _focusedDay = focusedDay; _rangeStart = null; // Important to clean those _rangeEnd = null; _rangeSelectionMode = RangeSelectionMode.toggledOff; }); } - - _focusedDay = focusedDay; }, onRangeSelected: (start, end, focusedDay) { setState(() { _selectedDay = null; + _focusedDay = focusedDay; _rangeStart = start; _rangeEnd = end; _rangeSelectionMode = RangeSelectionMode.toggledOn; }); - - _focusedDay = focusedDay; }, onFormatChanged: (format) { if (_calendarFormat != format) { diff --git a/example/lib/utils.dart b/example/lib/utils.dart index 4027d5db..06a6bfc2 100644 --- a/example/lib/utils.dart +++ b/example/lib/utils.dart @@ -46,3 +46,7 @@ List daysInRange(DateTime first, DateTime last) { (index) => DateTime.utc(first.year, first.month, first.day + index), ); } + +final kNow = DateTime.now(); +final kFirstDay = DateTime(kNow.year, kNow.month - 3, kNow.day); +final kLastDay = DateTime(kNow.year, kNow.month + 3, kNow.day); From f772f890fd4567b143f00c01801d7966476aee40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 21 Feb 2021 22:14:56 +0100 Subject: [PATCH 069/164] Improve control over range selection --- lib/src/table_calendar.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index 6dd18ef1..efe96993 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -292,6 +292,10 @@ class _TableCalendarState extends State> { _rangeSelectionMode != widget.rangeSelectionMode) { _rangeSelectionMode = widget.rangeSelectionMode; } + + if (widget.rangeStartDay == null && widget.rangeEndDay == null) { + _firstSelectedDay = null; + } } @override From f823e7bd31f15030dc45f5848d77ad54a69ac4cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 21 Feb 2021 22:17:31 +0100 Subject: [PATCH 070/164] Update examples --- example/lib/pages/table_events_example.dart | 1 + example/lib/pages/table_multi_example.dart | 1 + example/lib/utils.dart | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/example/lib/pages/table_events_example.dart b/example/lib/pages/table_events_example.dart index f77a9489..922c7410 100644 --- a/example/lib/pages/table_events_example.dart +++ b/example/lib/pages/table_events_example.dart @@ -119,6 +119,7 @@ class _TableEventsExampleState extends State { _focusedDay = focusedDay; }, ), + const SizedBox(height: 8.0), Expanded( child: ValueListenableBuilder>( valueListenable: _selectedEvents, diff --git a/example/lib/pages/table_multi_example.dart b/example/lib/pages/table_multi_example.dart index 25097a6d..6d219354 100644 --- a/example/lib/pages/table_multi_example.dart +++ b/example/lib/pages/table_multi_example.dart @@ -105,6 +105,7 @@ class _TableMultiExampleState extends State { }); }, ), + const SizedBox(height: 8.0), Expanded( child: ValueListenableBuilder>( valueListenable: _selectedEvents, diff --git a/example/lib/utils.dart b/example/lib/utils.dart index 06a6bfc2..2a579259 100644 --- a/example/lib/utils.dart +++ b/example/lib/utils.dart @@ -18,7 +18,7 @@ class Event { /// Example events. /// /// Using a [LinkedHashMap] is highly recommended if you decide to use a map. -final kEvents = LinkedHashMap( +final kEvents = LinkedHashMap>( equals: isSameDay, hashCode: getHashCode, )..addAll(_kEventSource); From 354d51d686a928fe0457da8720cb0763eedae5cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 21 Feb 2021 22:21:23 +0100 Subject: [PATCH 071/164] Add complex example --- example/lib/main.dart | 9 + example/lib/pages/table_complex_example.dart | 253 +++++++++++++++++++ 2 files changed, 262 insertions(+) create mode 100644 example/lib/pages/table_complex_example.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index 1f4f1358..670abc8b 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:intl/date_symbol_data_local.dart'; import 'pages/table_basics_example.dart'; +import 'pages/table_complex_example.dart'; import 'pages/table_events_example.dart'; import 'pages/table_multi_example.dart'; import 'pages/table_range_example.dart'; @@ -74,6 +75,14 @@ class _StartPageState extends State { MaterialPageRoute(builder: (_) => TableMultiExample()), ), ), + const SizedBox(height: 12.0), + RaisedButton( + child: Text('TableCalendar - Complex'), + onPressed: () => Navigator.push( + context, + MaterialPageRoute(builder: (_) => TableComplexExample()), + ), + ), const SizedBox(height: 20.0), ], ), diff --git a/example/lib/pages/table_complex_example.dart b/example/lib/pages/table_complex_example.dart new file mode 100644 index 00000000..3e259b83 --- /dev/null +++ b/example/lib/pages/table_complex_example.dart @@ -0,0 +1,253 @@ +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 + +import 'dart:collection'; + +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:table_calendar/table_calendar.dart'; + +import '../utils.dart'; + +class TableComplexExample extends StatefulWidget { + @override + _TableComplexExampleState createState() => _TableComplexExampleState(); +} + +class _TableComplexExampleState extends State { + CalendarFormat _calendarFormat = CalendarFormat.month; + RangeSelectionMode _rangeSelectionMode = RangeSelectionMode.toggledOff; + DateTime _rangeStart; + DateTime _rangeEnd; + Set _selectedDays; + ValueNotifier _focusedDay; + ValueNotifier> _selectedEvents; + PageController _pageController; + + @override + void initState() { + super.initState(); + + _selectedDays = LinkedHashSet( + equals: isSameDay, + hashCode: getHashCode, + ); + + _focusedDay = ValueNotifier(DateTime.now()); + _selectedDays.add(_focusedDay.value); + _selectedEvents = ValueNotifier(_getEventsForDay(_focusedDay.value)); + } + + @override + void dispose() { + _focusedDay.dispose(); + _selectedEvents.dispose(); + super.dispose(); + } + + bool get canClearSelection => + _selectedDays.isNotEmpty || _rangeStart != null || _rangeEnd != null; + + List _getEventsForDay(DateTime day) { + return kEvents[day] ?? []; + } + + List _getEventsForDays(Iterable days) { + return [ + for (final d in days) ..._getEventsForDay(d), + ]; + } + + List _getEventsForRange(DateTime start, DateTime end) { + final days = daysInRange(start, end); + return _getEventsForDays(days); + } + + void _onDaySelected(DateTime selectedDay, DateTime focusedDay) { + setState(() { + if (_selectedDays.contains(selectedDay)) { + _selectedDays.remove(selectedDay); + } else { + _selectedDays.add(selectedDay); + } + + _focusedDay.value = focusedDay; + _rangeStart = null; + _rangeEnd = null; + _rangeSelectionMode = RangeSelectionMode.toggledOff; + }); + + _selectedEvents.value = _getEventsForDays(_selectedDays); + } + + void _onRangeSelected(DateTime start, DateTime end, DateTime focusedDay) { + setState(() { + _focusedDay.value = focusedDay; + _rangeStart = start; + _rangeEnd = end; + _selectedDays.clear(); + _rangeSelectionMode = RangeSelectionMode.toggledOn; + }); + + if (start != null && end != null) { + _selectedEvents.value = _getEventsForRange(start, end); + } else if (start != null) { + _selectedEvents.value = _getEventsForDay(start); + } else { + _selectedEvents.value = _getEventsForDay(end); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('TableCalendar - Complex'), + ), + body: Column( + children: [ + ValueListenableBuilder( + valueListenable: _focusedDay, + builder: (context, value, _) { + return _CalendarHeader( + focusedDay: value, + clearButtonVisible: canClearSelection, + onTodayButtonTap: () { + setState(() => _focusedDay.value = DateTime.now()); + }, + onClearButtonTap: () { + setState(() { + _rangeStart = null; + _rangeEnd = null; + _selectedDays.clear(); + _selectedEvents.value = []; + }); + }, + onLeftArrowTap: () { + _pageController.previousPage( + duration: Duration(milliseconds: 300), + curve: Curves.easeOut, + ); + }, + onRightArrowTap: () { + _pageController.nextPage( + duration: Duration(milliseconds: 300), + curve: Curves.easeOut, + ); + }, + ); + }, + ), + TableCalendar( + firstDay: kFirstDay, + lastDay: kLastDay, + focusedDay: _focusedDay.value, + headerVisible: false, + selectedDayPredicate: (day) => _selectedDays.contains(day), + rangeStartDay: _rangeStart, + rangeEndDay: _rangeEnd, + calendarFormat: _calendarFormat, + rangeSelectionMode: _rangeSelectionMode, + eventLoader: _getEventsForDay, + onDaySelected: _onDaySelected, + onRangeSelected: _onRangeSelected, + onCalendarCreated: (controller) => _pageController = controller, + onPageChanged: (focusedDay) => _focusedDay.value = focusedDay, + onFormatChanged: (format) { + if (_calendarFormat != format) { + setState(() => _calendarFormat = format); + } + }, + ), + const SizedBox(height: 8.0), + Expanded( + child: ValueListenableBuilder>( + valueListenable: _selectedEvents, + builder: (context, value, _) { + return ListView.builder( + itemCount: value.length, + itemBuilder: (context, index) { + return Container( + margin: const EdgeInsets.symmetric( + horizontal: 12.0, + vertical: 4.0, + ), + decoration: BoxDecoration( + border: Border.all(), + borderRadius: BorderRadius.circular(12.0), + ), + child: ListTile( + onTap: () => print('${value[index]}'), + title: Text('${value[index]}'), + ), + ); + }, + ); + }, + ), + ), + ], + ), + ); + } +} + +class _CalendarHeader extends StatelessWidget { + final DateTime focusedDay; + final VoidCallback onLeftArrowTap; + final VoidCallback onRightArrowTap; + final VoidCallback onTodayButtonTap; + final VoidCallback onClearButtonTap; + final bool clearButtonVisible; + + const _CalendarHeader({ + Key key, + @required this.focusedDay, + @required this.onLeftArrowTap, + @required this.onRightArrowTap, + @required this.onTodayButtonTap, + @required this.onClearButtonTap, + @required this.clearButtonVisible, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final headerText = DateFormat.yMMM().format(focusedDay); + + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Row( + children: [ + const SizedBox(width: 16.0), + SizedBox( + width: 120.0, + child: Text( + headerText, + style: TextStyle(fontSize: 26.0), + ), + ), + IconButton( + icon: Icon(Icons.calendar_today, size: 20.0), + visualDensity: VisualDensity.compact, + onPressed: onTodayButtonTap, + ), + if (clearButtonVisible) + IconButton( + icon: Icon(Icons.clear, size: 20.0), + visualDensity: VisualDensity.compact, + onPressed: onClearButtonTap, + ), + const Spacer(), + IconButton( + icon: Icon(Icons.chevron_left), + onPressed: onLeftArrowTap, + ), + IconButton( + icon: Icon(Icons.chevron_right), + onPressed: onRightArrowTap, + ), + ], + ), + ); + } +} From 79433e4f1235c4db505447d3cb023579cc82b9b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Mon, 22 Feb 2021 17:54:32 +0100 Subject: [PATCH 072/164] Migrate package to null safety --- lib/src/customization/calendar_builders.dart | 30 +++--- lib/src/customization/calendar_style.dart | 10 +- lib/src/customization/days_of_week_style.dart | 2 +- lib/src/customization/header_style.dart | 4 +- lib/src/shared/utils.dart | 2 +- lib/src/table_calendar.dart | 99 +++++++++---------- lib/src/table_calendar_base.dart | 43 ++++---- lib/src/widgets/calendar_core.dart | 66 +++++++------ lib/src/widgets/calendar_header.dart | 50 +++++----- lib/src/widgets/calendar_page.dart | 16 ++- lib/src/widgets/cell_content.dart | 32 +++--- lib/src/widgets/custom_icon_button.dart | 10 +- lib/src/widgets/format_button.dart | 22 ++--- pubspec.lock | 44 ++++----- pubspec.yaml | 6 +- 15 files changed, 211 insertions(+), 225 deletions(-) diff --git a/lib/src/customization/calendar_builders.dart b/lib/src/customization/calendar_builders.dart index 15409f90..ddc42b08 100644 --- a/lib/src/customization/calendar_builders.dart +++ b/lib/src/customization/calendar_builders.dart @@ -25,58 +25,58 @@ typedef HighlightBuilder = Widget Function( /// Class containing all custom builders for `TableCalendar`. class CalendarBuilders { /// Custom builder for day cells, with a priority over any other builder. - final FocusedDayBuilder prioritizedBuilder; + final FocusedDayBuilder? prioritizedBuilder; /// Custom builder for a day cell that matches `DateTime.now()`. - final FocusedDayBuilder todayBuilder; + final FocusedDayBuilder? todayBuilder; /// Custom builder for day cells that are currently marked as selected by `selectedDayPredicate`. - final FocusedDayBuilder selectedBuilder; + final FocusedDayBuilder? selectedBuilder; /// Custom builder for a day cell that is the start of current range selection. - final FocusedDayBuilder rangeStartBuilder; + final FocusedDayBuilder? rangeStartBuilder; /// Custom builder for a day cell that is the end of current range selection. - final FocusedDayBuilder rangeEndBuilder; + final FocusedDayBuilder? rangeEndBuilder; /// Custom builder for day cells that fall within the currently selected range. - final FocusedDayBuilder withinRangeBuilder; + final FocusedDayBuilder? withinRangeBuilder; /// Custom builder for day cells, of which the `day.month` is different than `focusedDay.month`. /// This will affect day cells that do not match the currently focused month. - final FocusedDayBuilder outsideBuilder; + final FocusedDayBuilder? outsideBuilder; /// Custom builder for day cells that have been disabled. /// /// This refers to dates disabled by returning false in `enabledDayPredicate`, /// as well as dates that are outside of the bounds set up by `firstDay` and `lastDay`. - final FocusedDayBuilder disabledBuilder; + final FocusedDayBuilder? disabledBuilder; /// Custom builder for day cells that are marked as holidays by `holidayPredicate`. - final FocusedDayBuilder holidayBuilder; + final FocusedDayBuilder? holidayBuilder; /// Custom builder for day cells that do not match any other builder. - final FocusedDayBuilder defaultBuilder; + final FocusedDayBuilder? defaultBuilder; /// Custom builder for background highlight of range selection. /// If `isWithinRange` is true, then `day` is within the selected range. - final HighlightBuilder rangeHighlightBuilder; + final HighlightBuilder? rangeHighlightBuilder; /// Custom builder for a single event marker. Each of those will be displayed in a `Row` above of the day cell. /// You can adjust markers' position with `CalendarStyle` properties. /// /// If `singleMarkerBuilder` is not specified, a default event marker will be displayed (customizable with `CalendarStyle`). - final SingleMarkerBuilder singleMarkerBuilder; + final SingleMarkerBuilder? singleMarkerBuilder; /// Custom builder for event markers. Use to provide your own marker UI for each day cell. /// Using `markerBuilder` will override `singleMarkerBuilder` and default event markers. - final MarkerBuilder markerBuilder; + final MarkerBuilder? markerBuilder; /// Custom builder for days of the week labels (Mon, Tue, Wed, etc.). - final DayBuilder dowBuilder; + final DayBuilder? dowBuilder; /// Use to customize header's title using different widget - final DayBuilder headerTitleBuilder; + final DayBuilder? headerTitleBuilder; /// Creates `CalendarBuilders` for `TableCalendar` widget. const CalendarBuilders({ diff --git a/lib/src/customization/calendar_style.dart b/lib/src/customization/calendar_style.dart index ab28a3cd..bfb078d5 100644 --- a/lib/src/customization/calendar_style.dart +++ b/lib/src/customization/calendar_style.dart @@ -26,7 +26,7 @@ class CalendarStyle { /// The size of single event marker dot. /// /// By default `markerSizeScale` is used. To use `markerSize` instead, simply provide a non-null value. - final double markerSize; + final double? markerSize; /// Proportion of single event marker dot size in relation to day cell size. /// @@ -212,16 +212,16 @@ class CalendarStyle { /// Helper class containing data for internal `Positioned` widget. class PositionedOffset { /// Distance from the top edge. - final double top; + final double? top; /// Distance from the bottom edge. - final double bottom; + final double? bottom; /// Distance from the leading edge. - final double start; + final double? start; /// Distance from the trailing edge. - final double end; + final double? end; /// Creates a `PositionedOffset`. Values are set to `null` by default. const PositionedOffset({this.top, this.bottom, this.start, this.end}); diff --git a/lib/src/customization/days_of_week_style.dart b/lib/src/customization/days_of_week_style.dart index 99785fe9..b379ba94 100644 --- a/lib/src/customization/days_of_week_style.dart +++ b/lib/src/customization/days_of_week_style.dart @@ -15,7 +15,7 @@ class DaysOfWeekStyle { /// ```dart /// dowTextFormatter: (date, locale) => DateFormat.E(locale).format(date)[0], /// ``` - final TextFormatter dowTextFormatter; + final TextFormatter? dowTextFormatter; /// Decoration for the top row of the table final Decoration decoration; diff --git a/lib/src/customization/header_style.dart b/lib/src/customization/header_style.dart index 2f1d1da6..324fefba 100644 --- a/lib/src/customization/header_style.dart +++ b/lib/src/customization/header_style.dart @@ -26,7 +26,7 @@ class HeaderStyle { /// ```dart /// titleTextFormatter: (date, locale) => DateFormat.yM(locale).format(date), /// ``` - final TextFormatter titleTextFormatter; + final TextFormatter? titleTextFormatter; /// Style for title Text (month-year) displayed in header. final TextStyle titleTextStyle; @@ -89,7 +89,7 @@ class HeaderStyle { border: const Border.fromBorderSide(BorderSide()), borderRadius: const BorderRadius.all(Radius.circular(12.0)), ), - this.headerMargin, + this.headerMargin = const EdgeInsets.all(0.0), this.headerPadding = const EdgeInsets.symmetric(vertical: 8.0), this.formatButtonPadding = const EdgeInsets.symmetric(horizontal: 10.0, vertical: 4.0), diff --git a/lib/src/shared/utils.dart b/lib/src/shared/utils.dart index 0d861ce6..4ee7da50 100644 --- a/lib/src/shared/utils.dart +++ b/lib/src/shared/utils.dart @@ -42,7 +42,7 @@ DateTime normalizeDate(DateTime date) { /// Checks if two DateTime objects are the same day. /// Returns `false` if either of them is null. -bool isSameDay(DateTime a, DateTime b) { +bool isSameDay(DateTime? a, DateTime? b) { if (a == null || b == null) { return false; } diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index efe96993..52935dae 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -23,7 +23,7 @@ typedef OnDaySelected = void Function( /// Signature for `onRangeSelected` callback. /// Contains start and end of the selected range, as well as currently focused day. typedef OnRangeSelected = void Function( - DateTime start, DateTime end, DateTime focusedDay); + DateTime? start, DateTime? end, DateTime focusedDay); /// Modes that range selection can operate in. enum RangeSelectionMode { disabled, toggledOff, toggledOn, enforced } @@ -36,10 +36,10 @@ class TableCalendar extends StatefulWidget { final dynamic locale; /// The start of the selected day range. - final DateTime rangeStartDay; + final DateTime? rangeStartDay; /// The end of the selected day range. - final DateTime rangeEndDay; + final DateTime? rangeEndDay; /// DateTime that determines which days are currently visible and focused. final DateTime focusedDay; @@ -150,54 +150,54 @@ class TableCalendar extends StatefulWidget { final RangeSelectionMode rangeSelectionMode; /// Function that assigns a list of events to a specified day. - final List Function(DateTime day) eventLoader; + final List Function(DateTime day)? eventLoader; /// Function deciding whether given day should be enabled or not. /// If `false` is returned, this day will be disabled. - final bool Function(DateTime day) enabledDayPredicate; + final bool Function(DateTime day)? enabledDayPredicate; /// Function deciding whether given day should be marked as selected. - final bool Function(DateTime day) selectedDayPredicate; + final bool Function(DateTime day)? selectedDayPredicate; /// Function deciding whether given day is treated as a holiday. - final bool Function(DateTime day) holidayPredicate; + final bool Function(DateTime day)? holidayPredicate; /// Called whenever a day range gets selected. - final OnRangeSelected onRangeSelected; + final OnRangeSelected? onRangeSelected; /// Called whenever any day gets tapped. - final OnDaySelected onDaySelected; + final OnDaySelected? onDaySelected; /// Called whenever any day gets long pressed. - final OnDaySelected onDayLongPressed; + final OnDaySelected? onDayLongPressed; /// Called whenever any disabled day gets tapped. - final void Function(DateTime day) onDisabledDayTapped; + final void Function(DateTime day)? onDisabledDayTapped; /// Called whenever any disabled day gets long pressed. - final void Function(DateTime day) onDisabledDayLongPressed; + final void Function(DateTime day)? onDisabledDayLongPressed; /// Called whenever header gets tapped. - final void Function(DateTime focusedDay) onHeaderTapped; + final void Function(DateTime focusedDay)? onHeaderTapped; /// Called whenever header gets long pressed. - final void Function(DateTime focusedDay) onHeaderLongPressed; + final void Function(DateTime focusedDay)? onHeaderLongPressed; /// Called whenever currently visible calendar page is changed. - final void Function(DateTime focusedDay) onPageChanged; + final void Function(DateTime focusedDay)? onPageChanged; /// Called whenever `calendarFormat` is changed. - final void Function(CalendarFormat format) onFormatChanged; + final void Function(CalendarFormat format)? onFormatChanged; /// Called when the calendar is created. Exposes its PageController. - final void Function(PageController pageController) onCalendarCreated; + final void Function(PageController pageController)? onCalendarCreated; /// Creates a `TableCalendar` widget. TableCalendar({ - Key key, - @required DateTime focusedDay, - @required DateTime firstDay, - @required DateTime lastDay, + Key? key, + required DateTime focusedDay, + required DateTime firstDay, + required DateTime lastDay, this.locale, this.rangeStartDay, this.rangeEndDay, @@ -230,7 +230,7 @@ class TableCalendar extends StatefulWidget { this.daysOfWeekStyle = const DaysOfWeekStyle(), this.calendarStyle = const CalendarStyle(), this.calendarBuilders = const CalendarBuilders(), - this.rangeSelectionMode, + this.rangeSelectionMode = RangeSelectionMode.toggledOff, this.eventLoader, this.enabledDayPredicate, this.selectedDayPredicate, @@ -245,14 +245,8 @@ class TableCalendar extends StatefulWidget { this.onPageChanged, this.onFormatChanged, this.onCalendarCreated, - }) : assert(calendarFormat != null), - assert(rowHeight != null), - assert(firstDay != null), - assert(lastDay != null), - assert(focusedDay != null), - assert(availableCalendarFormats.keys.contains(calendarFormat)), + }) : assert(availableCalendarFormats.keys.contains(calendarFormat)), assert(availableCalendarFormats.length <= CalendarFormat.values.length), - assert(weekendDays != null), assert(weekendDays.isNotEmpty ? weekendDays.every( (day) => day >= DateTime.monday && day <= DateTime.sunday) @@ -267,29 +261,27 @@ class TableCalendar extends StatefulWidget { } class _TableCalendarState extends State> { - PageController _pageController; - ValueNotifier _focusedDay; - DateTime _firstSelectedDay; - RangeSelectionMode _rangeSelectionMode; + late final PageController _pageController; + late final ValueNotifier _focusedDay; + late RangeSelectionMode _rangeSelectionMode; + DateTime? _firstSelectedDay; @override void initState() { super.initState(); _focusedDay = ValueNotifier(widget.focusedDay); - _rangeSelectionMode = - widget.rangeSelectionMode ?? RangeSelectionMode.toggledOff; + _rangeSelectionMode = widget.rangeSelectionMode; } @override - void didUpdateWidget(TableCalendar oldWidget) { + void didUpdateWidget(TableCalendar oldWidget) { super.didUpdateWidget(oldWidget); if (_focusedDay.value != widget.focusedDay) { _focusedDay.value = widget.focusedDay; } - if (widget.rangeSelectionMode != null && - _rangeSelectionMode != widget.rangeSelectionMode) { + if (_rangeSelectionMode != widget.rangeSelectionMode) { _rangeSelectionMode = widget.rangeSelectionMode; } @@ -331,7 +323,7 @@ class _TableCalendarState extends State> { id = max(0, id - 1); } - widget.onFormatChanged(formats[id]); + widget.onFormatChanged!(formats[id]); } } @@ -350,13 +342,13 @@ class _TableCalendarState extends State> { if (_isRangeSelectionOn && widget.onRangeSelected != null) { if (_firstSelectedDay == null) { _firstSelectedDay = day; - widget.onRangeSelected(_firstSelectedDay, null, _focusedDay.value); + widget.onRangeSelected!(_firstSelectedDay, null, _focusedDay.value); } else { - if (day.isAfter(_firstSelectedDay)) { - widget.onRangeSelected(_firstSelectedDay, day, _focusedDay.value); + if (day.isAfter(_firstSelectedDay!)) { + widget.onRangeSelected!(_firstSelectedDay, day, _focusedDay.value); _firstSelectedDay = null; - } else if (day.isBefore(_firstSelectedDay)) { - widget.onRangeSelected(day, _firstSelectedDay, _focusedDay.value); + } else if (day.isBefore(_firstSelectedDay!)) { + widget.onRangeSelected!(day, _firstSelectedDay, _focusedDay.value); _firstSelectedDay = null; } } @@ -377,7 +369,7 @@ class _TableCalendarState extends State> { if (widget.onDayLongPressed != null) { _updateFocusOnTap(day); - return widget.onDayLongPressed(day, _focusedDay.value); + return widget.onDayLongPressed!(day, _focusedDay.value); } if (widget.onRangeSelected != null) { @@ -387,7 +379,7 @@ class _TableCalendarState extends State> { if (_isRangeSelectionOn) { _firstSelectedDay = day; - widget.onRangeSelected(_firstSelectedDay, null, _focusedDay.value); + widget.onRangeSelected!(_firstSelectedDay, null, _focusedDay.value); } else { _firstSelectedDay = null; widget.onDaySelected?.call(day, _focusedDay.value); @@ -450,8 +442,9 @@ class _TableCalendarState extends State> { focusedMonth: value, onLeftChevronTap: _onLeftChevronTap, onRightChevronTap: _onRightChevronTap, - onHeaderTap: () => widget.onHeaderTapped(value), - onHeaderLongPress: () => widget.onHeaderLongPressed(value), + onHeaderTap: () => widget.onHeaderTapped?.call(value), + onHeaderLongPress: () => + widget.onHeaderLongPressed?.call(value), headerStyle: widget.headerStyle, availableCalendarFormats: widget.availableCalendarFormats, calendarFormat: widget.calendarFormat, @@ -496,7 +489,7 @@ class _TableCalendarState extends State> { widget.onPageChanged?.call(focusedDay); }, dowBuilder: (BuildContext context, DateTime day) { - Widget dowCell = + Widget? dowCell = widget.calendarBuilders.dowBuilder?.call(context, day); if (dowCell == null) { @@ -550,12 +543,12 @@ class _TableCalendarState extends State> { final isWithinRange = widget.rangeStartDay != null && widget.rangeEndDay != null && - _isWithinRange(day, widget.rangeStartDay, widget.rangeEndDay); + _isWithinRange(day, widget.rangeStartDay!, widget.rangeEndDay!); final isRangeStart = isSameDay(day, widget.rangeStartDay); final isRangeEnd = isSameDay(day, widget.rangeEndDay); - Widget rangeHighlight = widget.calendarBuilders.rangeHighlightBuilder + Widget? rangeHighlight = widget.calendarBuilders.rangeHighlightBuilder ?.call(context, day, isWithinRange); if (rangeHighlight == null) { @@ -604,7 +597,7 @@ class _TableCalendarState extends State> { if (!isDisabled) { final events = widget.eventLoader?.call(day) ?? []; - Widget markerWidget = + Widget? markerWidget = widget.calendarBuilders.markerBuilder?.call(context, day, events); if (events.isNotEmpty && markerWidget == null) { @@ -689,7 +682,7 @@ class _TableCalendarState extends State> { bool _isDayAvailable(DateTime day) { return widget.enabledDayPredicate == null ? true - : widget.enabledDayPredicate(day); + : widget.enabledDayPredicate!(day); } DateTime _firstDayOfMonth(DateTime month) { diff --git a/lib/src/table_calendar_base.dart b/lib/src/table_calendar_base.dart index 85e36847..3c3ef880 100644 --- a/lib/src/table_calendar_base.dart +++ b/lib/src/table_calendar_base.dart @@ -12,34 +12,34 @@ class TableCalendarBase extends StatefulWidget { final DateTime lastDay; final DateTime focusedDay; final CalendarFormat calendarFormat; - final DayBuilder dowBuilder; + final DayBuilder? dowBuilder; final FocusedDayBuilder dayBuilder; - final double dowHeight; + final double? dowHeight; final double rowHeight; final bool sixWeekMonthsEnforced; final bool dowVisible; - final Decoration dowDecoration; - final Decoration rowDecoration; + final Decoration? dowDecoration; + final Decoration? rowDecoration; final Duration formatAnimationDuration; final Curve formatAnimationCurve; final StartingDayOfWeek startingDayOfWeek; final AvailableGestures availableGestures; final SimpleSwipeConfig simpleSwipeConfig; final Map availableCalendarFormats; - final SwipeCallback onVerticalSwipe; - final void Function(DateTime focusedDay) onPageChanged; - final void Function(PageController pageController) onCalendarCreated; + final SwipeCallback? onVerticalSwipe; + final void Function(DateTime focusedDay)? onPageChanged; + final void Function(PageController pageController)? onCalendarCreated; TableCalendarBase({ - Key key, - @required this.firstDay, - @required this.lastDay, - @required this.focusedDay, + Key? key, + required this.firstDay, + required this.lastDay, + required this.focusedDay, this.calendarFormat = CalendarFormat.month, this.dowBuilder, - @required this.dayBuilder, + required this.dayBuilder, this.dowHeight, - @required this.rowHeight, + required this.rowHeight, this.sixWeekMonthsEnforced = false, this.dowVisible = true, this.dowDecoration, @@ -61,11 +61,6 @@ class TableCalendarBase extends StatefulWidget { this.onPageChanged, this.onCalendarCreated, }) : assert(!dowVisible || (dowHeight != null && dowBuilder != null)), - assert(dayBuilder != null), - assert(rowHeight != null), - assert(firstDay != null), - assert(lastDay != null), - assert(focusedDay != null), assert(isSameDay(focusedDay, firstDay) || focusedDay.isAfter(firstDay)), assert(isSameDay(focusedDay, lastDay) || focusedDay.isBefore(lastDay)), super(key: key); @@ -76,11 +71,11 @@ class TableCalendarBase extends StatefulWidget { class _TableCalendarBaseState extends State with SingleTickerProviderStateMixin { - ValueNotifier _pageHeight; - PageController _pageController; - DateTime _focusedDay; - int _previousIndex; - bool _pageCallbackDisabled; + late final ValueNotifier _pageHeight; + late final PageController _pageController; + late DateTime _focusedDay; + late int _previousIndex; + late bool _pageCallbackDisabled; @override void initState() { @@ -230,7 +225,7 @@ class _TableCalendarBaseState extends State } double _getPageHeight(int rowCount) { - final dowHeight = widget.dowVisible ? widget.dowHeight : 0.0; + final dowHeight = widget.dowVisible ? widget.dowHeight! : 0.0; return dowHeight + rowCount * widget.rowHeight; } diff --git a/lib/src/widgets/calendar_core.dart b/lib/src/widgets/calendar_core.dart index 5b4e8c74..fe26f924 100644 --- a/lib/src/widgets/calendar_core.dart +++ b/lib/src/widgets/calendar_core.dart @@ -10,33 +10,33 @@ typedef _OnCalendarPageChanged = void Function( int pageIndex, DateTime focusedDay); class CalendarCore extends StatelessWidget { - final DateTime focusedDay; + final DateTime? focusedDay; final DateTime firstDay; final DateTime lastDay; final CalendarFormat calendarFormat; - final DayBuilder dowBuilder; + final DayBuilder? dowBuilder; final FocusedDayBuilder dayBuilder; final bool sixWeekMonthsEnforced; final bool dowVisible; - final Decoration dowDecoration; - final Decoration rowDecoration; - final double dowHeight; - final double rowHeight; + final Decoration? dowDecoration; + final Decoration? rowDecoration; + final double? dowHeight; + final double? rowHeight; final BoxConstraints constraints; - final int previousIndex; + final int? previousIndex; final StartingDayOfWeek startingDayOfWeek; - final PageController pageController; - final ScrollPhysics scrollPhysics; + final PageController? pageController; + final ScrollPhysics? scrollPhysics; final _OnCalendarPageChanged onPageChanged; const CalendarCore({ - Key key, + Key? key, this.dowBuilder, - @required this.dayBuilder, - @required this.onPageChanged, - @required this.firstDay, - @required this.lastDay, - @required this.constraints, + required this.dayBuilder, + required this.onPageChanged, + required this.firstDay, + required this.lastDay, + required this.constraints, this.dowHeight, this.rowHeight, this.startingDayOfWeek = StartingDayOfWeek.sunday, @@ -49,9 +49,7 @@ class CalendarCore extends StatelessWidget { this.dowDecoration, this.rowDecoration, this.scrollPhysics, - }) : assert(firstDay != null), - assert(lastDay != null), - assert(onPageChanged != null), + }) : assert(!dowVisible || (dowHeight != null && dowBuilder != null)), super(key: key); @override @@ -65,7 +63,7 @@ class CalendarCore extends StatelessWidget { final visibleRange = _getVisibleRange(calendarFormat, baseDay); final visibleDays = _daysInRange(visibleRange.start, visibleRange.end); - final actualDowHeight = dowVisible ? dowHeight : 0.0; + final actualDowHeight = dowVisible ? dowHeight! : 0.0; final constrainedRowHeight = constraints.hasBoundedHeight ? (constraints.maxHeight - actualDowHeight) / _getRowCount(calendarFormat, baseDay) @@ -79,15 +77,17 @@ class CalendarCore extends StatelessWidget { dowBuilder: (context, day) { return SizedBox( height: dowHeight, - child: dowBuilder(context, day), + child: dowBuilder?.call(context, day), ); }, dayBuilder: (context, day) { DateTime baseDay; - if (focusedDay == null || previousIndex == null) { + final previousFocusedDay = focusedDay; + if (previousFocusedDay == null || previousIndex == null) { baseDay = _getBaseDay(calendarFormat, index); } else { - baseDay = _getFocusedDay(calendarFormat, index); + baseDay = + _getFocusedDay(calendarFormat, previousFocusedDay, index); } return SizedBox( @@ -99,10 +99,11 @@ class CalendarCore extends StatelessWidget { }, onPageChanged: (index) { DateTime baseDay; - if (focusedDay == null || previousIndex == null) { + final previousFocusedDay = focusedDay; + if (previousFocusedDay == null || previousIndex == null) { baseDay = _getBaseDay(calendarFormat, index); } else { - baseDay = _getFocusedDay(calendarFormat, index); + baseDay = _getFocusedDay(calendarFormat, previousFocusedDay, index); } return onPageChanged(index, baseDay); @@ -138,25 +139,26 @@ class CalendarCore extends StatelessWidget { return last.difference(_firstDayOfWeek(first)).inDays ~/ 14; } - DateTime _getFocusedDay(CalendarFormat format, int pageIndex) { + DateTime _getFocusedDay( + CalendarFormat format, DateTime prevFocusedDay, int pageIndex) { if (pageIndex == previousIndex) { - return focusedDay; + return prevFocusedDay; } - final pageDif = pageIndex - previousIndex; + final pageDif = pageIndex - previousIndex!; DateTime day; switch (format) { case CalendarFormat.month: - day = DateTime.utc(focusedDay.year, focusedDay.month + pageDif); + day = DateTime.utc(prevFocusedDay.year, prevFocusedDay.month + pageDif); break; case CalendarFormat.twoWeeks: - day = DateTime.utc( - focusedDay.year, focusedDay.month, focusedDay.day + pageDif * 14); + day = DateTime.utc(prevFocusedDay.year, prevFocusedDay.month, + prevFocusedDay.day + pageDif * 14); break; case CalendarFormat.week: - day = DateTime.utc( - focusedDay.year, focusedDay.month, focusedDay.day + pageDif * 7); + day = DateTime.utc(prevFocusedDay.year, prevFocusedDay.month, + prevFocusedDay.day + pageDif * 7); break; } diff --git a/lib/src/widgets/calendar_header.dart b/lib/src/widgets/calendar_header.dart index e5728b65..cce74329 100644 --- a/lib/src/widgets/calendar_header.dart +++ b/lib/src/widgets/calendar_header.dart @@ -20,28 +20,27 @@ class CalendarHeader extends StatelessWidget { final VoidCallback onHeaderLongPress; final ValueChanged onFormatButtonTap; final Map availableCalendarFormats; - final DayBuilder headerTitleBuilder; + final DayBuilder? headerTitleBuilder; const CalendarHeader({ - Key key, + Key? key, this.locale, + required this.focusedMonth, + required this.calendarFormat, + required this.headerStyle, + required this.onLeftChevronTap, + required this.onRightChevronTap, + required this.onHeaderTap, + required this.onHeaderLongPress, + required this.onFormatButtonTap, + required this.availableCalendarFormats, this.headerTitleBuilder, - @required this.focusedMonth, - @required this.calendarFormat, - @required this.headerStyle, - @required this.onLeftChevronTap, - @required this.onRightChevronTap, - @required this.onHeaderTap, - @required this.onHeaderLongPress, - @required this.onFormatButtonTap, - @required this.availableCalendarFormats, }) : super(key: key); @override Widget build(BuildContext context) { - final text = headerStyle.titleTextFormatter != null - ? headerStyle.titleTextFormatter(focusedMonth, locale) - : DateFormat.yMMMM(locale).format(focusedMonth); + final text = headerStyle.titleTextFormatter?.call(focusedMonth, locale) ?? + DateFormat.yMMMM(locale).format(focusedMonth); return Container( decoration: headerStyle.decoration, @@ -58,17 +57,18 @@ class CalendarHeader extends StatelessWidget { padding: headerStyle.leftChevronPadding, ), Expanded( - child: headerTitleBuilder?.call(context, focusedMonth) ?? GestureDetector( - onTap: onHeaderTap, - onLongPress: onHeaderLongPress, - child: Text( - text, - style: headerStyle.titleTextStyle, - textAlign: headerStyle.titleCentered - ? TextAlign.center - : TextAlign.start, - ), - ), + child: headerTitleBuilder?.call(context, focusedMonth) ?? + GestureDetector( + onTap: onHeaderTap, + onLongPress: onHeaderLongPress, + child: Text( + text, + style: headerStyle.titleTextStyle, + textAlign: headerStyle.titleCentered + ? TextAlign.center + : TextAlign.start, + ), + ), ), if (headerStyle.formatButtonVisible && availableCalendarFormats.length > 1) diff --git a/lib/src/widgets/calendar_page.dart b/lib/src/widgets/calendar_page.dart index d2068e61..43ad64d0 100644 --- a/lib/src/widgets/calendar_page.dart +++ b/lib/src/widgets/calendar_page.dart @@ -7,23 +7,21 @@ import '../shared/utils.dart' show DayBuilder; class CalendarPage extends StatelessWidget { final List visibleDays; - final DayBuilder dowBuilder; + final DayBuilder? dowBuilder; final DayBuilder dayBuilder; - final Decoration dowDecoration; - final Decoration rowDecoration; + final Decoration? dowDecoration; + final Decoration? rowDecoration; final bool dowVisible; const CalendarPage({ - Key key, - @required this.visibleDays, + Key? key, + required this.visibleDays, this.dowBuilder, - @required this.dayBuilder, + required this.dayBuilder, this.dowDecoration, this.rowDecoration, this.dowVisible = true, }) : assert(!dowVisible || dowBuilder != null), - assert(dayBuilder != null), - assert(visibleDays != null), super(key: key); @override @@ -41,7 +39,7 @@ class CalendarPage extends StatelessWidget { decoration: dowDecoration, children: List.generate( 7, - (index) => dowBuilder(context, visibleDays[index]), + (index) => dowBuilder!(context, visibleDays[index]), ).toList(), ); } diff --git a/lib/src/widgets/cell_content.dart b/lib/src/widgets/cell_content.dart index 4b779466..5a1cf4c8 100644 --- a/lib/src/widgets/cell_content.dart +++ b/lib/src/widgets/cell_content.dart @@ -23,26 +23,26 @@ class CellContent extends StatelessWidget { final CalendarBuilders calendarBuilders; const CellContent({ - Key key, - @required this.day, - @required this.focusedDay, - @required this.calendarStyle, - @required this.calendarBuilders, - @required this.isTodayHighlighted, - @required this.isToday, - @required this.isSelected, - @required this.isRangeStart, - @required this.isRangeEnd, - @required this.isWithinRange, - @required this.isOutside, - @required this.isDisabled, - @required this.isHoliday, - @required this.isWeekend, + Key? key, + required this.day, + required this.focusedDay, + required this.calendarStyle, + required this.calendarBuilders, + required this.isTodayHighlighted, + required this.isToday, + required this.isSelected, + required this.isRangeStart, + required this.isRangeEnd, + required this.isWithinRange, + required this.isOutside, + required this.isDisabled, + required this.isHoliday, + required this.isWeekend, }) : super(key: key); @override Widget build(BuildContext context) { - Widget cell = + Widget? cell = calendarBuilders.prioritizedBuilder?.call(context, day, focusedDay); if (cell != null) { diff --git a/lib/src/widgets/custom_icon_button.dart b/lib/src/widgets/custom_icon_button.dart index 59528603..795ea535 100644 --- a/lib/src/widgets/custom_icon_button.dart +++ b/lib/src/widgets/custom_icon_button.dart @@ -10,14 +10,12 @@ class CustomIconButton extends StatelessWidget { final EdgeInsets padding; const CustomIconButton({ - Key key, - @required this.icon, - @required this.onTap, + Key? key, + required this.icon, + required this.onTap, this.margin = const EdgeInsets.all(0.0), this.padding = const EdgeInsets.all(8.0), - }) : assert(icon != null), - assert(onTap != null), - super(key: key); + }) : super(key: key); @override Widget build(BuildContext context) { diff --git a/lib/src/widgets/format_button.dart b/lib/src/widgets/format_button.dart index 41298276..4e0eced3 100644 --- a/lib/src/widgets/format_button.dart +++ b/lib/src/widgets/format_button.dart @@ -9,20 +9,20 @@ class FormatButton extends StatelessWidget { final CalendarFormat calendarFormat; final ValueChanged onTap; final TextStyle textStyle; - final BoxDecoration decoration; + final Decoration decoration; final EdgeInsets padding; final bool showsNextFormat; final Map availableCalendarFormats; const FormatButton({ - Key key, - @required this.calendarFormat, - @required this.onTap, - @required this.textStyle, - @required this.decoration, - @required this.padding, - @required this.showsNextFormat, - @required this.availableCalendarFormats, + Key? key, + required this.calendarFormat, + required this.onTap, + required this.textStyle, + required this.decoration, + required this.padding, + required this.showsNextFormat, + required this.availableCalendarFormats, }) : super(key: key); @override @@ -41,8 +41,8 @@ class FormatButton extends StatelessWidget { } String get _formatButtonText => showsNextFormat - ? availableCalendarFormats[_nextFormat()] - : availableCalendarFormats[calendarFormat]; + ? availableCalendarFormats[_nextFormat()]! + : availableCalendarFormats[calendarFormat]!; CalendarFormat _nextFormat() { final formats = availableCalendarFormats.keys.toList(); diff --git a/pubspec.lock b/pubspec.lock index e1823b82..01225d37 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,49 +7,49 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0-nullsafety.1" + version: "2.5.0" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.1" + version: "2.1.0" characters: dependency: transitive description: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.3" + version: "1.1.0" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0" clock: dependency: transitive description: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.1" + version: "1.1.0" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.15.0-nullsafety.3" + version: "1.15.0" fake_async: dependency: transitive description: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0" flutter: dependency: "direct main" description: flutter @@ -66,35 +66,35 @@ packages: name: intl url: "https://pub.dartlang.org" source: hosted - version: "0.16.1" + version: "0.17.0" matcher: dependency: transitive description: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10-nullsafety.1" + version: "0.12.10" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.3" + version: "1.3.0" path: dependency: transitive description: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety.1" + version: "1.8.0" simple_gesture_detector: dependency: "direct main" description: name: simple_gesture_detector url: "https://pub.dartlang.org" source: hosted - version: "0.1.6" + version: "0.2.0-nullsafety.1" sky_engine: dependency: transitive description: flutter @@ -106,56 +106,56 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety.2" + version: "1.8.0" stack_trace: dependency: transitive description: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.10.0-nullsafety.1" + version: "1.10.0" stream_channel: dependency: transitive description: name: stream_channel url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.1" + version: "2.1.0" string_scanner: dependency: transitive description: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.1" + version: "1.1.0" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19-nullsafety.2" + version: "0.2.19" typed_data: dependency: transitive description: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.3" + version: "1.3.0" vector_math: dependency: transitive description: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.3" + version: "2.1.0" sdks: - dart: ">=2.10.0-110 <2.11.0" - flutter: ">=1.17.0 <2.0.0" + dart: ">=2.12.0-0.0 <3.0.0" + flutter: ">=1.17.0" diff --git a/pubspec.yaml b/pubspec.yaml index 79188081..30cefc16 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,15 +5,15 @@ author: Aleksander Woźniak homepage: https://github.com/aleksanderwozniak/table_calendar environment: - sdk: ">=2.7.0 <3.0.0" + sdk: ">=2.12.0-0 <3.0.0" flutter: ">=1.17.0 <2.0.0" dependencies: flutter: sdk: flutter - intl: ">=0.15.0 <0.18.0" - simple_gesture_detector: ^0.1.6 + intl: ^0.17.0 + simple_gesture_detector: ^0.2.0-nullsafety.1 dev_dependencies: flutter_test: From f26b34d6b35d0716199104adbe78019ecad8558f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Mon, 22 Feb 2021 18:16:13 +0100 Subject: [PATCH 073/164] Migrate examples to null safety --- example/lib/pages/table_basics_example.dart | 2 +- example/lib/pages/table_complex_example.dart | 39 ++++++++--------- example/lib/pages/table_events_example.dart | 15 +++---- example/lib/pages/table_multi_example.dart | 22 +++------- example/lib/pages/table_range_example.dart | 6 +-- example/pubspec.lock | 46 ++++++++++---------- example/pubspec.yaml | 8 +++- 7 files changed, 65 insertions(+), 73 deletions(-) diff --git a/example/lib/pages/table_basics_example.dart b/example/lib/pages/table_basics_example.dart index 03165dd3..6a8f91b6 100644 --- a/example/lib/pages/table_basics_example.dart +++ b/example/lib/pages/table_basics_example.dart @@ -14,7 +14,7 @@ class TableBasicsExample extends StatefulWidget { class _TableBasicsExampleState extends State { CalendarFormat _calendarFormat = CalendarFormat.month; DateTime _focusedDay = DateTime.now(); - DateTime _selectedDay; + DateTime? _selectedDay; @override Widget build(BuildContext context) { diff --git a/example/lib/pages/table_complex_example.dart b/example/lib/pages/table_complex_example.dart index 3e259b83..fe5587e2 100644 --- a/example/lib/pages/table_complex_example.dart +++ b/example/lib/pages/table_complex_example.dart @@ -15,25 +15,22 @@ class TableComplexExample extends StatefulWidget { } class _TableComplexExampleState extends State { + late final PageController _pageController; + late final ValueNotifier> _selectedEvents; + final ValueNotifier _focusedDay = ValueNotifier(DateTime.now()); + final Set _selectedDays = LinkedHashSet( + equals: isSameDay, + hashCode: getHashCode, + ); CalendarFormat _calendarFormat = CalendarFormat.month; RangeSelectionMode _rangeSelectionMode = RangeSelectionMode.toggledOff; - DateTime _rangeStart; - DateTime _rangeEnd; - Set _selectedDays; - ValueNotifier _focusedDay; - ValueNotifier> _selectedEvents; - PageController _pageController; + DateTime? _rangeStart; + DateTime? _rangeEnd; @override void initState() { super.initState(); - _selectedDays = LinkedHashSet( - equals: isSameDay, - hashCode: getHashCode, - ); - - _focusedDay = ValueNotifier(DateTime.now()); _selectedDays.add(_focusedDay.value); _selectedEvents = ValueNotifier(_getEventsForDay(_focusedDay.value)); } @@ -80,7 +77,7 @@ class _TableComplexExampleState extends State { _selectedEvents.value = _getEventsForDays(_selectedDays); } - void _onRangeSelected(DateTime start, DateTime end, DateTime focusedDay) { + void _onRangeSelected(DateTime? start, DateTime? end, DateTime focusedDay) { setState(() { _focusedDay.value = focusedDay; _rangeStart = start; @@ -93,7 +90,7 @@ class _TableComplexExampleState extends State { _selectedEvents.value = _getEventsForRange(start, end); } else if (start != null) { _selectedEvents.value = _getEventsForDay(start); - } else { + } else if (end != null) { _selectedEvents.value = _getEventsForDay(end); } } @@ -201,13 +198,13 @@ class _CalendarHeader extends StatelessWidget { final bool clearButtonVisible; const _CalendarHeader({ - Key key, - @required this.focusedDay, - @required this.onLeftArrowTap, - @required this.onRightArrowTap, - @required this.onTodayButtonTap, - @required this.onClearButtonTap, - @required this.clearButtonVisible, + Key? key, + required this.focusedDay, + required this.onLeftArrowTap, + required this.onRightArrowTap, + required this.onTodayButtonTap, + required this.onClearButtonTap, + required this.clearButtonVisible, }) : super(key: key); @override diff --git a/example/lib/pages/table_events_example.dart b/example/lib/pages/table_events_example.dart index 922c7410..ce00dca2 100644 --- a/example/lib/pages/table_events_example.dart +++ b/example/lib/pages/table_events_example.dart @@ -12,22 +12,21 @@ class TableEventsExample extends StatefulWidget { } class _TableEventsExampleState extends State { + late final ValueNotifier> _selectedEvents; CalendarFormat _calendarFormat = CalendarFormat.month; RangeSelectionMode _rangeSelectionMode = RangeSelectionMode .toggledOff; // Can be toggled on/off by longpressing a date DateTime _focusedDay = DateTime.now(); - DateTime _selectedDay; - DateTime _rangeStart; - DateTime _rangeEnd; - - ValueNotifier> _selectedEvents; + DateTime? _selectedDay; + DateTime? _rangeStart; + DateTime? _rangeEnd; @override void initState() { super.initState(); _selectedDay = _focusedDay; - _selectedEvents = ValueNotifier(_getEventsForDay(_selectedDay)); + _selectedEvents = ValueNotifier(_getEventsForDay(_selectedDay!)); } @override @@ -64,7 +63,7 @@ class _TableEventsExampleState extends State { } } - void _onRangeSelected(DateTime start, DateTime end, DateTime focusedDay) { + void _onRangeSelected(DateTime? start, DateTime? end, DateTime focusedDay) { setState(() { _selectedDay = null; _focusedDay = focusedDay; @@ -78,7 +77,7 @@ class _TableEventsExampleState extends State { _selectedEvents.value = _getEventsForRange(start, end); } else if (start != null) { _selectedEvents.value = _getEventsForDay(start); - } else { + } else if (end != null) { _selectedEvents.value = _getEventsForDay(end); } } diff --git a/example/lib/pages/table_multi_example.dart b/example/lib/pages/table_multi_example.dart index 6d219354..c5d4f88d 100644 --- a/example/lib/pages/table_multi_example.dart +++ b/example/lib/pages/table_multi_example.dart @@ -14,23 +14,15 @@ class TableMultiExample extends StatefulWidget { } class _TableMultiExampleState extends State { + final ValueNotifier> _selectedEvents = ValueNotifier([]); + // Using a `LinkedHashSet` is recommended due to equality comparison override + final Set _selectedDays = LinkedHashSet( + equals: isSameDay, + hashCode: getHashCode, + ); + CalendarFormat _calendarFormat = CalendarFormat.month; DateTime _focusedDay = DateTime.now(); - Set _selectedDays; - - ValueNotifier> _selectedEvents; - - @override - void initState() { - super.initState(); - _selectedEvents = ValueNotifier([]); - - // Using a `LinkedHashSet` is recommended due to equality comparison override - _selectedDays = LinkedHashSet( - equals: isSameDay, - hashCode: getHashCode, - ); - } @override void dispose() { diff --git a/example/lib/pages/table_range_example.dart b/example/lib/pages/table_range_example.dart index 79308c2d..7cd7d771 100644 --- a/example/lib/pages/table_range_example.dart +++ b/example/lib/pages/table_range_example.dart @@ -16,9 +16,9 @@ class _TableRangeExampleState extends State { RangeSelectionMode _rangeSelectionMode = RangeSelectionMode .toggledOn; // Can be toggled on/off by longpressing a date DateTime _focusedDay = DateTime.now(); - DateTime _selectedDay; - DateTime _rangeStart; - DateTime _rangeEnd; + DateTime? _selectedDay; + DateTime? _rangeStart; + DateTime? _rangeEnd; @override Widget build(BuildContext context) { diff --git a/example/pubspec.lock b/example/pubspec.lock index 0c1dbfcb..9948b2ed 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -7,56 +7,56 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0-nullsafety.1" + version: "2.5.0" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.1" + version: "2.1.0" characters: dependency: transitive description: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.3" + version: "1.1.0" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0" clock: dependency: transitive description: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.1" + version: "1.1.0" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.15.0-nullsafety.3" + version: "1.15.0" cupertino_icons: dependency: "direct main" description: name: cupertino_icons url: "https://pub.dartlang.org" source: hosted - version: "1.0.0" + version: "1.0.2" fake_async: dependency: transitive description: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0" flutter: dependency: "direct main" description: flutter @@ -73,35 +73,35 @@ packages: name: intl url: "https://pub.dartlang.org" source: hosted - version: "0.16.1" + version: "0.17.0" matcher: dependency: transitive description: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10-nullsafety.1" + version: "0.12.10" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.3" + version: "1.3.0" path: dependency: transitive description: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety.1" + version: "1.8.0" simple_gesture_detector: dependency: transitive description: name: simple_gesture_detector url: "https://pub.dartlang.org" source: hosted - version: "0.1.6" + version: "0.2.0-nullsafety.1" sky_engine: dependency: transitive description: flutter @@ -113,28 +113,28 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety.2" + version: "1.8.0" stack_trace: dependency: transitive description: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.10.0-nullsafety.1" + version: "1.10.0" stream_channel: dependency: transitive description: name: stream_channel url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.1" + version: "2.1.0" string_scanner: dependency: transitive description: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.1" + version: "1.1.0" table_calendar: dependency: "direct main" description: @@ -148,28 +148,28 @@ packages: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19-nullsafety.2" + version: "0.2.19" typed_data: dependency: transitive description: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.3" + version: "1.3.0" vector_math: dependency: transitive description: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.3" + version: "2.1.0" sdks: - dart: ">=2.10.0-110 <2.11.0" - flutter: ">=1.17.0 <2.0.0" + dart: ">=2.12.0-0.0 <3.0.0" + flutter: ">=1.17.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 6663bdd2..e1f8e305 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,6 +1,10 @@ name: table_calendar_example description: A short demo of table_calendar package. +# The following line prevents the package from being accidentally published to +# pub.dev using `pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + # The following defines the version and build number for your application. # A version number is three numbers separated by dots, like 1.2.43 # followed by an optional build number separated by a +. @@ -14,7 +18,7 @@ description: A short demo of table_calendar package. version: 1.0.0+1 environment: - sdk: ">=2.7.0 <3.0.0" + sdk: ">=2.12.0-0 <3.0.0" dependencies: flutter: @@ -22,7 +26,7 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.0 + cupertino_icons: ^1.0.2 table_calendar: path: ../ From 60a627633137cb7cf3c9664d87c61c23816ba67e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Mon, 22 Feb 2021 18:22:18 +0100 Subject: [PATCH 074/164] Replace RaisedButton with ElevatedButton --- example/lib/main.dart | 10 +++++----- example/lib/pages/table_multi_example.dart | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 670abc8b..71fa5d31 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -44,7 +44,7 @@ class _StartPageState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ const SizedBox(height: 20.0), - RaisedButton( + ElevatedButton( child: Text('TableCalendar - Basics'), onPressed: () => Navigator.push( context, @@ -52,7 +52,7 @@ class _StartPageState extends State { ), ), const SizedBox(height: 12.0), - RaisedButton( + ElevatedButton( child: Text('TableCalendar - Range'), onPressed: () => Navigator.push( context, @@ -60,7 +60,7 @@ class _StartPageState extends State { ), ), const SizedBox(height: 12.0), - RaisedButton( + ElevatedButton( child: Text('TableCalendar - Events'), onPressed: () => Navigator.push( context, @@ -68,7 +68,7 @@ class _StartPageState extends State { ), ), const SizedBox(height: 12.0), - RaisedButton( + ElevatedButton( child: Text('TableCalendar - Multi'), onPressed: () => Navigator.push( context, @@ -76,7 +76,7 @@ class _StartPageState extends State { ), ), const SizedBox(height: 12.0), - RaisedButton( + ElevatedButton( child: Text('TableCalendar - Complex'), onPressed: () => Navigator.push( context, diff --git a/example/lib/pages/table_multi_example.dart b/example/lib/pages/table_multi_example.dart index c5d4f88d..0be4a10d 100644 --- a/example/lib/pages/table_multi_example.dart +++ b/example/lib/pages/table_multi_example.dart @@ -15,6 +15,7 @@ class TableMultiExample extends StatefulWidget { class _TableMultiExampleState extends State { final ValueNotifier> _selectedEvents = ValueNotifier([]); + // Using a `LinkedHashSet` is recommended due to equality comparison override final Set _selectedDays = LinkedHashSet( equals: isSameDay, @@ -88,7 +89,7 @@ class _TableMultiExampleState extends State { _focusedDay = focusedDay; }, ), - RaisedButton( + ElevatedButton( child: Text('Clear selection'), onPressed: () { setState(() { From 34b5ce4751132a0a47235f76dd1d9e275b72ccec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Mon, 22 Feb 2021 18:37:26 +0100 Subject: [PATCH 075/164] Update typedefs --- lib/src/shared/utils.dart | 4 ++-- lib/src/widgets/calendar_page.dart | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/src/shared/utils.dart b/lib/src/shared/utils.dart index 4ee7da50..906c5861 100644 --- a/lib/src/shared/utils.dart +++ b/lib/src/shared/utils.dart @@ -1,11 +1,11 @@ import 'package:flutter/widgets.dart'; /// Signature for a function that creates a widget for a given `day`. -typedef DayBuilder = Widget Function(BuildContext context, DateTime day); +typedef DayBuilder = Widget? Function(BuildContext context, DateTime day); /// Signature for a function that creates a widget for a given `day`. /// Additionally, contains the currently focused day. -typedef FocusedDayBuilder = Widget Function( +typedef FocusedDayBuilder = Widget? Function( BuildContext context, DateTime day, DateTime focusedDay); /// Signature for a function returning text that can be localized and formatted with `DateFormat`. diff --git a/lib/src/widgets/calendar_page.dart b/lib/src/widgets/calendar_page.dart index 43ad64d0..57ab4091 100644 --- a/lib/src/widgets/calendar_page.dart +++ b/lib/src/widgets/calendar_page.dart @@ -3,12 +3,10 @@ import 'package:flutter/widgets.dart'; -import '../shared/utils.dart' show DayBuilder; - class CalendarPage extends StatelessWidget { + final Widget Function(BuildContext context, DateTime day)? dowBuilder; + final Widget Function(BuildContext context, DateTime day) dayBuilder; final List visibleDays; - final DayBuilder? dowBuilder; - final DayBuilder dayBuilder; final Decoration? dowDecoration; final Decoration? rowDecoration; final bool dowVisible; From c30f8055f31aa984ba565d0a91ed1ebdfe62fa7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Mon, 22 Feb 2021 21:29:57 +0100 Subject: [PATCH 076/164] Add holidays to example --- example/lib/pages/table_complex_example.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/example/lib/pages/table_complex_example.dart b/example/lib/pages/table_complex_example.dart index fe5587e2..e30e806e 100644 --- a/example/lib/pages/table_complex_example.dart +++ b/example/lib/pages/table_complex_example.dart @@ -146,6 +146,10 @@ class _TableComplexExampleState extends State { calendarFormat: _calendarFormat, rangeSelectionMode: _rangeSelectionMode, eventLoader: _getEventsForDay, + holidayPredicate: (day) { + // Every 20th day of the month will be treated as a holiday + return day.day == 20; + }, onDaySelected: _onDaySelected, onRangeSelected: _onRangeSelected, onCalendarCreated: (controller) => _pageController = controller, From 170d8ca0b710241713b68fd132ed20a40acdd7a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Mon, 22 Feb 2021 22:22:11 +0100 Subject: [PATCH 077/164] Update README.md --- README.md | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 16d7a12a..b94ddf0e 100644 --- a/README.md +++ b/README.md @@ -20,26 +20,24 @@ Highly customizable, feature-packed calendar widget for Flutter. * Dynamic events and holidays * Vertical autosizing - fit the content, or fill the viewport * Multiple calendar formats (month, two weeks, week) -* Horizontal swipe boundaries (firstDay, lastDay) +* Horizontal swipe boundaries (first day, last day) ## Usage -Make sure to check out [examples](https://github.com/aleksanderwozniak/table_calendar/tree/3.0.0-beta/example/lib/pages) for more details. +Make sure to check out [examples](https://github.com/aleksanderwozniak/table_calendar/tree/3.0.0-nullsafety/example/lib/pages) and [API docs](https://pub.dev/documentation/table_calendar/3.0.0-nullsafety.0/) for more details. ### Installation -To use `3.0.0-beta` branch, add this to `pubspec.yaml`: +Add the following line to `pubspec.yaml`: ```yaml - table_calendar: - git: - url: git://github.com/aleksanderwozniak/table_calendar.git - ref: 3.0.0-beta +dependencies: + table_calendar: ^3.0.0-nullsafety.0 ``` ### Basic setup -*The complete example is available [here](https://github.com/aleksanderwozniak/table_calendar/blob/3.0.0-beta/example/lib/pages/table_basics_example.dart).* +*The complete example is available [here](https://github.com/aleksanderwozniak/table_calendar/blob/3.0.0-nullsafety/example/lib/pages/table_basics_example.dart).* **TableCalendar** requires you to provide `firstDay`, `lastDay` and `focusedDay`: * `firstDay` is the first available day for the calendar. Users will not be able to access days before it. @@ -48,8 +46,8 @@ To use `3.0.0-beta` branch, add this to `pubspec.yaml`: ```dart TableCalendar( - firstDay: DateTime.utc(2020, 10, 16), - lastDay: DateTime.utc(2021, 3, 14), + firstDay: DateTime.utc(2010, 10, 16), + lastDay: DateTime.utc(2030, 3, 14), focusedDay: DateTime.now(), ); ``` @@ -67,9 +65,8 @@ selectedDayPredicate: (day) { onDaySelected: (selectedDay, focusedDay) { setState(() { _selectedDay = selectedDay; + _focusedDay = focusedDay; // update `_focusedDay` here as well }); - - _focusedDay = focusedDay; // update `_focusedDay` here as well }, ``` @@ -98,13 +95,13 @@ onPageChanged: (focusedDay) { }, ``` -It is worth noting that you don't need to rebuild **TableCalendar** widget whenever `focusedDay` changes. You should just update the stored value, so that if the widget gets rebuilt, it will use the proper `focusedDay`. Don't call `setState()` just to trigger a rebuild with updated `focusedDay`. +It is worth noting that you don't need to call `setState()` inside `onPageChanged()` callback. You should just update the stored value, so that if the widget gets rebuilt later on, it will use the proper `focusedDay`. -*The complete example is available [here](https://github.com/aleksanderwozniak/table_calendar/blob/3.0.0-beta/example/lib/pages/table_basics_example.dart). You can find other examples [here](https://github.com/aleksanderwozniak/table_calendar/tree/3.0.0-beta/example/lib/pages).* +*The complete example is available [here](https://github.com/aleksanderwozniak/table_calendar/blob/3.0.0-nullsafety/example/lib/pages/table_basics_example.dart). You can find other examples [here](https://github.com/aleksanderwozniak/table_calendar/tree/3.0.0-nullsafety/example/lib/pages).* ### Events -*The complete example is available [here](https://github.com/aleksanderwozniak/table_calendar/blob/3.0.0-beta/example/lib/pages/table_events_example.dart).* +*The complete example is available [here](https://github.com/aleksanderwozniak/table_calendar/blob/3.0.0-nullsafety/example/lib/pages/table_events_example.dart).* You can supply custom events to **TableCalendar** widget. To do so, use `eventLoader` property - you will be given a `DateTime` object, to which you need to assign a list of events. @@ -155,16 +152,15 @@ Often times having a sublist of events that are selected by tapping on a day is void _onDaySelected(DateTime selectedDay, DateTime focusedDay) { if (!isSameDay(_selectedDay, selectedDay)) { setState(() { + _focusedDay = focusedDay; _selectedDay = selectedDay; _selectedEvents = _getEventsForDay(selectedDay); }); } - - _focusedDay = focusedDay; } ``` -*The complete example is available [here](https://github.com/aleksanderwozniak/table_calendar/blob/3.0.0-beta/example/lib/pages/table_events_example.dart).* +*The complete example is available [here](https://github.com/aleksanderwozniak/table_calendar/blob/3.0.0-nullsafety/example/lib/pages/table_events_example.dart).* ### Locale From ce9be9a3e7365c4d1c0dcb0920cda0a338897448 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Mon, 22 Feb 2021 22:25:46 +0100 Subject: [PATCH 078/164] Release version 3.0.0-nullsafety.0 --- CHANGELOG.md | 14 ++++++++++++++ example/pubspec.lock | 2 +- pubspec.yaml | 6 +++--- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fe4c088..e1742321 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +## [3.0.0-nullsafety.0] + +* Migrated to null safety +* Removed CalendarController +* Improved horizontal scrolling +* Improved widget performance +* Added date range selection +* Added multiple date selection +* Added selective CalendarBuilders +* Added firstDay and lastDay scroll boundaries +* Added shouldFillViewport property +* Added sixWeekMonthsEnforced property +* Added more options to customize calendar's behavior + ## [2.3.3] * Updated dependencies diff --git a/example/pubspec.lock b/example/pubspec.lock index 9948b2ed..0e4ff38b 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -141,7 +141,7 @@ packages: path: ".." relative: true source: path - version: "2.3.3" + version: "3.0.0-nullsafety.0" term_glyph: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 30cefc16..0e5d9c6d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,12 +1,12 @@ name: table_calendar -description: Highly customizable, feature-packed Flutter Calendar with gestures, animations and multiple formats. -version: 2.3.3 +description: Highly customizable, feature-packed calendar widget for Flutter. +version: 3.0.0-nullsafety.0 author: Aleksander Woźniak homepage: https://github.com/aleksanderwozniak/table_calendar environment: sdk: ">=2.12.0-0 <3.0.0" - flutter: ">=1.17.0 <2.0.0" + flutter: ">=1.17.0" dependencies: flutter: From 10fe6ada14dcf905df60ab428d40c469a5bcee93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 7 Mar 2021 19:12:25 +0100 Subject: [PATCH 079/164] Allow using widgets for header chevrons --- lib/src/customization/header_style.dart | 34 +++++++++++++------------ lib/src/widgets/custom_icon_button.dart | 2 +- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/lib/src/customization/header_style.dart b/lib/src/customization/header_style.dart index 324fefba..a6409f7a 100644 --- a/lib/src/customization/header_style.dart +++ b/lib/src/customization/header_style.dart @@ -37,44 +37,46 @@ class HeaderStyle { /// Background `Decoration` for FormatButton. final Decoration formatButtonDecoration; - /// Inside padding of the whole header. + /// Internal padding of the whole header. final EdgeInsets headerPadding; - /// Outside margin of the whole header. + /// External margin of the whole header. final EdgeInsets headerMargin; - /// Inside padding for FormatButton. + /// Internal padding of FormatButton. final EdgeInsets formatButtonPadding; - /// Inside padding for left chevron. + /// Internal padding of left chevron. + /// Determines how much of ripple animation is visible during taps. final EdgeInsets leftChevronPadding; - /// Inside padding for right chevron. + /// Internal padding of right chevron. + /// Determines how much of ripple animation is visible during taps. final EdgeInsets rightChevronPadding; - /// Outside margin for left chevron. + /// External margin of left chevron. final EdgeInsets leftChevronMargin; - /// Outside margin for right chevron. + /// External margin of right chevron. final EdgeInsets rightChevronMargin; - /// Icon used for left chevron. - /// Defaults to `Icon(Icons.chevron_left)`. - final Icon leftChevronIcon; + /// Widget used for left chevron. + /// + /// Tapping on it will navigate to previous calendar page. + final Widget leftChevronIcon; - /// Icon used for right chevron. - /// Defaults to `Icon(Icons.chevron_right)`. - final Icon rightChevronIcon; + /// Widget used for right chevron. + /// + /// Tapping on it will navigate to next calendar page. + final Widget rightChevronIcon; /// Determines left chevron's visibility. - /// Defaults to `true`. final bool leftChevronVisible; /// Determines right chevron's visibility. - /// Defaults to `true`. final bool rightChevronVisible; - /// Header decoration, used to draw border or shadow or change color of the header + /// Decoration of the header. final Decoration decoration; /// Creates a `HeaderStyle` used by `TableCalendar` widget. diff --git a/lib/src/widgets/custom_icon_button.dart b/lib/src/widgets/custom_icon_button.dart index 795ea535..c1ca9179 100644 --- a/lib/src/widgets/custom_icon_button.dart +++ b/lib/src/widgets/custom_icon_button.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; class CustomIconButton extends StatelessWidget { - final Icon icon; + final Widget icon; final VoidCallback onTap; final EdgeInsets margin; final EdgeInsets padding; From 4adb9bef258a30aeaf3679745ea4dffb07fe1b25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 7 Mar 2021 19:21:41 +0100 Subject: [PATCH 080/164] Update dependencies --- example/pubspec.lock | 4 ++-- example/pubspec.yaml | 2 +- pubspec.lock | 4 ++-- pubspec.yaml | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index 0e4ff38b..680073a0 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -101,7 +101,7 @@ packages: name: simple_gesture_detector url: "https://pub.dartlang.org" source: hosted - version: "0.2.0-nullsafety.1" + version: "0.2.0" sky_engine: dependency: transitive description: flutter @@ -171,5 +171,5 @@ packages: source: hosted version: "2.1.0" sdks: - dart: ">=2.12.0-0.0 <3.0.0" + dart: ">=2.12.0 <3.0.0" flutter: ">=1.17.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index e1f8e305..d72ead8a 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -18,7 +18,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: ">=2.12.0-0 <3.0.0" + sdk: ">=2.12.0 <3.0.0" dependencies: flutter: diff --git a/pubspec.lock b/pubspec.lock index 01225d37..a3ccb2d6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -94,7 +94,7 @@ packages: name: simple_gesture_detector url: "https://pub.dartlang.org" source: hosted - version: "0.2.0-nullsafety.1" + version: "0.2.0" sky_engine: dependency: transitive description: flutter @@ -157,5 +157,5 @@ packages: source: hosted version: "2.1.0" sdks: - dart: ">=2.12.0-0.0 <3.0.0" + dart: ">=2.12.0 <3.0.0" flutter: ">=1.17.0" diff --git a/pubspec.yaml b/pubspec.yaml index 0e5d9c6d..eec73cbd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,7 +5,7 @@ author: Aleksander Woźniak homepage: https://github.com/aleksanderwozniak/table_calendar environment: - sdk: ">=2.12.0-0 <3.0.0" + sdk: ">=2.12.0 <3.0.0" flutter: ">=1.17.0" dependencies: @@ -13,7 +13,7 @@ dependencies: sdk: flutter intl: ^0.17.0 - simple_gesture_detector: ^0.2.0-nullsafety.1 + simple_gesture_detector: ^0.2.0 dev_dependencies: flutter_test: From 7a979b846d6926032978257a4754e8a0f36b653a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 7 Mar 2021 20:26:49 +0100 Subject: [PATCH 081/164] Release version 3.0.0-nullsafety.1 --- CHANGELOG.md | 4 ++++ README.md | 2 +- example/pubspec.lock | 2 +- pubspec.yaml | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1742321..3d98b98a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## [3.0.0-nullsafety.1] + +* Header chevrons can now be any widget + ## [3.0.0-nullsafety.0] * Migrated to null safety diff --git a/README.md b/README.md index b94ddf0e..ee37834d 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Add the following line to `pubspec.yaml`: ```yaml dependencies: - table_calendar: ^3.0.0-nullsafety.0 + table_calendar: ^3.0.0-nullsafety.1 ``` ### Basic setup diff --git a/example/pubspec.lock b/example/pubspec.lock index 680073a0..3310ea9c 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -141,7 +141,7 @@ packages: path: ".." relative: true source: path - version: "3.0.0-nullsafety.0" + version: "3.0.0-nullsafety.1" term_glyph: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index eec73cbd..9cc1f9d0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: table_calendar description: Highly customizable, feature-packed calendar widget for Flutter. -version: 3.0.0-nullsafety.0 +version: 3.0.0-nullsafety.1 author: Aleksander Woźniak homepage: https://github.com/aleksanderwozniak/table_calendar From 53ec2bfc903ac441d5aded91c23a8650ea2add64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Mon, 29 Mar 2021 23:04:28 +0200 Subject: [PATCH 082/164] Update base day --- lib/src/widgets/calendar_core.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/src/widgets/calendar_core.dart b/lib/src/widgets/calendar_core.dart index fe26f924..957d94f8 100644 --- a/lib/src/widgets/calendar_core.dart +++ b/lib/src/widgets/calendar_core.dart @@ -176,8 +176,7 @@ class CalendarCore extends StatelessWidget { switch (format) { case CalendarFormat.month: - day = DateTime.utc( - firstDay.year, firstDay.month + pageIndex, firstDay.day); + day = DateTime.utc(firstDay.year, firstDay.month + pageIndex); break; case CalendarFormat.twoWeeks: day = DateTime.utc( From 6e7c055ebc7992360f61a2e2574db776f8cf953c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Mon, 29 Mar 2021 23:07:55 +0200 Subject: [PATCH 083/164] Rename buttons --- example/lib/main.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 71fa5d31..4a2db6eb 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -45,7 +45,7 @@ class _StartPageState extends State { children: [ const SizedBox(height: 20.0), ElevatedButton( - child: Text('TableCalendar - Basics'), + child: Text('Basics'), onPressed: () => Navigator.push( context, MaterialPageRoute(builder: (_) => TableBasicsExample()), @@ -53,7 +53,7 @@ class _StartPageState extends State { ), const SizedBox(height: 12.0), ElevatedButton( - child: Text('TableCalendar - Range'), + child: Text('Range Selection'), onPressed: () => Navigator.push( context, MaterialPageRoute(builder: (_) => TableRangeExample()), @@ -61,7 +61,7 @@ class _StartPageState extends State { ), const SizedBox(height: 12.0), ElevatedButton( - child: Text('TableCalendar - Events'), + child: Text('Events'), onPressed: () => Navigator.push( context, MaterialPageRoute(builder: (_) => TableEventsExample()), @@ -69,7 +69,7 @@ class _StartPageState extends State { ), const SizedBox(height: 12.0), ElevatedButton( - child: Text('TableCalendar - Multi'), + child: Text('Multiple Selection'), onPressed: () => Navigator.push( context, MaterialPageRoute(builder: (_) => TableMultiExample()), @@ -77,7 +77,7 @@ class _StartPageState extends State { ), const SizedBox(height: 12.0), ElevatedButton( - child: Text('TableCalendar - Complex'), + child: Text('Complex'), onPressed: () => Navigator.push( context, MaterialPageRoute(builder: (_) => TableComplexExample()), From d1d5a793ab8f22d7fd8d0750a61e8c3e6c765dbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Mon, 29 Mar 2021 23:16:39 +0200 Subject: [PATCH 084/164] Rename examples --- example/lib/main.dart | 10 +++++----- .../{table_basics_example.dart => basics_example.dart} | 0 ...table_complex_example.dart => complex_example.dart} | 0 .../{table_events_example.dart => events_example.dart} | 0 .../{table_multi_example.dart => multi_example.dart} | 0 .../{table_range_example.dart => range_example.dart} | 0 6 files changed, 5 insertions(+), 5 deletions(-) rename example/lib/pages/{table_basics_example.dart => basics_example.dart} (100%) rename example/lib/pages/{table_complex_example.dart => complex_example.dart} (100%) rename example/lib/pages/{table_events_example.dart => events_example.dart} (100%) rename example/lib/pages/{table_multi_example.dart => multi_example.dart} (100%) rename example/lib/pages/{table_range_example.dart => range_example.dart} (100%) diff --git a/example/lib/main.dart b/example/lib/main.dart index 4a2db6eb..ae2bc273 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -4,11 +4,11 @@ import 'package:flutter/material.dart'; import 'package:intl/date_symbol_data_local.dart'; -import 'pages/table_basics_example.dart'; -import 'pages/table_complex_example.dart'; -import 'pages/table_events_example.dart'; -import 'pages/table_multi_example.dart'; -import 'pages/table_range_example.dart'; +import 'pages/basics_example.dart'; +import 'pages/complex_example.dart'; +import 'pages/events_example.dart'; +import 'pages/multi_example.dart'; +import 'pages/range_example.dart'; void main() { initializeDateFormatting().then((_) => runApp(MyApp())); diff --git a/example/lib/pages/table_basics_example.dart b/example/lib/pages/basics_example.dart similarity index 100% rename from example/lib/pages/table_basics_example.dart rename to example/lib/pages/basics_example.dart diff --git a/example/lib/pages/table_complex_example.dart b/example/lib/pages/complex_example.dart similarity index 100% rename from example/lib/pages/table_complex_example.dart rename to example/lib/pages/complex_example.dart diff --git a/example/lib/pages/table_events_example.dart b/example/lib/pages/events_example.dart similarity index 100% rename from example/lib/pages/table_events_example.dart rename to example/lib/pages/events_example.dart diff --git a/example/lib/pages/table_multi_example.dart b/example/lib/pages/multi_example.dart similarity index 100% rename from example/lib/pages/table_multi_example.dart rename to example/lib/pages/multi_example.dart diff --git a/example/lib/pages/table_range_example.dart b/example/lib/pages/range_example.dart similarity index 100% rename from example/lib/pages/table_range_example.dart rename to example/lib/pages/range_example.dart From 6549993a78a33c80c40a2c3411ee237596b90c7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Mon, 29 Mar 2021 23:19:32 +0200 Subject: [PATCH 085/164] Update README.md --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index ee37834d..ef962043 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Highly customizable, feature-packed calendar widget for Flutter. ## Usage -Make sure to check out [examples](https://github.com/aleksanderwozniak/table_calendar/tree/3.0.0-nullsafety/example/lib/pages) and [API docs](https://pub.dev/documentation/table_calendar/3.0.0-nullsafety.0/) for more details. +Make sure to check out [examples](https://github.com/aleksanderwozniak/table_calendar/tree/3.0.0-nullsafety/example/lib/pages) and [API docs](https://pub.dev/documentation/table_calendar/3.0.0-nullsafety.1/) for more details. ### Installation @@ -37,7 +37,7 @@ dependencies: ### Basic setup -*The complete example is available [here](https://github.com/aleksanderwozniak/table_calendar/blob/3.0.0-nullsafety/example/lib/pages/table_basics_example.dart).* +*The complete example is available [here](https://github.com/aleksanderwozniak/table_calendar/blob/3.0.0-nullsafety/example/lib/pages/basics_example.dart).* **TableCalendar** requires you to provide `firstDay`, `lastDay` and `focusedDay`: * `firstDay` is the first available day for the calendar. Users will not be able to access days before it. @@ -60,7 +60,7 @@ Adding the following code to the calendar widget will allow it to respond to use ```dart selectedDayPredicate: (day) { - return _selectedDay == day; + return isSameDay(_selectedDay, day); }, onDaySelected: (selectedDay, focusedDay) { setState(() { @@ -97,11 +97,11 @@ onPageChanged: (focusedDay) { It is worth noting that you don't need to call `setState()` inside `onPageChanged()` callback. You should just update the stored value, so that if the widget gets rebuilt later on, it will use the proper `focusedDay`. -*The complete example is available [here](https://github.com/aleksanderwozniak/table_calendar/blob/3.0.0-nullsafety/example/lib/pages/table_basics_example.dart). You can find other examples [here](https://github.com/aleksanderwozniak/table_calendar/tree/3.0.0-nullsafety/example/lib/pages).* +*The complete example is available [here](https://github.com/aleksanderwozniak/table_calendar/blob/3.0.0-nullsafety/example/lib/pages/basics_example.dart). You can find other examples [here](https://github.com/aleksanderwozniak/table_calendar/tree/3.0.0-nullsafety/example/lib/pages).* ### Events -*The complete example is available [here](https://github.com/aleksanderwozniak/table_calendar/blob/3.0.0-nullsafety/example/lib/pages/table_events_example.dart).* +*The complete example is available [here](https://github.com/aleksanderwozniak/table_calendar/blob/3.0.0-nullsafety/example/lib/pages/events_example.dart).* You can supply custom events to **TableCalendar** widget. To do so, use `eventLoader` property - you will be given a `DateTime` object, to which you need to assign a list of events. @@ -160,7 +160,7 @@ void _onDaySelected(DateTime selectedDay, DateTime focusedDay) { } ``` -*The complete example is available [here](https://github.com/aleksanderwozniak/table_calendar/blob/3.0.0-nullsafety/example/lib/pages/table_events_example.dart).* +*The complete example is available [here](https://github.com/aleksanderwozniak/table_calendar/blob/3.0.0-nullsafety/example/lib/pages/events_example.dart).* ### Locale From f8dedd7666b8ef6903763cf6c8b400b2055d1320 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Tue, 6 Apr 2021 12:52:35 +0200 Subject: [PATCH 086/164] Make MarkerBuilder nullable --- lib/src/customization/calendar_builders.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/src/customization/calendar_builders.dart b/lib/src/customization/calendar_builders.dart index ddc42b08..a6a85420 100644 --- a/lib/src/customization/calendar_builders.dart +++ b/lib/src/customization/calendar_builders.dart @@ -7,19 +7,19 @@ import '../shared/utils.dart' show DayBuilder, FocusedDayBuilder; /// Signature for a function that creates a single event marker for a given `day`. /// Contains a single `event` associated with that `day`. -typedef SingleMarkerBuilder = Widget Function( +typedef SingleMarkerBuilder = Widget? Function( BuildContext context, DateTime day, T event); /// Signature for a function that creates an event marker for a given `day`. /// Contains a list of `events` associated with that `day`. -typedef MarkerBuilder = Widget Function( +typedef MarkerBuilder = Widget? Function( BuildContext context, DateTime day, List events); /// Signature for a function that creates a background highlight for a given `day`. /// /// Used for highlighting current range selection. /// Contains a value determining if the given `day` falls within the selected range. -typedef HighlightBuilder = Widget Function( +typedef HighlightBuilder = Widget? Function( BuildContext context, DateTime day, bool isWithinRange); /// Class containing all custom builders for `TableCalendar`. From c1fbe28ed26be123c991daeb714c1750f70008c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Tue, 6 Apr 2021 13:11:40 +0200 Subject: [PATCH 087/164] Update README.md --- README.md | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ef962043..7efd3a54 100644 --- a/README.md +++ b/README.md @@ -162,14 +162,40 @@ void _onDaySelected(DateTime selectedDay, DateTime focusedDay) { *The complete example is available [here](https://github.com/aleksanderwozniak/table_calendar/blob/3.0.0-nullsafety/example/lib/pages/events_example.dart).* +### Custom UI with CalendarBuilders + +To customize the UI with your own widgets, use [CalendarBuilders](https://pub.dev/documentation/table_calendar/3.0.0-nullsafety.1/table_calendar/CalendarBuilders-class.html). Each builder can be used to selectively override the UI, allowing you to implement highly specific designs with minimal hassle. + +You can return `null` from any builder to use the default style. For example, the following snippet will override only the Sunday's day of the week label (Sun), leaving other dow labels unchanged: + +```dart +calendarBuilders: CalendarBuilders( + dowBuilder: (context, day) { + if (day.weekday == DateTime.sunday) { + final text = DateFormat.E().format(day); + + return Center( + child: Text( + text, + style: TextStyle(color: Colors.red), + ), + ); + } + }, +), +``` + + + + ### Locale -**TableCalendar** supports locales. To display the calendar in desired language, use `locale` property. +To display the calendar in desired language, use `locale` property. If you don't specify it, a default locale will be used. #### Initialization -Before you can use a locale, you need to initialize the i18n formatting. +Before you can use a locale, you might need to initialize date formatting. A simple way of doing it is as follows: * First of all, add [intl](https://pub.dartlang.org/packages/intl) package to your pubspec.yaml file @@ -201,7 +227,6 @@ TableCalendar( | :------------: | :------------: | :------------: | :------------: | | `'en_US'` | `'pl_PL'` | `'fr_FR'` | `'zh_CN'` | -Note, that if you want to change the language of `FormatButton`'s text, you have to do this yourself. Use `availableCalendarFormats` property and pass the translated Strings there. -Use i18n method of your choice. +Note, that if you want to change the language of `FormatButton`'s text, you have to do this yourself. Use `availableCalendarFormats` property and pass the translated Strings there. Use i18n method of your choice. You can also hide the button altogether by setting `formatButtonVisible` to false. From 0bd7c55d46c41e2caf62e06ffa337b0a72ef6701 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Tue, 6 Apr 2021 14:09:45 +0200 Subject: [PATCH 088/164] Release version 3.0.0 --- CHANGELOG.md | 7 ++----- README.md | 16 ++++++++-------- example/pubspec.lock | 2 +- lib/src/shared/utils.dart | 3 +++ pubspec.yaml | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d98b98a..95284e24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,10 @@ -## [3.0.0-nullsafety.1] - -* Header chevrons can now be any widget - -## [3.0.0-nullsafety.0] +## [3.0.0] * Migrated to null safety * Removed CalendarController * Improved horizontal scrolling * Improved widget performance +* Improved documentation * Added date range selection * Added multiple date selection * Added selective CalendarBuilders diff --git a/README.md b/README.md index 7efd3a54..f04b477f 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Highly customizable, feature-packed calendar widget for Flutter. ## Usage -Make sure to check out [examples](https://github.com/aleksanderwozniak/table_calendar/tree/3.0.0-nullsafety/example/lib/pages) and [API docs](https://pub.dev/documentation/table_calendar/3.0.0-nullsafety.1/) for more details. +Make sure to check out [examples](https://github.com/aleksanderwozniak/table_calendar/tree/master/example/lib/pages) and [API docs](https://pub.dev/documentation/table_calendar/latest/) for more details. ### Installation @@ -32,12 +32,12 @@ Add the following line to `pubspec.yaml`: ```yaml dependencies: - table_calendar: ^3.0.0-nullsafety.1 + table_calendar: ^3.0.0 ``` ### Basic setup -*The complete example is available [here](https://github.com/aleksanderwozniak/table_calendar/blob/3.0.0-nullsafety/example/lib/pages/basics_example.dart).* +*The complete example is available [here](https://github.com/aleksanderwozniak/table_calendar/blob/master/example/lib/pages/basics_example.dart).* **TableCalendar** requires you to provide `firstDay`, `lastDay` and `focusedDay`: * `firstDay` is the first available day for the calendar. Users will not be able to access days before it. @@ -97,11 +97,11 @@ onPageChanged: (focusedDay) { It is worth noting that you don't need to call `setState()` inside `onPageChanged()` callback. You should just update the stored value, so that if the widget gets rebuilt later on, it will use the proper `focusedDay`. -*The complete example is available [here](https://github.com/aleksanderwozniak/table_calendar/blob/3.0.0-nullsafety/example/lib/pages/basics_example.dart). You can find other examples [here](https://github.com/aleksanderwozniak/table_calendar/tree/3.0.0-nullsafety/example/lib/pages).* +*The complete example is available [here](https://github.com/aleksanderwozniak/table_calendar/blob/master/example/lib/pages/basics_example.dart). You can find other examples [here](https://github.com/aleksanderwozniak/table_calendar/tree/master/example/lib/pages).* ### Events -*The complete example is available [here](https://github.com/aleksanderwozniak/table_calendar/blob/3.0.0-nullsafety/example/lib/pages/events_example.dart).* +*The complete example is available [here](https://github.com/aleksanderwozniak/table_calendar/blob/master/example/lib/pages/events_example.dart).* You can supply custom events to **TableCalendar** widget. To do so, use `eventLoader` property - you will be given a `DateTime` object, to which you need to assign a list of events. @@ -160,11 +160,11 @@ void _onDaySelected(DateTime selectedDay, DateTime focusedDay) { } ``` -*The complete example is available [here](https://github.com/aleksanderwozniak/table_calendar/blob/3.0.0-nullsafety/example/lib/pages/events_example.dart).* +*The complete example is available [here](https://github.com/aleksanderwozniak/table_calendar/blob/master/example/lib/pages/events_example.dart).* ### Custom UI with CalendarBuilders -To customize the UI with your own widgets, use [CalendarBuilders](https://pub.dev/documentation/table_calendar/3.0.0-nullsafety.1/table_calendar/CalendarBuilders-class.html). Each builder can be used to selectively override the UI, allowing you to implement highly specific designs with minimal hassle. +To customize the UI with your own widgets, use [CalendarBuilders](https://pub.dev/documentation/table_calendar/latest/table_calendar/CalendarBuilders-class.html). Each builder can be used to selectively override the UI, allowing you to implement highly specific designs with minimal hassle. You can return `null` from any builder to use the default style. For example, the following snippet will override only the Sunday's day of the week label (Sun), leaving other dow labels unchanged: @@ -198,7 +198,7 @@ If you don't specify it, a default locale will be used. Before you can use a locale, you might need to initialize date formatting. A simple way of doing it is as follows: -* First of all, add [intl](https://pub.dartlang.org/packages/intl) package to your pubspec.yaml file +* First of all, add [intl](https://pub.dev/packages/intl) package to your pubspec.yaml file * Then make modifications to your `main()`: ```dart diff --git a/example/pubspec.lock b/example/pubspec.lock index 3310ea9c..7d04cd32 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -141,7 +141,7 @@ packages: path: ".." relative: true source: path - version: "3.0.0-nullsafety.1" + version: "3.0.0" term_glyph: dependency: transitive description: diff --git a/lib/src/shared/utils.dart b/lib/src/shared/utils.dart index 906c5861..accc4497 100644 --- a/lib/src/shared/utils.dart +++ b/lib/src/shared/utils.dart @@ -1,3 +1,6 @@ +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 + import 'package:flutter/widgets.dart'; /// Signature for a function that creates a widget for a given `day`. diff --git a/pubspec.yaml b/pubspec.yaml index 9cc1f9d0..ce8725bd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: table_calendar description: Highly customizable, feature-packed calendar widget for Flutter. -version: 3.0.0-nullsafety.1 +version: 3.0.0 author: Aleksander Woźniak homepage: https://github.com/aleksanderwozniak/table_calendar From c40ed7a92510efbc970b7c6b8da78471ede74e69 Mon Sep 17 00:00:00 2001 From: FineFindus <63370021+FineFindus@users.noreply.github.com> Date: Thu, 15 Apr 2021 12:10:13 +0200 Subject: [PATCH 089/164] added animation when changing months --- example/lib/pages/basics_example.dart | 1 + lib/src/table_calendar.dart | 10 ++++++++-- lib/src/table_calendar_base.dart | 8 +++++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/example/lib/pages/basics_example.dart b/example/lib/pages/basics_example.dart index 6a8f91b6..156118b0 100644 --- a/example/lib/pages/basics_example.dart +++ b/example/lib/pages/basics_example.dart @@ -27,6 +27,7 @@ class _TableBasicsExampleState extends State { lastDay: kLastDay, focusedDay: _focusedDay, calendarFormat: _calendarFormat, + pageJumpingEnabled: true, selectedDayPredicate: (day) { // Use `selectedDayPredicate` to determine which day is currently selected. // If this returns true, then `day` will be marked as selected. diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index 52935dae..bf14a594 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -106,10 +106,14 @@ class TableCalendar extends StatefulWidget { /// Specifies the curve of size animation that takes place when `calendarFormat` is changed. final Curve formatAnimationCurve; - /// Specifies the duration of page change animation that takes place when left or right chevron is tapped. + /// Specifies the duration of page change animation that takes place when the visible month changes, + /// either by tapping the left or right chevron or when `pageJumpingEnabled` is set to true and + /// the user clicks outside of a month. final Duration pageAnimationDuration; - /// Specifies the curve of page change animation that takes place when left or right chevron is tapped. + /// Specifies the curve of page change animation that takes place when the visible month changes, + /// either by tapping the left or right chevron or when `pageJumpingEnabled` is set to true and + /// the user clicks outside of a month. final Curve pageAnimationCurve; /// `TableCalendar` will start weeks with provided day. @@ -480,6 +484,8 @@ class _TableCalendarState extends State> { rowHeight: widget.rowHeight, formatAnimationDuration: widget.formatAnimationDuration, formatAnimationCurve: widget.formatAnimationCurve, + pageChangeAnimationDuration: widget.pageAnimationDuration, + pageChangeAnimationCurve: widget.pageAnimationCurve, availableCalendarFormats: widget.availableCalendarFormats, simpleSwipeConfig: widget.simpleSwipeConfig, sixWeekMonthsEnforced: widget.sixWeekMonthsEnforced, diff --git a/lib/src/table_calendar_base.dart b/lib/src/table_calendar_base.dart index 3c3ef880..ed7ba4f9 100644 --- a/lib/src/table_calendar_base.dart +++ b/lib/src/table_calendar_base.dart @@ -22,6 +22,8 @@ class TableCalendarBase extends StatefulWidget { final Decoration? rowDecoration; final Duration formatAnimationDuration; final Curve formatAnimationCurve; + final Duration pageChangeAnimationDuration; + final Curve pageChangeAnimationCurve; final StartingDayOfWeek startingDayOfWeek; final AvailableGestures availableGestures; final SimpleSwipeConfig simpleSwipeConfig; @@ -46,6 +48,8 @@ class TableCalendarBase extends StatefulWidget { this.rowDecoration, this.formatAnimationDuration = const Duration(milliseconds: 200), this.formatAnimationCurve = Curves.linear, + this.pageChangeAnimationDuration = const Duration(milliseconds: 200), + this.pageChangeAnimationCurve = Curves.linear, this.startingDayOfWeek = StartingDayOfWeek.sunday, this.availableGestures = AvailableGestures.all, this.simpleSwipeConfig = const SimpleSwipeConfig( @@ -147,7 +151,9 @@ class _TableCalendarBaseState extends State final rowCount = _getRowCount(widget.calendarFormat, _focusedDay); _pageHeight.value = _getPageHeight(rowCount); - _pageController.jumpToPage(currentIndex); + _pageController.animateToPage(currentIndex, + duration: widget.pageChangeAnimationDuration, + curve: widget.pageChangeAnimationCurve); _pageCallbackDisabled = false; } From baeb4975ca045210a2a69458ec2474c6135b382d Mon Sep 17 00:00:00 2001 From: FineFindus <63370021+FineFindus@users.noreply.github.com> Date: Thu, 15 Apr 2021 12:22:33 +0200 Subject: [PATCH 090/164] added animatedPageScrolling option --- example/lib/pages/basics_example.dart | 1 - lib/src/table_calendar.dart | 9 +++++++-- lib/src/table_calendar_base.dart | 12 +++++++++--- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/example/lib/pages/basics_example.dart b/example/lib/pages/basics_example.dart index 156118b0..6a8f91b6 100644 --- a/example/lib/pages/basics_example.dart +++ b/example/lib/pages/basics_example.dart @@ -27,7 +27,6 @@ class _TableBasicsExampleState extends State { lastDay: kLastDay, focusedDay: _focusedDay, calendarFormat: _calendarFormat, - pageJumpingEnabled: true, selectedDayPredicate: (day) { // Use `selectedDayPredicate` to determine which day is currently selected. // If this returns true, then `day` will be marked as selected. diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index bf14a594..0149cf34 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -94,6 +94,9 @@ class TableCalendar extends StatefulWidget { /// When set to true, `TableCalendar` will fill available height. final bool shouldFillViewport; + /// When set to true, an animation is displayed when changing the visible month. + final bool animatedPageScrolling; + /// Used for setting the height of `TableCalendar`'s rows. final double rowHeight; @@ -107,12 +110,12 @@ class TableCalendar extends StatefulWidget { final Curve formatAnimationCurve; /// Specifies the duration of page change animation that takes place when the visible month changes, - /// either by tapping the left or right chevron or when `pageJumpingEnabled` is set to true and + /// either by tapping the left or right chevron or when `pageJumpingEnabled` and `animatedPageScrolling` are set to true and /// the user clicks outside of a month. final Duration pageAnimationDuration; /// Specifies the curve of page change animation that takes place when the visible month changes, - /// either by tapping the left or right chevron or when `pageJumpingEnabled` is set to true and + /// either by tapping the left or right chevron or when `pageJumpingEnabled` and `animatedPageScrolling` are set to true and /// the user clicks outside of a month. final Curve pageAnimationCurve; @@ -217,6 +220,7 @@ class TableCalendar extends StatefulWidget { this.pageJumpingEnabled = false, this.sixWeekMonthsEnforced = false, this.shouldFillViewport = false, + this.animatedPageScrolling = false, this.rowHeight = 52.0, this.daysOfWeekHeight = 16.0, this.formatAnimationDuration = const Duration(milliseconds: 200), @@ -484,6 +488,7 @@ class _TableCalendarState extends State> { rowHeight: widget.rowHeight, formatAnimationDuration: widget.formatAnimationDuration, formatAnimationCurve: widget.formatAnimationCurve, + animatedPageScrolling: widget.animatedPageScrolling, pageChangeAnimationDuration: widget.pageAnimationDuration, pageChangeAnimationCurve: widget.pageAnimationCurve, availableCalendarFormats: widget.availableCalendarFormats, diff --git a/lib/src/table_calendar_base.dart b/lib/src/table_calendar_base.dart index ed7ba4f9..e37e16d8 100644 --- a/lib/src/table_calendar_base.dart +++ b/lib/src/table_calendar_base.dart @@ -22,6 +22,7 @@ class TableCalendarBase extends StatefulWidget { final Decoration? rowDecoration; final Duration formatAnimationDuration; final Curve formatAnimationCurve; + final bool animatedPageScrolling; final Duration pageChangeAnimationDuration; final Curve pageChangeAnimationCurve; final StartingDayOfWeek startingDayOfWeek; @@ -48,6 +49,7 @@ class TableCalendarBase extends StatefulWidget { this.rowDecoration, this.formatAnimationDuration = const Duration(milliseconds: 200), this.formatAnimationCurve = Curves.linear, + this.animatedPageScrolling = false, this.pageChangeAnimationDuration = const Duration(milliseconds: 200), this.pageChangeAnimationCurve = Curves.linear, this.startingDayOfWeek = StartingDayOfWeek.sunday, @@ -151,9 +153,13 @@ class _TableCalendarBaseState extends State final rowCount = _getRowCount(widget.calendarFormat, _focusedDay); _pageHeight.value = _getPageHeight(rowCount); - _pageController.animateToPage(currentIndex, - duration: widget.pageChangeAnimationDuration, - curve: widget.pageChangeAnimationCurve); + if (widget.animatedPageScrolling) { + _pageController.animateToPage(currentIndex, + duration: widget.pageChangeAnimationDuration, + curve: widget.pageChangeAnimationCurve); + } else { + _pageController.jumpToPage(currentIndex); + } _pageCallbackDisabled = false; } From 91130bb026f37f84e503edcdbac03e5af675f7d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 18 Apr 2021 18:27:12 +0200 Subject: [PATCH 091/164] Fix calendarFormat bug, improve scrolling animation --- lib/src/table_calendar_base.dart | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/lib/src/table_calendar_base.dart b/lib/src/table_calendar_base.dart index e37e16d8..3e9c068e 100644 --- a/lib/src/table_calendar_base.dart +++ b/lib/src/table_calendar_base.dart @@ -108,8 +108,10 @@ class _TableCalendarBaseState extends State if (_focusedDay != widget.focusedDay || widget.calendarFormat != oldWidget.calendarFormat || widget.startingDayOfWeek != oldWidget.startingDayOfWeek) { + final shouldAnimate = _focusedDay != widget.focusedDay; + _focusedDay = widget.focusedDay; - _updatePage(); + _updatePage(shouldAnimate: shouldAnimate); } if (widget.rowHeight != oldWidget.rowHeight || @@ -136,7 +138,7 @@ class _TableCalendarBaseState extends State widget.availableGestures == AvailableGestures.all || widget.availableGestures == AvailableGestures.verticalSwipe; - void _updatePage() { + void _updatePage({bool shouldAnimate = false}) { final currentIndex = _calculateFocusedPage( widget.calendarFormat, widget.firstDay, _focusedDay); @@ -149,17 +151,27 @@ class _TableCalendarBaseState extends State _pageCallbackDisabled = true; } - _previousIndex = currentIndex; - final rowCount = _getRowCount(widget.calendarFormat, _focusedDay); - _pageHeight.value = _getPageHeight(rowCount); + if (shouldAnimate && widget.animatedPageScrolling) { + if ((currentIndex - _previousIndex).abs() > 1) { + final jumpIndex = + currentIndex > _previousIndex ? currentIndex - 1 : currentIndex + 1; + + _pageController.jumpToPage(jumpIndex); + } - if (widget.animatedPageScrolling) { - _pageController.animateToPage(currentIndex, - duration: widget.pageChangeAnimationDuration, - curve: widget.pageChangeAnimationCurve); + _pageController.animateToPage( + currentIndex, + duration: widget.pageChangeAnimationDuration, + curve: widget.pageChangeAnimationCurve, + ); } else { _pageController.jumpToPage(currentIndex); } + + _previousIndex = currentIndex; + final rowCount = _getRowCount(widget.calendarFormat, _focusedDay); + _pageHeight.value = _getPageHeight(rowCount); + _pageCallbackDisabled = false; } From 3c1321c275a02513d43f182ba3f5c7a42bc51959 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 18 Apr 2021 18:28:56 +0200 Subject: [PATCH 092/164] Rename properties, update docs --- lib/src/table_calendar.dart | 29 +++++++++++++---------------- lib/src/table_calendar_base.dart | 12 ++++++------ 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index 0149cf34..d1d2453e 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -84,9 +84,13 @@ class TableCalendar extends StatefulWidget { final bool daysOfWeekVisible; /// When set to true, tapping on an outside day in `CalendarFormat.month` format - /// will jump to a page related to the tapped month. + /// will jump to the calendar page of the tapped month. final bool pageJumpingEnabled; + /// When set to true, updating the `focusedDay` will display a scrolling animation + /// if the currently visible calendar page is changed. + final bool pageAnimationEnabled; + /// When set to true, `CalendarFormat.month` will always display six weeks, /// even if the content would fit in less. final bool sixWeekMonthsEnforced; @@ -94,29 +98,22 @@ class TableCalendar extends StatefulWidget { /// When set to true, `TableCalendar` will fill available height. final bool shouldFillViewport; - /// When set to true, an animation is displayed when changing the visible month. - final bool animatedPageScrolling; - /// Used for setting the height of `TableCalendar`'s rows. final double rowHeight; /// Used for setting the height of `TableCalendar`'s days of week row. final double daysOfWeekHeight; - /// Specifies the duration of size animation that takes place when `calendarFormat` is changed. + /// Specifies the duration of size animation that takes place whenever `calendarFormat` is changed. final Duration formatAnimationDuration; - /// Specifies the curve of size animation that takes place when `calendarFormat` is changed. + /// Specifies the curve of size animation that takes place whenever `calendarFormat` is changed. final Curve formatAnimationCurve; - /// Specifies the duration of page change animation that takes place when the visible month changes, - /// either by tapping the left or right chevron or when `pageJumpingEnabled` and `animatedPageScrolling` are set to true and - /// the user clicks outside of a month. + /// Specifies the duration of scrolling animation that takes place whenever the visible calendar page is changed. final Duration pageAnimationDuration; - /// Specifies the curve of page change animation that takes place when the visible month changes, - /// either by tapping the left or right chevron or when `pageJumpingEnabled` and `animatedPageScrolling` are set to true and - /// the user clicks outside of a month. + /// Specifies the curve of scrolling animation that takes place whenever the visible calendar page is changed. final Curve pageAnimationCurve; /// `TableCalendar` will start weeks with provided day. @@ -218,9 +215,9 @@ class TableCalendar extends StatefulWidget { this.headerVisible = true, this.daysOfWeekVisible = true, this.pageJumpingEnabled = false, + this.pageAnimationEnabled = true, this.sixWeekMonthsEnforced = false, this.shouldFillViewport = false, - this.animatedPageScrolling = false, this.rowHeight = 52.0, this.daysOfWeekHeight = 16.0, this.formatAnimationDuration = const Duration(milliseconds: 200), @@ -488,9 +485,9 @@ class _TableCalendarState extends State> { rowHeight: widget.rowHeight, formatAnimationDuration: widget.formatAnimationDuration, formatAnimationCurve: widget.formatAnimationCurve, - animatedPageScrolling: widget.animatedPageScrolling, - pageChangeAnimationDuration: widget.pageAnimationDuration, - pageChangeAnimationCurve: widget.pageAnimationCurve, + animatedPageScrolling: widget.pageAnimationEnabled, + pageAnimationDuration: widget.pageAnimationDuration, + pageAnimationCurve: widget.pageAnimationCurve, availableCalendarFormats: widget.availableCalendarFormats, simpleSwipeConfig: widget.simpleSwipeConfig, sixWeekMonthsEnforced: widget.sixWeekMonthsEnforced, diff --git a/lib/src/table_calendar_base.dart b/lib/src/table_calendar_base.dart index 3e9c068e..c76286e5 100644 --- a/lib/src/table_calendar_base.dart +++ b/lib/src/table_calendar_base.dart @@ -23,8 +23,8 @@ class TableCalendarBase extends StatefulWidget { final Duration formatAnimationDuration; final Curve formatAnimationCurve; final bool animatedPageScrolling; - final Duration pageChangeAnimationDuration; - final Curve pageChangeAnimationCurve; + final Duration pageAnimationDuration; + final Curve pageAnimationCurve; final StartingDayOfWeek startingDayOfWeek; final AvailableGestures availableGestures; final SimpleSwipeConfig simpleSwipeConfig; @@ -50,8 +50,8 @@ class TableCalendarBase extends StatefulWidget { this.formatAnimationDuration = const Duration(milliseconds: 200), this.formatAnimationCurve = Curves.linear, this.animatedPageScrolling = false, - this.pageChangeAnimationDuration = const Duration(milliseconds: 200), - this.pageChangeAnimationCurve = Curves.linear, + this.pageAnimationDuration = const Duration(milliseconds: 300), + this.pageAnimationCurve = Curves.easeOut, this.startingDayOfWeek = StartingDayOfWeek.sunday, this.availableGestures = AvailableGestures.all, this.simpleSwipeConfig = const SimpleSwipeConfig( @@ -161,8 +161,8 @@ class _TableCalendarBaseState extends State _pageController.animateToPage( currentIndex, - duration: widget.pageChangeAnimationDuration, - curve: widget.pageChangeAnimationCurve, + duration: widget.pageAnimationDuration, + curve: widget.pageAnimationCurve, ); } else { _pageController.jumpToPage(currentIndex); From 4d906d6b1350908facdedaae187634193b55853f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 18 Apr 2021 18:38:39 +0200 Subject: [PATCH 093/164] Rename animatedPageScrolling to pageAnimationEnabled --- lib/src/table_calendar.dart | 2 +- lib/src/table_calendar_base.dart | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index d1d2453e..e05eae38 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -485,7 +485,7 @@ class _TableCalendarState extends State> { rowHeight: widget.rowHeight, formatAnimationDuration: widget.formatAnimationDuration, formatAnimationCurve: widget.formatAnimationCurve, - animatedPageScrolling: widget.pageAnimationEnabled, + pageAnimationEnabled: widget.pageAnimationEnabled, pageAnimationDuration: widget.pageAnimationDuration, pageAnimationCurve: widget.pageAnimationCurve, availableCalendarFormats: widget.availableCalendarFormats, diff --git a/lib/src/table_calendar_base.dart b/lib/src/table_calendar_base.dart index c76286e5..b3525f62 100644 --- a/lib/src/table_calendar_base.dart +++ b/lib/src/table_calendar_base.dart @@ -22,7 +22,7 @@ class TableCalendarBase extends StatefulWidget { final Decoration? rowDecoration; final Duration formatAnimationDuration; final Curve formatAnimationCurve; - final bool animatedPageScrolling; + final bool pageAnimationEnabled; final Duration pageAnimationDuration; final Curve pageAnimationCurve; final StartingDayOfWeek startingDayOfWeek; @@ -49,7 +49,7 @@ class TableCalendarBase extends StatefulWidget { this.rowDecoration, this.formatAnimationDuration = const Duration(milliseconds: 200), this.formatAnimationCurve = Curves.linear, - this.animatedPageScrolling = false, + this.pageAnimationEnabled = true, this.pageAnimationDuration = const Duration(milliseconds: 300), this.pageAnimationCurve = Curves.easeOut, this.startingDayOfWeek = StartingDayOfWeek.sunday, @@ -151,7 +151,7 @@ class _TableCalendarBaseState extends State _pageCallbackDisabled = true; } - if (shouldAnimate && widget.animatedPageScrolling) { + if (shouldAnimate && widget.pageAnimationEnabled) { if ((currentIndex - _previousIndex).abs() > 1) { final jumpIndex = currentIndex > _previousIndex ? currentIndex - 1 : currentIndex + 1; From 3acacf97dab2f47f9d039ec3d9659c47ff134c3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sat, 26 Jun 2021 15:22:03 +0200 Subject: [PATCH 094/164] Add currentDay property --- lib/src/table_calendar.dart | 9 ++++++++- pubspec.lock | 6 +++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index e05eae38..c7caa2ac 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -56,6 +56,11 @@ class TableCalendar extends StatefulWidget { /// Days after it will use `disabledStyle` and trigger `onDisabledDayTapped` callback. final DateTime lastDay; + /// DateTime that will be treated as today. Defaults to `DateTime.now()`. + /// + /// Overriding this property might be useful for testing. + final DateTime? currentDay; + /// List of days treated as weekend days. /// Use built-in `DateTime` weekday constants (e.g. `DateTime.monday`) instead of `int` literals (e.g. `1`). final List weekendDays; @@ -202,6 +207,7 @@ class TableCalendar extends StatefulWidget { required DateTime focusedDay, required DateTime firstDay, required DateTime lastDay, + DateTime? currentDay, this.locale, this.rangeStartDay, this.rangeEndDay, @@ -259,6 +265,7 @@ class TableCalendar extends StatefulWidget { focusedDay = normalizeDate(focusedDay), firstDay = normalizeDate(firstDay), lastDay = normalizeDate(lastDay), + currentDay = currentDay ?? DateTime.now(), super(key: key); @override @@ -580,7 +587,7 @@ class _TableCalendarState extends State> { children.add(rangeHighlight); } - final isToday = isSameDay(day, DateTime.now()); + final isToday = isSameDay(day, widget.currentDay); final isDisabled = _isDayDisabled(day); final isWeekend = _isWeekend(day, weekendDays: widget.weekendDays); diff --git a/pubspec.lock b/pubspec.lock index a3ccb2d6..552f9a94 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,7 +7,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0" + version: "2.6.1" boolean_selector: dependency: transitive description: @@ -106,7 +106,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.8.1" stack_trace: dependency: transitive description: @@ -141,7 +141,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19" + version: "0.3.0" typed_data: dependency: transitive description: From 869367de62cf3d2c942e9396ae8089464c66edde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sat, 26 Jun 2021 15:23:17 +0200 Subject: [PATCH 095/164] Update docs --- lib/src/customization/calendar_builders.dart | 2 +- lib/src/customization/calendar_style.dart | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/src/customization/calendar_builders.dart b/lib/src/customization/calendar_builders.dart index a6a85420..32b5b4b0 100644 --- a/lib/src/customization/calendar_builders.dart +++ b/lib/src/customization/calendar_builders.dart @@ -27,7 +27,7 @@ class CalendarBuilders { /// Custom builder for day cells, with a priority over any other builder. final FocusedDayBuilder? prioritizedBuilder; - /// Custom builder for a day cell that matches `DateTime.now()`. + /// Custom builder for a day cell that matches the current day. final FocusedDayBuilder? todayBuilder; /// Custom builder for day cells that are currently marked as selected by `selectedDayPredicate`. diff --git a/lib/src/customization/calendar_style.dart b/lib/src/customization/calendar_style.dart index bfb078d5..73eedd02 100644 --- a/lib/src/customization/calendar_style.dart +++ b/lib/src/customization/calendar_style.dart @@ -62,13 +62,13 @@ class CalendarStyle { /// Affects only `CalendarFormat.month`. final bool outsideDaysVisible; - /// Determines if a day cell that matches `DateTime.now()` should be highlighted. + /// Determines if a day cell that matches the current day should be highlighted. final bool isTodayHighlighted; - /// TextStyle for a day cell that matches `DateTime.now()`. + /// TextStyle for a day cell that matches the current day. final TextStyle todayTextStyle; - /// Decoration for a day cell that matches `DateTime.now()`. + /// Decoration for a day cell that matches the current day. final Decoration todayDecoration; /// TextStyle for day cells that are currently marked as selected by `selectedDayPredicate`. From d50b97b9d4cfedf67fb1eab10ddf58410393cb98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sat, 26 Jun 2021 15:23:34 +0200 Subject: [PATCH 096/164] Update example --- example/lib/utils.dart | 10 +++++----- example/pubspec.lock | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/example/lib/utils.dart b/example/lib/utils.dart index 2a579259..c3f6e7d0 100644 --- a/example/lib/utils.dart +++ b/example/lib/utils.dart @@ -24,11 +24,11 @@ final kEvents = LinkedHashMap>( )..addAll(_kEventSource); final _kEventSource = Map.fromIterable(List.generate(50, (index) => index), - key: (item) => DateTime.utc(2020, 10, item * 5), + key: (item) => DateTime.utc(kFirstDay.year, kFirstDay.month, item * 5), value: (item) => List.generate( item % 4 + 1, (index) => Event('Event $item | ${index + 1}'))) ..addAll({ - DateTime.now(): [ + kToday: [ Event('Today\'s Event 1'), Event('Today\'s Event 2'), ], @@ -47,6 +47,6 @@ List daysInRange(DateTime first, DateTime last) { ); } -final kNow = DateTime.now(); -final kFirstDay = DateTime(kNow.year, kNow.month - 3, kNow.day); -final kLastDay = DateTime(kNow.year, kNow.month + 3, kNow.day); +final kToday = DateTime.now(); +final kFirstDay = DateTime(kToday.year, kToday.month - 3, kToday.day); +final kLastDay = DateTime(kToday.year, kToday.month + 3, kToday.day); diff --git a/example/pubspec.lock b/example/pubspec.lock index 7d04cd32..735b48a7 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -7,7 +7,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0" + version: "2.6.1" boolean_selector: dependency: transitive description: @@ -113,7 +113,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.8.1" stack_trace: dependency: transitive description: @@ -155,7 +155,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19" + version: "0.3.0" typed_data: dependency: transitive description: From 9f523a6df34776f0e479cda8b535e2565af994a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Mon, 28 Jun 2021 17:02:46 +0200 Subject: [PATCH 097/164] Release version 3.0.1 --- CHANGELOG.md | 5 +++++ README.md | 2 +- example/pubspec.lock | 2 +- pubspec.yaml | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95284e24..60f8ee26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## [3.0.1] + +* Added pageAnimationEnabled property +* Added currentDay property to improve widget testability + ## [3.0.0] * Migrated to null safety diff --git a/README.md b/README.md index f04b477f..c989b9d6 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Add the following line to `pubspec.yaml`: ```yaml dependencies: - table_calendar: ^3.0.0 + table_calendar: ^3.0.1 ``` ### Basic setup diff --git a/example/pubspec.lock b/example/pubspec.lock index 735b48a7..be1e0e4c 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -141,7 +141,7 @@ packages: path: ".." relative: true source: path - version: "3.0.0" + version: "3.0.1" term_glyph: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index ce8725bd..87dd725e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: table_calendar description: Highly customizable, feature-packed calendar widget for Flutter. -version: 3.0.0 +version: 3.0.1 author: Aleksander Woźniak homepage: https://github.com/aleksanderwozniak/table_calendar From ce95d14cefe511f5bb55dd2ac8709209ea1a653a Mon Sep 17 00:00:00 2001 From: honoratoamg <69965835+honoratoamg@users.noreply.github.com> Date: Thu, 15 Jul 2021 10:54:20 -0300 Subject: [PATCH 098/164] Change in screen reader semantics label of for the days of the month --- lib/src/table_calendar.dart | 13 ++++--- lib/src/widgets/cell_content.dart | 63 ++++++++++++++++++++++++------- 2 files changed, 58 insertions(+), 18 deletions(-) diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index c7caa2ac..befdad11 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -516,11 +516,13 @@ class _TableCalendarState extends State> { _isWeekend(day, weekendDays: widget.weekendDays); dowCell = Center( - child: Text( - weekdayString, - style: isWeekend - ? widget.daysOfWeekStyle.weekendStyle - : widget.daysOfWeekStyle.weekdayStyle, + child: ExcludeSemantics( + child: Text( + weekdayString, + style: isWeekend + ? widget.daysOfWeekStyle.weekendStyle + : widget.daysOfWeekStyle.weekdayStyle, + ), ), ); } @@ -606,6 +608,7 @@ class _TableCalendarState extends State> { isDisabled: isDisabled, isWeekend: isWeekend, isHoliday: widget.holidayPredicate?.call(day) ?? false, + locale: widget.locale, ); children.add(content); diff --git a/lib/src/widgets/cell_content.dart b/lib/src/widgets/cell_content.dart index 5a1cf4c8..f5d836a8 100644 --- a/lib/src/widgets/cell_content.dart +++ b/lib/src/widgets/cell_content.dart @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import 'package:flutter/widgets.dart'; +import 'package:intl/intl.dart'; import '../customization/calendar_builders.dart'; import '../customization/calendar_style.dart'; @@ -9,6 +10,7 @@ import '../customization/calendar_style.dart'; class CellContent extends StatelessWidget { final DateTime day; final DateTime focusedDay; + final dynamic locale; final bool isTodayHighlighted; final bool isToday; final bool isSelected; @@ -38,6 +40,7 @@ class CellContent extends StatelessWidget { required this.isDisabled, required this.isHoliday, required this.isWeekend, + this.locale, }) : super(key: key); @override @@ -50,6 +53,10 @@ class CellContent extends StatelessWidget { } final text = '${day.day}'; + final weekdayString = DateFormat.EEEE(locale).format(day); + final semanticsLabelString = + '$weekdayString, ${DateFormat.yMMMMd(locale).format(day)}'; + final margin = calendarStyle.cellMargin; final duration = const Duration(milliseconds: 250); @@ -60,7 +67,10 @@ class CellContent extends StatelessWidget { margin: margin, decoration: calendarStyle.disabledDecoration, alignment: Alignment.center, - child: Text(text, style: calendarStyle.disabledTextStyle), + child: Semantics( + label: semanticsLabelString, + excludeSemantics: true, + child: Text(text, style: calendarStyle.disabledTextStyle)), ); } else if (isSelected) { cell = calendarBuilders.selectedBuilder?.call(context, day, focusedDay) ?? @@ -69,7 +79,10 @@ class CellContent extends StatelessWidget { margin: margin, decoration: calendarStyle.selectedDecoration, alignment: Alignment.center, - child: Text(text, style: calendarStyle.selectedTextStyle), + child: Semantics( + label: semanticsLabelString, + excludeSemantics: true, + child: Text(text, style: calendarStyle.selectedTextStyle)), ); } else if (isRangeStart) { cell = @@ -79,7 +92,11 @@ class CellContent extends StatelessWidget { margin: margin, decoration: calendarStyle.rangeStartDecoration, alignment: Alignment.center, - child: Text(text, style: calendarStyle.rangeStartTextStyle), + child: Semantics( + label: semanticsLabelString, + excludeSemantics: true, + child: + Text(text, style: calendarStyle.rangeStartTextStyle)), ); } else if (isRangeEnd) { cell = calendarBuilders.rangeEndBuilder?.call(context, day, focusedDay) ?? @@ -88,7 +105,10 @@ class CellContent extends StatelessWidget { margin: margin, decoration: calendarStyle.rangeEndDecoration, alignment: Alignment.center, - child: Text(text, style: calendarStyle.rangeEndTextStyle), + child: Semantics( + label: semanticsLabelString, + excludeSemantics: true, + child: Text(text, style: calendarStyle.rangeEndTextStyle)), ); } else if (isToday && isTodayHighlighted) { cell = calendarBuilders.todayBuilder?.call(context, day, focusedDay) ?? @@ -97,7 +117,10 @@ class CellContent extends StatelessWidget { margin: margin, decoration: calendarStyle.todayDecoration, alignment: Alignment.center, - child: Text(text, style: calendarStyle.todayTextStyle), + child: Semantics( + label: semanticsLabelString, + excludeSemantics: true, + child: Text(text, style: calendarStyle.todayTextStyle)), ); } else if (isHoliday) { cell = calendarBuilders.holidayBuilder?.call(context, day, focusedDay) ?? @@ -106,7 +129,10 @@ class CellContent extends StatelessWidget { margin: margin, decoration: calendarStyle.holidayDecoration, alignment: Alignment.center, - child: Text(text, style: calendarStyle.holidayTextStyle), + child: Semantics( + label: semanticsLabelString, + excludeSemantics: true, + child: Text(text, style: calendarStyle.holidayTextStyle)), ); } else if (isWithinRange) { cell = @@ -116,7 +142,11 @@ class CellContent extends StatelessWidget { margin: margin, decoration: calendarStyle.withinRangeDecoration, alignment: Alignment.center, - child: Text(text, style: calendarStyle.withinRangeTextStyle), + child: Semantics( + label: semanticsLabelString, + excludeSemantics: true, + child: + Text(text, style: calendarStyle.withinRangeTextStyle)), ); } else if (isOutside) { cell = calendarBuilders.outsideBuilder?.call(context, day, focusedDay) ?? @@ -125,7 +155,10 @@ class CellContent extends StatelessWidget { margin: margin, decoration: calendarStyle.outsideDecoration, alignment: Alignment.center, - child: Text(text, style: calendarStyle.outsideTextStyle), + child: Semantics( + label: semanticsLabelString, + excludeSemantics: true, + child: Text(text, style: calendarStyle.outsideTextStyle)), ); } else { cell = calendarBuilders.defaultBuilder?.call(context, day, focusedDay) ?? @@ -136,11 +169,15 @@ class CellContent extends StatelessWidget { ? calendarStyle.weekendDecoration : calendarStyle.defaultDecoration, alignment: Alignment.center, - child: Text( - text, - style: isWeekend - ? calendarStyle.weekendTextStyle - : calendarStyle.defaultTextStyle, + child: Semantics( + label: semanticsLabelString, + excludeSemantics: true, + child: Text( + text, + style: isWeekend + ? calendarStyle.weekendTextStyle + : calendarStyle.defaultTextStyle, + ), ), ); } From e0a51b50586eb0195bbb4e63482b0d6f2d9bcb1b Mon Sep 17 00:00:00 2001 From: Ricardo Gobbo Date: Sat, 31 Jul 2021 20:54:20 -0300 Subject: [PATCH 099/164] added cell Padding and Alignment properties --- lib/src/customization/calendar_style.dart | 8 +++++++ lib/src/widgets/cell_content.dart | 29 ++++++++++++++++------- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/lib/src/customization/calendar_style.dart b/lib/src/customization/calendar_style.dart index 73eedd02..f72253ea 100644 --- a/lib/src/customization/calendar_style.dart +++ b/lib/src/customization/calendar_style.dart @@ -49,6 +49,12 @@ class CalendarStyle { /// Margin of each individual day cell. final EdgeInsets cellMargin; + /// Padding of each individual day cell. + final EdgeInsets cellPadding; + + /// Alignment of each individual day cell. + final AlignmentGeometry cellAlignment; + /// Proportion of range selection highlight size in relation to day cell size. /// /// Includes `cellMargin` for calculations. @@ -150,6 +156,8 @@ class CalendarStyle { this.markersAlignment = Alignment.bottomCenter, this.markersMaxCount = 4, this.cellMargin = const EdgeInsets.all(6.0), + this.cellPadding = const EdgeInsets.all(0), + this.cellAlignment = Alignment.center, this.markersOffset = const PositionedOffset(), this.rangeHighlightColor = const Color(0xFFBBDDFF), this.markerDecoration = const BoxDecoration( diff --git a/lib/src/widgets/cell_content.dart b/lib/src/widgets/cell_content.dart index 5a1cf4c8..89a5cc46 100644 --- a/lib/src/widgets/cell_content.dart +++ b/lib/src/widgets/cell_content.dart @@ -51,6 +51,8 @@ class CellContent extends StatelessWidget { final text = '${day.day}'; final margin = calendarStyle.cellMargin; + final padding = calendarStyle.cellPadding; + final alignment = calendarStyle.cellAlignment; final duration = const Duration(milliseconds: 250); if (isDisabled) { @@ -58,8 +60,9 @@ class CellContent extends StatelessWidget { AnimatedContainer( duration: duration, margin: margin, + padding: padding, decoration: calendarStyle.disabledDecoration, - alignment: Alignment.center, + alignment: alignment, child: Text(text, style: calendarStyle.disabledTextStyle), ); } else if (isSelected) { @@ -67,8 +70,9 @@ class CellContent extends StatelessWidget { AnimatedContainer( duration: duration, margin: margin, + padding: padding, decoration: calendarStyle.selectedDecoration, - alignment: Alignment.center, + alignment: alignment, child: Text(text, style: calendarStyle.selectedTextStyle), ); } else if (isRangeStart) { @@ -77,8 +81,9 @@ class CellContent extends StatelessWidget { AnimatedContainer( duration: duration, margin: margin, + padding: padding, decoration: calendarStyle.rangeStartDecoration, - alignment: Alignment.center, + alignment: alignment, child: Text(text, style: calendarStyle.rangeStartTextStyle), ); } else if (isRangeEnd) { @@ -86,8 +91,9 @@ class CellContent extends StatelessWidget { AnimatedContainer( duration: duration, margin: margin, + padding: padding, decoration: calendarStyle.rangeEndDecoration, - alignment: Alignment.center, + alignment: alignment, child: Text(text, style: calendarStyle.rangeEndTextStyle), ); } else if (isToday && isTodayHighlighted) { @@ -95,8 +101,9 @@ class CellContent extends StatelessWidget { AnimatedContainer( duration: duration, margin: margin, + padding: padding, decoration: calendarStyle.todayDecoration, - alignment: Alignment.center, + alignment: alignment, child: Text(text, style: calendarStyle.todayTextStyle), ); } else if (isHoliday) { @@ -104,8 +111,9 @@ class CellContent extends StatelessWidget { AnimatedContainer( duration: duration, margin: margin, + padding: padding, decoration: calendarStyle.holidayDecoration, - alignment: Alignment.center, + alignment: alignment, child: Text(text, style: calendarStyle.holidayTextStyle), ); } else if (isWithinRange) { @@ -114,8 +122,9 @@ class CellContent extends StatelessWidget { AnimatedContainer( duration: duration, margin: margin, + padding: padding, decoration: calendarStyle.withinRangeDecoration, - alignment: Alignment.center, + alignment: alignment, child: Text(text, style: calendarStyle.withinRangeTextStyle), ); } else if (isOutside) { @@ -123,8 +132,9 @@ class CellContent extends StatelessWidget { AnimatedContainer( duration: duration, margin: margin, + padding: padding, decoration: calendarStyle.outsideDecoration, - alignment: Alignment.center, + alignment: alignment, child: Text(text, style: calendarStyle.outsideTextStyle), ); } else { @@ -132,10 +142,11 @@ class CellContent extends StatelessWidget { AnimatedContainer( duration: duration, margin: margin, + padding: padding, decoration: isWeekend ? calendarStyle.weekendDecoration : calendarStyle.defaultDecoration, - alignment: Alignment.center, + alignment: alignment, child: Text( text, style: isWeekend From c2eebf72c80850bd116a14fc778eafb596f2dfb8 Mon Sep 17 00:00:00 2001 From: fuganti Date: Mon, 9 Aug 2021 07:46:27 -0300 Subject: [PATCH 100/164] Added TableBorder to CalendarStyle It was needed to improve internal table border, so added a tblBorder property to CalendarStyle to propagate a TableBorder() to configure Table border. --- lib/src/customization/calendar_style.dart | 4 ++++ lib/src/table_calendar.dart | 1 + lib/src/table_calendar_base.dart | 3 +++ lib/src/widgets/calendar_core.dart | 3 +++ lib/src/widgets/calendar_page.dart | 3 +++ 5 files changed, 14 insertions(+) diff --git a/lib/src/customization/calendar_style.dart b/lib/src/customization/calendar_style.dart index 73eedd02..148b0e6a 100644 --- a/lib/src/customization/calendar_style.dart +++ b/lib/src/customization/calendar_style.dart @@ -136,6 +136,9 @@ class CalendarStyle { /// Decoration for each interior row of day cells. final Decoration rowDecoration; + /// Border for table + final TableBorder tblBorder; + /// Creates a `CalendarStyle` used by `TableCalendar` widget. const CalendarStyle({ this.isTodayHighlighted = true, @@ -206,6 +209,7 @@ class CalendarStyle { this.defaultTextStyle = const TextStyle(), this.defaultDecoration = const BoxDecoration(shape: BoxShape.circle), this.rowDecoration = const BoxDecoration(), + this.tblBorder = const TableBorder(), }); } diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index c7caa2ac..2ad4e47b 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -487,6 +487,7 @@ class _TableCalendarState extends State> { startingDayOfWeek: widget.startingDayOfWeek, dowDecoration: widget.daysOfWeekStyle.decoration, rowDecoration: widget.calendarStyle.rowDecoration, + tblBorder: widget.calendarStyle.tblBorder, dowVisible: widget.daysOfWeekVisible, dowHeight: widget.daysOfWeekHeight, rowHeight: widget.rowHeight, diff --git a/lib/src/table_calendar_base.dart b/lib/src/table_calendar_base.dart index b3525f62..55fd8112 100644 --- a/lib/src/table_calendar_base.dart +++ b/lib/src/table_calendar_base.dart @@ -20,6 +20,7 @@ class TableCalendarBase extends StatefulWidget { final bool dowVisible; final Decoration? dowDecoration; final Decoration? rowDecoration; + final TableBorder? tblBorder; final Duration formatAnimationDuration; final Curve formatAnimationCurve; final bool pageAnimationEnabled; @@ -47,6 +48,7 @@ class TableCalendarBase extends StatefulWidget { this.dowVisible = true, this.dowDecoration, this.rowDecoration, + this.tblBorder, this.formatAnimationDuration = const Duration(milliseconds: 200), this.formatAnimationCurve = Curves.linear, this.pageAnimationEnabled = true, @@ -217,6 +219,7 @@ class _TableCalendarBaseState extends State rowHeight: widget.rowHeight, dowDecoration: widget.dowDecoration, rowDecoration: widget.rowDecoration, + tblBorder: widget.tblBorder, onPageChanged: (index, focusedMonth) { if (!_pageCallbackDisabled) { if (!isSameDay(_focusedDay, focusedMonth)) { diff --git a/lib/src/widgets/calendar_core.dart b/lib/src/widgets/calendar_core.dart index 957d94f8..8f6e5f46 100644 --- a/lib/src/widgets/calendar_core.dart +++ b/lib/src/widgets/calendar_core.dart @@ -20,6 +20,7 @@ class CalendarCore extends StatelessWidget { final bool dowVisible; final Decoration? dowDecoration; final Decoration? rowDecoration; + final TableBorder? tblBorder; final double? dowHeight; final double? rowHeight; final BoxConstraints constraints; @@ -48,6 +49,7 @@ class CalendarCore extends StatelessWidget { this.dowVisible = true, this.dowDecoration, this.rowDecoration, + this.tblBorder, this.scrollPhysics, }) : assert(!dowVisible || (dowHeight != null && dowBuilder != null)), super(key: key); @@ -74,6 +76,7 @@ class CalendarCore extends StatelessWidget { dowVisible: dowVisible, dowDecoration: dowDecoration, rowDecoration: rowDecoration, + tblBorder: tblBorder, dowBuilder: (context, day) { return SizedBox( height: dowHeight, diff --git a/lib/src/widgets/calendar_page.dart b/lib/src/widgets/calendar_page.dart index 57ab4091..f323651f 100644 --- a/lib/src/widgets/calendar_page.dart +++ b/lib/src/widgets/calendar_page.dart @@ -9,6 +9,7 @@ class CalendarPage extends StatelessWidget { final List visibleDays; final Decoration? dowDecoration; final Decoration? rowDecoration; + final TableBorder? tblBorder; final bool dowVisible; const CalendarPage({ @@ -18,6 +19,7 @@ class CalendarPage extends StatelessWidget { required this.dayBuilder, this.dowDecoration, this.rowDecoration, + this.tblBorder, this.dowVisible = true, }) : assert(!dowVisible || dowBuilder != null), super(key: key); @@ -25,6 +27,7 @@ class CalendarPage extends StatelessWidget { @override Widget build(BuildContext context) { return Table( + border: tblBorder, children: [ if (dowVisible) _buildDaysOfWeek(context), ..._buildCalendarDays(context), From 1c2272a891f78e10b3d392466923785e73b4f13c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Tue, 10 Aug 2021 18:30:47 +0200 Subject: [PATCH 101/164] Refactor code --- lib/src/widgets/cell_content.dart | 70 ++++++++++--------------------- 1 file changed, 22 insertions(+), 48 deletions(-) diff --git a/lib/src/widgets/cell_content.dart b/lib/src/widgets/cell_content.dart index f5d836a8..42e4313f 100644 --- a/lib/src/widgets/cell_content.dart +++ b/lib/src/widgets/cell_content.dart @@ -53,13 +53,13 @@ class CellContent extends StatelessWidget { } final text = '${day.day}'; - final weekdayString = DateFormat.EEEE(locale).format(day); - final semanticsLabelString = - '$weekdayString, ${DateFormat.yMMMMd(locale).format(day)}'; - final margin = calendarStyle.cellMargin; final duration = const Duration(milliseconds: 250); + final dowLabel = DateFormat.EEEE(locale).format(day); + final dayLabel = DateFormat.yMMMMd(locale).format(day); + final semanticsLabel = '$dowLabel, $dayLabel'; + if (isDisabled) { cell = calendarBuilders.disabledBuilder?.call(context, day, focusedDay) ?? AnimatedContainer( @@ -67,10 +67,7 @@ class CellContent extends StatelessWidget { margin: margin, decoration: calendarStyle.disabledDecoration, alignment: Alignment.center, - child: Semantics( - label: semanticsLabelString, - excludeSemantics: true, - child: Text(text, style: calendarStyle.disabledTextStyle)), + child: Text(text, style: calendarStyle.disabledTextStyle), ); } else if (isSelected) { cell = calendarBuilders.selectedBuilder?.call(context, day, focusedDay) ?? @@ -79,10 +76,7 @@ class CellContent extends StatelessWidget { margin: margin, decoration: calendarStyle.selectedDecoration, alignment: Alignment.center, - child: Semantics( - label: semanticsLabelString, - excludeSemantics: true, - child: Text(text, style: calendarStyle.selectedTextStyle)), + child: Text(text, style: calendarStyle.selectedTextStyle), ); } else if (isRangeStart) { cell = @@ -92,11 +86,7 @@ class CellContent extends StatelessWidget { margin: margin, decoration: calendarStyle.rangeStartDecoration, alignment: Alignment.center, - child: Semantics( - label: semanticsLabelString, - excludeSemantics: true, - child: - Text(text, style: calendarStyle.rangeStartTextStyle)), + child: Text(text, style: calendarStyle.rangeStartTextStyle), ); } else if (isRangeEnd) { cell = calendarBuilders.rangeEndBuilder?.call(context, day, focusedDay) ?? @@ -105,10 +95,7 @@ class CellContent extends StatelessWidget { margin: margin, decoration: calendarStyle.rangeEndDecoration, alignment: Alignment.center, - child: Semantics( - label: semanticsLabelString, - excludeSemantics: true, - child: Text(text, style: calendarStyle.rangeEndTextStyle)), + child: Text(text, style: calendarStyle.rangeEndTextStyle), ); } else if (isToday && isTodayHighlighted) { cell = calendarBuilders.todayBuilder?.call(context, day, focusedDay) ?? @@ -117,10 +104,7 @@ class CellContent extends StatelessWidget { margin: margin, decoration: calendarStyle.todayDecoration, alignment: Alignment.center, - child: Semantics( - label: semanticsLabelString, - excludeSemantics: true, - child: Text(text, style: calendarStyle.todayTextStyle)), + child: Text(text, style: calendarStyle.todayTextStyle), ); } else if (isHoliday) { cell = calendarBuilders.holidayBuilder?.call(context, day, focusedDay) ?? @@ -129,10 +113,7 @@ class CellContent extends StatelessWidget { margin: margin, decoration: calendarStyle.holidayDecoration, alignment: Alignment.center, - child: Semantics( - label: semanticsLabelString, - excludeSemantics: true, - child: Text(text, style: calendarStyle.holidayTextStyle)), + child: Text(text, style: calendarStyle.holidayTextStyle), ); } else if (isWithinRange) { cell = @@ -142,11 +123,7 @@ class CellContent extends StatelessWidget { margin: margin, decoration: calendarStyle.withinRangeDecoration, alignment: Alignment.center, - child: Semantics( - label: semanticsLabelString, - excludeSemantics: true, - child: - Text(text, style: calendarStyle.withinRangeTextStyle)), + child: Text(text, style: calendarStyle.withinRangeTextStyle), ); } else if (isOutside) { cell = calendarBuilders.outsideBuilder?.call(context, day, focusedDay) ?? @@ -155,10 +132,7 @@ class CellContent extends StatelessWidget { margin: margin, decoration: calendarStyle.outsideDecoration, alignment: Alignment.center, - child: Semantics( - label: semanticsLabelString, - excludeSemantics: true, - child: Text(text, style: calendarStyle.outsideTextStyle)), + child: Text(text, style: calendarStyle.outsideTextStyle), ); } else { cell = calendarBuilders.defaultBuilder?.call(context, day, focusedDay) ?? @@ -169,19 +143,19 @@ class CellContent extends StatelessWidget { ? calendarStyle.weekendDecoration : calendarStyle.defaultDecoration, alignment: Alignment.center, - child: Semantics( - label: semanticsLabelString, - excludeSemantics: true, - child: Text( - text, - style: isWeekend - ? calendarStyle.weekendTextStyle - : calendarStyle.defaultTextStyle, - ), + child: Text( + text, + style: isWeekend + ? calendarStyle.weekendTextStyle + : calendarStyle.defaultTextStyle, ), ); } - return cell; + return Semantics( + label: semanticsLabel, + excludeSemantics: true, + child: cell, + ); } } From 7aa42f2e413d21498f94fca08de45babdb201d36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Tue, 10 Aug 2021 18:42:39 +0200 Subject: [PATCH 102/164] Release version 3.0.2 --- CHANGELOG.md | 4 ++++ README.md | 5 +---- example/pubspec.lock | 2 +- pubspec.yaml | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60f8ee26..8dbe510a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## [3.0.2] + +* Improved semantic labels for screen readers + ## [3.0.1] * Added pageAnimationEnabled property diff --git a/README.md b/README.md index c989b9d6..1ab5a697 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Add the following line to `pubspec.yaml`: ```yaml dependencies: - table_calendar: ^3.0.1 + table_calendar: ^3.0.2 ``` ### Basic setup @@ -185,9 +185,6 @@ calendarBuilders: CalendarBuilders( ), ``` - - - ### Locale To display the calendar in desired language, use `locale` property. diff --git a/example/pubspec.lock b/example/pubspec.lock index be1e0e4c..aa9b234b 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -141,7 +141,7 @@ packages: path: ".." relative: true source: path - version: "3.0.1" + version: "3.0.2" term_glyph: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 87dd725e..3b659ab1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: table_calendar description: Highly customizable, feature-packed calendar widget for Flutter. -version: 3.0.1 +version: 3.0.2 author: Aleksander Woźniak homepage: https://github.com/aleksanderwozniak/table_calendar From b1106784df4377d4758cf4eb71c6570fbbd745e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Wed, 15 Sep 2021 23:25:17 +0200 Subject: [PATCH 103/164] Rename new property --- lib/src/customization/calendar_style.dart | 6 +++--- lib/src/table_calendar.dart | 2 +- lib/src/table_calendar_base.dart | 6 +++--- lib/src/widgets/calendar_core.dart | 6 +++--- lib/src/widgets/calendar_page.dart | 6 +++--- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/src/customization/calendar_style.dart b/lib/src/customization/calendar_style.dart index 148b0e6a..f343e519 100644 --- a/lib/src/customization/calendar_style.dart +++ b/lib/src/customization/calendar_style.dart @@ -136,8 +136,8 @@ class CalendarStyle { /// Decoration for each interior row of day cells. final Decoration rowDecoration; - /// Border for table - final TableBorder tblBorder; + /// Border for the internal `Table` widget. + final TableBorder tableBorder; /// Creates a `CalendarStyle` used by `TableCalendar` widget. const CalendarStyle({ @@ -209,7 +209,7 @@ class CalendarStyle { this.defaultTextStyle = const TextStyle(), this.defaultDecoration = const BoxDecoration(shape: BoxShape.circle), this.rowDecoration = const BoxDecoration(), - this.tblBorder = const TableBorder(), + this.tableBorder = const TableBorder(), }); } diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index 2ad4e47b..90f942bd 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -487,7 +487,7 @@ class _TableCalendarState extends State> { startingDayOfWeek: widget.startingDayOfWeek, dowDecoration: widget.daysOfWeekStyle.decoration, rowDecoration: widget.calendarStyle.rowDecoration, - tblBorder: widget.calendarStyle.tblBorder, + tableBorder: widget.calendarStyle.tableBorder, dowVisible: widget.daysOfWeekVisible, dowHeight: widget.daysOfWeekHeight, rowHeight: widget.rowHeight, diff --git a/lib/src/table_calendar_base.dart b/lib/src/table_calendar_base.dart index 55fd8112..3341e323 100644 --- a/lib/src/table_calendar_base.dart +++ b/lib/src/table_calendar_base.dart @@ -20,7 +20,7 @@ class TableCalendarBase extends StatefulWidget { final bool dowVisible; final Decoration? dowDecoration; final Decoration? rowDecoration; - final TableBorder? tblBorder; + final TableBorder? tableBorder; final Duration formatAnimationDuration; final Curve formatAnimationCurve; final bool pageAnimationEnabled; @@ -48,7 +48,7 @@ class TableCalendarBase extends StatefulWidget { this.dowVisible = true, this.dowDecoration, this.rowDecoration, - this.tblBorder, + this.tableBorder, this.formatAnimationDuration = const Duration(milliseconds: 200), this.formatAnimationCurve = Curves.linear, this.pageAnimationEnabled = true, @@ -219,7 +219,7 @@ class _TableCalendarBaseState extends State rowHeight: widget.rowHeight, dowDecoration: widget.dowDecoration, rowDecoration: widget.rowDecoration, - tblBorder: widget.tblBorder, + tableBorder: widget.tableBorder, onPageChanged: (index, focusedMonth) { if (!_pageCallbackDisabled) { if (!isSameDay(_focusedDay, focusedMonth)) { diff --git a/lib/src/widgets/calendar_core.dart b/lib/src/widgets/calendar_core.dart index 8f6e5f46..2edc148c 100644 --- a/lib/src/widgets/calendar_core.dart +++ b/lib/src/widgets/calendar_core.dart @@ -20,7 +20,7 @@ class CalendarCore extends StatelessWidget { final bool dowVisible; final Decoration? dowDecoration; final Decoration? rowDecoration; - final TableBorder? tblBorder; + final TableBorder? tableBorder; final double? dowHeight; final double? rowHeight; final BoxConstraints constraints; @@ -49,7 +49,7 @@ class CalendarCore extends StatelessWidget { this.dowVisible = true, this.dowDecoration, this.rowDecoration, - this.tblBorder, + this.tableBorder, this.scrollPhysics, }) : assert(!dowVisible || (dowHeight != null && dowBuilder != null)), super(key: key); @@ -76,7 +76,7 @@ class CalendarCore extends StatelessWidget { dowVisible: dowVisible, dowDecoration: dowDecoration, rowDecoration: rowDecoration, - tblBorder: tblBorder, + tableBorder: tableBorder, dowBuilder: (context, day) { return SizedBox( height: dowHeight, diff --git a/lib/src/widgets/calendar_page.dart b/lib/src/widgets/calendar_page.dart index f323651f..2f909bb8 100644 --- a/lib/src/widgets/calendar_page.dart +++ b/lib/src/widgets/calendar_page.dart @@ -9,7 +9,7 @@ class CalendarPage extends StatelessWidget { final List visibleDays; final Decoration? dowDecoration; final Decoration? rowDecoration; - final TableBorder? tblBorder; + final TableBorder? tableBorder; final bool dowVisible; const CalendarPage({ @@ -19,7 +19,7 @@ class CalendarPage extends StatelessWidget { required this.dayBuilder, this.dowDecoration, this.rowDecoration, - this.tblBorder, + this.tableBorder, this.dowVisible = true, }) : assert(!dowVisible || dowBuilder != null), super(key: key); @@ -27,7 +27,7 @@ class CalendarPage extends StatelessWidget { @override Widget build(BuildContext context) { return Table( - border: tblBorder, + border: tableBorder, children: [ if (dowVisible) _buildDaysOfWeek(context), ..._buildCalendarDays(context), From a713ec6cb9741247bd3375a9031237b43cda5551 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Thu, 21 Oct 2021 10:59:34 +0200 Subject: [PATCH 104/164] Add Semantics to prioritizedBuilder --- lib/src/widgets/cell_content.dart | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/src/widgets/cell_content.dart b/lib/src/widgets/cell_content.dart index c4915051..eb75716c 100644 --- a/lib/src/widgets/cell_content.dart +++ b/lib/src/widgets/cell_content.dart @@ -45,11 +45,19 @@ class CellContent extends StatelessWidget { @override Widget build(BuildContext context) { + final dowLabel = DateFormat.EEEE(locale).format(day); + final dayLabel = DateFormat.yMMMMd(locale).format(day); + final semanticsLabel = '$dowLabel, $dayLabel'; + Widget? cell = calendarBuilders.prioritizedBuilder?.call(context, day, focusedDay); if (cell != null) { - return cell; + return Semantics( + label: semanticsLabel, + excludeSemantics: true, + child: cell, + ); } final text = '${day.day}'; @@ -58,10 +66,6 @@ class CellContent extends StatelessWidget { final alignment = calendarStyle.cellAlignment; final duration = const Duration(milliseconds: 250); - final dowLabel = DateFormat.EEEE(locale).format(day); - final dayLabel = DateFormat.yMMMMd(locale).format(day); - final semanticsLabel = '$dowLabel, $dayLabel'; - if (isDisabled) { cell = calendarBuilders.disabledBuilder?.call(context, day, focusedDay) ?? AnimatedContainer( From 9744467c1ac0871b8871c06f25b23562344affb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Thu, 21 Oct 2021 11:54:55 +0200 Subject: [PATCH 105/164] Update .gitignore --- .gitignore | 4 +--- table_calendar.iml | 19 ------------------- 2 files changed, 1 insertion(+), 22 deletions(-) delete mode 100644 table_calendar.iml diff --git a/.gitignore b/.gitignore index e9f86be8..6395d38a 100644 --- a/.gitignore +++ b/.gitignore @@ -15,9 +15,6 @@ *.iws .idea/ -# Visual Studio Code related -vscode/ - # Flutter/Dart/Pub related **/doc/api/ .dart_tool/ @@ -58,6 +55,7 @@ build/ **/ios/Flutter/Flutter.framework **/ios/Flutter/Flutter.podspec **/ios/Flutter/Generated.xcconfig +**/ios/Flutter/ephemeral **/ios/Flutter/app.flx **/ios/Flutter/app.zip **/ios/Flutter/flutter_assets/ diff --git a/table_calendar.iml b/table_calendar.iml deleted file mode 100644 index 6048a33b..00000000 --- a/table_calendar.iml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file From bc8ad29b2d7200da74239d5bf4af4a8f513b790d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Thu, 21 Oct 2021 11:59:41 +0200 Subject: [PATCH 106/164] Release version 3.0.3 --- CHANGELOG.md | 7 +++++++ README.md | 2 +- example/pubspec.lock | 10 +++++----- pubspec.lock | 8 ++++---- pubspec.yaml | 2 +- 5 files changed, 18 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8dbe510a..a7fb5fe2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [3.0.3] + +* Added semantic label to prioritizedBuilder +* Added tableBorder property to CalendarStyle +* Added cellAlignment property to CalendarStyle +* Added cellPadding property to CalendarStyle + ## [3.0.2] * Improved semantic labels for screen readers diff --git a/README.md b/README.md index 1ab5a697..8b556204 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Add the following line to `pubspec.yaml`: ```yaml dependencies: - table_calendar: ^3.0.2 + table_calendar: ^3.0.3 ``` ### Basic setup diff --git a/example/pubspec.lock b/example/pubspec.lock index aa9b234b..7c23c4e4 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -7,7 +7,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.6.1" + version: "2.8.1" boolean_selector: dependency: transitive description: @@ -28,7 +28,7 @@ packages: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.3.1" clock: dependency: transitive description: @@ -87,7 +87,7 @@ packages: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.7.0" path: dependency: transitive description: @@ -141,7 +141,7 @@ packages: path: ".." relative: true source: path - version: "3.0.2" + version: "3.0.3" term_glyph: dependency: transitive description: @@ -155,7 +155,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.3.0" + version: "0.4.2" typed_data: dependency: transitive description: diff --git a/pubspec.lock b/pubspec.lock index 552f9a94..36f4b2bb 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,7 +7,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.6.1" + version: "2.8.1" boolean_selector: dependency: transitive description: @@ -28,7 +28,7 @@ packages: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.3.1" clock: dependency: transitive description: @@ -80,7 +80,7 @@ packages: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.7.0" path: dependency: transitive description: @@ -141,7 +141,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.3.0" + version: "0.4.2" typed_data: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 3b659ab1..70d5e343 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: table_calendar description: Highly customizable, feature-packed calendar widget for Flutter. -version: 3.0.2 +version: 3.0.3 author: Aleksander Woźniak homepage: https://github.com/aleksanderwozniak/table_calendar From d9b840ce1a2afd7872dfd215592a434d3ee281bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sat, 6 Nov 2021 12:23:20 +0100 Subject: [PATCH 107/164] Add utils tests --- test/utils_test.dart | 69 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 test/utils_test.dart diff --git a/test/utils_test.dart b/test/utils_test.dart new file mode 100644 index 00000000..472c0afb --- /dev/null +++ b/test/utils_test.dart @@ -0,0 +1,69 @@ +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 + +import 'package:flutter_test/flutter_test.dart'; +import 'package:table_calendar/src/shared/utils.dart'; + +void main() { + group('isSameDay() tests:', () { + test('Same day, different time', () { + final dateA = DateTime(2020, 5, 10, 4, 32, 16); + final dateB = DateTime(2020, 5, 10, 8, 21, 44); + + expect(isSameDay(dateA, dateB), true); + }); + + test('Different day, same time', () { + final dateA = DateTime(2020, 5, 10, 4, 32, 16); + final dateB = DateTime(2020, 5, 11, 4, 32, 16); + + expect(isSameDay(dateA, dateB), false); + }); + + test('UTC and local time zone', () { + final dateA = DateTime.utc(2020, 5, 10); + final dateB = DateTime(2020, 5, 10); + + expect(isSameDay(dateA, dateB), true); + }); + }); + + group('normalizeDate() tests:', () { + test('Local time zone gets converted to UTC', () { + final dateA = DateTime(2020, 5, 10, 4, 32, 16); + final dateB = normalizeDate(dateA); + + expect(dateB.isUtc, true); + }); + + test('Date is unchanged', () { + final dateA = DateTime(2020, 5, 10, 4, 32, 16); + final dateB = normalizeDate(dateA); + + expect(dateB.year, 2020); + expect(dateB.month, 5); + expect(dateB.day, 10); + }); + + test('Time gets trimmed', () { + final dateA = DateTime(2020, 5, 10, 4, 32, 16); + final dateB = normalizeDate(dateA); + + expect(dateB.hour, 0); + expect(dateB.minute, 0); + expect(dateB.second, 0); + expect(dateB.millisecond, 0); + expect(dateB.microsecond, 0); + }); + }); + + group('getWeekdayNumber() tests:', () { + test('Monday returns number 1', () { + expect(getWeekdayNumber(StartingDayOfWeek.monday), 1); + }); + + test('Sunday returns number 7', () { + expect(getWeekdayNumber(StartingDayOfWeek.sunday), 7); + }); + }); +} From a85719f017bfb9b4a637b35ac62a911747b7753e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sat, 6 Nov 2021 12:32:33 +0100 Subject: [PATCH 108/164] Add FormatButton tests --- test/format_button_test.dart | 193 +++++++++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 test/format_button_test.dart diff --git a/test/format_button_test.dart b/test/format_button_test.dart new file mode 100644 index 00000000..3f7040a3 --- /dev/null +++ b/test/format_button_test.dart @@ -0,0 +1,193 @@ +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:table_calendar/src/customization/header_style.dart'; +import 'package:table_calendar/src/widgets/format_button.dart'; +import 'package:table_calendar/table_calendar.dart'; + +const availableCalendarFormats = const { + CalendarFormat.month: 'Month', + CalendarFormat.twoWeeks: 'Two weeks', + CalendarFormat.week: 'week', +}; + +Widget setupTestWidget(Widget child) { + return Directionality( + textDirection: TextDirection.ltr, + child: child, + ); +} + +void main() { + group('onTap callback tests:', () { + testWidgets( + 'Initial format month returns twoWeeks when tapped', + (tester) async { + final headerStyle = HeaderStyle(); + CalendarFormat? calendarFormat; + + await tester.pumpWidget( + setupTestWidget( + FormatButton( + availableCalendarFormats: availableCalendarFormats, + calendarFormat: CalendarFormat.month, + decoration: headerStyle.formatButtonDecoration, + padding: headerStyle.formatButtonPadding, + textStyle: headerStyle.formatButtonTextStyle, + showsNextFormat: headerStyle.formatButtonShowsNext, + onTap: (format) { + calendarFormat = format; + }, + ), + ), + ); + + await tester.pumpAndSettle(); + expect(find.byType(FormatButton), findsOneWidget); + expect(calendarFormat, isNull); + + await tester.tap(find.byType(FormatButton)); + await tester.pumpAndSettle(); + expect(calendarFormat, CalendarFormat.twoWeeks); + }, + ); + + testWidgets( + 'Initial format twoWeeks returns week when tapped', + (tester) async { + final headerStyle = HeaderStyle(); + CalendarFormat? calendarFormat; + + await tester.pumpWidget( + setupTestWidget( + FormatButton( + availableCalendarFormats: availableCalendarFormats, + calendarFormat: CalendarFormat.twoWeeks, + decoration: headerStyle.formatButtonDecoration, + padding: headerStyle.formatButtonPadding, + textStyle: headerStyle.formatButtonTextStyle, + showsNextFormat: headerStyle.formatButtonShowsNext, + onTap: (format) { + calendarFormat = format; + }, + ), + ), + ); + + await tester.pumpAndSettle(); + expect(find.byType(FormatButton), findsOneWidget); + expect(calendarFormat, isNull); + + await tester.tap(find.byType(FormatButton)); + await tester.pumpAndSettle(); + expect(calendarFormat, CalendarFormat.week); + }, + ); + + testWidgets( + 'Initial format week return month when tapped', + (tester) async { + final headerStyle = HeaderStyle(); + CalendarFormat? calendarFormat; + + await tester.pumpWidget( + setupTestWidget( + FormatButton( + availableCalendarFormats: availableCalendarFormats, + calendarFormat: CalendarFormat.week, + decoration: headerStyle.formatButtonDecoration, + padding: headerStyle.formatButtonPadding, + textStyle: headerStyle.formatButtonTextStyle, + showsNextFormat: headerStyle.formatButtonShowsNext, + onTap: (format) { + calendarFormat = format; + }, + ), + ), + ); + + await tester.pumpAndSettle(); + expect(find.byType(FormatButton), findsOneWidget); + expect(calendarFormat, isNull); + + await tester.tap(find.byType(FormatButton)); + await tester.pumpAndSettle(); + expect(calendarFormat, CalendarFormat.month); + }, + ); + }); + + group('showsNextFormat tests:', () { + testWidgets( + 'true - display next calendar format', + (tester) async { + final headerStyle = HeaderStyle(formatButtonShowsNext: true); + + final currentFormatIndex = 0; + final currentFormat = + availableCalendarFormats.keys.elementAt(currentFormatIndex); + final currentFormatText = + availableCalendarFormats.values.elementAt(currentFormatIndex); + + final nextFormatIndex = 1; + final nextFormatText = + availableCalendarFormats.values.elementAt(nextFormatIndex); + + await tester.pumpWidget( + setupTestWidget( + FormatButton( + availableCalendarFormats: availableCalendarFormats, + calendarFormat: currentFormat, + decoration: headerStyle.formatButtonDecoration, + padding: headerStyle.formatButtonPadding, + textStyle: headerStyle.formatButtonTextStyle, + showsNextFormat: headerStyle.formatButtonShowsNext, + onTap: (format) {}, + ), + ), + ); + + await tester.pumpAndSettle(); + expect(find.byType(FormatButton), findsOneWidget); + expect(currentFormatText, isNotNull); + expect(find.text(currentFormatText), findsNothing); + expect(nextFormatText, isNotNull); + expect(find.text(nextFormatText), findsOneWidget); + }, + ); + + testWidgets( + 'false - display current calendar format', + (tester) async { + final headerStyle = HeaderStyle(formatButtonShowsNext: false); + + final currentFormatIndex = 0; + final currentFormat = + availableCalendarFormats.keys.elementAt(currentFormatIndex); + final currentFormatText = + availableCalendarFormats.values.elementAt(currentFormatIndex); + + await tester.pumpWidget( + setupTestWidget( + FormatButton( + availableCalendarFormats: availableCalendarFormats, + calendarFormat: currentFormat, + decoration: headerStyle.formatButtonDecoration, + padding: headerStyle.formatButtonPadding, + textStyle: headerStyle.formatButtonTextStyle, + showsNextFormat: headerStyle.formatButtonShowsNext, + onTap: (format) {}, + ), + ), + ); + + await tester.pumpAndSettle(); + expect(find.byType(FormatButton), findsOneWidget); + expect(currentFormatText, isNotNull); + expect(find.text(currentFormatText), findsOneWidget); + }, + ); + }); +} From 5d747225bb18cef3aa15799e77964f8c540fa78a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sat, 6 Nov 2021 17:06:14 +0100 Subject: [PATCH 109/164] Add CustomIconButton tests --- test/custom_icon_button_test.dart | 43 +++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 test/custom_icon_button_test.dart diff --git a/test/custom_icon_button_test.dart b/test/custom_icon_button_test.dart new file mode 100644 index 00000000..7080a07b --- /dev/null +++ b/test/custom_icon_button_test.dart @@ -0,0 +1,43 @@ +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 + +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:table_calendar/src/widgets/custom_icon_button.dart'; + +Widget setupTestWidget(Widget child) { + return Directionality( + textDirection: TextDirection.ltr, + child: Material(child: child), + ); +} + +void main() { + testWidgets( + 'onTap gets called when CustomIconButton is tapped', + (tester) async { + bool buttonTapped = false; + + await tester.pumpWidget( + setupTestWidget( + CustomIconButton( + icon: Icon(Icons.chevron_left), + onTap: () { + buttonTapped = true; + }, + ), + ), + ); + + await tester.pumpAndSettle(); + final button = find.byType(CustomIconButton); + expect(button, findsOneWidget); + expect(buttonTapped, false); + + await tester.tap(button); + await tester.pumpAndSettle(); + expect(buttonTapped, true); + }, + ); +} From cf31c86edd3bd436efae3dec40dca60ce16cedb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sat, 6 Nov 2021 17:41:38 +0100 Subject: [PATCH 110/164] Add CalendarHeader tests --- test/calendar_header_test.dart | 202 +++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 test/calendar_header_test.dart diff --git a/test/calendar_header_test.dart b/test/calendar_header_test.dart new file mode 100644 index 00000000..aea086ed --- /dev/null +++ b/test/calendar_header_test.dart @@ -0,0 +1,202 @@ +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 + +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:table_calendar/src/customization/header_style.dart'; +import 'package:table_calendar/src/shared/utils.dart'; +import 'package:table_calendar/src/widgets/calendar_header.dart'; +import 'package:table_calendar/src/widgets/custom_icon_button.dart'; +import 'package:table_calendar/src/widgets/format_button.dart'; + +const calendarFormatMap = const { + CalendarFormat.month: 'Month', + CalendarFormat.twoWeeks: 'Two weeks', + CalendarFormat.week: 'week', +}; + +Widget setupTestWidget({ + HeaderStyle headerStyle = const HeaderStyle(), + VoidCallback? onLeftChevronTap, + VoidCallback? onRightChevronTap, + VoidCallback? onHeaderTap, + VoidCallback? onHeaderLongPress, + Function(CalendarFormat)? onFormatButtonTap, + Map availableCalendarFormats = calendarFormatMap, +}) { + return Directionality( + textDirection: TextDirection.ltr, + child: Material( + child: CalendarHeader( + focusedMonth: DateTime.utc(2021, 7, 15), + calendarFormat: CalendarFormat.month, + headerStyle: headerStyle, + onLeftChevronTap: () => onLeftChevronTap?.call(), + onRightChevronTap: () => onRightChevronTap?.call(), + onHeaderTap: () => onHeaderTap?.call(), + onHeaderLongPress: () => onHeaderLongPress?.call(), + onFormatButtonTap: (format) => onFormatButtonTap?.call(format), + availableCalendarFormats: availableCalendarFormats, + ), + ), + ); +} + +void main() { + testWidgets( + 'Ensure chevrons and FormatButton are visible by default, test onTap callbacks', + (tester) async { + bool leftChevronTapped = false; + bool rightChevronTapped = false; + bool headerTapped = false; + bool headerLongPressed = false; + bool formatButtonTapped = false; + + await tester.pumpWidget( + setupTestWidget( + onLeftChevronTap: () => leftChevronTapped = true, + onRightChevronTap: () => rightChevronTapped = true, + onHeaderTap: () => headerTapped = true, + onHeaderLongPress: () => headerLongPressed = true, + onFormatButtonTap: (_) => formatButtonTapped = true, + ), + ); + + await tester.pumpAndSettle(); + + final leftChevron = find.widgetWithIcon( + CustomIconButton, + Icons.chevron_left, + ); + + final rightChevron = find.widgetWithIcon( + CustomIconButton, + Icons.chevron_right, + ); + + final header = find.byType(CalendarHeader); + final formatButton = find.byType(FormatButton); + + expect(leftChevron, findsOneWidget); + expect(rightChevron, findsOneWidget); + expect(header, findsOneWidget); + expect(formatButton, findsOneWidget); + + expect(leftChevronTapped, false); + expect(rightChevronTapped, false); + expect(headerTapped, false); + expect(headerLongPressed, false); + expect(formatButtonTapped, false); + + await tester.tap(leftChevron); + await tester.pumpAndSettle(); + + await tester.tap(rightChevron); + await tester.pumpAndSettle(); + + await tester.tap(header); + await tester.pumpAndSettle(); + + await tester.longPress(header); + await tester.pumpAndSettle(); + + await tester.tap(formatButton); + await tester.pumpAndSettle(); + + expect(leftChevronTapped, true); + expect(rightChevronTapped, true); + expect(headerTapped, true); + expect(headerLongPressed, true); + expect(formatButtonTapped, true); + }, + ); + + testWidgets( + 'When leftChevronVisible is false, do not show the left chevron', + (tester) async { + await tester.pumpWidget( + setupTestWidget( + headerStyle: HeaderStyle( + leftChevronVisible: false, + ), + ), + ); + + await tester.pumpAndSettle(); + + final leftChevron = find.widgetWithIcon( + CustomIconButton, + Icons.chevron_left, + ); + + final rightChevron = find.widgetWithIcon( + CustomIconButton, + Icons.chevron_right, + ); + + expect(leftChevron, findsNothing); + expect(rightChevron, findsOneWidget); + }, + ); + + testWidgets( + 'When rightChevronVisible is false, do not show the right chevron', + (tester) async { + await tester.pumpWidget( + setupTestWidget( + headerStyle: HeaderStyle( + rightChevronVisible: false, + ), + ), + ); + + await tester.pumpAndSettle(); + + final leftChevron = find.widgetWithIcon( + CustomIconButton, + Icons.chevron_left, + ); + + final rightChevron = find.widgetWithIcon( + CustomIconButton, + Icons.chevron_right, + ); + + expect(leftChevron, findsOneWidget); + expect(rightChevron, findsNothing); + }, + ); + + testWidgets( + 'When availableCalendarFormats has a single format, do not show the FormatButton', + (tester) async { + await tester.pumpWidget( + setupTestWidget( + availableCalendarFormats: const {CalendarFormat.month: 'Month'}, + ), + ); + + await tester.pumpAndSettle(); + + final formatButton = find.byType(FormatButton); + expect(formatButton, findsNothing); + }, + ); + + testWidgets( + 'When formatButtonVisible is false, do not show the FormatButton', + (tester) async { + await tester.pumpWidget( + setupTestWidget( + headerStyle: HeaderStyle(formatButtonVisible: false), + ), + ); + + await tester.pumpAndSettle(); + + final formatButton = find.byType(FormatButton); + expect(formatButton, findsNothing); + }, + ); +} From f182043eb56f701dbc1d420fee6e83562fcbdc62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sat, 6 Nov 2021 18:29:15 +0100 Subject: [PATCH 111/164] Add CalendarPage tests --- test/calendar_page_test.dart | 97 ++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 test/calendar_page_test.dart diff --git a/test/calendar_page_test.dart b/test/calendar_page_test.dart new file mode 100644 index 00000000..c55f9a57 --- /dev/null +++ b/test/calendar_page_test.dart @@ -0,0 +1,97 @@ +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 + +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:table_calendar/src/widgets/calendar_page.dart'; + +Widget setupTestWidget(Widget child) { + return Directionality( + textDirection: TextDirection.ltr, + child: child, + ); +} + +List visibleDays = getDaysInRange( + DateTime.utc(2021, 6, 27), + DateTime.utc(2021, 7, 31), +); + +List getDaysInRange(DateTime first, DateTime last) { + final dayCount = last.difference(first).inDays + 1; + return List.generate( + dayCount, + (index) => DateTime.utc(first.year, first.month, first.day + index), + ); +} + +void main() { + testWidgets( + 'CalendarPage lays out all the visible days', + (tester) async { + await tester.pumpWidget( + setupTestWidget( + CalendarPage( + visibleDays: visibleDays, + dayBuilder: (context, day) { + return Text('${day.day}'); + }, + dowVisible: false, + ), + ), + ); + + await tester.pumpAndSettle(); + final expectedCellCount = visibleDays.length; + expect(find.byType(Text), findsNWidgets(expectedCellCount)); + }, + ); + + testWidgets( + 'CalendarPage lays out 7 DOW labels', + (tester) async { + await tester.pumpWidget( + setupTestWidget( + CalendarPage( + visibleDays: visibleDays, + dayBuilder: (context, day) { + return Text('${day.day}'); + }, + dowVisible: true, + dowBuilder: (context, day) { + return Text('${day.weekday}'); + }, + ), + ), + ); + + await tester.pumpAndSettle(); + final expectedCellCount = visibleDays.length; + final expectedDowLabels = 7; + expect(find.byType(Text), + findsNWidgets(expectedCellCount + expectedDowLabels)); + }, + ); + + testWidgets( + 'Throw AssertionError when CalendarPage is built with dowVisible set to true, but dowBuilder is absent', + (tester) async { + expect(() async { + await tester.pumpWidget( + setupTestWidget( + CalendarPage( + visibleDays: visibleDays, + dayBuilder: (context, day) { + return Text('${day.day}'); + }, + dowVisible: true, + ), + ), + ); + + await tester.pumpAndSettle(); + }, throwsAssertionError); + }, + ); +} From ce3ac553233aa1e60ff66f813afe0898ee9c6dda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 7 Nov 2021 11:03:26 +0100 Subject: [PATCH 112/164] Add CellContent tests --- test/cell_content_test.dart | 313 ++++++++++++++++++++++++++++++++++++ 1 file changed, 313 insertions(+) create mode 100644 test/cell_content_test.dart diff --git a/test/cell_content_test.dart b/test/cell_content_test.dart new file mode 100644 index 00000000..514c129d --- /dev/null +++ b/test/cell_content_test.dart @@ -0,0 +1,313 @@ +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 + +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:table_calendar/src/widgets/cell_content.dart'; +import 'package:table_calendar/table_calendar.dart'; + +Widget setupTestWidget( + DateTime cellDay, { + CalendarBuilders calendarBuilders = const CalendarBuilders(), + bool isDisabled = false, + bool isToday = false, + bool isWeekend = false, + bool isOutside = false, + bool isSelected = false, + bool isRangeStart = false, + bool isRangeEnd = false, + bool isWithinRange = false, + bool isHoliday = false, + bool isTodayHighlighted = true, +}) { + final calendarStyle = CalendarStyle(); + + return Directionality( + textDirection: TextDirection.ltr, + child: CellContent( + day: cellDay, + focusedDay: cellDay, + calendarBuilders: calendarBuilders, + calendarStyle: calendarStyle, + isDisabled: isDisabled, + isToday: isToday, + isWeekend: isWeekend, + isOutside: isOutside, + isSelected: isSelected, + isRangeStart: isRangeStart, + isRangeEnd: isRangeEnd, + isWithinRange: isWithinRange, + isHoliday: isHoliday, + isTodayHighlighted: isTodayHighlighted, + ), + ); +} + +void main() { + group('CalendarBuilders flag test:', () { + testWidgets('selectedBuilder', (tester) async { + DateTime? builderDay; + + final calendarBuilders = CalendarBuilders( + selectedBuilder: (context, day, focusedDay) { + builderDay = day; + return Text('${day.day}'); + }, + ); + + final cellDay = DateTime.utc(2021, 7, 15); + expect(builderDay, isNull); + + await tester.pumpWidget( + setupTestWidget( + cellDay, + calendarBuilders: calendarBuilders, + isSelected: true, + ), + ); + + expect(builderDay, cellDay); + }); + + testWidgets('rangeStartBuilder', (tester) async { + DateTime? builderDay; + + final calendarBuilders = CalendarBuilders( + rangeStartBuilder: (context, day, focusedDay) { + builderDay = day; + return Text('${day.day}'); + }, + ); + + final cellDay = DateTime.utc(2021, 7, 15); + expect(builderDay, isNull); + + await tester.pumpWidget( + setupTestWidget( + cellDay, + calendarBuilders: calendarBuilders, + isRangeStart: true, + ), + ); + + expect(builderDay, cellDay); + }); + + testWidgets('rangeEndBuilder', (tester) async { + DateTime? builderDay; + + final calendarBuilders = CalendarBuilders( + rangeEndBuilder: (context, day, focusedDay) { + builderDay = day; + return Text('${day.day}'); + }, + ); + + final cellDay = DateTime.utc(2021, 7, 15); + expect(builderDay, isNull); + + await tester.pumpWidget( + setupTestWidget( + cellDay, + calendarBuilders: calendarBuilders, + isRangeEnd: true, + ), + ); + + expect(builderDay, cellDay); + }); + + testWidgets('withinRangeBuilder', (tester) async { + DateTime? builderDay; + + final calendarBuilders = CalendarBuilders( + withinRangeBuilder: (context, day, focusedDay) { + builderDay = day; + return Text('${day.day}'); + }, + ); + + final cellDay = DateTime.utc(2021, 7, 15); + expect(builderDay, isNull); + + await tester.pumpWidget( + setupTestWidget( + cellDay, + calendarBuilders: calendarBuilders, + isWithinRange: true, + ), + ); + + expect(builderDay, cellDay); + }); + + testWidgets('todayBuilder', (tester) async { + DateTime? builderDay; + + final calendarBuilders = CalendarBuilders( + todayBuilder: (context, day, focusedDay) { + builderDay = day; + return Text('${day.day}'); + }, + ); + + final cellDay = DateTime.utc(2021, 7, 15); + expect(builderDay, isNull); + + await tester.pumpWidget( + setupTestWidget( + cellDay, + calendarBuilders: calendarBuilders, + isToday: true, + ), + ); + + expect(builderDay, cellDay); + }); + + testWidgets('holidayBuilder', (tester) async { + DateTime? builderDay; + + final calendarBuilders = CalendarBuilders( + holidayBuilder: (context, day, focusedDay) { + builderDay = day; + return Text('${day.day}'); + }, + ); + + final cellDay = DateTime.utc(2021, 7, 15); + expect(builderDay, isNull); + + await tester.pumpWidget( + setupTestWidget( + cellDay, + calendarBuilders: calendarBuilders, + isHoliday: true, + ), + ); + + expect(builderDay, cellDay); + }); + + testWidgets('outsideBuilder', (tester) async { + DateTime? builderDay; + + final calendarBuilders = CalendarBuilders( + outsideBuilder: (context, day, focusedDay) { + builderDay = day; + return Text('${day.day}'); + }, + ); + + final cellDay = DateTime.utc(2021, 7, 15); + expect(builderDay, isNull); + + await tester.pumpWidget( + setupTestWidget( + cellDay, + calendarBuilders: calendarBuilders, + isOutside: true, + ), + ); + + expect(builderDay, cellDay); + }); + + testWidgets( + 'defaultBuilder gets triggered when no other flags are active', + (tester) async { + DateTime? builderDay; + + final calendarBuilders = CalendarBuilders( + defaultBuilder: (context, day, focusedDay) { + builderDay = day; + return Text('${day.day}'); + }, + ); + + final cellDay = DateTime.utc(2021, 7, 15); + expect(builderDay, isNull); + + await tester.pumpWidget( + setupTestWidget( + cellDay, + calendarBuilders: calendarBuilders, + ), + ); + + expect(builderDay, cellDay); + }, + ); + + testWidgets( + 'disabledBuilder has higher build order priority than selectedBuilder', + (tester) async { + DateTime? builderDay; + String builderName = ''; + + final calendarBuilders = CalendarBuilders( + selectedBuilder: (context, day, focusedDay) { + builderName = 'selectedBuilder'; + builderDay = day; + return Text('${day.day}'); + }, + disabledBuilder: (context, day, focusedDay) { + builderName = 'disabledBuilder'; + builderDay = day; + return Text('${day.day}'); + }, + ); + + final cellDay = DateTime.utc(2021, 7, 15); + expect(builderDay, isNull); + + await tester.pumpWidget( + setupTestWidget( + cellDay, + calendarBuilders: calendarBuilders, + isDisabled: true, + isSelected: true, + ), + ); + + expect(builderDay, cellDay); + expect(builderName, 'disabledBuilder'); + }, + ); + + testWidgets( + 'prioritizedBuilder has the highest build order priority', + (tester) async { + DateTime? builderDay; + String builderName = ''; + + final calendarBuilders = CalendarBuilders( + prioritizedBuilder: (context, day, focusedDay) { + builderName = 'prioritizedBuilder'; + builderDay = day; + return Text('${day.day}'); + }, + disabledBuilder: (context, day, focusedDay) { + builderName = 'disabledBuilder'; + builderDay = day; + return Text('${day.day}'); + }, + ); + + final cellDay = DateTime.utc(2021, 7, 15); + expect(builderDay, isNull); + + await tester.pumpWidget( + setupTestWidget( + cellDay, + calendarBuilders: calendarBuilders, + isDisabled: true, + ), + ); + + expect(builderDay, cellDay); + expect(builderName, 'prioritizedBuilder'); + }, + ); + }); +} From 6a8e109a71cb7d418d291ebc976f4f3f69f14fc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 7 Nov 2021 12:46:15 +0100 Subject: [PATCH 113/164] Add TableCalendarBase tests --- test/table_calendar_base_test.dart | 385 +++++++++++++++++++++++++++++ 1 file changed, 385 insertions(+) create mode 100644 test/table_calendar_base_test.dart diff --git a/test/table_calendar_base_test.dart b/test/table_calendar_base_test.dart new file mode 100644 index 00000000..d21a8222 --- /dev/null +++ b/test/table_calendar_base_test.dart @@ -0,0 +1,385 @@ +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 + +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:simple_gesture_detector/simple_gesture_detector.dart'; +import 'package:table_calendar/table_calendar.dart'; + +Widget setupTestWidget(Widget child) { + return Directionality( + textDirection: TextDirection.ltr, + child: child, + ); +} + +ValueKey dateToKey(DateTime date, {String prefix = 'cell-'}) { + return ValueKey('$prefix${date.year}-${date.month}-${date.day}'); +} + +void main() { + group('Correct days are displayed for given focusedDay when:', () { + testWidgets( + 'in month format, starting day is Sunday', + (tester) async { + final focusedDay = DateTime.utc(2021, 7, 15); + + await tester.pumpWidget( + setupTestWidget( + TableCalendarBase( + firstDay: DateTime.utc(2021, 5, 15), + lastDay: DateTime.utc(2021, 8, 18), + focusedDay: focusedDay, + dayBuilder: (context, day, focusedDay) { + return Text( + '${day.day}', + key: dateToKey(day), + ); + }, + rowHeight: 52, + dowVisible: false, + calendarFormat: CalendarFormat.month, + startingDayOfWeek: StartingDayOfWeek.sunday, + ), + ), + ); + + final firstVisibleDay = DateTime.utc(2021, 6, 27); + final lastVisibleDay = DateTime.utc(2021, 7, 31); + + final focusedDayKey = dateToKey(focusedDay); + final firstVisibleDayKey = dateToKey(firstVisibleDay); + final lastVisibleDayKey = dateToKey(lastVisibleDay); + + final startOOBKey = + dateToKey(firstVisibleDay.subtract(const Duration(days: 1))); + final endOOBKey = + dateToKey(lastVisibleDay.add(const Duration(days: 1))); + + expect(find.byKey(focusedDayKey), findsOneWidget); + expect(find.byKey(firstVisibleDayKey), findsOneWidget); + expect(find.byKey(lastVisibleDayKey), findsOneWidget); + + expect(find.byKey(startOOBKey), findsNothing); + expect(find.byKey(endOOBKey), findsNothing); + }, + ); + + testWidgets( + 'in two weeks format, starting day is Sunday', + (tester) async { + final focusedDay = DateTime.utc(2021, 7, 15); + + await tester.pumpWidget( + setupTestWidget( + TableCalendarBase( + firstDay: DateTime.utc(2021, 5, 15), + lastDay: DateTime.utc(2021, 8, 18), + focusedDay: focusedDay, + dayBuilder: (context, day, focusedDay) { + return Text( + '${day.day}', + key: dateToKey(day), + ); + }, + rowHeight: 52, + dowVisible: false, + calendarFormat: CalendarFormat.twoWeeks, + startingDayOfWeek: StartingDayOfWeek.sunday, + ), + ), + ); + + final firstVisibleDay = DateTime.utc(2021, 7, 4); + final lastVisibleDay = DateTime.utc(2021, 7, 17); + + final focusedDayKey = dateToKey(focusedDay); + final firstVisibleDayKey = dateToKey(firstVisibleDay); + final lastVisibleDayKey = dateToKey(lastVisibleDay); + + final startOOBKey = + dateToKey(firstVisibleDay.subtract(const Duration(days: 1))); + final endOOBKey = + dateToKey(lastVisibleDay.add(const Duration(days: 1))); + + expect(find.byKey(focusedDayKey), findsOneWidget); + expect(find.byKey(firstVisibleDayKey), findsOneWidget); + expect(find.byKey(lastVisibleDayKey), findsOneWidget); + + expect(find.byKey(startOOBKey), findsNothing); + expect(find.byKey(endOOBKey), findsNothing); + }, + ); + + testWidgets( + 'in week format, starting day is Sunday', + (tester) async { + final focusedDay = DateTime.utc(2021, 7, 15); + + await tester.pumpWidget( + setupTestWidget( + TableCalendarBase( + firstDay: DateTime.utc(2021, 5, 15), + lastDay: DateTime.utc(2021, 8, 18), + focusedDay: focusedDay, + dayBuilder: (context, day, focusedDay) { + return Text( + '${day.day}', + key: dateToKey(day), + ); + }, + rowHeight: 52, + dowVisible: false, + calendarFormat: CalendarFormat.week, + startingDayOfWeek: StartingDayOfWeek.sunday, + ), + ), + ); + + final firstVisibleDay = DateTime.utc(2021, 7, 11); + final lastVisibleDay = DateTime.utc(2021, 7, 17); + + final focusedDayKey = dateToKey(focusedDay); + final firstVisibleDayKey = dateToKey(firstVisibleDay); + final lastVisibleDayKey = dateToKey(lastVisibleDay); + + final startOOBKey = + dateToKey(firstVisibleDay.subtract(const Duration(days: 1))); + final endOOBKey = + dateToKey(lastVisibleDay.add(const Duration(days: 1))); + + expect(find.byKey(focusedDayKey), findsOneWidget); + expect(find.byKey(firstVisibleDayKey), findsOneWidget); + expect(find.byKey(lastVisibleDayKey), findsOneWidget); + + expect(find.byKey(startOOBKey), findsNothing); + expect(find.byKey(endOOBKey), findsNothing); + }, + ); + + testWidgets( + 'in month format, starting day is Monday', + (tester) async { + final focusedDay = DateTime.utc(2021, 7, 15); + + await tester.pumpWidget( + setupTestWidget( + TableCalendarBase( + firstDay: DateTime.utc(2021, 5, 15), + lastDay: DateTime.utc(2021, 8, 18), + focusedDay: focusedDay, + dayBuilder: (context, day, focusedDay) { + return Text( + '${day.day}', + key: dateToKey(day), + ); + }, + rowHeight: 52, + dowVisible: false, + calendarFormat: CalendarFormat.month, + startingDayOfWeek: StartingDayOfWeek.monday, + ), + ), + ); + + final firstVisibleDay = DateTime.utc(2021, 6, 28); + final lastVisibleDay = DateTime.utc(2021, 8, 1); + + final focusedDayKey = dateToKey(focusedDay); + final firstVisibleDayKey = dateToKey(firstVisibleDay); + final lastVisibleDayKey = dateToKey(lastVisibleDay); + + final startOOBKey = + dateToKey(firstVisibleDay.subtract(const Duration(days: 1))); + final endOOBKey = + dateToKey(lastVisibleDay.add(const Duration(days: 1))); + + expect(find.byKey(focusedDayKey), findsOneWidget); + expect(find.byKey(firstVisibleDayKey), findsOneWidget); + expect(find.byKey(lastVisibleDayKey), findsOneWidget); + + expect(find.byKey(startOOBKey), findsNothing); + expect(find.byKey(endOOBKey), findsNothing); + }, + ); + + testWidgets( + 'in two weeks format, starting day is Monday', + (tester) async { + final focusedDay = DateTime.utc(2021, 7, 15); + + await tester.pumpWidget( + setupTestWidget( + TableCalendarBase( + firstDay: DateTime.utc(2021, 5, 15), + lastDay: DateTime.utc(2021, 8, 18), + focusedDay: focusedDay, + dayBuilder: (context, day, focusedDay) { + return Text( + '${day.day}', + key: dateToKey(day), + ); + }, + rowHeight: 52, + dowVisible: false, + calendarFormat: CalendarFormat.twoWeeks, + startingDayOfWeek: StartingDayOfWeek.monday, + ), + ), + ); + + final firstVisibleDay = DateTime.utc(2021, 7, 5); + final lastVisibleDay = DateTime.utc(2021, 7, 18); + + final focusedDayKey = dateToKey(focusedDay); + final firstVisibleDayKey = dateToKey(firstVisibleDay); + final lastVisibleDayKey = dateToKey(lastVisibleDay); + + final startOOBKey = + dateToKey(firstVisibleDay.subtract(const Duration(days: 1))); + final endOOBKey = + dateToKey(lastVisibleDay.add(const Duration(days: 1))); + + expect(find.byKey(focusedDayKey), findsOneWidget); + expect(find.byKey(firstVisibleDayKey), findsOneWidget); + expect(find.byKey(lastVisibleDayKey), findsOneWidget); + + expect(find.byKey(startOOBKey), findsNothing); + expect(find.byKey(endOOBKey), findsNothing); + }, + ); + + testWidgets( + 'in week format, starting day is Monday', + (tester) async { + final focusedDay = DateTime.utc(2021, 7, 15); + + await tester.pumpWidget( + setupTestWidget( + TableCalendarBase( + firstDay: DateTime.utc(2021, 5, 15), + lastDay: DateTime.utc(2021, 8, 18), + focusedDay: focusedDay, + dayBuilder: (context, day, focusedDay) { + return Text( + '${day.day}', + key: dateToKey(day), + ); + }, + rowHeight: 52, + dowVisible: false, + calendarFormat: CalendarFormat.week, + startingDayOfWeek: StartingDayOfWeek.monday, + ), + ), + ); + + final firstVisibleDay = DateTime.utc(2021, 7, 12); + final lastVisibleDay = DateTime.utc(2021, 7, 18); + + final focusedDayKey = dateToKey(focusedDay); + final firstVisibleDayKey = dateToKey(firstVisibleDay); + final lastVisibleDayKey = dateToKey(lastVisibleDay); + + final startOOBKey = + dateToKey(firstVisibleDay.subtract(const Duration(days: 1))); + final endOOBKey = + dateToKey(lastVisibleDay.add(const Duration(days: 1))); + + expect(find.byKey(focusedDayKey), findsOneWidget); + expect(find.byKey(firstVisibleDayKey), findsOneWidget); + expect(find.byKey(lastVisibleDayKey), findsOneWidget); + + expect(find.byKey(startOOBKey), findsNothing); + expect(find.byKey(endOOBKey), findsNothing); + }, + ); + }); + + testWidgets( + 'Callbacks return expected values', + (tester) async { + DateTime focusedDay = DateTime.utc(2021, 7, 15); + final nextMonth = focusedDay.add(const Duration(days: 31)).month; + + bool calendarCreatedFlag = false; + SwipeDirection? verticalSwipeDirection; + + await tester.pumpWidget( + setupTestWidget( + TableCalendarBase( + firstDay: DateTime.utc(2021, 5, 15), + lastDay: DateTime.utc(2021, 8, 18), + focusedDay: focusedDay, + dayBuilder: (context, day, focusedDay) { + return Text( + '${day.day}', + key: dateToKey(day), + ); + }, + onCalendarCreated: (pageController) { + calendarCreatedFlag = true; + }, + onPageChanged: (focusedDay2) { + focusedDay = focusedDay2; + }, + onVerticalSwipe: (direction) { + verticalSwipeDirection = direction; + }, + rowHeight: 52, + dowVisible: false, + ), + ), + ); + + expect(calendarCreatedFlag, true); + + // Swipe left + await tester.drag( + find.byKey(dateToKey(focusedDay)), + const Offset(-500, 0), + ); + await tester.pumpAndSettle(); + expect(focusedDay.month, nextMonth); + + // Swipe up + await tester.drag( + find.byKey(dateToKey(focusedDay)), + const Offset(0, -500), + ); + await tester.pumpAndSettle(); + expect(verticalSwipeDirection, SwipeDirection.up); + }, + ); + + testWidgets( + 'Throw AssertionError when TableCalendarBase is built with dowVisible and dowBuilder, but dowHeight is absent', + (tester) async { + expect(() async { + await tester.pumpWidget( + setupTestWidget( + TableCalendarBase( + firstDay: DateTime.utc(2021, 5, 15), + lastDay: DateTime.utc(2021, 8, 18), + focusedDay: DateTime.utc(2021, 7, 15), + dayBuilder: (context, day, focusedDay) { + return Text( + '${day.day}', + key: dateToKey(day), + ); + }, + rowHeight: 52, + dowVisible: true, + dowBuilder: (context, day) { + return Text('${day.weekday}'); + }, + ), + ), + ); + + await tester.pumpAndSettle(); + }, throwsAssertionError); + }, + ); +} From 641c297e17583b0eca67fc8fbbb90149f38683d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 7 Nov 2021 17:44:37 +0100 Subject: [PATCH 114/164] Add a Key to CellContent to improve testing --- lib/src/table_calendar.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index 535e0da0..3ed84386 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -595,6 +595,7 @@ class _TableCalendarState extends State> { final isWeekend = _isWeekend(day, weekendDays: widget.weekendDays); Widget content = CellContent( + key: ValueKey('cellContent-${day.year}-${day.month}-${day.day}'), day: day, focusedDay: focusedDay, calendarStyle: widget.calendarStyle, From d5641eedd3e27121a2cd3fda790d0f4ee63fd2fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 7 Nov 2021 17:45:09 +0100 Subject: [PATCH 115/164] Add TableCalendar tests --- test/common.dart | 8 + test/table_calendar_test.dart | 1284 +++++++++++++++++++++++++++++++++ 2 files changed, 1292 insertions(+) create mode 100644 test/common.dart create mode 100644 test/table_calendar_test.dart diff --git a/test/common.dart b/test/common.dart new file mode 100644 index 00000000..febaee50 --- /dev/null +++ b/test/common.dart @@ -0,0 +1,8 @@ +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 + +import 'package:flutter/foundation.dart'; + +ValueKey dateToKey(DateTime date, {String prefix = 'cell-'}) { + return ValueKey('$prefix${date.year}-${date.month}-${date.day}'); +} diff --git a/test/table_calendar_test.dart b/test/table_calendar_test.dart new file mode 100644 index 00000000..de5a4acd --- /dev/null +++ b/test/table_calendar_test.dart @@ -0,0 +1,1284 @@ +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 + +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:intl/intl.dart' as intl; +import 'package:table_calendar/src/widgets/calendar_header.dart'; +import 'package:table_calendar/src/widgets/cell_content.dart'; +import 'package:table_calendar/src/widgets/custom_icon_button.dart'; +import 'package:table_calendar/table_calendar.dart'; + +import 'common.dart'; + +final initialFocusedDay = DateTime.utc(2021, 7, 15); +final today = initialFocusedDay; +final firstDay = DateTime.utc(2021, 5, 15); +final lastDay = DateTime.utc(2021, 9, 18); + +Widget setupTestWidget(Widget child) { + return Directionality( + textDirection: TextDirection.ltr, + child: Material(child: child), + ); +} + +Widget createTableCalendar({ + DateTime? focusedDay, + CalendarFormat calendarFormat = CalendarFormat.month, + Function(DateTime)? onPageChanged, + bool sixWeekMonthsEnforced = false, +}) { + return setupTestWidget( + TableCalendar( + focusedDay: focusedDay ?? initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + calendarFormat: calendarFormat, + onPageChanged: onPageChanged, + sixWeekMonthsEnforced: sixWeekMonthsEnforced, + ), + ); +} + +ValueKey cellContentKey(DateTime date) { + return dateToKey(date, prefix: 'cellContent-'); +} + +void main() { + group('TableCalendar correctly displays:', () { + testWidgets( + 'visible day cells for given focusedDay', + (tester) async { + await tester.pumpWidget(createTableCalendar()); + + final firstVisibleDay = DateTime.utc(2021, 6, 27); + final lastVisibleDay = DateTime.utc(2021, 7, 31); + + final focusedDayKey = cellContentKey(initialFocusedDay); + final firstVisibleDayKey = cellContentKey(firstVisibleDay); + final lastVisibleDayKey = cellContentKey(lastVisibleDay); + + final startOOBKey = + cellContentKey(firstVisibleDay.subtract(const Duration(days: 1))); + final endOOBKey = + cellContentKey(lastVisibleDay.add(const Duration(days: 1))); + + expect(find.byKey(focusedDayKey), findsOneWidget); + expect(find.byKey(firstVisibleDayKey), findsOneWidget); + expect(find.byKey(lastVisibleDayKey), findsOneWidget); + + expect(find.byKey(startOOBKey), findsNothing); + expect(find.byKey(endOOBKey), findsNothing); + }, + ); + + testWidgets( + 'visible day cells after swipe right when in week format', + (tester) async { + DateTime? updatedFocusedDay; + + await tester.pumpWidget( + createTableCalendar( + calendarFormat: CalendarFormat.week, + onPageChanged: (focusedDay) { + updatedFocusedDay = focusedDay; + }, + ), + ); + + await tester.drag( + find.byType(CellContent).first, + const Offset(500, 0), + ); + await tester.pumpAndSettle(); + + expect(updatedFocusedDay, isNotNull); + + final firstVisibleDay = DateTime.utc(2021, 7, 4); + final lastVisibleDay = DateTime.utc(2021, 7, 10); + + final focusedDayKey = cellContentKey(updatedFocusedDay!); + final firstVisibleDayKey = cellContentKey(firstVisibleDay); + final lastVisibleDayKey = cellContentKey(lastVisibleDay); + + final startOOBKey = + cellContentKey(firstVisibleDay.subtract(const Duration(days: 1))); + final endOOBKey = + cellContentKey(lastVisibleDay.add(const Duration(days: 1))); + + expect(find.byKey(focusedDayKey), findsOneWidget); + expect(find.byKey(firstVisibleDayKey), findsOneWidget); + expect(find.byKey(lastVisibleDayKey), findsOneWidget); + + expect(find.byKey(startOOBKey), findsNothing); + expect(find.byKey(endOOBKey), findsNothing); + }, + ); + + testWidgets( + 'visible day cells after swipe left when in week format', + (tester) async { + DateTime? updatedFocusedDay; + + await tester.pumpWidget( + createTableCalendar( + calendarFormat: CalendarFormat.week, + onPageChanged: (focusedDay) { + updatedFocusedDay = focusedDay; + }, + ), + ); + + await tester.drag( + find.byType(CellContent).first, + const Offset(-500, 0), + ); + await tester.pumpAndSettle(); + + expect(updatedFocusedDay, isNotNull); + + final firstVisibleDay = DateTime.utc(2021, 7, 18); + final lastVisibleDay = DateTime.utc(2021, 7, 24); + + final focusedDayKey = cellContentKey(updatedFocusedDay!); + final firstVisibleDayKey = cellContentKey(firstVisibleDay); + final lastVisibleDayKey = cellContentKey(lastVisibleDay); + + final startOOBKey = + cellContentKey(firstVisibleDay.subtract(const Duration(days: 1))); + final endOOBKey = + cellContentKey(lastVisibleDay.add(const Duration(days: 1))); + + expect(find.byKey(focusedDayKey), findsOneWidget); + expect(find.byKey(firstVisibleDayKey), findsOneWidget); + expect(find.byKey(lastVisibleDayKey), findsOneWidget); + + expect(find.byKey(startOOBKey), findsNothing); + expect(find.byKey(endOOBKey), findsNothing); + }, + ); + + testWidgets( + 'visible day cells after swipe right when in two weeks format', + (tester) async { + DateTime? updatedFocusedDay; + + await tester.pumpWidget( + createTableCalendar( + calendarFormat: CalendarFormat.twoWeeks, + onPageChanged: (focusedDay) { + updatedFocusedDay = focusedDay; + }, + ), + ); + + await tester.drag( + find.byType(CellContent).first, + const Offset(500, 0), + ); + await tester.pumpAndSettle(); + + expect(updatedFocusedDay, isNotNull); + + final firstVisibleDay = DateTime.utc(2021, 6, 20); + final lastVisibleDay = DateTime.utc(2021, 7, 3); + + final focusedDayKey = cellContentKey(updatedFocusedDay!); + final firstVisibleDayKey = cellContentKey(firstVisibleDay); + final lastVisibleDayKey = cellContentKey(lastVisibleDay); + + final startOOBKey = + cellContentKey(firstVisibleDay.subtract(const Duration(days: 1))); + final endOOBKey = + cellContentKey(lastVisibleDay.add(const Duration(days: 1))); + + expect(find.byKey(focusedDayKey), findsOneWidget); + expect(find.byKey(firstVisibleDayKey), findsOneWidget); + expect(find.byKey(lastVisibleDayKey), findsOneWidget); + + expect(find.byKey(startOOBKey), findsNothing); + expect(find.byKey(endOOBKey), findsNothing); + }, + ); + + testWidgets( + 'visible day cells after swipe left when in two weeks format', + (tester) async { + DateTime? updatedFocusedDay; + + await tester.pumpWidget( + createTableCalendar( + calendarFormat: CalendarFormat.twoWeeks, + onPageChanged: (focusedDay) { + updatedFocusedDay = focusedDay; + }, + ), + ); + + await tester.drag( + find.byType(CellContent).first, + const Offset(-500, 0), + ); + await tester.pumpAndSettle(); + + expect(updatedFocusedDay, isNotNull); + + final firstVisibleDay = DateTime.utc(2021, 7, 18); + final lastVisibleDay = DateTime.utc(2021, 7, 31); + + final focusedDayKey = cellContentKey(updatedFocusedDay!); + final firstVisibleDayKey = cellContentKey(firstVisibleDay); + final lastVisibleDayKey = cellContentKey(lastVisibleDay); + + final startOOBKey = + cellContentKey(firstVisibleDay.subtract(const Duration(days: 1))); + final endOOBKey = + cellContentKey(lastVisibleDay.add(const Duration(days: 1))); + + expect(find.byKey(focusedDayKey), findsOneWidget); + expect(find.byKey(firstVisibleDayKey), findsOneWidget); + expect(find.byKey(lastVisibleDayKey), findsOneWidget); + + expect(find.byKey(startOOBKey), findsNothing); + expect(find.byKey(endOOBKey), findsNothing); + }, + ); + + testWidgets( + '7 day cells in week format', + (tester) async { + await tester.pumpWidget( + createTableCalendar( + calendarFormat: CalendarFormat.week, + ), + ); + + var dayCells = tester.widgetList(find.byType(CellContent)); + expect(dayCells.length, 7); + }, + ); + + testWidgets( + '14 day cells in two weeks format', + (tester) async { + await tester.pumpWidget( + createTableCalendar( + calendarFormat: CalendarFormat.twoWeeks, + ), + ); + + var dayCells = tester.widgetList(find.byType(CellContent)); + expect(dayCells.length, 14); + }, + ); + + testWidgets( + '35 day cells in month format for July 2021', + (tester) async { + await tester.pumpWidget( + createTableCalendar( + calendarFormat: CalendarFormat.month, + ), + ); + + var dayCells = tester.widgetList(find.byType(CellContent)); + expect(dayCells.length, 35); + }, + ); + + testWidgets( + '42 day cells in month format for July 2021, when sixWeekMonthsEnforced is set to true', + (tester) async { + await tester.pumpWidget( + createTableCalendar( + calendarFormat: CalendarFormat.month, + sixWeekMonthsEnforced: true, + ), + ); + + var dayCells = tester.widgetList(find.byType(CellContent)); + expect(dayCells.length, 42); + }, + ); + + testWidgets( + 'CalendarHeader with updated month and year when focusedDay is changed', + (tester) async { + await tester.pumpWidget(createTableCalendar()); + + String headerText = intl.DateFormat.yMMMM().format(initialFocusedDay); + expect(find.byType(CalendarHeader), findsOneWidget); + expect(find.text(headerText), findsOneWidget); + + final updatedFocusedDay = DateTime.utc(2021, 8, 4); + + await tester.pumpWidget( + createTableCalendar(focusedDay: updatedFocusedDay), + ); + + headerText = intl.DateFormat.yMMMM().format(updatedFocusedDay); + expect(find.byType(CalendarHeader), findsOneWidget); + expect(find.text(headerText), findsOneWidget); + }, + ); + + testWidgets( + 'CalendarHeader with updated month and year when TableCalendar is swiped left', + (tester) async { + DateTime? updatedFocusedDay; + + await tester.pumpWidget( + createTableCalendar( + onPageChanged: (focusedDay) { + updatedFocusedDay = focusedDay; + }, + ), + ); + + String headerText = intl.DateFormat.yMMMM().format(initialFocusedDay); + expect(find.byType(CalendarHeader), findsOneWidget); + expect(find.text(headerText), findsOneWidget); + + await tester.drag( + find.byType(CellContent).first, + const Offset(-500, 0), + ); + await tester.pumpAndSettle(); + + expect(updatedFocusedDay, isNotNull); + expect(updatedFocusedDay!.month, initialFocusedDay.month + 1); + + headerText = intl.DateFormat.yMMMM().format(updatedFocusedDay!); + expect(find.byType(CalendarHeader), findsOneWidget); + expect(find.text(headerText), findsOneWidget); + + updatedFocusedDay = null; + + await tester.drag( + find.byType(CellContent).first, + const Offset(-500, 0), + ); + await tester.pumpAndSettle(); + + expect(updatedFocusedDay, isNotNull); + expect(updatedFocusedDay!.month, initialFocusedDay.month + 2); + + headerText = intl.DateFormat.yMMMM().format(updatedFocusedDay!); + expect(find.byType(CalendarHeader), findsOneWidget); + expect(find.text(headerText), findsOneWidget); + }, + ); + + testWidgets( + 'CalendarHeader with updated month and year when TableCalendar is swiped right', + (tester) async { + DateTime? updatedFocusedDay; + + await tester.pumpWidget( + createTableCalendar( + onPageChanged: (focusedDay) { + updatedFocusedDay = focusedDay; + }, + ), + ); + + String headerText = intl.DateFormat.yMMMM().format(initialFocusedDay); + expect(find.byType(CalendarHeader), findsOneWidget); + expect(find.text(headerText), findsOneWidget); + + await tester.drag( + find.byType(CellContent).first, + const Offset(500, 0), + ); + await tester.pumpAndSettle(); + + expect(updatedFocusedDay, isNotNull); + expect(updatedFocusedDay!.month, initialFocusedDay.month - 1); + + headerText = intl.DateFormat.yMMMM().format(updatedFocusedDay!); + expect(find.byType(CalendarHeader), findsOneWidget); + expect(find.text(headerText), findsOneWidget); + + updatedFocusedDay = null; + + await tester.drag( + find.byType(CellContent).first, + const Offset(500, 0), + ); + await tester.pumpAndSettle(); + + expect(updatedFocusedDay, isNotNull); + expect(updatedFocusedDay!.month, initialFocusedDay.month - 2); + + headerText = intl.DateFormat.yMMMM().format(updatedFocusedDay!); + expect(find.byType(CalendarHeader), findsOneWidget); + expect(find.text(headerText), findsOneWidget); + }, + ); + + testWidgets( + '3 event markers are visible when 3 events are assigned to a given day', + (tester) async { + final eventDay = DateTime.utc(2021, 7, 20); + + await tester.pumpWidget(setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + eventLoader: (day) { + if (day.day == eventDay.day && day.month == eventDay.month) { + return ['Event 1', 'Event 2', 'Event 3']; + } + + return []; + }, + ), + )); + + final eventDayKey = cellContentKey(eventDay); + final eventDayCellContent = find.byKey(eventDayKey); + + final eventDayStack = find.ancestor( + of: eventDayCellContent, + matching: find.byType(Stack), + ); + + final eventMarkers = tester.widgetList( + find.descendant( + of: eventDayStack, + matching: find.byWidgetPredicate( + (Widget marker) => marker is Container && marker.child == null, + ), + ), + ); + + expect(eventMarkers.length, 3); + }, + ); + }); + + group('CalendarHeader chevrons test:', () { + testWidgets( + 'tapping on a left chevron navigates to previous calendar page', + (tester) async { + await tester.pumpWidget(createTableCalendar()); + + expect(find.text('July 2021'), findsOneWidget); + + final leftChevron = find.widgetWithIcon( + CustomIconButton, + Icons.chevron_left, + ); + + await tester.tap(leftChevron); + await tester.pumpAndSettle(); + + expect(find.text('June 2021'), findsOneWidget); + }, + ); + + testWidgets( + 'tapping on a right chevron navigates to next calendar page', + (tester) async { + await tester.pumpWidget(createTableCalendar()); + + expect(find.text('July 2021'), findsOneWidget); + + final rightChevron = find.widgetWithIcon( + CustomIconButton, + Icons.chevron_right, + ); + + await tester.tap(rightChevron); + await tester.pumpAndSettle(); + + expect(find.text('August 2021'), findsOneWidget); + }, + ); + }); + + group('Scrolling boundaries are set up properly:', () { + testWidgets('starting scroll boundary works correctly', (tester) async { + final focusedDay = DateTime.utc(2021, 6, 15); + + await tester.pumpWidget(createTableCalendar(focusedDay: focusedDay)); + + expect(find.byType(TableCalendar), findsOneWidget); + expect(find.text('June 2021'), findsOneWidget); + + await tester.drag(find.byType(CellContent).first, const Offset(500, 0)); + await tester.pumpAndSettle(); + expect(find.text('May 2021'), findsOneWidget); + + await tester.drag(find.byType(CellContent).first, const Offset(500, 0)); + await tester.pumpAndSettle(); + expect(find.text('May 2021'), findsOneWidget); + }); + + testWidgets('ending scroll boundary works correctly', (tester) async { + final focusedDay = DateTime.utc(2021, 8, 15); + + await tester.pumpWidget(createTableCalendar(focusedDay: focusedDay)); + + expect(find.byType(TableCalendar), findsOneWidget); + expect(find.text('August 2021'), findsOneWidget); + + await tester.drag(find.byType(CellContent).first, const Offset(-500, 0)); + await tester.pumpAndSettle(); + expect(find.text('September 2021'), findsOneWidget); + + await tester.drag(find.byType(CellContent).first, const Offset(-500, 0)); + await tester.pumpAndSettle(); + expect(find.text('September 2021'), findsOneWidget); + }); + }); + + group('onFormatChanged callback returns correct values:', () { + testWidgets('when initial format is month', (tester) async { + CalendarFormat calendarFormat = CalendarFormat.month; + + await tester.pumpWidget(setupTestWidget( + TableCalendar( + focusedDay: today, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + calendarFormat: calendarFormat, + onFormatChanged: (format) { + calendarFormat = format; + }, + ), + )); + + await tester.drag(find.byType(CellContent).first, const Offset(0, -500)); + await tester.pumpAndSettle(); + expect(calendarFormat, CalendarFormat.twoWeeks); + + await tester.drag(find.byType(CellContent).first, const Offset(0, 500)); + await tester.pumpAndSettle(); + expect(calendarFormat, CalendarFormat.month); + }); + + testWidgets('when initial format is two weeks', (tester) async { + CalendarFormat calendarFormat = CalendarFormat.twoWeeks; + + await tester.pumpWidget(setupTestWidget( + TableCalendar( + focusedDay: today, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + calendarFormat: calendarFormat, + onFormatChanged: (format) { + calendarFormat = format; + }, + ), + )); + + await tester.drag(find.byType(CellContent).first, const Offset(0, -500)); + await tester.pumpAndSettle(); + expect(calendarFormat, CalendarFormat.week); + + await tester.drag(find.byType(CellContent).first, const Offset(0, 500)); + await tester.pumpAndSettle(); + expect(calendarFormat, CalendarFormat.month); + }); + + testWidgets('when initial format is week', (tester) async { + CalendarFormat calendarFormat = CalendarFormat.week; + + await tester.pumpWidget(setupTestWidget( + TableCalendar( + focusedDay: today, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + calendarFormat: calendarFormat, + onFormatChanged: (format) { + calendarFormat = format; + }, + ), + )); + + await tester.drag(find.byType(CellContent).first, const Offset(0, -500)); + await tester.pumpAndSettle(); + expect(calendarFormat, CalendarFormat.week); + + await tester.drag(find.byType(CellContent).first, const Offset(0, 500)); + await tester.pumpAndSettle(); + expect(calendarFormat, CalendarFormat.twoWeeks); + }); + }); + + group('onDaySelected callback test:', () { + testWidgets( + 'selects correct day when tapped', + (tester) async { + DateTime? selectedDay; + + await tester.pumpWidget(setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + onDaySelected: (selected, focused) { + selectedDay = selected; + }, + ), + )); + + expect(selectedDay, isNull); + + final tappedDay = DateTime.utc(2021, 7, 18); + final tappedDayKey = cellContentKey(tappedDay); + + await tester.tap(find.byKey(tappedDayKey)); + await tester.pumpAndSettle(); + expect(selectedDay, tappedDay); + }, + ); + + testWidgets( + 'focuses correct day when tapped', + (tester) async { + DateTime? focusedDay; + + await tester.pumpWidget(setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + onDaySelected: (selected, focused) { + focusedDay = focused; + }, + ), + )); + + expect(focusedDay, isNull); + + final tappedDay = DateTime.utc(2021, 7, 18); + final tappedDayKey = cellContentKey(tappedDay); + + await tester.tap(find.byKey(tappedDayKey)); + await tester.pumpAndSettle(); + expect(focusedDay, tappedDay); + }, + ); + + testWidgets( + 'properly selects and focuses on outside cell tap - previous month (when in month format)', + (tester) async { + DateTime? selectedDay; + DateTime? focusedDay; + + await tester.pumpWidget(setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + onDaySelected: (selected, focused) { + selectedDay = selected; + focusedDay = focused; + }, + ), + )); + + expect(selectedDay, isNull); + expect(focusedDay, isNull); + + final tappedDay = DateTime.utc(2021, 6, 30); + final tappedDayKey = cellContentKey(tappedDay); + + final expectedFocusedDay = DateTime.utc(2021, 7, 1); + + await tester.tap(find.byKey(tappedDayKey)); + await tester.pumpAndSettle(); + expect(selectedDay, tappedDay); + expect(focusedDay, expectedFocusedDay); + }, + ); + + testWidgets( + 'properly selects and focuses on outside cell tap - next month (when in month format)', + (tester) async { + DateTime? selectedDay; + DateTime? focusedDay; + + await tester.pumpWidget(setupTestWidget( + TableCalendar( + focusedDay: DateTime.utc(2021, 8, 16), + firstDay: firstDay, + lastDay: lastDay, + currentDay: DateTime.utc(2021, 8, 16), + onDaySelected: (selected, focused) { + selectedDay = selected; + focusedDay = focused; + }, + ), + )); + + expect(selectedDay, isNull); + expect(focusedDay, isNull); + + final tappedDay = DateTime.utc(2021, 9, 1); + final tappedDayKey = cellContentKey(tappedDay); + + final expectedFocusedDay = DateTime.utc(2021, 8, 31); + + await tester.tap(find.byKey(tappedDayKey)); + await tester.pumpAndSettle(); + expect(selectedDay, tappedDay); + expect(focusedDay, expectedFocusedDay); + }, + ); + }); + + group('onDayLongPressed callback test:', () { + testWidgets( + 'selects correct day when long pressed', + (tester) async { + DateTime? selectedDay; + + await tester.pumpWidget(setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + onDayLongPressed: (selected, focused) { + selectedDay = selected; + }, + ), + )); + + expect(selectedDay, isNull); + + final longPressedDay = DateTime.utc(2021, 7, 18); + final longPressedDayKey = cellContentKey(longPressedDay); + + await tester.longPress(find.byKey(longPressedDayKey)); + await tester.pumpAndSettle(); + expect(selectedDay, longPressedDay); + }, + ); + + testWidgets( + 'focuses correct day when long pressed', + (tester) async { + DateTime? focusedDay; + + await tester.pumpWidget(setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + onDayLongPressed: (selected, focused) { + focusedDay = focused; + }, + ), + )); + + expect(focusedDay, isNull); + + final longPressedDay = DateTime.utc(2021, 7, 18); + final longPressedDayKey = cellContentKey(longPressedDay); + + await tester.longPress(find.byKey(longPressedDayKey)); + await tester.pumpAndSettle(); + expect(focusedDay, longPressedDay); + }, + ); + + testWidgets( + 'properly selects and focuses on outside cell long press - previous month (when in month format)', + (tester) async { + DateTime? selectedDay; + DateTime? focusedDay; + + await tester.pumpWidget(setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + onDayLongPressed: (selected, focused) { + selectedDay = selected; + focusedDay = focused; + }, + ), + )); + + expect(selectedDay, isNull); + expect(focusedDay, isNull); + + final longPressedDay = DateTime.utc(2021, 6, 30); + final longPressedDayKey = cellContentKey(longPressedDay); + + final expectedFocusedDay = DateTime.utc(2021, 7, 1); + + await tester.longPress(find.byKey(longPressedDayKey)); + await tester.pumpAndSettle(); + expect(selectedDay, longPressedDay); + expect(focusedDay, expectedFocusedDay); + }, + ); + + testWidgets( + 'properly selects and focuses on outside cell long press - next month (when in month format)', + (tester) async { + DateTime? selectedDay; + DateTime? focusedDay; + + await tester.pumpWidget(setupTestWidget( + TableCalendar( + focusedDay: DateTime.utc(2021, 8, 16), + firstDay: firstDay, + lastDay: lastDay, + currentDay: DateTime.utc(2021, 8, 16), + onDayLongPressed: (selected, focused) { + selectedDay = selected; + focusedDay = focused; + }, + ), + )); + + expect(selectedDay, isNull); + expect(focusedDay, isNull); + + final longPressedDay = DateTime.utc(2021, 9, 1); + final longPressedDayKey = cellContentKey(longPressedDay); + + final expectedFocusedDay = DateTime.utc(2021, 8, 31); + + await tester.longPress(find.byKey(longPressedDayKey)); + await tester.pumpAndSettle(); + expect(selectedDay, longPressedDay); + expect(focusedDay, expectedFocusedDay); + }, + ); + }); + + group('onRangeSelection callback test:', () { + testWidgets( + 'proper values are returned when second tapped day is after the first one', + (tester) async { + DateTime? rangeStart; + DateTime? rangeEnd; + DateTime? focusedDay; + + await tester.pumpWidget(setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + rangeSelectionMode: RangeSelectionMode.enforced, + onRangeSelected: (start, end, focused) { + rangeStart = start; + rangeEnd = end; + focusedDay = focused; + }, + ), + )); + + expect(rangeStart, isNull); + expect(rangeEnd, isNull); + expect(focusedDay, isNull); + + final firstTappedDay = DateTime.utc(2021, 7, 8); + final secondTappedDay = DateTime.utc(2021, 7, 21); + + final firstTappedDayKey = cellContentKey(firstTappedDay); + final secondTappedDayKey = cellContentKey(secondTappedDay); + + final expectedFocusedDay = secondTappedDay; + + await tester.tap(find.byKey(firstTappedDayKey)); + await tester.pumpAndSettle(); + await tester.tap(find.byKey(secondTappedDayKey)); + await tester.pumpAndSettle(); + expect(rangeStart, firstTappedDay); + expect(rangeEnd, secondTappedDay); + expect(focusedDay, expectedFocusedDay); + }, + ); + + testWidgets( + 'proper values are returned when second tapped day is before the first one', + (tester) async { + DateTime? rangeStart; + DateTime? rangeEnd; + DateTime? focusedDay; + + await tester.pumpWidget(setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + rangeSelectionMode: RangeSelectionMode.enforced, + onRangeSelected: (start, end, focused) { + rangeStart = start; + rangeEnd = end; + focusedDay = focused; + }, + ), + )); + + expect(rangeStart, isNull); + expect(rangeEnd, isNull); + expect(focusedDay, isNull); + + final firstTappedDay = DateTime.utc(2021, 7, 14); + final secondTappedDay = DateTime.utc(2021, 7, 7); + + final firstTappedDayKey = cellContentKey(firstTappedDay); + final secondTappedDayKey = cellContentKey(secondTappedDay); + + final expectedFocusedDay = secondTappedDay; + + await tester.tap(find.byKey(firstTappedDayKey)); + await tester.pumpAndSettle(); + await tester.tap(find.byKey(secondTappedDayKey)); + await tester.pumpAndSettle(); + expect(rangeStart, secondTappedDay); + expect(rangeEnd, firstTappedDay); + expect(focusedDay, expectedFocusedDay); + }, + ); + + testWidgets( + 'long press toggles rangeSelectionMode when onDayLongPress callback is null - initial mode is toggledOff', + (tester) async { + DateTime? rangeStart; + DateTime? rangeEnd; + DateTime? focusedDay; + DateTime? selectedDay; + + await tester.pumpWidget(setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + rangeSelectionMode: RangeSelectionMode.toggledOff, + onDaySelected: (selected, focused) { + selectedDay = selected; + focusedDay = focused; + }, + onRangeSelected: (start, end, focused) { + rangeStart = start; + rangeEnd = end; + focusedDay = focused; + }, + onDayLongPressed: null, + ), + )); + + expect(rangeStart, isNull); + expect(rangeEnd, isNull); + expect(focusedDay, isNull); + expect(selectedDay, isNull); + + final firstTappedDay = DateTime.utc(2021, 7, 8); + final secondTappedDay = DateTime.utc(2021, 7, 21); + + final firstTappedDayKey = cellContentKey(firstTappedDay); + final secondTappedDayKey = cellContentKey(secondTappedDay); + + final expectedFocusedDay = secondTappedDay; + + await tester.longPress(find.byKey(firstTappedDayKey)); + await tester.pumpAndSettle(); + + await tester.tap(find.byKey(firstTappedDayKey)); + await tester.pumpAndSettle(); + await tester.tap(find.byKey(secondTappedDayKey)); + await tester.pumpAndSettle(); + expect(rangeStart, firstTappedDay); + expect(rangeEnd, secondTappedDay); + expect(focusedDay, expectedFocusedDay); + expect(selectedDay, isNull); + }, + ); + + testWidgets( + 'long press toggles rangeSelectionMode when onDayLongPress callback is null - initial mode is toggledOn', + (tester) async { + DateTime? rangeStart; + DateTime? rangeEnd; + DateTime? focusedDay; + DateTime? selectedDay; + + await tester.pumpWidget(setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + rangeSelectionMode: RangeSelectionMode.toggledOn, + onDaySelected: (selected, focused) { + selectedDay = selected; + focusedDay = focused; + }, + onRangeSelected: (start, end, focused) { + rangeStart = start; + rangeEnd = end; + focusedDay = focused; + }, + onDayLongPressed: null, + ), + )); + + expect(rangeStart, isNull); + expect(rangeEnd, isNull); + expect(focusedDay, isNull); + expect(selectedDay, isNull); + + final firstTappedDay = DateTime.utc(2021, 7, 8); + final secondTappedDay = DateTime.utc(2021, 7, 21); + + final firstTappedDayKey = cellContentKey(firstTappedDay); + final secondTappedDayKey = cellContentKey(secondTappedDay); + + final expectedFocusedDay = secondTappedDay; + + await tester.longPress(find.byKey(firstTappedDayKey)); + await tester.pumpAndSettle(); + + await tester.tap(find.byKey(firstTappedDayKey)); + await tester.pumpAndSettle(); + await tester.tap(find.byKey(secondTappedDayKey)); + await tester.pumpAndSettle(); + expect(rangeStart, isNull); + expect(rangeEnd, isNull); + expect(focusedDay, expectedFocusedDay); + expect(selectedDay, secondTappedDay); + }, + ); + + testWidgets( + 'rangeSelectionMode.enforced disables onDaySelected callback', + (tester) async { + DateTime? rangeStart; + DateTime? rangeEnd; + DateTime? focusedDay; + DateTime? selectedDay; + + await tester.pumpWidget(setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + rangeSelectionMode: RangeSelectionMode.enforced, + onDaySelected: (selected, focused) { + selectedDay = selected; + focusedDay = focused; + }, + onRangeSelected: (start, end, focused) { + rangeStart = start; + rangeEnd = end; + focusedDay = focused; + }, + onDayLongPressed: null, + ), + )); + + expect(rangeStart, isNull); + expect(rangeEnd, isNull); + expect(focusedDay, isNull); + expect(selectedDay, isNull); + + final firstTappedDay = DateTime.utc(2021, 7, 8); + final secondTappedDay = DateTime.utc(2021, 7, 21); + + final firstTappedDayKey = cellContentKey(firstTappedDay); + final secondTappedDayKey = cellContentKey(secondTappedDay); + + final expectedFocusedDay = secondTappedDay; + + await tester.longPress(find.byKey(firstTappedDayKey)); + await tester.pumpAndSettle(); + + await tester.tap(find.byKey(firstTappedDayKey)); + await tester.pumpAndSettle(); + await tester.tap(find.byKey(secondTappedDayKey)); + await tester.pumpAndSettle(); + expect(rangeStart, firstTappedDay); + expect(rangeEnd, secondTappedDay); + expect(focusedDay, expectedFocusedDay); + expect(selectedDay, isNull); + }, + ); + + testWidgets( + 'rangeSelectionMode.disabled enforces onDaySelected callback', + (tester) async { + DateTime? rangeStart; + DateTime? rangeEnd; + DateTime? focusedDay; + DateTime? selectedDay; + + await tester.pumpWidget(setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + rangeSelectionMode: RangeSelectionMode.disabled, + onDaySelected: (selected, focused) { + selectedDay = selected; + focusedDay = focused; + }, + onRangeSelected: (start, end, focused) { + rangeStart = start; + rangeEnd = end; + focusedDay = focused; + }, + onDayLongPressed: null, + ), + )); + + expect(rangeStart, isNull); + expect(rangeEnd, isNull); + expect(focusedDay, isNull); + expect(selectedDay, isNull); + + final firstTappedDay = DateTime.utc(2021, 7, 8); + final secondTappedDay = DateTime.utc(2021, 7, 21); + + final firstTappedDayKey = cellContentKey(firstTappedDay); + final secondTappedDayKey = cellContentKey(secondTappedDay); + + final expectedFocusedDay = secondTappedDay; + + await tester.longPress(find.byKey(firstTappedDayKey)); + await tester.pumpAndSettle(); + + await tester.tap(find.byKey(firstTappedDayKey)); + await tester.pumpAndSettle(); + await tester.tap(find.byKey(secondTappedDayKey)); + await tester.pumpAndSettle(); + expect(rangeStart, isNull); + expect(rangeEnd, isNull); + expect(focusedDay, expectedFocusedDay); + expect(selectedDay, secondTappedDay); + }, + ); + }); + + group('Range selection test:', () { + testWidgets( + 'range selection has correct start and end point', + (tester) async { + final rangeStart = DateTime.utc(2021, 7, 8); + final rangeEnd = DateTime.utc(2021, 7, 21); + + await tester.pumpWidget(setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + rangeStartDay: rangeStart, + rangeEndDay: rangeEnd, + ), + )); + + final rangeStartKey = cellContentKey(rangeStart); + final rangeStartCellContent = + tester.widget(find.byKey(rangeStartKey)) as CellContent; + + expect(rangeStartCellContent.isRangeStart, true); + expect(rangeStartCellContent.isRangeEnd, false); + expect(rangeStartCellContent.isWithinRange, true); + + final rangeEndKey = cellContentKey(rangeEnd); + final rangeEndCellContent = + tester.widget(find.byKey(rangeEndKey)) as CellContent; + + expect(rangeEndCellContent.isRangeStart, false); + expect(rangeEndCellContent.isRangeEnd, true); + expect(rangeEndCellContent.isWithinRange, true); + }, + ); + + testWidgets( + 'days within range selection are marked as inWithinRange', + (tester) async { + final rangeStart = DateTime.utc(2021, 7, 8); + final rangeEnd = DateTime.utc(2021, 7, 13); + + await tester.pumpWidget(setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + rangeStartDay: rangeStart, + rangeEndDay: rangeEnd, + ), + )); + + final dayCount = rangeEnd.difference(rangeStart).inDays - 1; + expect(dayCount, 4); + + for (int i = 1; i <= dayCount; i++) { + final testDay = rangeStart.add(Duration(days: i)); + + expect(testDay.isAfter(rangeStart), true); + expect(testDay.isBefore(rangeEnd), true); + + final testDayKey = cellContentKey(testDay); + final testDayCellContent = + tester.widget(find.byKey(testDayKey)) as CellContent; + + expect(testDayCellContent.isWithinRange, true); + } + }, + ); + + testWidgets( + 'days outside range selection are not marked as inWithinRange', + (tester) async { + final rangeStart = DateTime.utc(2021, 7, 8); + final rangeEnd = DateTime.utc(2021, 7, 13); + + await tester.pumpWidget(setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + rangeStartDay: rangeStart, + rangeEndDay: rangeEnd, + ), + )); + + final oobStart = rangeStart.subtract(const Duration(days: 1)); + final oobEnd = rangeEnd.add(const Duration(days: 1)); + + final oobStartKey = cellContentKey(oobStart); + final oobStartCellContent = + tester.widget(find.byKey(oobStartKey)) as CellContent; + + final oobEndKey = cellContentKey(oobEnd); + final oobEndCellContent = + tester.widget(find.byKey(oobEndKey)) as CellContent; + + expect(oobStartCellContent.isWithinRange, false); + expect(oobEndCellContent.isWithinRange, false); + }, + ); + }); +} From 52b926eb47854771ca7f8e09eb04549293b644de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 7 Nov 2021 18:41:20 +0100 Subject: [PATCH 116/164] Update tests --- test/calendar_header_test.dart | 32 +++++----- test/calendar_page_test.dart | 11 ++-- test/common.dart | 7 +++ test/custom_icon_button_test.dart | 1 - test/format_button_test.dart | 31 ++++------ test/table_calendar_base_test.dart | 8 +-- test/table_calendar_test.dart | 93 ++++++++++++++++++++++++++++++ 7 files changed, 134 insertions(+), 49 deletions(-) diff --git a/test/calendar_header_test.dart b/test/calendar_header_test.dart index aea086ed..6ae01ca1 100644 --- a/test/calendar_header_test.dart +++ b/test/calendar_header_test.dart @@ -4,17 +4,16 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:intl/intl.dart' as intl; import 'package:table_calendar/src/customization/header_style.dart'; import 'package:table_calendar/src/shared/utils.dart'; import 'package:table_calendar/src/widgets/calendar_header.dart'; import 'package:table_calendar/src/widgets/custom_icon_button.dart'; import 'package:table_calendar/src/widgets/format_button.dart'; -const calendarFormatMap = const { - CalendarFormat.month: 'Month', - CalendarFormat.twoWeeks: 'Two weeks', - CalendarFormat.week: 'week', -}; +import 'common.dart'; + +final focusedMonth = DateTime.utc(2021, 7, 15); Widget setupTestWidget({ HeaderStyle headerStyle = const HeaderStyle(), @@ -29,7 +28,7 @@ Widget setupTestWidget({ textDirection: TextDirection.ltr, child: Material( child: CalendarHeader( - focusedMonth: DateTime.utc(2021, 7, 15), + focusedMonth: focusedMonth, calendarFormat: CalendarFormat.month, headerStyle: headerStyle, onLeftChevronTap: () => onLeftChevronTap?.call(), @@ -44,6 +43,17 @@ Widget setupTestWidget({ } void main() { + testWidgets( + 'Displays corrent month and year for given focusedMonth', + (tester) async { + await tester.pumpWidget(setupTestWidget()); + + final headerText = intl.DateFormat.yMMMM().format(focusedMonth); + + expect(find.byType(CalendarHeader), findsOneWidget); + expect(find.text(headerText), findsOneWidget); + }, + ); testWidgets( 'Ensure chevrons and FormatButton are visible by default, test onTap callbacks', (tester) async { @@ -63,8 +73,6 @@ void main() { ), ); - await tester.pumpAndSettle(); - final leftChevron = find.widgetWithIcon( CustomIconButton, Icons.chevron_left, @@ -123,8 +131,6 @@ void main() { ), ); - await tester.pumpAndSettle(); - final leftChevron = find.widgetWithIcon( CustomIconButton, Icons.chevron_left, @@ -151,8 +157,6 @@ void main() { ), ); - await tester.pumpAndSettle(); - final leftChevron = find.widgetWithIcon( CustomIconButton, Icons.chevron_left, @@ -177,8 +181,6 @@ void main() { ), ); - await tester.pumpAndSettle(); - final formatButton = find.byType(FormatButton); expect(formatButton, findsNothing); }, @@ -193,8 +195,6 @@ void main() { ), ); - await tester.pumpAndSettle(); - final formatButton = find.byType(FormatButton); expect(formatButton, findsNothing); }, diff --git a/test/calendar_page_test.dart b/test/calendar_page_test.dart index c55f9a57..c8f7dae6 100644 --- a/test/calendar_page_test.dart +++ b/test/calendar_page_test.dart @@ -42,7 +42,6 @@ void main() { ), ); - await tester.pumpAndSettle(); final expectedCellCount = visibleDays.length; expect(find.byType(Text), findsNWidgets(expectedCellCount)); }, @@ -66,11 +65,13 @@ void main() { ), ); - await tester.pumpAndSettle(); final expectedCellCount = visibleDays.length; final expectedDowLabels = 7; - expect(find.byType(Text), - findsNWidgets(expectedCellCount + expectedDowLabels)); + + expect( + find.byType(Text), + findsNWidgets(expectedCellCount + expectedDowLabels), + ); }, ); @@ -89,8 +90,6 @@ void main() { ), ), ); - - await tester.pumpAndSettle(); }, throwsAssertionError); }, ); diff --git a/test/common.dart b/test/common.dart index febaee50..fb12d158 100644 --- a/test/common.dart +++ b/test/common.dart @@ -2,7 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 import 'package:flutter/foundation.dart'; +import 'package:table_calendar/src/shared/utils.dart'; ValueKey dateToKey(DateTime date, {String prefix = 'cell-'}) { return ValueKey('$prefix${date.year}-${date.month}-${date.day}'); } + +const calendarFormatMap = const { + CalendarFormat.month: 'Month', + CalendarFormat.twoWeeks: 'Two weeks', + CalendarFormat.week: 'week', +}; diff --git a/test/custom_icon_button_test.dart b/test/custom_icon_button_test.dart index 7080a07b..71a26e36 100644 --- a/test/custom_icon_button_test.dart +++ b/test/custom_icon_button_test.dart @@ -30,7 +30,6 @@ void main() { ), ); - await tester.pumpAndSettle(); final button = find.byType(CustomIconButton); expect(button, findsOneWidget); expect(buttonTapped, false); diff --git a/test/format_button_test.dart b/test/format_button_test.dart index 3f7040a3..498da579 100644 --- a/test/format_button_test.dart +++ b/test/format_button_test.dart @@ -7,11 +7,7 @@ import 'package:table_calendar/src/customization/header_style.dart'; import 'package:table_calendar/src/widgets/format_button.dart'; import 'package:table_calendar/table_calendar.dart'; -const availableCalendarFormats = const { - CalendarFormat.month: 'Month', - CalendarFormat.twoWeeks: 'Two weeks', - CalendarFormat.week: 'week', -}; +import 'common.dart'; Widget setupTestWidget(Widget child) { return Directionality( @@ -31,7 +27,7 @@ void main() { await tester.pumpWidget( setupTestWidget( FormatButton( - availableCalendarFormats: availableCalendarFormats, + availableCalendarFormats: calendarFormatMap, calendarFormat: CalendarFormat.month, decoration: headerStyle.formatButtonDecoration, padding: headerStyle.formatButtonPadding, @@ -44,7 +40,6 @@ void main() { ), ); - await tester.pumpAndSettle(); expect(find.byType(FormatButton), findsOneWidget); expect(calendarFormat, isNull); @@ -63,7 +58,7 @@ void main() { await tester.pumpWidget( setupTestWidget( FormatButton( - availableCalendarFormats: availableCalendarFormats, + availableCalendarFormats: calendarFormatMap, calendarFormat: CalendarFormat.twoWeeks, decoration: headerStyle.formatButtonDecoration, padding: headerStyle.formatButtonPadding, @@ -76,7 +71,6 @@ void main() { ), ); - await tester.pumpAndSettle(); expect(find.byType(FormatButton), findsOneWidget); expect(calendarFormat, isNull); @@ -95,7 +89,7 @@ void main() { await tester.pumpWidget( setupTestWidget( FormatButton( - availableCalendarFormats: availableCalendarFormats, + availableCalendarFormats: calendarFormatMap, calendarFormat: CalendarFormat.week, decoration: headerStyle.formatButtonDecoration, padding: headerStyle.formatButtonPadding, @@ -108,7 +102,6 @@ void main() { ), ); - await tester.pumpAndSettle(); expect(find.byType(FormatButton), findsOneWidget); expect(calendarFormat, isNull); @@ -127,18 +120,18 @@ void main() { final currentFormatIndex = 0; final currentFormat = - availableCalendarFormats.keys.elementAt(currentFormatIndex); + calendarFormatMap.keys.elementAt(currentFormatIndex); final currentFormatText = - availableCalendarFormats.values.elementAt(currentFormatIndex); + calendarFormatMap.values.elementAt(currentFormatIndex); final nextFormatIndex = 1; final nextFormatText = - availableCalendarFormats.values.elementAt(nextFormatIndex); + calendarFormatMap.values.elementAt(nextFormatIndex); await tester.pumpWidget( setupTestWidget( FormatButton( - availableCalendarFormats: availableCalendarFormats, + availableCalendarFormats: calendarFormatMap, calendarFormat: currentFormat, decoration: headerStyle.formatButtonDecoration, padding: headerStyle.formatButtonPadding, @@ -149,7 +142,6 @@ void main() { ), ); - await tester.pumpAndSettle(); expect(find.byType(FormatButton), findsOneWidget); expect(currentFormatText, isNotNull); expect(find.text(currentFormatText), findsNothing); @@ -165,14 +157,14 @@ void main() { final currentFormatIndex = 0; final currentFormat = - availableCalendarFormats.keys.elementAt(currentFormatIndex); + calendarFormatMap.keys.elementAt(currentFormatIndex); final currentFormatText = - availableCalendarFormats.values.elementAt(currentFormatIndex); + calendarFormatMap.values.elementAt(currentFormatIndex); await tester.pumpWidget( setupTestWidget( FormatButton( - availableCalendarFormats: availableCalendarFormats, + availableCalendarFormats: calendarFormatMap, calendarFormat: currentFormat, decoration: headerStyle.formatButtonDecoration, padding: headerStyle.formatButtonPadding, @@ -183,7 +175,6 @@ void main() { ), ); - await tester.pumpAndSettle(); expect(find.byType(FormatButton), findsOneWidget); expect(currentFormatText, isNotNull); expect(find.text(currentFormatText), findsOneWidget); diff --git a/test/table_calendar_base_test.dart b/test/table_calendar_base_test.dart index d21a8222..4076a540 100644 --- a/test/table_calendar_base_test.dart +++ b/test/table_calendar_base_test.dart @@ -7,6 +7,8 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:simple_gesture_detector/simple_gesture_detector.dart'; import 'package:table_calendar/table_calendar.dart'; +import 'common.dart'; + Widget setupTestWidget(Widget child) { return Directionality( textDirection: TextDirection.ltr, @@ -14,10 +16,6 @@ Widget setupTestWidget(Widget child) { ); } -ValueKey dateToKey(DateTime date, {String prefix = 'cell-'}) { - return ValueKey('$prefix${date.year}-${date.month}-${date.day}'); -} - void main() { group('Correct days are displayed for given focusedDay when:', () { testWidgets( @@ -377,8 +375,6 @@ void main() { ), ), ); - - await tester.pumpAndSettle(); }, throwsAssertionError); }, ); diff --git a/test/table_calendar_test.dart b/test/table_calendar_test.dart index de5a4acd..353888f9 100644 --- a/test/table_calendar_test.dart +++ b/test/table_calendar_test.dart @@ -460,6 +460,99 @@ void main() { expect(eventMarkers.length, 3); }, ); + + testWidgets( + 'currentDay correctly marks given day as today', + (tester) async { + await tester.pumpWidget(setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + ), + )); + + final currentDayKey = cellContentKey(today); + final currentDayCellContent = + tester.widget(find.byKey(currentDayKey)) as CellContent; + + expect(currentDayCellContent.isToday, true); + }, + ); + + testWidgets( + 'if currentDay is absent, DateTime.now() is marked as today', + (tester) async { + final now = DateTime.now(); + final firstDay = DateTime.utc(now.year, now.month - 3, now.day); + final lastDay = DateTime.utc(now.year, now.month + 3, now.day); + + await tester.pumpWidget(setupTestWidget( + TableCalendar( + focusedDay: now, + firstDay: firstDay, + lastDay: lastDay, + ), + )); + + final currentDayKey = cellContentKey(now); + final currentDayCellContent = + tester.widget(find.byKey(currentDayKey)) as CellContent; + + expect(currentDayCellContent.isToday, true); + }, + ); + + testWidgets( + 'selectedDayPredicate correctly marks given day as selected', + (tester) async { + final selectedDay = DateTime.utc(2021, 7, 20); + + await tester.pumpWidget(setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + selectedDayPredicate: (day) { + return isSameDay(day, selectedDay); + }, + ), + )); + + final selectedDayKey = cellContentKey(selectedDay); + final selectedDayCellContent = + tester.widget(find.byKey(selectedDayKey)) as CellContent; + + expect(selectedDayCellContent.isSelected, true); + }, + ); + + testWidgets( + 'holidayPredicate correctly marks given day as holiday', + (tester) async { + final holiday = DateTime.utc(2021, 7, 20); + + await tester.pumpWidget(setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + holidayPredicate: (day) { + return isSameDay(day, holiday); + }, + ), + )); + + final holidayKey = cellContentKey(holiday); + final holidayCellContent = + tester.widget(find.byKey(holidayKey)) as CellContent; + + expect(holidayCellContent.isHoliday, true); + }, + ); }); group('CalendarHeader chevrons test:', () { From 60cfac1f700165581daaba3a803e1542e070e840 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 7 Nov 2021 19:29:15 +0100 Subject: [PATCH 117/164] Refactor key prefix --- lib/src/table_calendar.dart | 2 +- test/common.dart | 2 +- test/table_calendar_test.dart | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index 3ed84386..e3d0f2ab 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -595,7 +595,7 @@ class _TableCalendarState extends State> { final isWeekend = _isWeekend(day, weekendDays: widget.weekendDays); Widget content = CellContent( - key: ValueKey('cellContent-${day.year}-${day.month}-${day.day}'), + key: ValueKey('CellContent-${day.year}-${day.month}-${day.day}'), day: day, focusedDay: focusedDay, calendarStyle: widget.calendarStyle, diff --git a/test/common.dart b/test/common.dart index fb12d158..e351cea2 100644 --- a/test/common.dart +++ b/test/common.dart @@ -4,7 +4,7 @@ import 'package:flutter/foundation.dart'; import 'package:table_calendar/src/shared/utils.dart'; -ValueKey dateToKey(DateTime date, {String prefix = 'cell-'}) { +ValueKey dateToKey(DateTime date, {String prefix = ''}) { return ValueKey('$prefix${date.year}-${date.month}-${date.day}'); } diff --git a/test/table_calendar_test.dart b/test/table_calendar_test.dart index 353888f9..7c15f547 100644 --- a/test/table_calendar_test.dart +++ b/test/table_calendar_test.dart @@ -44,7 +44,7 @@ Widget createTableCalendar({ } ValueKey cellContentKey(DateTime date) { - return dateToKey(date, prefix: 'cellContent-'); + return dateToKey(date, prefix: 'CellContent-'); } void main() { From 5d92076284617a6866ff0f538e5e4c7fa84e610f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 20 Feb 2022 19:14:02 +0100 Subject: [PATCH 118/164] Update dependencies --- example/android/app/build.gradle | 4 ++-- .../android/app/src/main/AndroidManifest.xml | 13 ++----------- example/android/build.gradle | 4 ++-- .../gradle/wrapper/gradle-wrapper.properties | 2 +- example/ios/Runner.xcodeproj/project.pbxproj | 4 ++-- .../contents.xcworkspacedata | 2 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- example/ios/Runner/Info.plist | 2 +- example/pubspec.lock | 19 +++++++++++++------ pubspec.lock | 19 +++++++++++++------ 10 files changed, 38 insertions(+), 33 deletions(-) diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 3932aa91..f95d0d7f 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -26,7 +26,7 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 29 + compileSdkVersion 31 sourceSets { main.java.srcDirs += 'src/main/kotlin' @@ -40,7 +40,7 @@ android { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.example.example" minSdkVersion 16 - targetSdkVersion 29 + targetSdkVersion 31 versionCode flutterVersionCode.toInteger() versionName flutterVersionName } diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 55ca830c..1dcbe397 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -6,8 +6,8 @@ additional functionality it is fine to subclass or reimplement FlutterApplication and put your custom class here. --> - - diff --git a/example/android/build.gradle b/example/android/build.gradle index 3100ad2d..714549c2 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,12 +1,12 @@ buildscript { - ext.kotlin_version = '1.3.50' + ext.kotlin_version = '1.6.10' repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.android.tools.build:gradle:4.1.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index 296b146b..bc6a58af 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 1aec2aa0..9690c8f2 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 50; objects = { /* Begin PBXBuildFile section */ @@ -127,7 +127,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1020; + LastUpgradeCheck = 1300; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 1d526a16..919434a6 100644 --- a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index a28140cf..3db53b6e 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleName - example + table_calendar example CFBundlePackageType APPL CFBundleShortVersionString diff --git a/example/pubspec.lock b/example/pubspec.lock index 7c23c4e4..8e13c981 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -7,7 +7,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.8.1" + version: "2.8.2" boolean_selector: dependency: transitive description: @@ -21,7 +21,7 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.0" charcode: dependency: transitive description: @@ -80,7 +80,14 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10" + version: "0.12.11" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" meta: dependency: transitive description: @@ -155,7 +162,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.2" + version: "0.4.8" typed_data: dependency: transitive description: @@ -169,7 +176,7 @@ packages: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.1.1" sdks: - dart: ">=2.12.0 <3.0.0" + dart: ">=2.14.0 <3.0.0" flutter: ">=1.17.0" diff --git a/pubspec.lock b/pubspec.lock index 36f4b2bb..efc97c88 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,7 +7,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.8.1" + version: "2.8.2" boolean_selector: dependency: transitive description: @@ -21,7 +21,7 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.0" charcode: dependency: transitive description: @@ -73,7 +73,14 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10" + version: "0.12.11" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" meta: dependency: transitive description: @@ -141,7 +148,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.2" + version: "0.4.8" typed_data: dependency: transitive description: @@ -155,7 +162,7 @@ packages: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.1.1" sdks: - dart: ">=2.12.0 <3.0.0" + dart: ">=2.14.0 <3.0.0" flutter: ">=1.17.0" From 4db6e6055ead95ba62fe18565cab5ea4aff52a6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 20 Feb 2022 19:14:47 +0100 Subject: [PATCH 119/164] Clean redundant fields --- lib/src/table_calendar_base.dart | 4 +--- test/calendar_header_test.dart | 1 - test/custom_icon_button_test.dart | 1 - test/format_button_test.dart | 1 - test/table_calendar_test.dart | 1 - 5 files changed, 1 insertion(+), 7 deletions(-) diff --git a/lib/src/table_calendar_base.dart b/lib/src/table_calendar_base.dart index 3341e323..5dc72e49 100644 --- a/lib/src/table_calendar_base.dart +++ b/lib/src/table_calendar_base.dart @@ -77,8 +77,7 @@ class TableCalendarBase extends StatefulWidget { _TableCalendarBaseState createState() => _TableCalendarBaseState(); } -class _TableCalendarBaseState extends State - with SingleTickerProviderStateMixin { +class _TableCalendarBaseState extends State { late final ValueNotifier _pageHeight; late final PageController _pageController; late DateTime _focusedDay; @@ -191,7 +190,6 @@ class _TableCalendarBaseState extends State constraints.hasBoundedHeight ? constraints.maxHeight : value; return AnimatedSize( - vsync: this, duration: widget.formatAnimationDuration, curve: widget.formatAnimationCurve, alignment: Alignment.topCenter, diff --git a/test/calendar_header_test.dart b/test/calendar_header_test.dart index 6ae01ca1..ad91c8af 100644 --- a/test/calendar_header_test.dart +++ b/test/calendar_header_test.dart @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:intl/intl.dart' as intl; import 'package:table_calendar/src/customization/header_style.dart'; diff --git a/test/custom_icon_button_test.dart b/test/custom_icon_button_test.dart index 71a26e36..b608f978 100644 --- a/test/custom_icon_button_test.dart +++ b/test/custom_icon_button_test.dart @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:table_calendar/src/widgets/custom_icon_button.dart'; diff --git a/test/format_button_test.dart b/test/format_button_test.dart index 498da579..0dd69c8e 100644 --- a/test/format_button_test.dart +++ b/test/format_button_test.dart @@ -3,7 +3,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:table_calendar/src/customization/header_style.dart'; import 'package:table_calendar/src/widgets/format_button.dart'; import 'package:table_calendar/table_calendar.dart'; diff --git a/test/table_calendar_test.dart b/test/table_calendar_test.dart index 7c15f547..3361255b 100644 --- a/test/table_calendar_test.dart +++ b/test/table_calendar_test.dart @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:intl/intl.dart' as intl; import 'package:table_calendar/src/widgets/calendar_header.dart'; From 08c3c66b10fb9e01718b44b05f6c213b4133c468 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 20 Feb 2022 19:22:13 +0100 Subject: [PATCH 120/164] Release version 3.0.4 --- CHANGELOG.md | 5 +++++ README.md | 2 +- example/pubspec.lock | 2 +- pubspec.yaml | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7fb5fe2..f2febc5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## [3.0.4] + +* Updated dependencies +* Removed deprecated fields + ## [3.0.3] * Added semantic label to prioritizedBuilder diff --git a/README.md b/README.md index 8b556204..cfbf7890 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Add the following line to `pubspec.yaml`: ```yaml dependencies: - table_calendar: ^3.0.3 + table_calendar: ^3.0.4 ``` ### Basic setup diff --git a/example/pubspec.lock b/example/pubspec.lock index 8e13c981..e3e59460 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -148,7 +148,7 @@ packages: path: ".." relative: true source: path - version: "3.0.3" + version: "3.0.4" term_glyph: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 70d5e343..c2eb42c1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: table_calendar description: Highly customizable, feature-packed calendar widget for Flutter. -version: 3.0.3 +version: 3.0.4 author: Aleksander Woźniak homepage: https://github.com/aleksanderwozniak/table_calendar From c898d5afbaf52c7836a1c1541dee105275fd0e9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Fri, 11 Mar 2022 18:08:19 +0100 Subject: [PATCH 121/164] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 38 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 ++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..82082166 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Report unexpected behavior +title: '' +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To reproduce** +Please include a short code sample that can be used to reproduce the problem. +
+Code sample +
+ +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Output of `flutter doctor`** +Paste the result of this command here. +
+Output +
+ +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..876ae2a9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for table_calendar +title: '' +labels: enhancement +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From 0abad2097d027aa49b2a7c8962dd8e974440e96d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 13 Mar 2022 16:02:47 +0100 Subject: [PATCH 122/164] Make CustomIconButton platform aware --- lib/src/widgets/custom_icon_button.dart | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/lib/src/widgets/custom_icon_button.dart b/lib/src/widgets/custom_icon_button.dart index c1ca9179..e0b88b08 100644 --- a/lib/src/widgets/custom_icon_button.dart +++ b/lib/src/widgets/custom_icon_button.dart @@ -1,6 +1,9 @@ // Copyright 2019 Aleksander Woźniak // SPDX-License-Identifier: Apache-2.0 +import 'dart:io'; + +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; class CustomIconButton extends StatelessWidget { @@ -21,14 +24,20 @@ class CustomIconButton extends StatelessWidget { Widget build(BuildContext context) { return Padding( padding: margin, - child: InkWell( - onTap: onTap, - borderRadius: BorderRadius.circular(100.0), - child: Padding( - padding: padding, - child: icon, - ), - ), + child: Platform.isIOS || Platform.isMacOS + ? CupertinoButton( + onPressed: onTap, + padding: padding, + child: icon, + ) + : InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(100.0), + child: Padding( + padding: padding, + child: icon, + ), + ), ); } } From 782edad42fd8d5d4dff305f80e2b8b045fd16a1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Tue, 22 Mar 2022 14:36:21 +0100 Subject: [PATCH 123/164] Add Flutter Web to example --- .../res/drawable-v21/launch_background.xml | 12 ++ .../app/src/main/res/values-night/styles.xml | 18 +++ example/web/favicon.png | Bin 0 -> 917 bytes example/web/icons/Icon-192.png | Bin 0 -> 5292 bytes example/web/icons/Icon-512.png | Bin 0 -> 8252 bytes example/web/icons/Icon-maskable-192.png | Bin 0 -> 5594 bytes example/web/icons/Icon-maskable-512.png | Bin 0 -> 20998 bytes example/web/index.html | 104 ++++++++++++++++++ example/web/manifest.json | 35 ++++++ 9 files changed, 169 insertions(+) create mode 100644 example/android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 example/android/app/src/main/res/values-night/styles.xml create mode 100644 example/web/favicon.png create mode 100644 example/web/icons/Icon-192.png create mode 100644 example/web/icons/Icon-512.png create mode 100644 example/web/icons/Icon-maskable-192.png create mode 100644 example/web/icons/Icon-maskable-512.png create mode 100644 example/web/index.html create mode 100644 example/web/manifest.json diff --git a/example/android/app/src/main/res/drawable-v21/launch_background.xml b/example/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 00000000..f74085f3 --- /dev/null +++ b/example/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/example/android/app/src/main/res/values-night/styles.xml b/example/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 00000000..3db14bb5 --- /dev/null +++ b/example/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/example/web/favicon.png b/example/web/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8aaa46ac1ae21512746f852a42ba87e4165dfdd1 GIT binary patch literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM literal 0 HcmV?d00001 diff --git a/example/web/icons/Icon-192.png b/example/web/icons/Icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b749bfef07473333cf1dd31e9eed89862a5d52aa GIT binary patch literal 5292 zcmZ`-2T+sGz6~)*FVZ`aW+(v>MIm&M-g^@e2u-B-DoB?qO+b1Tq<5uCCv>ESfRum& zp%X;f!~1{tzL__3=gjVJ=j=J>+nMj%ncXj1Q(b|Ckbw{Y0FWpt%4y%$uD=Z*c-x~o zE;IoE;xa#7Ll5nj-e4CuXB&G*IM~D21rCP$*xLXAK8rIMCSHuSu%bL&S3)8YI~vyp@KBu9Ph7R_pvKQ@xv>NQ`dZp(u{Z8K3yOB zn7-AR+d2JkW)KiGx0hosml;+eCXp6+w%@STjFY*CJ?udJ64&{BCbuebcuH;}(($@@ znNlgBA@ZXB)mcl9nbX#F!f_5Z=W>0kh|UVWnf!At4V*LQP%*gPdCXd6P@J4Td;!Ur z<2ZLmwr(NG`u#gDEMP19UcSzRTL@HsK+PnIXbVBT@oHm53DZr?~V(0{rsalAfwgo zEh=GviaqkF;}F_5-yA!1u3!gxaR&Mj)hLuj5Q-N-@Lra{%<4ONja8pycD90&>yMB` zchhd>0CsH`^|&TstH-8+R`CfoWqmTTF_0?zDOY`E`b)cVi!$4xA@oO;SyOjJyP^_j zx^@Gdf+w|FW@DMdOi8=4+LJl$#@R&&=UM`)G!y%6ZzQLoSL%*KE8IO0~&5XYR9 z&N)?goEiWA(YoRfT{06&D6Yuu@Qt&XVbuW@COb;>SP9~aRc+z`m`80pB2o%`#{xD@ zI3RAlukL5L>px6b?QW1Ac_0>ew%NM!XB2(H+1Y3AJC?C?O`GGs`331Nd4ZvG~bMo{lh~GeL zSL|tT*fF-HXxXYtfu5z+T5Mx9OdP7J4g%@oeC2FaWO1D{=NvL|DNZ}GO?O3`+H*SI z=grGv=7dL{+oY0eJFGO!Qe(e2F?CHW(i!!XkGo2tUvsQ)I9ev`H&=;`N%Z{L zO?vV%rDv$y(@1Yj@xfr7Kzr<~0{^T8wM80xf7IGQF_S-2c0)0D6b0~yD7BsCy+(zL z#N~%&e4iAwi4F$&dI7x6cE|B{f@lY5epaDh=2-(4N05VO~A zQT3hanGy_&p+7Fb^I#ewGsjyCEUmSCaP6JDB*=_()FgQ(-pZ28-{qx~2foO4%pM9e z*_63RT8XjgiaWY|*xydf;8MKLd{HnfZ2kM%iq}fstImB-K6A79B~YoPVa@tYN@T_$ zea+9)<%?=Fl!kd(Y!G(-o}ko28hg2!MR-o5BEa_72uj7Mrc&{lRh3u2%Y=Xk9^-qa zBPWaD=2qcuJ&@Tf6ue&)4_V*45=zWk@Z}Q?f5)*z)-+E|-yC4fs5CE6L_PH3=zI8p z*Z3!it{1e5_^(sF*v=0{`U9C741&lub89gdhKp|Y8CeC{_{wYK-LSbp{h)b~9^j!s z7e?Y{Z3pZv0J)(VL=g>l;<}xk=T*O5YR|hg0eg4u98f2IrA-MY+StQIuK-(*J6TRR z|IM(%uI~?`wsfyO6Tgmsy1b3a)j6M&-jgUjVg+mP*oTKdHg?5E`!r`7AE_#?Fc)&a z08KCq>Gc=ne{PCbRvs6gVW|tKdcE1#7C4e`M|j$C5EYZ~Y=jUtc zj`+?p4ba3uy7><7wIokM79jPza``{Lx0)zGWg;FW1^NKY+GpEi=rHJ+fVRGfXO zPHV52k?jxei_!YYAw1HIz}y8ZMwdZqU%ESwMn7~t zdI5%B;U7RF=jzRz^NuY9nM)&<%M>x>0(e$GpU9th%rHiZsIT>_qp%V~ILlyt^V`=d z!1+DX@ah?RnB$X!0xpTA0}lN@9V-ePx>wQ?-xrJr^qDlw?#O(RsXeAvM%}rg0NT#t z!CsT;-vB=B87ShG`GwO;OEbeL;a}LIu=&@9cb~Rsx(ZPNQ!NT7H{@j0e(DiLea>QD zPmpe90gEKHEZ8oQ@6%E7k-Ptn#z)b9NbD@_GTxEhbS+}Bb74WUaRy{w;E|MgDAvHw zL)ycgM7mB?XVh^OzbC?LKFMotw3r@i&VdUV%^Efdib)3@soX%vWCbnOyt@Y4swW925@bt45y0HY3YI~BnnzZYrinFy;L?2D3BAL`UQ zEj))+f>H7~g8*VuWQ83EtGcx`hun$QvuurSMg3l4IP8Fe`#C|N6mbYJ=n;+}EQm;< z!!N=5j1aAr_uEnnzrEV%_E|JpTb#1p1*}5!Ce!R@d$EtMR~%9# zd;h8=QGT)KMW2IKu_fA_>p_und#-;Q)p%%l0XZOXQicfX8M~7?8}@U^ihu;mizj)t zgV7wk%n-UOb z#!P5q?Ex+*Kx@*p`o$q8FWL*E^$&1*!gpv?Za$YO~{BHeGY*5%4HXUKa_A~~^d z=E*gf6&+LFF^`j4$T~dR)%{I)T?>@Ma?D!gi9I^HqvjPc3-v~=qpX1Mne@*rzT&Xw zQ9DXsSV@PqpEJO-g4A&L{F&;K6W60D!_vs?Vx!?w27XbEuJJP&);)^+VF1nHqHBWu z^>kI$M9yfOY8~|hZ9WB!q-9u&mKhEcRjlf2nm_@s;0D#c|@ED7NZE% zzR;>P5B{o4fzlfsn3CkBK&`OSb-YNrqx@N#4CK!>bQ(V(D#9|l!e9(%sz~PYk@8zt zPN9oK78&-IL_F zhsk1$6p;GqFbtB^ZHHP+cjMvA0(LqlskbdYE_rda>gvQLTiqOQ1~*7lg%z*&p`Ry& zRcG^DbbPj_jOKHTr8uk^15Boj6>hA2S-QY(W-6!FIq8h$<>MI>PYYRenQDBamO#Fv zAH5&ImqKBDn0v5kb|8i0wFhUBJTpT!rB-`zK)^SNnRmLraZcPYK7b{I@+}wXVdW-{Ps17qdRA3JatEd?rPV z4@}(DAMf5EqXCr4-B+~H1P#;t@O}B)tIJ(W6$LrK&0plTmnPpb1TKn3?f?Kk``?D+ zQ!MFqOX7JbsXfQrz`-M@hq7xlfNz;_B{^wbpG8des56x(Q)H)5eLeDwCrVR}hzr~= zM{yXR6IM?kXxauLza#@#u?Y|o;904HCqF<8yT~~c-xyRc0-vxofnxG^(x%>bj5r}N zyFT+xnn-?B`ohA>{+ZZQem=*Xpqz{=j8i2TAC#x-m;;mo{{sLB_z(UoAqD=A#*juZ zCv=J~i*O8;F}A^Wf#+zx;~3B{57xtoxC&j^ie^?**T`WT2OPRtC`xj~+3Kprn=rVM zVJ|h5ux%S{dO}!mq93}P+h36mZ5aZg1-?vhL$ke1d52qIiXSE(llCr5i=QUS?LIjc zV$4q=-)aaR4wsrQv}^shL5u%6;`uiSEs<1nG^?$kl$^6DL z43CjY`M*p}ew}}3rXc7Xck@k41jx}c;NgEIhKZ*jsBRZUP-x2cm;F1<5$jefl|ppO zmZd%%?gMJ^g9=RZ^#8Mf5aWNVhjAS^|DQO+q$)oeob_&ZLFL(zur$)); zU19yRm)z<4&4-M}7!9+^Wl}Uk?`S$#V2%pQ*SIH5KI-mn%i;Z7-)m$mN9CnI$G7?# zo`zVrUwoSL&_dJ92YhX5TKqaRkfPgC4=Q&=K+;_aDs&OU0&{WFH}kKX6uNQC6%oUH z2DZa1s3%Vtk|bglbxep-w)PbFG!J17`<$g8lVhqD2w;Z0zGsh-r zxZ13G$G<48leNqR!DCVt9)@}(zMI5w6Wo=N zpP1*3DI;~h2WDWgcKn*f!+ORD)f$DZFwgKBafEZmeXQMAsq9sxP9A)7zOYnkHT9JU zRA`umgmP9d6=PHmFIgx=0$(sjb>+0CHG)K@cPG{IxaJ&Ueo8)0RWgV9+gO7+Bl1(F z7!BslJ2MP*PWJ;x)QXbR$6jEr5q3 z(3}F@YO_P1NyTdEXRLU6fp?9V2-S=E+YaeLL{Y)W%6`k7$(EW8EZSA*(+;e5@jgD^I zaJQ2|oCM1n!A&-8`;#RDcZyk*+RPkn_r8?Ak@agHiSp*qFNX)&i21HE?yuZ;-C<3C zwJGd1lx5UzViP7sZJ&|LqH*mryb}y|%AOw+v)yc`qM)03qyyrqhX?ub`Cjwx2PrR! z)_z>5*!*$x1=Qa-0uE7jy0z`>|Ni#X+uV|%_81F7)b+nf%iz=`fF4g5UfHS_?PHbr zB;0$bK@=di?f`dS(j{l3-tSCfp~zUuva+=EWxJcRfp(<$@vd(GigM&~vaYZ0c#BTs z3ijkxMl=vw5AS&DcXQ%eeKt!uKvh2l3W?&3=dBHU=Gz?O!40S&&~ei2vg**c$o;i89~6DVns zG>9a*`k5)NI9|?W!@9>rzJ;9EJ=YlJTx1r1BA?H`LWijk(rTax9(OAu;q4_wTj-yj z1%W4GW&K4T=uEGb+E!>W0SD_C0RR91 literal 0 HcmV?d00001 diff --git a/example/web/icons/Icon-512.png b/example/web/icons/Icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..88cfd48dff1169879ba46840804b412fe02fefd6 GIT binary patch literal 8252 zcmd5=2T+s!lYZ%-(h(2@5fr2dC?F^$C=i-}R6$UX8af(!je;W5yC_|HmujSgN*6?W z3knF*TL1$|?oD*=zPbBVex*RUIKsL<(&Rj9%^UD2IK3W?2j>D?eWQgvS-HLymHo9%~|N2Q{~j za?*X-{b9JRowv_*Mh|;*-kPFn>PI;r<#kFaxFqbn?aq|PduQg=2Q;~Qc}#z)_T%x9 zE|0!a70`58wjREmAH38H1)#gof)U3g9FZ^ zF7&-0^Hy{4XHWLoC*hOG(dg~2g6&?-wqcpf{ z&3=o8vw7lMi22jCG9RQbv8H}`+}9^zSk`nlR8?Z&G2dlDy$4#+WOlg;VHqzuE=fM@ z?OI6HEJH4&tA?FVG}9>jAnq_^tlw8NbjNhfqk2rQr?h(F&WiKy03Sn=-;ZJRh~JrD zbt)zLbnabttEZ>zUiu`N*u4sfQaLE8-WDn@tHp50uD(^r-}UsUUu)`!Rl1PozAc!a z?uj|2QDQ%oV-jxUJmJycySBINSKdX{kDYRS=+`HgR2GO19fg&lZKyBFbbXhQV~v~L za^U944F1_GtuFXtvDdDNDvp<`fqy);>Vw=ncy!NB85Tw{&sT5&Ox%-p%8fTS;OzlRBwErvO+ROe?{%q-Zge=%Up|D4L#>4K@Ke=x%?*^_^P*KD zgXueMiS63!sEw@fNLB-i^F|@Oib+S4bcy{eu&e}Xvb^(mA!=U=Xr3||IpV~3K zQWzEsUeX_qBe6fky#M zzOJm5b+l;~>=sdp%i}}0h zO?B?i*W;Ndn02Y0GUUPxERG`3Bjtj!NroLoYtyVdLtl?SE*CYpf4|_${ku2s`*_)k zN=a}V8_2R5QANlxsq!1BkT6$4>9=-Ix4As@FSS;1q^#TXPrBsw>hJ}$jZ{kUHoP+H zvoYiR39gX}2OHIBYCa~6ERRPJ#V}RIIZakUmuIoLF*{sO8rAUEB9|+A#C|@kw5>u0 zBd=F!4I)Be8ycH*)X1-VPiZ+Ts8_GB;YW&ZFFUo|Sw|x~ZajLsp+_3gv((Q#N>?Jz zFBf`~p_#^${zhPIIJY~yo!7$-xi2LK%3&RkFg}Ax)3+dFCjGgKv^1;lUzQlPo^E{K zmCnrwJ)NuSaJEmueEPO@(_6h3f5mFffhkU9r8A8(JC5eOkux{gPmx_$Uv&|hyj)gN zd>JP8l2U&81@1Hc>#*su2xd{)T`Yw< zN$dSLUN}dfx)Fu`NcY}TuZ)SdviT{JHaiYgP4~@`x{&h*Hd>c3K_To9BnQi@;tuoL z%PYQo&{|IsM)_>BrF1oB~+`2_uZQ48z9!)mtUR zdfKE+b*w8cPu;F6RYJiYyV;PRBbThqHBEu_(U{(gGtjM}Zi$pL8Whx}<JwE3RM0F8x7%!!s)UJVq|TVd#hf1zVLya$;mYp(^oZQ2>=ZXU1c$}f zm|7kfk>=4KoQoQ!2&SOW5|JP1)%#55C$M(u4%SP~tHa&M+=;YsW=v(Old9L3(j)`u z2?#fK&1vtS?G6aOt@E`gZ9*qCmyvc>Ma@Q8^I4y~f3gs7*d=ATlP>1S zyF=k&6p2;7dn^8?+!wZO5r~B+;@KXFEn^&C=6ma1J7Au6y29iMIxd7#iW%=iUzq&C=$aPLa^Q zncia$@TIy6UT@69=nbty5epP>*fVW@5qbUcb2~Gg75dNd{COFLdiz3}kODn^U*=@E z0*$7u7Rl2u)=%fk4m8EK1ctR!6%Ve`e!O20L$0LkM#f+)n9h^dn{n`T*^~d+l*Qlx z$;JC0P9+en2Wlxjwq#z^a6pdnD6fJM!GV7_%8%c)kc5LZs_G^qvw)&J#6WSp< zmsd~1-(GrgjC56Pdf6#!dt^y8Rg}!#UXf)W%~PeU+kU`FeSZHk)%sFv++#Dujk-~m zFHvVJC}UBn2jN& zs!@nZ?e(iyZPNo`p1i#~wsv9l@#Z|ag3JR>0#u1iW9M1RK1iF6-RbJ4KYg?B`dET9 zyR~DjZ>%_vWYm*Z9_+^~hJ_|SNTzBKx=U0l9 z9x(J96b{`R)UVQ$I`wTJ@$_}`)_DyUNOso6=WOmQKI1e`oyYy1C&%AQU<0-`(ow)1 zT}gYdwWdm4wW6|K)LcfMe&psE0XGhMy&xS`@vLi|1#Za{D6l@#D!?nW87wcscUZgELT{Cz**^;Zb~7 z(~WFRO`~!WvyZAW-8v!6n&j*PLm9NlN}BuUN}@E^TX*4Or#dMMF?V9KBeLSiLO4?B zcE3WNIa-H{ThrlCoN=XjOGk1dT=xwwrmt<1a)mrRzg{35`@C!T?&_;Q4Ce=5=>z^*zE_c(0*vWo2_#TD<2)pLXV$FlwP}Ik74IdDQU@yhkCr5h zn5aa>B7PWy5NQ!vf7@p_qtC*{dZ8zLS;JetPkHi>IvPjtJ#ThGQD|Lq#@vE2xdl%`x4A8xOln}BiQ92Po zW;0%A?I5CQ_O`@Ad=`2BLPPbBuPUp@Hb%a_OOI}y{Rwa<#h z5^6M}s7VzE)2&I*33pA>e71d78QpF>sNK;?lj^Kl#wU7G++`N_oL4QPd-iPqBhhs| z(uVM}$ItF-onXuuXO}o$t)emBO3Hjfyil@*+GF;9j?`&67GBM;TGkLHi>@)rkS4Nj zAEk;u)`jc4C$qN6WV2dVd#q}2X6nKt&X*}I@jP%Srs%%DS92lpDY^K*Sx4`l;aql$ zt*-V{U&$DM>pdO?%jt$t=vg5|p+Rw?SPaLW zB6nvZ69$ne4Z(s$3=Rf&RX8L9PWMV*S0@R zuIk&ba#s6sxVZ51^4Kon46X^9`?DC9mEhWB3f+o4#2EXFqy0(UTc>GU| zGCJmI|Dn-dX#7|_6(fT)>&YQ0H&&JX3cTvAq(a@ydM4>5Njnuere{J8p;3?1az60* z$1E7Yyxt^ytULeokgDnRVKQw9vzHg1>X@@jM$n$HBlveIrKP5-GJq%iWH#odVwV6cF^kKX(@#%%uQVb>#T6L^mC@)%SMd4DF? zVky!~ge27>cpUP1Vi}Z32lbLV+CQy+T5Wdmva6Fg^lKb!zrg|HPU=5Qu}k;4GVH+x z%;&pN1LOce0w@9i1Mo-Y|7|z}fbch@BPp2{&R-5{GLoeu8@limQmFF zaJRR|^;kW_nw~0V^ zfTnR!Ni*;-%oSHG1yItARs~uxra|O?YJxBzLjpeE-=~TO3Dn`JL5Gz;F~O1u3|FE- zvK2Vve`ylc`a}G`gpHg58Cqc9fMoy1L}7x7T>%~b&irrNMo?np3`q;d3d;zTK>nrK zOjPS{@&74-fA7j)8uT9~*g23uGnxwIVj9HorzUX#s0pcp2?GH6i}~+kv9fWChtPa_ z@T3m+$0pbjdQw7jcnHn;Pi85hk_u2-1^}c)LNvjdam8K-XJ+KgKQ%!?2n_!#{$H|| zLO=%;hRo6EDmnOBKCL9Cg~ETU##@u^W_5joZ%Et%X_n##%JDOcsO=0VL|Lkk!VdRJ z^|~2pB@PUspT?NOeO?=0Vb+fAGc!j%Ufn-cB`s2A~W{Zj{`wqWq_-w0wr@6VrM zbzni@8c>WS!7c&|ZR$cQ;`niRw{4kG#e z70e!uX8VmP23SuJ*)#(&R=;SxGAvq|&>geL&!5Z7@0Z(No*W561n#u$Uc`f9pD70# z=sKOSK|bF~#khTTn)B28h^a1{;>EaRnHj~>i=Fnr3+Fa4 z`^+O5_itS#7kPd20rq66_wH`%?HNzWk@XFK0n;Z@Cx{kx==2L22zWH$Yg?7 zvDj|u{{+NR3JvUH({;b*$b(U5U z7(lF!1bz2%06+|-v(D?2KgwNw7( zJB#Tz+ZRi&U$i?f34m7>uTzO#+E5cbaiQ&L}UxyOQq~afbNB4EI{E04ZWg53w0A{O%qo=lF8d zf~ktGvIgf-a~zQoWf>loF7pOodrd0a2|BzwwPDV}ShauTK8*fmF6NRbO>Iw9zZU}u zw8Ya}?seBnEGQDmH#XpUUkj}N49tP<2jYwTFp!P+&Fd(%Z#yo80|5@zN(D{_pNow*&4%ql zW~&yp@scb-+Qj-EmErY+Tu=dUmf@*BoXY2&oKT8U?8?s1d}4a`Aq>7SV800m$FE~? zjmz(LY+Xx9sDX$;vU`xgw*jLw7dWOnWWCO8o|;}f>cu0Q&`0I{YudMn;P;L3R-uz# zfns_mZED_IakFBPP2r_S8XM$X)@O-xVKi4`7373Jkd5{2$M#%cRhWer3M(vr{S6>h zj{givZJ3(`yFL@``(afn&~iNx@B1|-qfYiZu?-_&Z8+R~v`d6R-}EX9IVXWO-!hL5 z*k6T#^2zAXdardU3Ao~I)4DGdAv2bx{4nOK`20rJo>rmk3S2ZDu}))8Z1m}CKigf0 z3L`3Y`{huj`xj9@`$xTZzZc3je?n^yG<8sw$`Y%}9mUsjUR%T!?k^(q)6FH6Af^b6 zlPg~IEwg0y;`t9y;#D+uz!oE4VP&Je!<#q*F?m5L5?J3i@!0J6q#eu z!RRU`-)HeqGi_UJZ(n~|PSNsv+Wgl{P-TvaUQ9j?ZCtvb^37U$sFpBrkT{7Jpd?HpIvj2!}RIq zH{9~+gErN2+}J`>Jvng2hwM`=PLNkc7pkjblKW|+Fk9rc)G1R>Ww>RC=r-|!m-u7( zc(a$9NG}w#PjWNMS~)o=i~WA&4L(YIW25@AL9+H9!?3Y}sv#MOdY{bb9j>p`{?O(P zIvb`n?_(gP2w3P#&91JX*md+bBEr%xUHMVqfB;(f?OPtMnAZ#rm5q5mh;a2f_si2_ z3oXWB?{NF(JtkAn6F(O{z@b76OIqMC$&oJ_&S|YbFJ*)3qVX_uNf5b8(!vGX19hsG z(OP>RmZp29KH9Ge2kKjKigUmOe^K_!UXP`von)PR8Qz$%=EmOB9xS(ZxE_tnyzo}7 z=6~$~9k0M~v}`w={AeqF?_)9q{m8K#6M{a&(;u;O41j)I$^T?lx5(zlebpY@NT&#N zR+1bB)-1-xj}R8uwqwf=iP1GbxBjneCC%UrSdSxK1vM^i9;bUkS#iRZw2H>rS<2<$ zNT3|sDH>{tXb=zq7XZi*K?#Zsa1h1{h5!Tq_YbKFm_*=A5-<~j63he;4`77!|LBlo zR^~tR3yxcU=gDFbshyF6>o0bdp$qmHS7D}m3;^QZq9kBBU|9$N-~oU?G5;jyFR7>z hN`IR97YZXIo@y!QgFWddJ3|0`sjFx!m))><{BI=FK%f8s literal 0 HcmV?d00001 diff --git a/example/web/icons/Icon-maskable-192.png b/example/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000000000000000000000000000000000000..eb9b4d76e525556d5d89141648c724331630325d GIT binary patch literal 5594 zcmdT|`#%%j|KDb2V@0DPm$^(Lx5}lO%Yv(=e*7hl@QqKS50#~#^IQPxBmuh|i9sXnt4ch@VT0F7% zMtrs@KWIOo+QV@lSs66A>2pz6-`9Jk=0vv&u?)^F@HZ)-6HT=B7LF;rdj zskUyBfbojcX#CS>WrIWo9D=DIwcXM8=I5D{SGf$~=gh-$LwY?*)cD%38%sCc?5OsX z-XfkyL-1`VavZ?>(pI-xp-kYq=1hsnyP^TLb%0vKRSo^~r{x?ISLY1i7KjSp z*0h&jG(Rkkq2+G_6eS>n&6>&Xk+ngOMcYrk<8KrukQHzfx675^^s$~<@d$9X{VBbg z2Fd4Z%g`!-P}d#`?B4#S-9x*eNlOVRnDrn#jY@~$jfQ-~3Od;A;x-BI1BEDdvr`pI z#D)d)!2_`GiZOUu1crb!hqH=ezs0qk<_xDm_Kkw?r*?0C3|Io6>$!kyDl;eH=aqg$B zsH_|ZD?jP2dc=)|L>DZmGyYKa06~5?C2Lc0#D%62p(YS;%_DRCB1k(+eLGXVMe+=4 zkKiJ%!N6^mxqM=wq`0+yoE#VHF%R<{mMamR9o_1JH8jfnJ?NPLs$9U!9!dq8 z0B{dI2!M|sYGH&9TAY34OlpIsQ4i5bnbG>?cWwat1I13|r|_inLE?FS@Hxdxn_YZN z3jfUO*X9Q@?HZ>Q{W0z60!bbGh557XIKu1?)u|cf%go`pwo}CD=0tau-}t@R2OrSH zQzZr%JfYa`>2!g??76=GJ$%ECbQh7Q2wLRp9QoyiRHP7VE^>JHm>9EqR3<$Y=Z1K^SHuwxCy-5@z3 zVM{XNNm}yM*pRdLKp??+_2&!bp#`=(Lh1vR{~j%n;cJv~9lXeMv)@}Odta)RnK|6* zC+IVSWumLo%{6bLDpn)Gz>6r&;Qs0^+Sz_yx_KNz9Dlt^ax`4>;EWrIT#(lJ_40<= z750fHZ7hI{}%%5`;lwkI4<_FJw@!U^vW;igL0k+mK)-j zYuCK#mCDK3F|SC}tC2>m$ZCqNB7ac-0UFBJ|8RxmG@4a4qdjvMzzS&h9pQmu^x&*= zGvapd1#K%Da&)8f?<9WN`2H^qpd@{7In6DNM&916TRqtF4;3`R|Nhwbw=(4|^Io@T zIjoR?tB8d*sO>PX4vaIHF|W;WVl6L1JvSmStgnRQq zTX4(>1f^5QOAH{=18Q2Vc1JI{V=yOr7yZJf4Vpfo zeHXdhBe{PyY;)yF;=ycMW@Kb>t;yE>;f79~AlJ8k`xWucCxJfsXf2P72bAavWL1G#W z;o%kdH(mYCM{$~yw4({KatNGim49O2HY6O07$B`*K7}MvgI=4x=SKdKVb8C$eJseA$tmSFOztFd*3W`J`yIB_~}k%Sd_bPBK8LxH)?8#jM{^%J_0|L z!gFI|68)G}ex5`Xh{5pB%GtlJ{Z5em*e0sH+sU1UVl7<5%Bq+YrHWL7?X?3LBi1R@_)F-_OqI1Zv`L zb6^Lq#H^2@d_(Z4E6xA9Z4o3kvf78ZDz!5W1#Mp|E;rvJz&4qj2pXVxKB8Vg0}ek%4erou@QM&2t7Cn5GwYqy%{>jI z)4;3SAgqVi#b{kqX#$Mt6L8NhZYgonb7>+r#BHje)bvaZ2c0nAvrN3gez+dNXaV;A zmyR0z@9h4@6~rJik-=2M-T+d`t&@YWhsoP_XP-NsVO}wmo!nR~QVWU?nVlQjNfgcTzE-PkfIX5G z1?&MwaeuzhF=u)X%Vpg_e@>d2yZwxl6-r3OMqDn8_6m^4z3zG##cK0Fsgq8fcvmhu z{73jseR%X%$85H^jRAcrhd&k!i^xL9FrS7qw2$&gwAS8AfAk#g_E_tP;x66fS`Mn@SNVrcn_N;EQm z`Mt3Z%rw%hDqTH-s~6SrIL$hIPKL5^7ejkLTBr46;pHTQDdoErS(B>``t;+1+M zvU&Se9@T_BeK;A^p|n^krIR+6rH~BjvRIugf`&EuX9u69`9C?9ANVL8l(rY6#mu^i z=*5Q)-%o*tWl`#b8p*ZH0I}hn#gV%|jt6V_JanDGuekR*-wF`u;amTCpGG|1;4A5$ zYbHF{?G1vv5;8Ph5%kEW)t|am2_4ik!`7q{ymfHoe^Z99c|$;FAL+NbxE-_zheYbV z3hb0`uZGTsgA5TG(X|GVDSJyJxsyR7V5PS_WSnYgwc_D60m7u*x4b2D79r5UgtL18 zcCHWk+K6N1Pg2c;0#r-)XpwGX?|Iv)^CLWqwF=a}fXUSM?n6E;cCeW5ER^om#{)Jr zJR81pkK?VoFm@N-s%hd7@hBS0xuCD0-UDVLDDkl7Ck=BAj*^ps`393}AJ+Ruq@fl9 z%R(&?5Nc3lnEKGaYMLmRzKXow1+Gh|O-LG7XiNxkG^uyv zpAtLINwMK}IWK65hOw&O>~EJ}x@lDBtB`yKeV1%GtY4PzT%@~wa1VgZn7QRwc7C)_ zpEF~upeDRg_<#w=dLQ)E?AzXUQpbKXYxkp>;c@aOr6A|dHA?KaZkL0svwB^U#zmx0 zzW4^&G!w7YeRxt<9;d@8H=u(j{6+Uj5AuTluvZZD4b+#+6Rp?(yJ`BC9EW9!b&KdPvzJYe5l7 zMJ9aC@S;sA0{F0XyVY{}FzW0Vh)0mPf_BX82E+CD&)wf2!x@{RO~XBYu80TONl3e+ zA7W$ra6LcDW_j4s-`3tI^VhG*sa5lLc+V6ONf=hO@q4|p`CinYqk1Ko*MbZ6_M05k zSwSwkvu;`|I*_Vl=zPd|dVD0lh&Ha)CSJJvV{AEdF{^Kn_Yfsd!{Pc1GNgw}(^~%)jk5~0L~ms|Rez1fiK~s5t(p1ci5Gq$JC#^JrXf?8 z-Y-Zi_Hvi>oBzV8DSRG!7dm|%IlZg3^0{5~;>)8-+Nk&EhAd(}s^7%MuU}lphNW9Q zT)DPo(ob{tB7_?u;4-qGDo!sh&7gHaJfkh43QwL|bbFVi@+oy;i;M zM&CP^v~lx1U`pi9PmSr&Mc<%HAq0DGH?Ft95)WY`P?~7O z`O^Nr{Py9M#Ls4Y7OM?e%Y*Mvrme%=DwQaye^Qut_1pOMrg^!5u(f9p(D%MR%1K>% zRGw%=dYvw@)o}Fw@tOtPjz`45mfpn;OT&V(;z75J*<$52{sB65$gDjwX3Xa!x_wE- z!#RpwHM#WrO*|~f7z}(}o7US(+0FYLM}6de>gQdtPazXz?OcNv4R^oYLJ_BQOd_l172oSK$6!1r@g+B@0ofJ4*{>_AIxfe-#xp>(1 z@Y3Nfd>fmqvjL;?+DmZk*KsfXJf<%~(gcLwEez%>1c6XSboURUh&k=B)MS>6kw9bY z{7vdev7;A}5fy*ZE23DS{J?8at~xwVk`pEwP5^k?XMQ7u64;KmFJ#POzdG#np~F&H ze-BUh@g54)dsS%nkBb}+GuUEKU~pHcYIg4vSo$J(J|U36bs0Use+3A&IMcR%6@jv$ z=+QI+@wW@?iu}Hpyzlvj-EYeop{f65GX0O%>w#0t|V z1-svWk`hU~m`|O$kw5?Yn5UhI%9P-<45A(v0ld1n+%Ziq&TVpBcV9n}L9Tus-TI)f zd_(g+nYCDR@+wYNQm1GwxhUN4tGMLCzDzPqY$~`l<47{+l<{FZ$L6(>J)|}!bi<)| zE35dl{a2)&leQ@LlDxLQOfUDS`;+ZQ4ozrleQwaR-K|@9T{#hB5Z^t#8 zC-d_G;B4;F#8A2EBL58s$zF-=SCr`P#z zNCTnHF&|X@q>SkAoYu>&s9v@zCpv9lLSH-UZzfhJh`EZA{X#%nqw@@aW^vPcfQrlPs(qQxmC|4tp^&sHy!H!2FH5eC{M@g;ElWNzlb-+ zxpfc0m4<}L){4|RZ>KReag2j%Ot_UKkgpJN!7Y_y3;Ssz{9 z!K3isRtaFtQII5^6}cm9RZd5nTp9psk&u1C(BY`(_tolBwzV_@0F*m%3G%Y?2utyS zY`xM0iDRT)yTyYukFeGQ&W@ReM+ADG1xu@ruq&^GK35`+2r}b^V!m1(VgH|QhIPDE X>c!)3PgKfL&lX^$Z>Cpu&6)6jvi^Z! literal 0 HcmV?d00001 diff --git a/example/web/icons/Icon-maskable-512.png b/example/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000000000000000000000000000000000000..d69c56691fbdb0b7efa65097c7cc1edac12a6d3e GIT binary patch literal 20998 zcmeFZ_gj-)&^4Nb2tlbLMU<{!p(#yjqEe+=0IA_oih%ScH9@5#MNp&}Y#;;(h=A0@ zh7{>lT2MkSQ344eAvrhici!td|HJuyvJm#Y_w1Q9Yu3!26dNlO-oxUDK_C#XnW^Co z5C{VN6#{~B0)K2j7}*1Xq(Nqemv23A-6&=ZpEijkVnSwVGqLv40?n0=p;k3-U5e5+ z+z3>aS`u9DS=!wg8ROu?X4TFoW6CFLL&{GzoVT)ldhLekLM|+j3tIxRd|*5=c{=s&*vfPdBr(Fyj(v@%eQj1Soy7m4^@VRl1~@-PV7y+c!xz$8436WBn$t{=}mEdK#k`aystimGgI{(IBx$!pAwFoE9Y`^t^;> zKAD)C(Dl^s%`?q5$P|fZf8Xymrtu^Pv(7D`rn>Z-w$Ahs!z9!94WNVxrJuXfHAaxg zC6s@|Z1$7R$(!#t%Jb{{s6(Y?NoQXDYq)!}X@jKPhe`{9KQ@sAU8y-5`xt?S9$jKH zoi}6m5PcG*^{kjvt+kwPpyQzVg4o)a>;LK`aaN2x4@itBD3Aq?yWTM20VRn1rrd+2 zKO=P0rMjEGq_UqpMa`~7B|p?xAN1SCoCp}QxAv8O`jLJ5CVh@umR%c%i^)6!o+~`F zaalSTQcl5iwOLC&H)efzd{8(88mo`GI(56T<(&p7>Qd^;R1hn1Y~jN~tApaL8>##U zd65bo8)79CplWxr#z4!6HvLz&N7_5AN#x;kLG?zQ(#p|lj<8VUlKY=Aw!ATqeL-VG z42gA!^cMNPj>(`ZMEbCrnkg*QTsn*u(nQPWI9pA{MQ=IsPTzd7q5E#7+z>Ch=fx$~ z;J|?(5jTo5UWGvsJa(Sx0?S#56+8SD!I^tftyeh_{5_31l6&Hywtn`bbqYDqGZXI( zCG7hBgvksX2ak8+)hB4jnxlO@A32C_RM&g&qDSb~3kM&)@A_j1*oTO@nicGUyv+%^ z=vB)4(q!ykzT==Z)3*3{atJ5}2PV*?Uw+HhN&+RvKvZL3p9E?gHjv{6zM!A|z|UHK z-r6jeLxbGn0D@q5aBzlco|nG2tr}N@m;CJX(4#Cn&p&sLKwzLFx1A5izu?X_X4x8r@K*d~7>t1~ zDW1Mv5O&WOxbzFC`DQ6yNJ(^u9vJdj$fl2dq`!Yba_0^vQHXV)vqv1gssZYzBct!j zHr9>ydtM8wIs}HI4=E}qAkv|BPWzh3^_yLH(|kdb?x56^BlDC)diWyPd*|f!`^12_U>TD^^94OCN0lVv~Sgvs94ecpE^}VY$w`qr_>Ue zTfH~;C<3H<0dS5Rkf_f@1x$Gms}gK#&k()IC0zb^QbR!YLoll)c$Agfi6MKI0dP_L z=Uou&u~~^2onea2%XZ@>`0x^L8CK6=I{ge;|HXMj)-@o~h&O{CuuwBX8pVqjJ*o}5 z#8&oF_p=uSo~8vn?R0!AMWvcbZmsrj{ZswRt(aEdbi~;HeVqIe)-6*1L%5u$Gbs}| zjFh?KL&U(rC2izSGtwP5FnsR@6$-1toz?RvLD^k~h9NfZgzHE7m!!7s6(;)RKo2z} zB$Ci@h({l?arO+vF;s35h=|WpefaOtKVx>l399}EsX@Oe3>>4MPy%h&^3N_`UTAHJ zI$u(|TYC~E4)|JwkWW3F!Tib=NzjHs5ii2uj0^m|Qlh-2VnB#+X~RZ|`SA*}}&8j9IDv?F;(Y^1=Z0?wWz;ikB zewU>MAXDi~O7a~?jx1x=&8GcR-fTp>{2Q`7#BE#N6D@FCp`?ht-<1|y(NArxE_WIu zP+GuG=Qq>SHWtS2M>34xwEw^uvo4|9)4s|Ac=ud?nHQ>ax@LvBqusFcjH0}{T3ZPQ zLO1l<@B_d-(IS682}5KA&qT1+{3jxKolW+1zL4inqBS-D>BohA!K5++41tM@ z@xe<-qz27}LnV#5lk&iC40M||JRmZ*A##K3+!j93eouU8@q-`W0r%7N`V$cR&JV;iX(@cS{#*5Q>~4BEDA)EikLSP@>Oo&Bt1Z~&0d5)COI%3$cLB_M?dK# z{yv2OqW!al-#AEs&QFd;WL5zCcp)JmCKJEdNsJlL9K@MnPegK23?G|O%v`@N{rIRa zi^7a}WBCD77@VQ-z_v{ZdRsWYrYgC$<^gRQwMCi6);%R~uIi31OMS}=gUTE(GKmCI z$zM>mytL{uNN+a&S38^ez(UT=iSw=l2f+a4)DyCA1Cs_N-r?Q@$3KTYosY!;pzQ0k zzh1G|kWCJjc(oZVBji@kN%)UBw(s{KaYGy=i{g3{)Z+&H8t2`^IuLLKWT6lL<-C(! zSF9K4xd-|VO;4}$s?Z7J_dYqD#Mt)WCDnsR{Kpjq275uUq6`v0y*!PHyS(}Zmv)_{>Vose9-$h8P0|y;YG)Bo}$(3Z%+Gs0RBmFiW!^5tBmDK-g zfe5%B*27ib+7|A*Fx5e)2%kIxh7xWoc3pZcXS2zik!63lAG1;sC1ja>BqH7D zODdi5lKW$$AFvxgC-l-)!c+9@YMC7a`w?G(P#MeEQ5xID#<}W$3bSmJ`8V*x2^3qz zVe<^^_8GHqYGF$nIQm0Xq2kAgYtm#UC1A(=&85w;rmg#v906 zT;RyMgbMpYOmS&S9c38^40oUp?!}#_84`aEVw;T;r%gTZkWeU;;FwM@0y0adt{-OK z(vGnPSlR=Nv2OUN!2=xazlnHPM9EWxXg2EKf0kI{iQb#FoP>xCB<)QY>OAM$Dcdbm zU6dU|%Mo(~avBYSjRc13@|s>axhrPl@Sr81{RSZUdz4(=|82XEbV*JAX6Lfbgqgz584lYgi0 z2-E{0XCVON$wHfvaLs;=dqhQJ&6aLn$D#0i(FkAVrXG9LGm3pSTf&f~RQb6|1_;W> z?n-;&hrq*~L=(;u#jS`*Yvh@3hU-33y_Kv1nxqrsf>pHVF&|OKkoC)4DWK%I!yq?P z=vXo8*_1iEWo8xCa{HJ4tzxOmqS0&$q+>LroMKI*V-rxhOc%3Y!)Y|N6p4PLE>Yek>Y(^KRECg8<|%g*nQib_Yc#A5q8Io z6Ig&V>k|~>B6KE%h4reAo*DfOH)_01tE0nWOxX0*YTJgyw7moaI^7gW*WBAeiLbD?FV9GSB zPv3`SX*^GRBM;zledO`!EbdBO_J@fEy)B{-XUTVQv}Qf~PSDpK9+@I`7G7|>Dgbbu z_7sX9%spVo$%qwRwgzq7!_N;#Td08m5HV#?^dF-EV1o)Q=Oa+rs2xH#g;ykLbwtCh znUnA^dW!XjspJ;otq$yV@I^s9Up(5k7rqhQd@OLMyyxVLj_+$#Vc*}Usevp^I(^vH zmDgHc0VMme|K&X?9&lkN{yq_(If)O`oUPW8X}1R5pSVBpfJe0t{sPA(F#`eONTh_) zxeLqHMfJX#?P(@6w4CqRE@Eiza; z;^5)Kk=^5)KDvd9Q<`=sJU8rjjxPmtWMTmzcH={o$U)j=QBuHarp?=}c??!`3d=H$nrJMyr3L-& zA#m?t(NqLM?I3mGgWA_C+0}BWy3-Gj7bR+d+U?n*mN$%5P`ugrB{PeV>jDUn;eVc- zzeMB1mI4?fVJatrNyq|+zn=!AiN~<}eoM#4uSx^K?Iw>P2*r=k`$<3kT00BE_1c(02MRz4(Hq`L^M&xt!pV2 zn+#U3@j~PUR>xIy+P>51iPayk-mqIK_5rlQMSe5&tDkKJk_$i(X&;K(11YGpEc-K= zq4Ln%^j>Zi_+Ae9eYEq_<`D+ddb8_aY!N;)(&EHFAk@Ekg&41ABmOXfWTo)Z&KotA zh*jgDGFYQ^y=m)<_LCWB+v48DTJw*5dwMm_YP0*_{@HANValf?kV-Ic3xsC}#x2h8 z`q5}d8IRmqWk%gR)s~M}(Qas5+`np^jW^oEd-pzERRPMXj$kS17g?H#4^trtKtq;C?;c ztd|%|WP2w2Nzg@)^V}!Gv++QF2!@FP9~DFVISRW6S?eP{H;;8EH;{>X_}NGj^0cg@ z!2@A>-CTcoN02^r6@c~^QUa={0xwK0v4i-tQ9wQq^=q*-{;zJ{Qe%7Qd!&X2>rV@4 z&wznCz*63_vw4>ZF8~%QCM?=vfzW0r_4O^>UA@otm_!N%mH)!ERy&b!n3*E*@?9d^ zu}s^By@FAhG(%?xgJMuMzuJw2&@$-oK>n z=UF}rt%vuaP9fzIFCYN-1&b#r^Cl6RDFIWsEsM|ROf`E?O(cy{BPO2Ie~kT+^kI^i zp>Kbc@C?}3vy-$ZFVX#-cx)Xj&G^ibX{pWggtr(%^?HeQL@Z( zM-430g<{>vT*)jK4aY9(a{lSy{8vxLbP~n1MXwM527ne#SHCC^F_2@o`>c>>KCq9c(4c$VSyMl*y3Nq1s+!DF| z^?d9PipQN(mw^j~{wJ^VOXDCaL$UtwwTpyv8IAwGOg<|NSghkAR1GSNLZ1JwdGJYm zP}t<=5=sNNUEjc=g(y)1n5)ynX(_$1-uGuDR*6Y^Wgg(LT)Jp><5X|}bt z_qMa&QP?l_n+iVS>v%s2Li_;AIeC=Ca^v1jX4*gvB$?H?2%ndnqOaK5-J%7a} zIF{qYa&NfVY}(fmS0OmXA70{znljBOiv5Yod!vFU{D~*3B3Ka{P8?^ zfhlF6o7aNT$qi8(w<}OPw5fqA7HUje*r*Oa(YV%*l0|9FP9KW@U&{VSW{&b0?@y)M zs%4k1Ax;TGYuZ9l;vP5@?3oQsp3)rjBeBvQQ>^B;z5pc=(yHhHtq6|0m(h4envn_j787fizY@V`o(!SSyE7vlMT zbo=Z1c=atz*G!kwzGB;*uPL$Ei|EbZLh8o+1BUMOpnU(uX&OG1MV@|!&HOOeU#t^x zr9=w2ow!SsTuJWT7%Wmt14U_M*3XiWBWHxqCVZI0_g0`}*^&yEG9RK9fHK8e+S^m? zfCNn$JTswUVbiC#>|=wS{t>-MI1aYPLtzO5y|LJ9nm>L6*wpr_m!)A2Fb1RceX&*|5|MwrvOk4+!0p99B9AgP*9D{Yt|x=X}O% zgIG$MrTB=n-!q%ROT|SzH#A$Xm;|ym)0>1KR}Yl0hr-KO&qMrV+0Ej3d@?FcgZ+B3 ztEk16g#2)@x=(ko8k7^Tq$*5pfZHC@O@}`SmzT1(V@x&NkZNM2F#Q-Go7-uf_zKC( zB(lHZ=3@dHaCOf6C!6i8rDL%~XM@rVTJbZL09?ht@r^Z_6x}}atLjvH^4Vk#Ibf(^LiBJFqorm?A=lE zzFmwvp4bT@Nv2V>YQT92X;t9<2s|Ru5#w?wCvlhcHLcsq0TaFLKy(?nzezJ>CECqj zggrI~Hd4LudM(m{L@ezfnpELsRFVFw>fx;CqZtie`$BXRn#Ns%AdoE$-Pf~{9A8rV zf7FbgpKmVzmvn-z(g+&+-ID=v`;6=)itq8oM*+Uz**SMm_{%eP_c0{<%1JGiZS19o z@Gj7$Se~0lsu}w!%;L%~mIAO;AY-2i`9A*ZfFs=X!LTd6nWOZ7BZH2M{l2*I>Xu)0 z`<=;ObglnXcVk!T>e$H?El}ra0WmPZ$YAN0#$?|1v26^(quQre8;k20*dpd4N{i=b zuN=y}_ew9SlE~R{2+Rh^7%PA1H5X(p8%0TpJ=cqa$65XL)$#ign-y!qij3;2>j}I; ziO@O|aYfn&up5F`YtjGw68rD3{OSGNYmBnl?zdwY$=RFsegTZ=kkzRQ`r7ZjQP!H( zp4>)&zf<*N!tI00xzm-ME_a{_I!TbDCr;8E;kCH4LlL-tqLxDuBn-+xgPk37S&S2^ z2QZumkIimwz!c@!r0)j3*(jPIs*V!iLTRl0Cpt_UVNUgGZzdvs0(-yUghJfKr7;=h zD~y?OJ-bWJg;VdZ^r@vlDoeGV&8^--!t1AsIMZ5S440HCVr%uk- z2wV>!W1WCvFB~p$P$$_}|H5>uBeAe>`N1FI8AxM|pq%oNs;ED8x+tb44E) zTj{^fbh@eLi%5AqT?;d>Es5D*Fi{Bpk)q$^iF!!U`r2hHAO_?#!aYmf>G+jHsES4W zgpTKY59d?hsb~F0WE&dUp6lPt;Pm zcbTUqRryw^%{ViNW%Z(o8}dd00H(H-MmQmOiTq{}_rnwOr*Ybo7*}3W-qBT!#s0Ie z-s<1rvvJx_W;ViUD`04%1pra*Yw0BcGe)fDKUK8aF#BwBwMPU;9`!6E(~!043?SZx z13K%z@$$#2%2ovVlgFIPp7Q6(vO)ud)=*%ZSucL2Dh~K4B|%q4KnSpj#n@(0B})!9 z8p*hY@5)NDn^&Pmo;|!>erSYg`LkO?0FB@PLqRvc>4IsUM5O&>rRv|IBRxi(RX(gJ ztQ2;??L~&Mv;aVr5Q@(?y^DGo%pO^~zijld41aA0KKsy_6FeHIn?fNHP-z>$OoWer zjZ5hFQTy*-f7KENRiCE$ZOp4|+Wah|2=n@|W=o}bFM}Y@0e62+_|#fND5cwa3;P{^pEzlJbF1Yq^}>=wy8^^^$I2M_MH(4Dw{F6hm+vrWV5!q;oX z;tTNhz5`-V={ew|bD$?qcF^WPR{L(E%~XG8eJx(DoGzt2G{l8r!QPJ>kpHeOvCv#w zr=SSwMDaUX^*~v%6K%O~i)<^6`{go>a3IdfZ8hFmz&;Y@P%ZygShQZ2DSHd`m5AR= zx$wWU06;GYwXOf(%MFyj{8rPFXD};JCe85Bdp4$YJ2$TzZ7Gr#+SwCvBI1o$QP0(c zy`P51FEBV2HTisM3bHqpmECT@H!Y2-bv2*SoSPoO?wLe{M#zDTy@ujAZ!Izzky~3k zRA1RQIIoC*Mej1PH!sUgtkR0VCNMX(_!b65mo66iM*KQ7xT8t2eev$v#&YdUXKwGm z7okYAqYF&bveHeu6M5p9xheRCTiU8PFeb1_Rht0VVSbm%|1cOVobc8mvqcw!RjrMRM#~=7xibH&Fa5Imc|lZ{eC|R__)OrFg4@X_ ze+kk*_sDNG5^ELmHnZ7Ue?)#6!O)#Nv*Dl2mr#2)w{#i-;}0*_h4A%HidnmclH#;Q zmQbq+P4DS%3}PpPm7K_K3d2s#k~x+PlTul7+kIKol0@`YN1NG=+&PYTS->AdzPv!> zQvzT=)9se*Jr1Yq+C{wbK82gAX`NkbXFZ)4==j4t51{|-v!!$H8@WKA={d>CWRW+g z*`L>9rRucS`vbXu0rzA1#AQ(W?6)}1+oJSF=80Kf_2r~Qm-EJ6bbB3k`80rCv(0d` zvCf3;L2ovYG_TES%6vSuoKfIHC6w;V31!oqHM8-I8AFzcd^+_86!EcCOX|Ta9k1!s z_Vh(EGIIsI3fb&dF$9V8v(sTBC%!#<&KIGF;R+;MyC0~}$gC}}= zR`DbUVc&Bx`lYykFZ4{R{xRaUQkWCGCQlEc;!mf=+nOk$RUg*7 z;kP7CVLEc$CA7@6VFpsp3_t~m)W0aPxjsA3e5U%SfY{tp5BV5jH-5n?YX7*+U+Zs%LGR>U- z!x4Y_|4{gx?ZPJobISy991O znrmrC3otC;#4^&Rg_iK}XH(XX+eUHN0@Oe06hJk}F?`$)KmH^eWz@@N%wEc)%>?Ft z#9QAroDeyfztQ5Qe{m*#R#T%-h*&XvSEn@N$hYRTCMXS|EPwzF3IIysD2waj`vQD{ zv_#^Pgr?s~I*NE=acf@dWVRNWTr(GN0wrL)Z2=`Dr>}&ZDNX|+^Anl{Di%v1Id$_p zK5_H5`RDjJx`BW7hc85|> zHMMsWJ4KTMRHGu+vy*kBEMjz*^K8VtU=bXJYdhdZ-?jTXa$&n)C?QQIZ7ln$qbGlr zS*TYE+ppOrI@AoPP=VI-OXm}FzgXRL)OPvR$a_=SsC<3Jb+>5makX|U!}3lx4tX&L z^C<{9TggZNoeX!P1jX_K5HkEVnQ#s2&c#umzV6s2U-Q;({l+j^?hi7JnQ7&&*oOy9 z(|0asVTWUCiCnjcOnB2pN0DpuTglKq;&SFOQ3pUdye*eT<2()7WKbXp1qq9=bhMWlF-7BHT|i3TEIT77AcjD(v=I207wi-=vyiw5mxgPdTVUC z&h^FEUrXwWs9en2C{ywZp;nvS(Mb$8sBEh-*_d-OEm%~p1b2EpcwUdf<~zmJmaSTO zSX&&GGCEz-M^)G$fBvLC2q@wM$;n4jp+mt0MJFLuJ%c`tSp8$xuP|G81GEd2ci$|M z4XmH{5$j?rqDWoL4vs!}W&!?!rtj=6WKJcE>)?NVske(p;|#>vL|M_$as=mi-n-()a*OU3Okmk0wC<9y7t^D(er-&jEEak2!NnDiOQ99Wx8{S8}=Ng!e0tzj*#T)+%7;aM$ z&H}|o|J1p{IK0Q7JggAwipvHvko6>Epmh4RFRUr}$*2K4dz85o7|3#Bec9SQ4Y*;> zXWjT~f+d)dp_J`sV*!w>B%)#GI_;USp7?0810&3S=WntGZ)+tzhZ+!|=XlQ&@G@~3 z-dw@I1>9n1{+!x^Hz|xC+P#Ab`E@=vY?3%Bc!Po~e&&&)Qp85!I|U<-fCXy*wMa&t zgDk!l;gk;$taOCV$&60z+}_$ykz=Ea*)wJQ3-M|p*EK(cvtIre0Pta~(95J7zoxBN zS(yE^3?>88AL0Wfuou$BM{lR1hkrRibz=+I9ccwd`ZC*{NNqL)3pCcw^ygMmrG^Yp zn5f}Xf>%gncC=Yq96;rnfp4FQL#{!Y*->e82rHgY4Zwy{`JH}b9*qr^VA{%~Z}jtp z_t$PlS6}5{NtTqXHN?uI8ut8rOaD#F1C^ls73S=b_yI#iZDOGz3#^L@YheGd>L;<( z)U=iYj;`{>VDNzIxcjbTk-X3keXR8Xbc`A$o5# zKGSk-7YcoBYuAFFSCjGi;7b<;n-*`USs)IX z=0q6WZ=L!)PkYtZE-6)azhXV|+?IVGTOmMCHjhkBjfy@k1>?yFO3u!)@cl{fFAXnRYsWk)kpT?X{_$J=|?g@Q}+kFw|%n!;Zo}|HE@j=SFMvT8v`6Y zNO;tXN^036nOB2%=KzxB?n~NQ1K8IO*UE{;Xy;N^ZNI#P+hRZOaHATz9(=)w=QwV# z`z3+P>9b?l-@$@P3<;w@O1BdKh+H;jo#_%rr!ute{|YX4g5}n?O7Mq^01S5;+lABE+7`&_?mR_z7k|Ja#8h{!~j)| zbBX;*fsbUak_!kXU%HfJ2J+G7;inu#uRjMb|8a){=^))y236LDZ$$q3LRlat1D)%7K0!q5hT5V1j3qHc7MG9 z_)Q=yQ>rs>3%l=vu$#VVd$&IgO}Za#?aN!xY>-<3PhzS&q!N<=1Q7VJBfHjug^4|) z*fW^;%3}P7X#W3d;tUs3;`O&>;NKZBMR8au6>7?QriJ@gBaorz-+`pUWOP73DJL=M z(33uT6Gz@Sv40F6bN|H=lpcO z^AJl}&=TIjdevuDQ!w0K*6oZ2JBOhb31q!XDArFyKpz!I$p4|;c}@^bX{>AXdt7Bm zaLTk?c%h@%xq02reu~;t@$bv`b3i(P=g}~ywgSFpM;}b$zAD+=I!7`V~}ARB(Wx0C(EAq@?GuxOL9X+ffbkn3+Op0*80TqmpAq~EXmv%cq36celXmRz z%0(!oMp&2?`W)ALA&#|fu)MFp{V~~zIIixOxY^YtO5^FSox8v$#d0*{qk0Z)pNTt0QVZ^$`4vImEB>;Lo2!7K05TpY-sl#sWBz_W-aDIV`Ksabi zvpa#93Svo!70W*Ydh)Qzm{0?CU`y;T^ITg-J9nfWeZ-sbw)G@W?$Eomf%Bg2frfh5 zRm1{|E0+(4zXy){$}uC3%Y-mSA2-^I>Tw|gQx|7TDli_hB>``)Q^aZ`LJC2V3U$SABP}T)%}9g2pF9dT}aC~!rFFgkl1J$ z`^z{Arn3On-m%}r}TGF8KQe*OjSJ=T|caa_E;v89A{t@$yT^(G9=N9F?^kT*#s3qhJq!IH5|AhnqFd z0B&^gm3w;YbMNUKU>naBAO@fbz zqw=n!@--}o5;k6DvTW9pw)IJVz;X}ncbPVrmH>4x);8cx;q3UyiML1PWp%bxSiS|^ zC5!kc4qw%NSOGQ*Kcd#&$30=lDvs#*4W4q0u8E02U)7d=!W7+NouEyuF1dyH$D@G& zaFaxo9Ex|ZXA5y{eZT*i*dP~INSMAi@mvEX@q5i<&o&#sM}Df?Og8n8Ku4vOux=T% zeuw~z1hR}ZNwTn8KsQHKLwe2>p^K`YWUJEdVEl|mO21Bov!D0D$qPoOv=vJJ`)|%_ z>l%`eexY7t{BlVKP!`a^U@nM?#9OC*t76My_E_<16vCz1x_#82qj2PkWiMWgF8bM9 z(1t4VdHcJ;B~;Q%x01k_gQ0>u2*OjuEWNOGX#4}+N?Gb5;+NQMqp}Puqw2HnkYuKA zzKFWGHc&K>gwVgI1Sc9OT1s6fq=>$gZU!!xsilA$fF`kLdGoX*^t}ao@+^WBpk>`8 z4v_~gK|c2rCq#DZ+H)$3v~Hoi=)=1D==e3P zpKrRQ+>O^cyTuWJ%2}__0Z9SM_z9rptd*;-9uC1tDw4+A!=+K%8~M&+Zk#13hY$Y$ zo-8$*8dD5@}XDi19RjK6T^J~DIXbF5w&l?JLHMrf0 zLv0{7*G!==o|B%$V!a=EtVHdMwXLtmO~vl}P6;S(R2Q>*kTJK~!}gloxj)m|_LYK{ zl(f1cB=EON&wVFwK?MGn^nWuh@f95SHatPs(jcwSY#Dnl1@_gkOJ5=f`%s$ZHljRH0 z+c%lrb=Gi&N&1>^L_}#m>=U=(oT^vTA&3!xXNyqi$pdW1BDJ#^{h|2tZc{t^vag3& zAD7*8C`chNF|27itjBUo^CCDyEpJLX3&u+(L;YeeMwnXEoyN(ytoEabcl$lSgx~Ltatn}b$@j_yyMrBb03)shJE*$;Mw=;mZd&8e>IzE+4WIoH zCSZE7WthNUL$|Y#m!Hn?x7V1CK}V`KwW2D$-7&ODy5Cj;!_tTOOo1Mm%(RUt)#$@3 zhurA)t<7qik%%1Et+N1?R#hdBB#LdQ7{%-C zn$(`5e0eFh(#c*hvF>WT*07fk$N_631?W>kfjySN8^XC9diiOd#s?4tybICF;wBjp zIPzilX3{j%4u7blhq)tnaOBZ_`h_JqHXuI7SuIlNTgBk9{HIS&3|SEPfrvcE<@}E` zKk$y*nzsqZ{J{uWW9;#n=de&&h>m#A#q)#zRonr(?mDOYU&h&aQWD;?Z(22wY?t$U3qo`?{+amA$^TkxL+Ex2dh`q7iR&TPd0Ymwzo#b? zP$#t=elB5?k$#uE$K>C$YZbYUX_JgnXA`oF_Ifz4H7LEOW~{Gww&3s=wH4+j8*TU| zSX%LtJWqhr-xGNSe{;(16kxnak6RnZ{0qZ^kJI5X*It_YuynSpi(^-}Lolr{)#z_~ zw!(J-8%7Ybo^c3(mED`Xz8xecP35a6M8HarxRn%+NJBE;dw>>Y2T&;jzRd4FSDO3T zt*y+zXCtZQ0bP0yf6HRpD|WmzP;DR^-g^}{z~0x~z4j8m zucTe%k&S9Nt-?Jb^gYW1w6!Y3AUZ0Jcq;pJ)Exz%7k+mUOm6%ApjjSmflfKwBo6`B zhNb@$NHTJ>guaj9S{@DX)!6)b-Shav=DNKWy(V00k(D!v?PAR0f0vDNq*#mYmUp6> z76KxbFDw5U{{qx{BRj(>?|C`82ICKbfLxoldov-M?4Xl+3;I4GzLHyPOzYw7{WQST zPNYcx5onA%MAO9??41Po*1zW(Y%Zzn06-lUp{s<3!_9vv9HBjT02On0Hf$}NP;wF) zP<`2p3}A^~1YbvOh{ePMx$!JGUPX-tbBzp3mDZMY;}h;sQ->!p97GA)9a|tF(Gh{1$xk7 zUw?ELkT({Xw!KIr);kTRb1b|UL`r2_`a+&UFVCdJ)1T#fdh;71EQl9790Br0m_`$x z9|ZANuchFci8GNZ{XbP=+uXSJRe(;V5laQz$u18#?X*9}x7cIEbnr%<=1cX3EIu7$ zhHW6pe5M(&qEtsqRa>?)*{O;OJT+YUhG5{km|YI7I@JL_3Hwao9aXneiSA~a* z|Lp@c-oMNyeAEuUz{F?kuou3x#C*gU?lon!RC1s37gW^0Frc`lqQWH&(J4NoZg3m8 z;Lin#8Q+cFPD7MCzj}#|ws7b@?D9Q4dVjS4dpco=4yX5SSH=A@U@yqPdp@?g?qeia zH=Tt_9)G=6C2QIPsi-QipnK(mc0xXIN;j$WLf@n8eYvMk;*H-Q4tK%(3$CN}NGgO8n}fD~+>?<3UzvsrMf*J~%i;VKQHbF%TPalFi=#sgj)(P#SM^0Q=Tr>4kJVw8X3iWsP|e8tj}NjlMdWp z@2+M4HQu~3!=bZpjh;;DIDk&X}=c8~kn)FWWH z2KL1w^rA5&1@@^X%MjZ7;u(kH=YhH2pJPFQe=hn>tZd5RC5cfGYis8s9PKaxi*}-s6*W zRA^PwR=y^5Z){!(4D9-KC;0~;b*ploznFOaU`bJ_7U?qAi#mTo!&rIECRL$_y@yI27x2?W+zqDBD5~KCVYKFZLK+>ABC(Kj zeAll)KMgIlAG`r^rS{loBrGLtzhHY8$)<_S<(Dpkr(Ym@@vnQ&rS@FC*>2@XCH}M+an74WcRDcoQ+a3@A z9tYhl5$z7bMdTvD2r&jztBuo37?*k~wcU9GK2-)MTFS-lux-mIRYUuGUCI~V$?s#< z?1qAWb(?ZLm(N>%S%y10COdaq_Tm5c^%ooIxpR=`3e4C|@O5wY+eLik&XVi5oT7oe zmxH)Jd*5eo@!7t`x8!K=-+zJ-Sz)B_V$)s1pW~CDU$=q^&ABvf6S|?TOMB-RIm@CoFg>mjIQE)?+A1_3s6zmFU_oW&BqyMz1mY*IcP_2knjq5 zqw~JK(cVsmzc7*EvTT2rvpeqhg)W=%TOZ^>f`rD4|7Z5fq*2D^lpCttIg#ictgqZ$P@ru6P#f$x#KfnfTZj~LG6U_d-kE~`;kU_X)`H5so@?C zWmb!7x|xk@0L~0JFall*@ltyiL^)@3m4MqC7(7H0sH!WidId1#f#6R{Q&A!XzO1IAcIx;$k66dumt6lpUw@nL2MvqJ5^kbOVZ<^2jt5-njy|2@`07}0w z;M%I1$FCoLy`8xp8Tk)bFr;7aJeQ9KK6p=O$U0-&JYYy8woV*>b+FB?xLX`=pirYM z5K$BA(u)+jR{?O2r$c_Qvl?M{=Ar{yQ!UVsVn4k@0!b?_lA;dVz9uaQUgBH8Oz(Sb zrEs;&Ey>_ex8&!N{PmQjp+-Hlh|OA&wvDai#GpU=^-B70V0*LF=^bi+Nhe_o|azZ%~ZZ1$}LTmWt4aoB1 zPgccm$EwYU+jrdBaQFxQfn5gd(gM`Y*Ro1n&Zi?j=(>T3kmf94vdhf?AuS8>$Va#P zGL5F+VHpxdsCUa}+RqavXCobI-@B;WJbMphpK2%6t=XvKWWE|ruvREgM+|V=i6;;O zx$g=7^`$XWn0fu!gF=Xe9cMB8Z_SelD>&o&{1XFS`|nInK3BXlaeD*rc;R-#osyIS zWv&>~^TLIyBB6oDX+#>3<_0+2C4u2zK^wmHXXDD9_)kmLYJ!0SzM|%G9{pi)`X$uf zW}|%%#LgyK7m(4{V&?x_0KEDq56tk|0YNY~B(Sr|>WVz-pO3A##}$JCT}5P7DY+@W z#gJv>pA5>$|E3WO2tV7G^SuymB?tY`ooKcN3!vaQMnBNk-WATF{-$#}FyzgtJ8M^; zUK6KWSG)}6**+rZ&?o@PK3??uN{Q)#+bDP9i1W&j)oaU5d0bIWJ_9T5ac!qc?x66Q z$KUSZ`nYY94qfN_dpTFr8OW~A?}LD;Yty-BA)-be5Z3S#t2Io%q+cAbnGj1t$|qFR z9o?8B7OA^KjCYL=-!p}w(dkC^G6Nd%_I=1))PC0w5}ZZGJxfK)jP4Fwa@b-SYBw?% zdz9B-<`*B2dOn(N;mcTm%Do)rIvfXRNFX&1h`?>Rzuj~Wx)$p13nrDlS8-jwq@e@n zNIj_|8or==8~1h*Ih?w*8K7rYkGlwlTWAwLKc5}~dfz3y`kM&^Q|@C%1VAp_$wnw6zG~W4O+^ z>i?NY?oXf^Puc~+fDM$VgRNBpOZj{2cMP~gCqWAX4 z7>%$ux8@a&_B(pt``KSt;r+sR-$N;jdpY>|pyvPiN)9ohd*>mVST3wMo)){`B(&eX z1?zZJ-4u9NZ|~j1rdZYq4R$?swf}<6(#ex%7r{kh%U@kT)&kWuAszS%oJts=*OcL9 zaZwK<5DZw%1IFHXgFplP6JiL^dk8+SgM$D?8X+gE4172hXh!WeqIO>}$I9?Nry$*S zQ#f)RuH{P7RwA3v9f<-w>{PSzom;>(i&^l{E0(&Xp4A-*q-@{W1oE3K;1zb{&n28dSC2$N+6auXe0}e4b z)KLJ?5c*>@9K#I^)W;uU_Z`enquTUxr>mNq z1{0_puF-M7j${rs!dxxo3EelGodF1TvjV;Zpo;s{5f1pyCuRp=HDZ?s#IA4f?h|-p zGd|Mq^4hDa@Bh!c4ZE?O&x&XZ_ptZGYK4$9F4~{%R!}G1leCBx`dtNUS|K zL-7J5s4W@%mhXg1!}a4PD%!t&Qn%f_oquRajn3@C*)`o&K9o7V6DwzVMEhjVdDJ1fjhr#@=lp#@4EBqi=CCQ>73>R(>QKPNM&_Jpe5G`n4wegeC`FYEPJ{|vwS>$-`fuRSp3927qOv|NC3T3G-0 zA{K`|+tQy1yqE$ShWt8ny&5~)%ITb@^+x$w0)f&om;P8B)@}=Wzy59BwUfZ1vqw87 za2lB8J(&*l#(V}Id8SyQ0C(2amzkz3EqG&Ed0Jq1)$|&>4_|NIe=5|n=3?siFV0fI z{As5DLW^gs|B-b4C;Hd(SM-S~GQhzb>HgF2|2Usww0nL^;x@1eaB)=+Clj+$fF@H( z-fqP??~QMT$KI-#m;QC*&6vkp&8699G3)Bq0*kFZXINw=b9OVaed(3(3kS|IZ)CM? zJdnW&%t8MveBuK21uiYj)_a{Fnw0OErMzMN?d$QoPwkhOwcP&p+t>P)4tHlYw-pPN z^oJ=uc$Sl>pv@fZH~ZqxSvdhF@F1s=oZawpr^-#l{IIOGG=T%QXjtwPhIg-F@k@uIlr?J->Ia zpEUQ*=4g|XYn4Gez&aHr*;t$u3oODPmc2Ku)2Og|xjc%w;q!Zz+zY)*3{7V8bK4;& zYV82FZ+8?v)`J|G1w4I0fWdKg|2b#iaazCv;|?(W-q}$o&Y}Q5d@BRk^jL7#{kbCK zSgkyu;=DV+or2)AxCBgq-nj5=@n^`%T#V+xBGEkW4lCqrE)LMv#f;AvD__cQ@Eg3`~x| zW+h9mofSXCq5|M)9|ez(#X?-sxB%Go8};sJ?2abp(Y!lyi>k)|{M*Z$c{e1-K4ky` MPgg&ebxsLQ025IeI{*Lx literal 0 HcmV?d00001 diff --git a/example/web/index.html b/example/web/index.html new file mode 100644 index 00000000..b6b9dd23 --- /dev/null +++ b/example/web/index.html @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + example + + + + + + + diff --git a/example/web/manifest.json b/example/web/manifest.json new file mode 100644 index 00000000..096edf8f --- /dev/null +++ b/example/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "example", + "short_name": "example", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} From a14a259988606ab81e9e63c27dcb4ce41e4796b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Tue, 22 Mar 2022 14:37:25 +0100 Subject: [PATCH 124/164] Improve header buttons, make them platform-aware --- lib/src/customization/header_style.dart | 6 ++--- lib/src/widgets/custom_icon_button.dart | 3 ++- lib/src/widgets/format_button.dart | 34 +++++++++++++++++-------- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/lib/src/customization/header_style.dart b/lib/src/customization/header_style.dart index a6409f7a..3fcc1089 100644 --- a/lib/src/customization/header_style.dart +++ b/lib/src/customization/header_style.dart @@ -35,7 +35,7 @@ class HeaderStyle { final TextStyle formatButtonTextStyle; /// Background `Decoration` for FormatButton. - final Decoration formatButtonDecoration; + final BoxDecoration formatButtonDecoration; /// Internal padding of the whole header. final EdgeInsets headerPadding; @@ -77,7 +77,7 @@ class HeaderStyle { final bool rightChevronVisible; /// Decoration of the header. - final Decoration decoration; + final BoxDecoration decoration; /// Creates a `HeaderStyle` used by `TableCalendar` widget. const HeaderStyle({ @@ -86,7 +86,7 @@ class HeaderStyle { this.formatButtonShowsNext = true, this.titleTextFormatter, this.titleTextStyle = const TextStyle(fontSize: 17.0), - this.formatButtonTextStyle = const TextStyle(), + this.formatButtonTextStyle = const TextStyle(fontSize: 14.0), this.formatButtonDecoration = const BoxDecoration( border: const Border.fromBorderSide(BorderSide()), borderRadius: const BorderRadius.all(Radius.circular(12.0)), diff --git a/lib/src/widgets/custom_icon_button.dart b/lib/src/widgets/custom_icon_button.dart index e0b88b08..09a956c7 100644 --- a/lib/src/widgets/custom_icon_button.dart +++ b/lib/src/widgets/custom_icon_button.dart @@ -4,6 +4,7 @@ import 'dart:io'; import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; class CustomIconButton extends StatelessWidget { @@ -24,7 +25,7 @@ class CustomIconButton extends StatelessWidget { Widget build(BuildContext context) { return Padding( padding: margin, - child: Platform.isIOS || Platform.isMacOS + child: !kIsWeb && (Platform.isIOS || Platform.isMacOS) ? CupertinoButton( onPressed: onTap, padding: padding, diff --git a/lib/src/widgets/format_button.dart b/lib/src/widgets/format_button.dart index 4e0eced3..3933a842 100644 --- a/lib/src/widgets/format_button.dart +++ b/lib/src/widgets/format_button.dart @@ -1,6 +1,10 @@ // Copyright 2019 Aleksander Woźniak // SPDX-License-Identifier: Apache-2.0 +import 'dart:io'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import '../shared/utils.dart' show CalendarFormat; @@ -9,7 +13,7 @@ class FormatButton extends StatelessWidget { final CalendarFormat calendarFormat; final ValueChanged onTap; final TextStyle textStyle; - final Decoration decoration; + final BoxDecoration decoration; final EdgeInsets padding; final bool showsNextFormat; final Map availableCalendarFormats; @@ -27,17 +31,27 @@ class FormatButton extends StatelessWidget { @override Widget build(BuildContext context) { - return GestureDetector( - onTap: () => onTap(_nextFormat()), - child: Container( - decoration: decoration, - padding: padding, - child: Text( - _formatButtonText, - style: textStyle, - ), + final child = Container( + decoration: decoration, + padding: padding, + child: Text( + _formatButtonText, + style: textStyle, ), ); + + return !kIsWeb && (Platform.isIOS || Platform.isMacOS) + ? CupertinoButton( + onPressed: () => onTap(_nextFormat()), + padding: EdgeInsets.zero, + child: child, + ) + : InkWell( + borderRadius: + decoration.borderRadius?.resolve(Directionality.of(context)), + onTap: () => onTap(_nextFormat()), + child: child, + ); } String get _formatButtonText => showsNextFormat From 999905422ad098f2ad79d3e1099cc05655d778d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Tue, 22 Mar 2022 15:38:24 +0100 Subject: [PATCH 125/164] Release version 3.0.5 --- CHANGELOG.md | 5 +++++ README.md | 2 +- example/pubspec.lock | 2 +- pubspec.yaml | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2febc5e..c3183b82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## [3.0.5] + +* Added a visual indicator to FormatButton +* Header buttons are now platform-aware + ## [3.0.4] * Updated dependencies diff --git a/README.md b/README.md index cfbf7890..19d034bb 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Add the following line to `pubspec.yaml`: ```yaml dependencies: - table_calendar: ^3.0.4 + table_calendar: ^3.0.5 ``` ### Basic setup diff --git a/example/pubspec.lock b/example/pubspec.lock index e3e59460..b5f61cf1 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -148,7 +148,7 @@ packages: path: ".." relative: true source: path - version: "3.0.4" + version: "3.0.5" term_glyph: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index c2eb42c1..61688df6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: table_calendar description: Highly customizable, feature-packed calendar widget for Flutter. -version: 3.0.4 +version: 3.0.5 author: Aleksander Woźniak homepage: https://github.com/aleksanderwozniak/table_calendar From cec66f5a196fca7474addd8deae469c5e6941ce4 Mon Sep 17 00:00:00 2001 From: Jesper Skand Date: Mon, 9 May 2022 10:23:51 +0300 Subject: [PATCH 126/164] New feature: Show week numbers --- example/lib/main.dart | 9 ++ example/lib/pages/week_numbers_example.dart | 93 ++++++++++++++++++++ lib/src/customization/calendar_builders.dart | 5 ++ lib/src/customization/calendar_style.dart | 5 ++ lib/src/table_calendar.dart | 38 ++++++++ lib/src/table_calendar_base.dart | 6 ++ lib/src/widgets/calendar_core.dart | 12 +++ lib/src/widgets/calendar_page.dart | 37 ++++++-- test/table_calendar_base_test.dart | 8 ++ 9 files changed, 208 insertions(+), 5 deletions(-) create mode 100644 example/lib/pages/week_numbers_example.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index ae2bc273..c26c65db 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -9,6 +9,7 @@ import 'pages/complex_example.dart'; import 'pages/events_example.dart'; import 'pages/multi_example.dart'; import 'pages/range_example.dart'; +import 'pages/week_numbers_example.dart'; void main() { initializeDateFormatting().then((_) => runApp(MyApp())); @@ -52,6 +53,14 @@ class _StartPageState extends State { ), ), const SizedBox(height: 12.0), + ElevatedButton( + child: Text('Week numbers'), + onPressed: () => Navigator.push( + context, + MaterialPageRoute(builder: (_) => TableWeekNumbersExample()), + ), + ), + const SizedBox(height: 12.0), ElevatedButton( child: Text('Range Selection'), onPressed: () => Navigator.push( diff --git a/example/lib/pages/week_numbers_example.dart b/example/lib/pages/week_numbers_example.dart new file mode 100644 index 00000000..1740c419 --- /dev/null +++ b/example/lib/pages/week_numbers_example.dart @@ -0,0 +1,93 @@ +// Copyright 2019 Aleksander Woźniak +// SPDX-License-Identifier: Apache-2.0 + +// ignore_for_file: avoid_redundant_argument_values +import 'package:flutter/material.dart'; +import 'package:table_calendar/table_calendar.dart'; +import '../utils.dart'; + +class TableWeekNumbersExample extends StatefulWidget { + const TableWeekNumbersExample({Key? key}) : super(key: key); + + @override + _TableWeekNumbersExampleState createState() => _TableWeekNumbersExampleState(); +} + +class _TableWeekNumbersExampleState extends State { + CalendarFormat _calendarFormat = CalendarFormat.month; + DateTime _focusedDay = DateTime.now(); + DateTime? _selectedDay; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('TableCalendar - Week numbers'), + ), + body: TableCalendar( + firstDay: kFirstDay, + lastDay: kLastDay, + focusedDay: _focusedDay, + calendarFormat: _calendarFormat, + selectedDayPredicate: (day) { + // Use `selectedDayPredicate` to determine which day is currently selected. + // If this returns true, then `day` will be marked as selected. + + // Using `isSameDay` is recommended to disregard + // the time-part of compared DateTime objects. + return isSameDay(_selectedDay, day); + }, + onDaySelected: (selectedDay, focusedDay) { + if (!isSameDay(_selectedDay, selectedDay)) { + // Call `setState()` when updating the selected day + setState(() { + _selectedDay = selectedDay; + _focusedDay = focusedDay; + }); + } + }, + onFormatChanged: (format) { + if (_calendarFormat != format) { + // Call `setState()` when updating calendar format + setState(() { + _calendarFormat = format; + }); + } + }, + onPageChanged: (focusedDay) { + // No need to call `setState()` here + _focusedDay = focusedDay; + }, + calendarStyle: const CalendarStyle( + /// Override week numbers textstyle here, e.g + weekNumberTextStyle: TextStyle( + fontSize: 12, + color: Color(0xFFBFBFBF), + ), + ), + calendarBuilders: CalendarBuilders( + /// Override week numbers builder here, e.g. + weekNumberBuilder: (context, weekNumber) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: Container( + alignment: Alignment.center, + child: Text( + weekNumber.toString(), + /// This will override calendarStyle -> weekNumberTextStyle. + style: const TextStyle( + fontSize: 12, + color: Color(0xFFBFBFBF), + ), + ), + ), + ); + }, + ), + startingDayOfWeek: StartingDayOfWeek.monday, + /// Show week numbers + weekNumbersVisible: true, + ), + ); + } +} diff --git a/lib/src/customization/calendar_builders.dart b/lib/src/customization/calendar_builders.dart index 32b5b4b0..af69c23e 100644 --- a/lib/src/customization/calendar_builders.dart +++ b/lib/src/customization/calendar_builders.dart @@ -78,6 +78,10 @@ class CalendarBuilders { /// Use to customize header's title using different widget final DayBuilder? headerTitleBuilder; + /// Custom builder for number of the week labels. + final Widget? Function(BuildContext context, int weekNumber)? + weekNumberBuilder; + /// Creates `CalendarBuilders` for `TableCalendar` widget. const CalendarBuilders({ this.prioritizedBuilder, @@ -95,5 +99,6 @@ class CalendarBuilders { this.markerBuilder, this.dowBuilder, this.headerTitleBuilder, + this.weekNumberBuilder, }); } diff --git a/lib/src/customization/calendar_style.dart b/lib/src/customization/calendar_style.dart index 7f44fa2f..2933ca53 100644 --- a/lib/src/customization/calendar_style.dart +++ b/lib/src/customization/calendar_style.dart @@ -133,6 +133,9 @@ class CalendarStyle { /// Decoration for day cells that match `weekendDay` list. final Decoration weekendDecoration; + /// TextStyle for week number. + final TextStyle weekNumberTextStyle; + /// TextStyle for day cells that do not match any other styles. final TextStyle defaultTextStyle; @@ -214,6 +217,8 @@ class CalendarStyle { ), this.weekendTextStyle = const TextStyle(color: const Color(0xFF5A5A5A)), this.weekendDecoration = const BoxDecoration(shape: BoxShape.circle), + this.weekNumberTextStyle = + const TextStyle(fontSize: 12, color: const Color(0xFFBFBFBF)), this.defaultTextStyle = const TextStyle(), this.defaultDecoration = const BoxDecoration(shape: BoxShape.circle), this.rowDecoration = const BoxDecoration(), diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index e3d0f2ab..35e36d31 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -103,6 +103,9 @@ class TableCalendar extends StatefulWidget { /// When set to true, `TableCalendar` will fill available height. final bool shouldFillViewport; + /// Whether to display week numbers on calendar. + final bool weekNumbersVisible; + /// Used for setting the height of `TableCalendar`'s rows. final double rowHeight; @@ -224,6 +227,7 @@ class TableCalendar extends StatefulWidget { this.pageAnimationEnabled = true, this.sixWeekMonthsEnforced = false, this.shouldFillViewport = false, + this.weekNumbersVisible = false, this.rowHeight = 52.0, this.daysOfWeekHeight = 16.0, this.formatAnimationDuration = const Duration(milliseconds: 200), @@ -504,6 +508,27 @@ class _TableCalendarState extends State> { _focusedDay.value = focusedDay; widget.onPageChanged?.call(focusedDay); }, + weekNumbersVisible: widget.weekNumbersVisible, + weekNumberBuilder: (BuildContext context, DateTime day) { + final weekNumber = isoWeekNumber(day); + Widget? cell = widget.calendarBuilders.weekNumberBuilder + ?.call(context, weekNumber); + + if (cell == null) { + cell = Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: Container( + alignment: Alignment.center, + child: Text( + weekNumber.toString(), + style: widget.calendarStyle.weekNumberTextStyle, + ), + ), + ); + } + + return cell; + }, dowBuilder: (BuildContext context, DateTime day) { Widget? dowCell = widget.calendarBuilders.dowBuilder?.call(context, day); @@ -544,6 +569,19 @@ class _TableCalendarState extends State> { ); } + int isoWeekNumber(DateTime date) { + int daysToAdd = DateTime.thursday - date.weekday; + DateTime thursdayDate = daysToAdd > 0 + ? date.add(Duration(days: daysToAdd)) + : date.subtract(Duration(days: daysToAdd.abs())); + int dayOfYearThursday = dayOfYear(thursdayDate); + return 1 + ((dayOfYearThursday - 1) / 7).floor(); + } + + int dayOfYear(DateTime date) { + return date.difference(DateTime(date.year, 1, 1)).inDays; + } + Widget _buildCell(DateTime day, DateTime focusedDay) { final isOutside = day.month != focusedDay.month; diff --git a/lib/src/table_calendar_base.dart b/lib/src/table_calendar_base.dart index 5dc72e49..bcc8b572 100644 --- a/lib/src/table_calendar_base.dart +++ b/lib/src/table_calendar_base.dart @@ -13,11 +13,13 @@ class TableCalendarBase extends StatefulWidget { final DateTime focusedDay; final CalendarFormat calendarFormat; final DayBuilder? dowBuilder; + final DayBuilder? weekNumberBuilder; final FocusedDayBuilder dayBuilder; final double? dowHeight; final double rowHeight; final bool sixWeekMonthsEnforced; final bool dowVisible; + final bool weekNumbersVisible; final Decoration? dowDecoration; final Decoration? rowDecoration; final TableBorder? tableBorder; @@ -46,6 +48,8 @@ class TableCalendarBase extends StatefulWidget { required this.rowHeight, this.sixWeekMonthsEnforced = false, this.dowVisible = true, + this.weekNumberBuilder, + required this.weekNumbersVisible, this.dowDecoration, this.rowDecoration, this.tableBorder, @@ -215,6 +219,8 @@ class _TableCalendarBaseState extends State { dowVisible: widget.dowVisible, dowHeight: widget.dowHeight, rowHeight: widget.rowHeight, + weekNumbersVisible: widget.weekNumbersVisible, + weekNumberBuilder: widget.weekNumberBuilder, dowDecoration: widget.dowDecoration, rowDecoration: widget.rowDecoration, tableBorder: widget.tableBorder, diff --git a/lib/src/widgets/calendar_core.dart b/lib/src/widgets/calendar_core.dart index 2edc148c..aa7ffd00 100644 --- a/lib/src/widgets/calendar_core.dart +++ b/lib/src/widgets/calendar_core.dart @@ -15,9 +15,11 @@ class CalendarCore extends StatelessWidget { final DateTime lastDay; final CalendarFormat calendarFormat; final DayBuilder? dowBuilder; + final DayBuilder? weekNumberBuilder; final FocusedDayBuilder dayBuilder; final bool sixWeekMonthsEnforced; final bool dowVisible; + final bool weekNumbersVisible; final Decoration? dowDecoration; final Decoration? rowDecoration; final TableBorder? tableBorder; @@ -47,6 +49,8 @@ class CalendarCore extends StatelessWidget { this.previousIndex, this.sixWeekMonthsEnforced = false, this.dowVisible = true, + this.weekNumberBuilder, + required this.weekNumbersVisible, this.dowDecoration, this.rowDecoration, this.tableBorder, @@ -98,6 +102,14 @@ class CalendarCore extends StatelessWidget { child: dayBuilder(context, day, baseDay), ); }, + dowHeight: dowHeight, + weekNumberVisible: weekNumbersVisible, + weekNumberBuilder: (context, day) { + return SizedBox( + height: constrainedRowHeight ?? rowHeight, + child: weekNumberBuilder?.call(context, day), + ); + }, ); }, onPageChanged: (index) { diff --git a/lib/src/widgets/calendar_page.dart b/lib/src/widgets/calendar_page.dart index 2f909bb8..1093257a 100644 --- a/lib/src/widgets/calendar_page.dart +++ b/lib/src/widgets/calendar_page.dart @@ -6,31 +6,58 @@ import 'package:flutter/widgets.dart'; class CalendarPage extends StatelessWidget { final Widget Function(BuildContext context, DateTime day)? dowBuilder; final Widget Function(BuildContext context, DateTime day) dayBuilder; + final Widget Function(BuildContext context, DateTime day)? weekNumberBuilder; final List visibleDays; final Decoration? dowDecoration; final Decoration? rowDecoration; final TableBorder? tableBorder; final bool dowVisible; + final bool weekNumberVisible; + final double? dowHeight; const CalendarPage({ Key? key, required this.visibleDays, this.dowBuilder, required this.dayBuilder, + this.weekNumberBuilder, this.dowDecoration, this.rowDecoration, this.tableBorder, this.dowVisible = true, - }) : assert(!dowVisible || dowBuilder != null), + this.weekNumberVisible = false, + this.dowHeight, + }) : assert(!dowVisible || (dowHeight != null && dowBuilder != null)), + assert(!weekNumberVisible || weekNumberBuilder != null), super(key: key); @override Widget build(BuildContext context) { - return Table( - border: tableBorder, + return Row( + crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - if (dowVisible) _buildDaysOfWeek(context), - ..._buildCalendarDays(context), + if (weekNumberVisible) _buildWeekNumbers(context), + Expanded( + child: Table( + border: tableBorder, + children: [ + if (dowVisible) _buildDaysOfWeek(context), + ..._buildCalendarDays(context), + ], + ), + ), + ], + ); + } + + Widget _buildWeekNumbers(BuildContext context) { + final rowAmount = visibleDays.length ~/ 7; + return Column( + children: [ + if (dowVisible) SizedBox(height: dowHeight ?? 0), + ...List.generate(rowAmount, (index) => index * 7) + .map((index) => weekNumberBuilder!(context, visibleDays[index])) + .toList() ], ); } diff --git a/test/table_calendar_base_test.dart b/test/table_calendar_base_test.dart index 4076a540..488a5a75 100644 --- a/test/table_calendar_base_test.dart +++ b/test/table_calendar_base_test.dart @@ -39,6 +39,7 @@ void main() { dowVisible: false, calendarFormat: CalendarFormat.month, startingDayOfWeek: StartingDayOfWeek.sunday, + weekNumbersVisible: false, ), ), ); @@ -85,6 +86,7 @@ void main() { dowVisible: false, calendarFormat: CalendarFormat.twoWeeks, startingDayOfWeek: StartingDayOfWeek.sunday, + weekNumbersVisible: false, ), ), ); @@ -131,6 +133,7 @@ void main() { dowVisible: false, calendarFormat: CalendarFormat.week, startingDayOfWeek: StartingDayOfWeek.sunday, + weekNumbersVisible: false, ), ), ); @@ -177,6 +180,7 @@ void main() { dowVisible: false, calendarFormat: CalendarFormat.month, startingDayOfWeek: StartingDayOfWeek.monday, + weekNumbersVisible: false, ), ), ); @@ -223,6 +227,7 @@ void main() { dowVisible: false, calendarFormat: CalendarFormat.twoWeeks, startingDayOfWeek: StartingDayOfWeek.monday, + weekNumbersVisible: false, ), ), ); @@ -269,6 +274,7 @@ void main() { dowVisible: false, calendarFormat: CalendarFormat.week, startingDayOfWeek: StartingDayOfWeek.monday, + weekNumbersVisible: false, ), ), ); @@ -327,6 +333,7 @@ void main() { }, rowHeight: 52, dowVisible: false, + weekNumbersVisible: false, ), ), ); @@ -372,6 +379,7 @@ void main() { dowBuilder: (context, day) { return Text('${day.weekday}'); }, + weekNumbersVisible: false, ), ), ); From 0b91bc49c33cd59a1324ce154ebb4881c1052d5c Mon Sep 17 00:00:00 2001 From: Jesper Skand Date: Mon, 9 May 2022 10:24:40 +0300 Subject: [PATCH 127/164] Fix RenderFlex issue when onPageChanged is invoked in CalendarCore --- lib/src/widgets/calendar_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/widgets/calendar_page.dart b/lib/src/widgets/calendar_page.dart index 1093257a..ef78388b 100644 --- a/lib/src/widgets/calendar_page.dart +++ b/lib/src/widgets/calendar_page.dart @@ -56,7 +56,7 @@ class CalendarPage extends StatelessWidget { children: [ if (dowVisible) SizedBox(height: dowHeight ?? 0), ...List.generate(rowAmount, (index) => index * 7) - .map((index) => weekNumberBuilder!(context, visibleDays[index])) + .map((index) => Expanded(child: weekNumberBuilder!(context, visibleDays[index]))) .toList() ], ); From 8012bd9585edaf791668d9906cd46f7e741b5925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 29 May 2022 17:44:57 +0200 Subject: [PATCH 128/164] Fix missing Flutter Web tag --- lib/src/widgets/custom_icon_button.dart | 8 +++++--- lib/src/widgets/format_button.dart | 7 ++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/src/widgets/custom_icon_button.dart b/lib/src/widgets/custom_icon_button.dart index 09a956c7..9e652c60 100644 --- a/lib/src/widgets/custom_icon_button.dart +++ b/lib/src/widgets/custom_icon_button.dart @@ -1,8 +1,6 @@ // Copyright 2019 Aleksander Woźniak // SPDX-License-Identifier: Apache-2.0 -import 'dart:io'; - import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -23,9 +21,13 @@ class CustomIconButton extends StatelessWidget { @override Widget build(BuildContext context) { + final platform = Theme.of(context).platform; + return Padding( padding: margin, - child: !kIsWeb && (Platform.isIOS || Platform.isMacOS) + child: !kIsWeb && + (platform == TargetPlatform.iOS || + platform == TargetPlatform.macOS) ? CupertinoButton( onPressed: onTap, padding: padding, diff --git a/lib/src/widgets/format_button.dart b/lib/src/widgets/format_button.dart index 3933a842..1c11c643 100644 --- a/lib/src/widgets/format_button.dart +++ b/lib/src/widgets/format_button.dart @@ -1,8 +1,6 @@ // Copyright 2019 Aleksander Woźniak // SPDX-License-Identifier: Apache-2.0 -import 'dart:io'; - import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -40,7 +38,10 @@ class FormatButton extends StatelessWidget { ), ); - return !kIsWeb && (Platform.isIOS || Platform.isMacOS) + final platform = Theme.of(context).platform; + + return !kIsWeb && + (platform == TargetPlatform.iOS || platform == TargetPlatform.macOS) ? CupertinoButton( onPressed: () => onTap(_nextFormat()), padding: EdgeInsets.zero, From 47d3649f37ae5f7108252461604bb81dbb523361 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 29 May 2022 17:48:21 +0200 Subject: [PATCH 129/164] Release version 3.0.6 --- CHANGELOG.md | 4 ++++ README.md | 2 +- example/ios/Runner/Info.plist | 2 ++ example/pubspec.lock | 25 +++++++++---------------- pubspec.lock | 23 ++++++++--------------- pubspec.yaml | 2 +- 6 files changed, 25 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3183b82..8365c5f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## [3.0.6] + +* Fixed issue with missing Flutter Web platform tag + ## [3.0.5] * Added a visual indicator to FormatButton diff --git a/README.md b/README.md index 19d034bb..e61d33fc 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Add the following line to `pubspec.yaml`: ```yaml dependencies: - table_calendar: ^3.0.5 + table_calendar: ^3.0.6 ``` ### Basic setup diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist index 0f368fb4..8ac112d9 100644 --- a/example/ios/Runner/Info.plist +++ b/example/ios/Runner/Info.plist @@ -41,5 +41,7 @@ UIViewControllerBasedStatusBarAppearance + CADisableMinimumFrameDurationOnPhone + diff --git a/example/pubspec.lock b/example/pubspec.lock index b5f61cf1..33835374 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -42,7 +42,7 @@ packages: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.15.0" + version: "1.16.0" cupertino_icons: dependency: "direct main" description: @@ -56,7 +56,7 @@ packages: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.3.0" flutter: dependency: "direct main" description: flutter @@ -87,7 +87,7 @@ packages: name: material_color_utilities url: "https://pub.dartlang.org" source: hosted - version: "0.1.3" + version: "0.1.4" meta: dependency: transitive description: @@ -101,7 +101,7 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.8.1" simple_gesture_detector: dependency: transitive description: @@ -120,7 +120,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.1" + version: "1.8.2" stack_trace: dependency: transitive description: @@ -148,7 +148,7 @@ packages: path: ".." relative: true source: path - version: "3.0.5" + version: "3.0.6" term_glyph: dependency: transitive description: @@ -162,21 +162,14 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.8" - typed_data: - dependency: transitive - description: - name: typed_data - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.0" + version: "0.4.9" vector_math: dependency: transitive description: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" + version: "2.1.2" sdks: - dart: ">=2.14.0 <3.0.0" + dart: ">=2.17.0-0 <3.0.0" flutter: ">=1.17.0" diff --git a/pubspec.lock b/pubspec.lock index efc97c88..e28f35c5 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -42,14 +42,14 @@ packages: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.15.0" + version: "1.16.0" fake_async: dependency: transitive description: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.3.0" flutter: dependency: "direct main" description: flutter @@ -80,7 +80,7 @@ packages: name: material_color_utilities url: "https://pub.dartlang.org" source: hosted - version: "0.1.3" + version: "0.1.4" meta: dependency: transitive description: @@ -94,7 +94,7 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.8.1" simple_gesture_detector: dependency: "direct main" description: @@ -113,7 +113,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.1" + version: "1.8.2" stack_trace: dependency: transitive description: @@ -148,21 +148,14 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.8" - typed_data: - dependency: transitive - description: - name: typed_data - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.0" + version: "0.4.9" vector_math: dependency: transitive description: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" + version: "2.1.2" sdks: - dart: ">=2.14.0 <3.0.0" + dart: ">=2.17.0-0 <3.0.0" flutter: ">=1.17.0" diff --git a/pubspec.yaml b/pubspec.yaml index 61688df6..54f8f5dd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: table_calendar description: Highly customizable, feature-packed calendar widget for Flutter. -version: 3.0.5 +version: 3.0.6 author: Aleksander Woźniak homepage: https://github.com/aleksanderwozniak/table_calendar From eb697031d6f921a4343195a784d15ad58741b2c0 Mon Sep 17 00:00:00 2001 From: Jesper Skand Date: Tue, 31 May 2022 22:13:50 +0300 Subject: [PATCH 130/164] PR feedback fixes --- example/lib/main.dart | 9 -- example/lib/pages/basics_example.dart | 2 + example/lib/pages/week_numbers_example.dart | 93 --------------------- lib/src/table_calendar_base.dart | 2 +- test/table_calendar_base_test.dart | 8 -- 5 files changed, 3 insertions(+), 111 deletions(-) delete mode 100644 example/lib/pages/week_numbers_example.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index c26c65db..ae2bc273 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -9,7 +9,6 @@ import 'pages/complex_example.dart'; import 'pages/events_example.dart'; import 'pages/multi_example.dart'; import 'pages/range_example.dart'; -import 'pages/week_numbers_example.dart'; void main() { initializeDateFormatting().then((_) => runApp(MyApp())); @@ -53,14 +52,6 @@ class _StartPageState extends State { ), ), const SizedBox(height: 12.0), - ElevatedButton( - child: Text('Week numbers'), - onPressed: () => Navigator.push( - context, - MaterialPageRoute(builder: (_) => TableWeekNumbersExample()), - ), - ), - const SizedBox(height: 12.0), ElevatedButton( child: Text('Range Selection'), onPressed: () => Navigator.push( diff --git a/example/lib/pages/basics_example.dart b/example/lib/pages/basics_example.dart index 6a8f91b6..7034a995 100644 --- a/example/lib/pages/basics_example.dart +++ b/example/lib/pages/basics_example.dart @@ -56,6 +56,8 @@ class _TableBasicsExampleState extends State { // No need to call `setState()` here _focusedDay = focusedDay; }, + // Enable week numbers (disabled by default). + weekNumbersVisible: true, ), ); } diff --git a/example/lib/pages/week_numbers_example.dart b/example/lib/pages/week_numbers_example.dart deleted file mode 100644 index 1740c419..00000000 --- a/example/lib/pages/week_numbers_example.dart +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2019 Aleksander Woźniak -// SPDX-License-Identifier: Apache-2.0 - -// ignore_for_file: avoid_redundant_argument_values -import 'package:flutter/material.dart'; -import 'package:table_calendar/table_calendar.dart'; -import '../utils.dart'; - -class TableWeekNumbersExample extends StatefulWidget { - const TableWeekNumbersExample({Key? key}) : super(key: key); - - @override - _TableWeekNumbersExampleState createState() => _TableWeekNumbersExampleState(); -} - -class _TableWeekNumbersExampleState extends State { - CalendarFormat _calendarFormat = CalendarFormat.month; - DateTime _focusedDay = DateTime.now(); - DateTime? _selectedDay; - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text('TableCalendar - Week numbers'), - ), - body: TableCalendar( - firstDay: kFirstDay, - lastDay: kLastDay, - focusedDay: _focusedDay, - calendarFormat: _calendarFormat, - selectedDayPredicate: (day) { - // Use `selectedDayPredicate` to determine which day is currently selected. - // If this returns true, then `day` will be marked as selected. - - // Using `isSameDay` is recommended to disregard - // the time-part of compared DateTime objects. - return isSameDay(_selectedDay, day); - }, - onDaySelected: (selectedDay, focusedDay) { - if (!isSameDay(_selectedDay, selectedDay)) { - // Call `setState()` when updating the selected day - setState(() { - _selectedDay = selectedDay; - _focusedDay = focusedDay; - }); - } - }, - onFormatChanged: (format) { - if (_calendarFormat != format) { - // Call `setState()` when updating calendar format - setState(() { - _calendarFormat = format; - }); - } - }, - onPageChanged: (focusedDay) { - // No need to call `setState()` here - _focusedDay = focusedDay; - }, - calendarStyle: const CalendarStyle( - /// Override week numbers textstyle here, e.g - weekNumberTextStyle: TextStyle( - fontSize: 12, - color: Color(0xFFBFBFBF), - ), - ), - calendarBuilders: CalendarBuilders( - /// Override week numbers builder here, e.g. - weekNumberBuilder: (context, weekNumber) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 4), - child: Container( - alignment: Alignment.center, - child: Text( - weekNumber.toString(), - /// This will override calendarStyle -> weekNumberTextStyle. - style: const TextStyle( - fontSize: 12, - color: Color(0xFFBFBFBF), - ), - ), - ), - ); - }, - ), - startingDayOfWeek: StartingDayOfWeek.monday, - /// Show week numbers - weekNumbersVisible: true, - ), - ); - } -} diff --git a/lib/src/table_calendar_base.dart b/lib/src/table_calendar_base.dart index bcc8b572..7fe150f1 100644 --- a/lib/src/table_calendar_base.dart +++ b/lib/src/table_calendar_base.dart @@ -49,7 +49,7 @@ class TableCalendarBase extends StatefulWidget { this.sixWeekMonthsEnforced = false, this.dowVisible = true, this.weekNumberBuilder, - required this.weekNumbersVisible, + this.weekNumbersVisible = false, this.dowDecoration, this.rowDecoration, this.tableBorder, diff --git a/test/table_calendar_base_test.dart b/test/table_calendar_base_test.dart index 488a5a75..4076a540 100644 --- a/test/table_calendar_base_test.dart +++ b/test/table_calendar_base_test.dart @@ -39,7 +39,6 @@ void main() { dowVisible: false, calendarFormat: CalendarFormat.month, startingDayOfWeek: StartingDayOfWeek.sunday, - weekNumbersVisible: false, ), ), ); @@ -86,7 +85,6 @@ void main() { dowVisible: false, calendarFormat: CalendarFormat.twoWeeks, startingDayOfWeek: StartingDayOfWeek.sunday, - weekNumbersVisible: false, ), ), ); @@ -133,7 +131,6 @@ void main() { dowVisible: false, calendarFormat: CalendarFormat.week, startingDayOfWeek: StartingDayOfWeek.sunday, - weekNumbersVisible: false, ), ), ); @@ -180,7 +177,6 @@ void main() { dowVisible: false, calendarFormat: CalendarFormat.month, startingDayOfWeek: StartingDayOfWeek.monday, - weekNumbersVisible: false, ), ), ); @@ -227,7 +223,6 @@ void main() { dowVisible: false, calendarFormat: CalendarFormat.twoWeeks, startingDayOfWeek: StartingDayOfWeek.monday, - weekNumbersVisible: false, ), ), ); @@ -274,7 +269,6 @@ void main() { dowVisible: false, calendarFormat: CalendarFormat.week, startingDayOfWeek: StartingDayOfWeek.monday, - weekNumbersVisible: false, ), ), ); @@ -333,7 +327,6 @@ void main() { }, rowHeight: 52, dowVisible: false, - weekNumbersVisible: false, ), ), ); @@ -379,7 +372,6 @@ void main() { dowBuilder: (context, day) { return Text('${day.weekday}'); }, - weekNumbersVisible: false, ), ), ); From 974620134e83f17878dbba4f915620af4d6e81e0 Mon Sep 17 00:00:00 2001 From: Jesper Skand Date: Tue, 31 May 2022 22:34:18 +0300 Subject: [PATCH 131/164] Add test related to week numbers --- test/calendar_page_test.dart | 56 ++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/test/calendar_page_test.dart b/test/calendar_page_test.dart index c8f7dae6..f9d15020 100644 --- a/test/calendar_page_test.dart +++ b/test/calendar_page_test.dart @@ -61,6 +61,7 @@ void main() { dowBuilder: (context, day) { return Text('${day.weekday}'); }, + dowHeight: 5, ), ), ); @@ -93,4 +94,59 @@ void main() { }, throwsAssertionError); }, ); + + testWidgets( + 'Week numbers are not visible by default', + (tester) async { + await tester.pumpWidget( + setupTestWidget( + CalendarPage( + visibleDays: visibleDays, + dayBuilder: (context, day) { + return Text('${day.day}'); + }, + dowVisible: true, + dowBuilder: (context, day) { + return Text('${day.weekday}'); + }, + dowHeight: 5, + ), + ), + ); + + expect( + find.byType(Column), + findsNWidgets(0), + ); + }, + ); + + testWidgets( + 'Week numbers are visible', + (tester) async { + await tester.pumpWidget(setupTestWidget( + CalendarPage( + visibleDays: visibleDays, + dayBuilder: (context, day) { + return Text('${day.day}'); + }, + dowVisible: true, + dowBuilder: (context, day) { + return Text('${day.weekday}'); + }, + dowHeight: 5, + weekNumberVisible: true, + weekNumberBuilder: (BuildContext context, DateTime day) { + return Text(day.weekday.toString()); + }, + ), + )); + + expect( + find.byType(Column), + findsNWidgets(1), + ); + }, + ); + } From aafa2d86dcd6fbd1e9e395db6946d31d17f45fe1 Mon Sep 17 00:00:00 2001 From: soraef Date: Wed, 13 Jul 2022 16:30:56 +0900 Subject: [PATCH 132/164] add table padding --- lib/src/customization/calendar_style.dart | 4 ++++ lib/src/table_calendar.dart | 1 + lib/src/table_calendar_base.dart | 6 +++++- lib/src/widgets/calendar_core.dart | 3 +++ lib/src/widgets/calendar_page.dart | 17 +++++++++++------ 5 files changed, 24 insertions(+), 7 deletions(-) diff --git a/lib/src/customization/calendar_style.dart b/lib/src/customization/calendar_style.dart index 7f44fa2f..917dd27d 100644 --- a/lib/src/customization/calendar_style.dart +++ b/lib/src/customization/calendar_style.dart @@ -145,6 +145,9 @@ class CalendarStyle { /// Border for the internal `Table` widget. final TableBorder tableBorder; + /// Padding for the internal `Table` widget. + final EdgeInsets tablePadding; + /// Creates a `CalendarStyle` used by `TableCalendar` widget. const CalendarStyle({ this.isTodayHighlighted = true, @@ -218,6 +221,7 @@ class CalendarStyle { this.defaultDecoration = const BoxDecoration(shape: BoxShape.circle), this.rowDecoration = const BoxDecoration(), this.tableBorder = const TableBorder(), + this.tablePadding = const EdgeInsets.all(0), }); } diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index e3d0f2ab..58d2f5fc 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -488,6 +488,7 @@ class _TableCalendarState extends State> { dowDecoration: widget.daysOfWeekStyle.decoration, rowDecoration: widget.calendarStyle.rowDecoration, tableBorder: widget.calendarStyle.tableBorder, + tablePadding: widget.calendarStyle.tablePadding, dowVisible: widget.daysOfWeekVisible, dowHeight: widget.daysOfWeekHeight, rowHeight: widget.rowHeight, diff --git a/lib/src/table_calendar_base.dart b/lib/src/table_calendar_base.dart index 5dc72e49..33fd02b8 100644 --- a/lib/src/table_calendar_base.dart +++ b/lib/src/table_calendar_base.dart @@ -21,6 +21,7 @@ class TableCalendarBase extends StatefulWidget { final Decoration? dowDecoration; final Decoration? rowDecoration; final TableBorder? tableBorder; + final EdgeInsets? tablePadding; final Duration formatAnimationDuration; final Curve formatAnimationCurve; final bool pageAnimationEnabled; @@ -49,6 +50,7 @@ class TableCalendarBase extends StatefulWidget { this.dowDecoration, this.rowDecoration, this.tableBorder, + this.tablePadding, this.formatAnimationDuration = const Duration(milliseconds: 200), this.formatAnimationCurve = Curves.linear, this.pageAnimationEnabled = true, @@ -218,6 +220,7 @@ class _TableCalendarBaseState extends State { dowDecoration: widget.dowDecoration, rowDecoration: widget.rowDecoration, tableBorder: widget.tableBorder, + tablePadding: widget.tablePadding, onPageChanged: (index, focusedMonth) { if (!_pageCallbackDisabled) { if (!isSameDay(_focusedDay, focusedMonth)) { @@ -250,8 +253,9 @@ class _TableCalendarBaseState extends State { } double _getPageHeight(int rowCount) { + final tablePaddingHeight = widget.tablePadding?.vertical ?? 0.0; final dowHeight = widget.dowVisible ? widget.dowHeight! : 0.0; - return dowHeight + rowCount * widget.rowHeight; + return dowHeight + rowCount * widget.rowHeight + tablePaddingHeight; } int _calculateFocusedPage( diff --git a/lib/src/widgets/calendar_core.dart b/lib/src/widgets/calendar_core.dart index 2edc148c..8df5b735 100644 --- a/lib/src/widgets/calendar_core.dart +++ b/lib/src/widgets/calendar_core.dart @@ -21,6 +21,7 @@ class CalendarCore extends StatelessWidget { final Decoration? dowDecoration; final Decoration? rowDecoration; final TableBorder? tableBorder; + final EdgeInsets? tablePadding; final double? dowHeight; final double? rowHeight; final BoxConstraints constraints; @@ -50,6 +51,7 @@ class CalendarCore extends StatelessWidget { this.dowDecoration, this.rowDecoration, this.tableBorder, + this.tablePadding, this.scrollPhysics, }) : assert(!dowVisible || (dowHeight != null && dowBuilder != null)), super(key: key); @@ -77,6 +79,7 @@ class CalendarCore extends StatelessWidget { dowDecoration: dowDecoration, rowDecoration: rowDecoration, tableBorder: tableBorder, + tablePadding: tablePadding, dowBuilder: (context, day) { return SizedBox( height: dowHeight, diff --git a/lib/src/widgets/calendar_page.dart b/lib/src/widgets/calendar_page.dart index 2f909bb8..2ffb79cf 100644 --- a/lib/src/widgets/calendar_page.dart +++ b/lib/src/widgets/calendar_page.dart @@ -10,6 +10,7 @@ class CalendarPage extends StatelessWidget { final Decoration? dowDecoration; final Decoration? rowDecoration; final TableBorder? tableBorder; + final EdgeInsets? tablePadding; final bool dowVisible; const CalendarPage({ @@ -20,18 +21,22 @@ class CalendarPage extends StatelessWidget { this.dowDecoration, this.rowDecoration, this.tableBorder, + this.tablePadding, this.dowVisible = true, }) : assert(!dowVisible || dowBuilder != null), super(key: key); @override Widget build(BuildContext context) { - return Table( - border: tableBorder, - children: [ - if (dowVisible) _buildDaysOfWeek(context), - ..._buildCalendarDays(context), - ], + return Padding( + padding: tablePadding ?? EdgeInsets.zero, + child: Table( + border: tableBorder, + children: [ + if (dowVisible) _buildDaysOfWeek(context), + ..._buildCalendarDays(context), + ], + ), ); } From 276d6b90de16782e66a5dc58d7037c304b787aca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Wed, 20 Jul 2022 20:24:03 +0200 Subject: [PATCH 133/164] Adapt week number calculation --- lib/src/table_calendar.dart | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index 35e36d31..3f083166 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -510,15 +510,14 @@ class _TableCalendarState extends State> { }, weekNumbersVisible: widget.weekNumbersVisible, weekNumberBuilder: (BuildContext context, DateTime day) { - final weekNumber = isoWeekNumber(day); + final weekNumber = _calculateWeekNumber(day); Widget? cell = widget.calendarBuilders.weekNumberBuilder ?.call(context, weekNumber); if (cell == null) { cell = Padding( padding: const EdgeInsets.symmetric(horizontal: 4), - child: Container( - alignment: Alignment.center, + child: Center( child: Text( weekNumber.toString(), style: widget.calendarStyle.weekNumberTextStyle, @@ -569,19 +568,6 @@ class _TableCalendarState extends State> { ); } - int isoWeekNumber(DateTime date) { - int daysToAdd = DateTime.thursday - date.weekday; - DateTime thursdayDate = daysToAdd > 0 - ? date.add(Duration(days: daysToAdd)) - : date.subtract(Duration(days: daysToAdd.abs())); - int dayOfYearThursday = dayOfYear(thursdayDate); - return 1 + ((dayOfYearThursday - 1) / 7).floor(); - } - - int dayOfYear(DateTime date) { - return date.difference(DateTime(date.year, 1, 1)).inDays; - } - Widget _buildCell(DateTime day, DateTime focusedDay) { final isOutside = day.month != focusedDay.month; @@ -719,6 +705,20 @@ class _TableCalendarState extends State> { ); } + int _calculateWeekNumber(DateTime date) { + final middleDay = date.add(const Duration(days: 3)); + final dayOfYear = _dayOfYear(middleDay); + + return 1 + ((dayOfYear - 1) / 7).floor(); + } + + int _dayOfYear(DateTime date) { + return normalizeDate(date) + .difference(DateTime.utc(date.year, 1, 1)) + .inDays + + 1; + } + bool _isWithinRange(DateTime day, DateTime start, DateTime end) { if (isSameDay(day, start) || isSameDay(day, end)) { return true; From 59f4140fcb61f99fa4c143c008c6962046ae79cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Tue, 6 Sep 2022 14:08:54 +0200 Subject: [PATCH 134/164] Add Material parent widget --- test/format_button_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/format_button_test.dart b/test/format_button_test.dart index 0dd69c8e..d562260d 100644 --- a/test/format_button_test.dart +++ b/test/format_button_test.dart @@ -11,7 +11,7 @@ import 'common.dart'; Widget setupTestWidget(Widget child) { return Directionality( textDirection: TextDirection.ltr, - child: child, + child: Material(child: child), ); } From 8e6842c1158451be06847cd7258723b4ae2f280f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Tue, 6 Sep 2022 14:09:21 +0200 Subject: [PATCH 135/164] Update BasicsExample --- example/lib/pages/basics_example.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/example/lib/pages/basics_example.dart b/example/lib/pages/basics_example.dart index 7034a995..6a8f91b6 100644 --- a/example/lib/pages/basics_example.dart +++ b/example/lib/pages/basics_example.dart @@ -56,8 +56,6 @@ class _TableBasicsExampleState extends State { // No need to call `setState()` here _focusedDay = focusedDay; }, - // Enable week numbers (disabled by default). - weekNumbersVisible: true, ), ); } From ea077a8e53e06649323bde71737a41470ecaa8ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Tue, 6 Sep 2022 14:13:13 +0200 Subject: [PATCH 136/164] Release version 3.0.7 --- CHANGELOG.md | 4 ++++ README.md | 2 +- example/pubspec.lock | 2 +- pubspec.yaml | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8365c5f7..f609f694 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## [3.0.7] + +* Added week numbering feature + ## [3.0.6] * Fixed issue with missing Flutter Web platform tag diff --git a/README.md b/README.md index e61d33fc..a53204a8 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Add the following line to `pubspec.yaml`: ```yaml dependencies: - table_calendar: ^3.0.6 + table_calendar: ^3.0.7 ``` ### Basic setup diff --git a/example/pubspec.lock b/example/pubspec.lock index 33835374..62504b33 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -148,7 +148,7 @@ packages: path: ".." relative: true source: path - version: "3.0.6" + version: "3.0.7" term_glyph: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 54f8f5dd..e1ef825a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: table_calendar description: Highly customizable, feature-packed calendar widget for Flutter. -version: 3.0.6 +version: 3.0.7 author: Aleksander Woźniak homepage: https://github.com/aleksanderwozniak/table_calendar From 0e7222de10a26579c5dc1cf45a0ac9b9b6384670 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 30 Oct 2022 17:30:48 +0100 Subject: [PATCH 137/164] Update complex example --- example/lib/pages/complex_example.dart | 3 ++- lib/src/widgets/calendar_page.dart | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/example/lib/pages/complex_example.dart b/example/lib/pages/complex_example.dart index e30e806e..61635b3c 100644 --- a/example/lib/pages/complex_example.dart +++ b/example/lib/pages/complex_example.dart @@ -15,13 +15,14 @@ class TableComplexExample extends StatefulWidget { } class _TableComplexExampleState extends State { - late final PageController _pageController; late final ValueNotifier> _selectedEvents; final ValueNotifier _focusedDay = ValueNotifier(DateTime.now()); final Set _selectedDays = LinkedHashSet( equals: isSameDay, hashCode: getHashCode, ); + + late PageController _pageController; CalendarFormat _calendarFormat = CalendarFormat.month; RangeSelectionMode _rangeSelectionMode = RangeSelectionMode.toggledOff; DateTime? _rangeStart; diff --git a/lib/src/widgets/calendar_page.dart b/lib/src/widgets/calendar_page.dart index 713591ac..a13cb62b 100644 --- a/lib/src/widgets/calendar_page.dart +++ b/lib/src/widgets/calendar_page.dart @@ -57,12 +57,14 @@ class CalendarPage extends StatelessWidget { Widget _buildWeekNumbers(BuildContext context) { final rowAmount = visibleDays.length ~/ 7; + return Column( children: [ if (dowVisible) SizedBox(height: dowHeight ?? 0), ...List.generate(rowAmount, (index) => index * 7) .map((index) => Expanded( - child: weekNumberBuilder!(context, visibleDays[index]))) + child: weekNumberBuilder!(context, visibleDays[index]), + )) .toList() ], ); From 41dfa62bfbb66f53f7c79565cb7d9772139a72b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 30 Oct 2022 17:31:17 +0100 Subject: [PATCH 138/164] Update dependencies --- example/ios/Flutter/AppFrameworkInfo.plist | 2 +- example/ios/Runner.xcodeproj/project.pbxproj | 6 ++-- example/pubspec.lock | 31 ++++++++------------ pubspec.lock | 31 ++++++++------------ 4 files changed, 28 insertions(+), 42 deletions(-) diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist index f2872cf4..4f8d4d24 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 9.0 + 11.0 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 9690c8f2..bb9a61d7 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -272,7 +272,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -354,7 +354,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -403,7 +403,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; diff --git a/example/pubspec.lock b/example/pubspec.lock index 62504b33..dfe0f8e1 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -7,7 +7,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.8.2" + version: "2.9.0" boolean_selector: dependency: transitive description: @@ -21,21 +21,14 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.1" + version: "1.2.1" clock: dependency: transitive description: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.1" collection: dependency: transitive description: @@ -56,7 +49,7 @@ packages: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.3.1" flutter: dependency: "direct main" description: flutter @@ -80,28 +73,28 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.11" + version: "0.12.12" material_color_utilities: dependency: transitive description: name: material_color_utilities url: "https://pub.dartlang.org" source: hosted - version: "0.1.4" + version: "0.1.5" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.7.0" + version: "1.8.0" path: dependency: transitive description: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.1" + version: "1.8.2" simple_gesture_detector: dependency: transitive description: @@ -120,7 +113,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.2" + version: "1.9.0" stack_trace: dependency: transitive description: @@ -141,7 +134,7 @@ packages: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.1" table_calendar: dependency: "direct main" description: @@ -155,14 +148,14 @@ packages: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.2.1" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.9" + version: "0.4.12" vector_math: dependency: transitive description: diff --git a/pubspec.lock b/pubspec.lock index e28f35c5..885327d3 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,7 +7,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.8.2" + version: "2.9.0" boolean_selector: dependency: transitive description: @@ -21,21 +21,14 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.1" + version: "1.2.1" clock: dependency: transitive description: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.1" collection: dependency: transitive description: @@ -49,7 +42,7 @@ packages: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.3.1" flutter: dependency: "direct main" description: flutter @@ -73,28 +66,28 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.11" + version: "0.12.12" material_color_utilities: dependency: transitive description: name: material_color_utilities url: "https://pub.dartlang.org" source: hosted - version: "0.1.4" + version: "0.1.5" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.7.0" + version: "1.8.0" path: dependency: transitive description: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.1" + version: "1.8.2" simple_gesture_detector: dependency: "direct main" description: @@ -113,7 +106,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.2" + version: "1.9.0" stack_trace: dependency: transitive description: @@ -134,21 +127,21 @@ packages: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.1" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.2.1" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.9" + version: "0.4.12" vector_math: dependency: transitive description: From cfdc5b5ce6d2aa40c03fb8db8f9372145ab2bd21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 30 Oct 2022 17:34:15 +0100 Subject: [PATCH 139/164] Release version 3.0.8 --- CHANGELOG.md | 4 ++++ README.md | 2 +- example/pubspec.lock | 2 +- pubspec.yaml | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f609f694..d0541c0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## [3.0.8] + +* Added tablePadding property to CalendarStyle + ## [3.0.7] * Added week numbering feature diff --git a/README.md b/README.md index a53204a8..87ed372d 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Add the following line to `pubspec.yaml`: ```yaml dependencies: - table_calendar: ^3.0.7 + table_calendar: ^3.0.8 ``` ### Basic setup diff --git a/example/pubspec.lock b/example/pubspec.lock index dfe0f8e1..293987e4 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -141,7 +141,7 @@ packages: path: ".." relative: true source: path - version: "3.0.7" + version: "3.0.8" term_glyph: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index e1ef825a..b544fbbb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: table_calendar description: Highly customizable, feature-packed calendar widget for Flutter. -version: 3.0.7 +version: 3.0.8 author: Aleksander Woźniak homepage: https://github.com/aleksanderwozniak/table_calendar From 5bd3a8fecb7e3d684351b2afc67696b9e4dcf551 Mon Sep 17 00:00:00 2001 From: porum Date: Wed, 23 Nov 2022 16:39:21 +0800 Subject: [PATCH 140/164] Fix: Targeting S+ (version 31 and above) requires that an explicit value for android:exported be defined when intent filters are present. --- example/android/app/src/main/AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 1dcbe397..e8825bfa 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -11,6 +11,7 @@ android:icon="@mipmap/ic_launcher"> Date: Fri, 6 Jan 2023 17:06:14 +0100 Subject: [PATCH 141/164] Update intl to 0.18.0 --- example/pubspec.lock | 2 +- pubspec.lock | 2 +- pubspec.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index 293987e4..f0157969 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -66,7 +66,7 @@ packages: name: intl url: "https://pub.dartlang.org" source: hosted - version: "0.17.0" + version: "0.18.0" matcher: dependency: transitive description: diff --git a/pubspec.lock b/pubspec.lock index 885327d3..f13096b0 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -59,7 +59,7 @@ packages: name: intl url: "https://pub.dartlang.org" source: hosted - version: "0.17.0" + version: "0.18.0" matcher: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index b544fbbb..ad72a56e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,7 +12,7 @@ dependencies: flutter: sdk: flutter - intl: ^0.17.0 + intl: ^0.18.0 simple_gesture_detector: ^0.2.0 dev_dependencies: From 05db2a2024a78f5ade30eb7b87b0037a829f8a8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Fri, 6 Jan 2023 17:09:16 +0100 Subject: [PATCH 142/164] Release version 3.0.9 --- CHANGELOG.md | 5 +++++ README.md | 2 +- example/pubspec.lock | 2 +- pubspec.yaml | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d0541c0d..540af589 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## [3.0.9] + +* Updated intl version to 0.18.0 +* Added explicit android:exported value to AndroidManifest + ## [3.0.8] * Added tablePadding property to CalendarStyle diff --git a/README.md b/README.md index 87ed372d..5c7f72a2 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Add the following line to `pubspec.yaml`: ```yaml dependencies: - table_calendar: ^3.0.8 + table_calendar: ^3.0.9 ``` ### Basic setup diff --git a/example/pubspec.lock b/example/pubspec.lock index f0157969..40582f4c 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -141,7 +141,7 @@ packages: path: ".." relative: true source: path - version: "3.0.8" + version: "3.0.9" term_glyph: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index ad72a56e..f87cfdc8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: table_calendar description: Highly customizable, feature-packed calendar widget for Flutter. -version: 3.0.8 +version: 3.0.9 author: Aleksander Woźniak homepage: https://github.com/aleksanderwozniak/table_calendar From e11c667636d0ba000857505d960df151964b269b Mon Sep 17 00:00:00 2001 From: Roman Nakonechnyi Date: Tue, 10 Jan 2023 13:21:41 +0000 Subject: [PATCH 143/164] Add loadEventsForDisabledDays to allow loading events for disabled days too --- lib/src/table_calendar.dart | 8 ++++++- test/table_calendar_test.dart | 44 +++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index bcd52656..b1d650f3 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -161,6 +161,11 @@ class TableCalendar extends StatefulWidget { /// * `RangeSelectionMode.enforced` - range selection is always on. final RangeSelectionMode rangeSelectionMode; + /// Allows to load events for days that are not enabled + /// If `true` it will ignore `enabledDayPredicate` when calling `eventLoader`. + /// If `false` then `enabledDayPredicate` will be used to check when to call `eventLoader` + final bool loadEventsForDisabledDays; + /// Function that assigns a list of events to a specified day. final List Function(DateTime day)? eventLoader; @@ -248,6 +253,7 @@ class TableCalendar extends StatefulWidget { this.rangeSelectionMode = RangeSelectionMode.toggledOff, this.eventLoader, this.enabledDayPredicate, + this.loadEventsForDisabledDays = false, this.selectedDayPredicate, this.holidayPredicate, this.onRangeSelected, @@ -640,7 +646,7 @@ class _TableCalendarState extends State> { children.add(content); - if (!isDisabled) { + if (widget.loadEventsForDisabledDays || !isDisabled) { final events = widget.eventLoader?.call(day) ?? []; Widget? markerWidget = widget.calendarBuilders.markerBuilder?.call(context, day, events); diff --git a/test/table_calendar_test.dart b/test/table_calendar_test.dart index 3361255b..e3d3c806 100644 --- a/test/table_calendar_test.dart +++ b/test/table_calendar_test.dart @@ -460,6 +460,50 @@ void main() { }, ); + testWidgets( + 'Event loader is called for disabled days when loadEventForDisabledDays is set to true', + (tester) async { + final eventDay = DateTime.utc(2021, 7, 20); + + await tester.pumpWidget(setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + loadEventsForDisabledDays: true, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + eventLoader: (day) { + if (day.day == eventDay.day && day.month == eventDay.month) { + return ['Event 1', 'Event 2', 'Event 3']; + } + + return []; + }, + enabledDayPredicate: (day) => false, + ), + )); + + final eventDayKey = cellContentKey(eventDay); + final eventDayCellContent = find.byKey(eventDayKey); + + final eventDayStack = find.ancestor( + of: eventDayCellContent, + matching: find.byType(Stack), + ); + + final eventMarkers = tester.widgetList( + find.descendant( + of: eventDayStack, + matching: find.byWidgetPredicate( + (Widget marker) => marker is Container && marker.child == null, + ), + ), + ); + + expect(eventMarkers.length, 3); + }, + ); + testWidgets( 'currentDay correctly marks given day as today', (tester) async { From 347d8591bd861e84d0add89e27838dc21369602d Mon Sep 17 00:00:00 2001 From: Arijit <59216699+arijit121@users.noreply.github.com> Date: Fri, 26 Jan 2024 00:17:15 +0530 Subject: [PATCH 144/164] Update pubspec.yaml --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index f87cfdc8..f8ea60e7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,7 +12,7 @@ dependencies: flutter: sdk: flutter - intl: ^0.18.0 + intl: ^0.19.0 simple_gesture_detector: ^0.2.0 dev_dependencies: From d33bfa516b1a40410c7ed5eb049b2ef7833e46b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Fri, 9 Feb 2024 12:50:07 +0100 Subject: [PATCH 145/164] Upgrade to Dart 3 --- example/pubspec.lock | 102 +++++++++++++++++++++++++++---------------- example/pubspec.yaml | 2 +- pubspec.lock | 97 +++++++++++++++++++++++++--------------- pubspec.yaml | 3 +- 4 files changed, 129 insertions(+), 75 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index 40582f4c..586843ad 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -5,49 +5,56 @@ packages: dependency: transitive description: name: async - url: "https://pub.dartlang.org" + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" source: hosted - version: "2.9.0" + version: "2.11.0" boolean_selector: dependency: transitive description: name: boolean_selector - url: "https://pub.dartlang.org" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" characters: dependency: transitive description: name: characters - url: "https://pub.dartlang.org" + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" clock: dependency: transitive description: name: clock - url: "https://pub.dartlang.org" + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" source: hosted version: "1.1.1" collection: dependency: transitive description: name: collection - url: "https://pub.dartlang.org" + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.17.2" cupertino_icons: dependency: "direct main" description: name: cupertino_icons - url: "https://pub.dartlang.org" + sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d + url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.0.6" fake_async: dependency: transitive description: name: fake_async - url: "https://pub.dartlang.org" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" source: hosted version: "1.3.1" flutter: @@ -64,42 +71,48 @@ packages: dependency: transitive description: name: intl - url: "https://pub.dartlang.org" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + url: "https://pub.dev" source: hosted - version: "0.18.0" + version: "0.19.0" matcher: dependency: transitive description: name: matcher - url: "https://pub.dartlang.org" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + url: "https://pub.dev" source: hosted - version: "0.12.12" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - url: "https://pub.dartlang.org" + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + url: "https://pub.dev" source: hosted - version: "0.1.5" + version: "0.5.0" meta: dependency: transitive description: name: meta - url: "https://pub.dartlang.org" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" path: dependency: transitive description: name: path - url: "https://pub.dartlang.org" + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + url: "https://pub.dev" source: hosted - version: "1.8.2" + version: "1.8.3" simple_gesture_detector: dependency: transitive description: name: simple_gesture_detector - url: "https://pub.dartlang.org" + sha256: "86d08f85f1f58583b7b4b941d989f48ea6ce08c1724a1d10954a277c2ec36592" + url: "https://pub.dev" source: hosted version: "0.2.0" sky_engine: @@ -111,30 +124,34 @@ packages: dependency: transitive description: name: source_span - url: "https://pub.dartlang.org" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - url: "https://pub.dartlang.org" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - url: "https://pub.dartlang.org" + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" string_scanner: dependency: transitive description: name: string_scanner - url: "https://pub.dartlang.org" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.2.0" table_calendar: dependency: "direct main" description: @@ -146,23 +163,34 @@ packages: dependency: transitive description: name: term_glyph - url: "https://pub.dartlang.org" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" source: hosted version: "1.2.1" test_api: dependency: transitive description: name: test_api - url: "https://pub.dartlang.org" + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + url: "https://pub.dev" source: hosted - version: "0.4.12" + version: "0.6.0" vector_math: dependency: transitive description: name: vector_math - url: "https://pub.dartlang.org" + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + web: + dependency: transitive + description: + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "0.1.4-beta" sdks: - dart: ">=2.17.0-0 <3.0.0" + dart: ">=3.1.0-185.0.dev <4.0.0" flutter: ">=1.17.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index d72ead8a..85145c18 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -18,7 +18,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: ">=2.12.0 <3.0.0" + sdk: ">=3.0.0 <4.0.0" dependencies: flutter: diff --git a/pubspec.lock b/pubspec.lock index f13096b0..36b6452e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,42 +5,48 @@ packages: dependency: transitive description: name: async - url: "https://pub.dartlang.org" + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" source: hosted - version: "2.9.0" + version: "2.11.0" boolean_selector: dependency: transitive description: name: boolean_selector - url: "https://pub.dartlang.org" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" characters: dependency: transitive description: name: characters - url: "https://pub.dartlang.org" + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" clock: dependency: transitive description: name: clock - url: "https://pub.dartlang.org" + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" source: hosted version: "1.1.1" collection: dependency: transitive description: name: collection - url: "https://pub.dartlang.org" + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.17.2" fake_async: dependency: transitive description: name: fake_async - url: "https://pub.dartlang.org" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" source: hosted version: "1.3.1" flutter: @@ -57,42 +63,48 @@ packages: dependency: "direct main" description: name: intl - url: "https://pub.dartlang.org" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + url: "https://pub.dev" source: hosted - version: "0.18.0" + version: "0.19.0" matcher: dependency: transitive description: name: matcher - url: "https://pub.dartlang.org" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + url: "https://pub.dev" source: hosted - version: "0.12.12" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - url: "https://pub.dartlang.org" + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + url: "https://pub.dev" source: hosted - version: "0.1.5" + version: "0.5.0" meta: dependency: transitive description: name: meta - url: "https://pub.dartlang.org" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" path: dependency: transitive description: name: path - url: "https://pub.dartlang.org" + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + url: "https://pub.dev" source: hosted - version: "1.8.2" + version: "1.8.3" simple_gesture_detector: dependency: "direct main" description: name: simple_gesture_detector - url: "https://pub.dartlang.org" + sha256: "86d08f85f1f58583b7b4b941d989f48ea6ce08c1724a1d10954a277c2ec36592" + url: "https://pub.dev" source: hosted version: "0.2.0" sky_engine: @@ -104,51 +116,66 @@ packages: dependency: transitive description: name: source_span - url: "https://pub.dartlang.org" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - url: "https://pub.dartlang.org" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - url: "https://pub.dartlang.org" + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" string_scanner: dependency: transitive description: name: string_scanner - url: "https://pub.dartlang.org" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.2.0" term_glyph: dependency: transitive description: name: term_glyph - url: "https://pub.dartlang.org" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" source: hosted version: "1.2.1" test_api: dependency: transitive description: name: test_api - url: "https://pub.dartlang.org" + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + url: "https://pub.dev" source: hosted - version: "0.4.12" + version: "0.6.0" vector_math: dependency: transitive description: name: vector_math - url: "https://pub.dartlang.org" + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + web: + dependency: transitive + description: + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "0.1.4-beta" sdks: - dart: ">=2.17.0-0 <3.0.0" + dart: ">=3.1.0-185.0.dev <4.0.0" flutter: ">=1.17.0" diff --git a/pubspec.yaml b/pubspec.yaml index f8ea60e7..8e42b1ce 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,8 +5,7 @@ author: Aleksander Woźniak homepage: https://github.com/aleksanderwozniak/table_calendar environment: - sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.17.0" + sdk: ">=3.0.0 <4.0.0" dependencies: flutter: From c757c1c07f1aab8e9f117eb54160cab9a605e87e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Fri, 9 Feb 2024 12:50:25 +0100 Subject: [PATCH 146/164] Update build.gradle --- example/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/android/build.gradle b/example/android/build.gradle index 714549c2..05744a00 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -26,6 +26,6 @@ subprojects { project.evaluationDependsOn(':app') } -task clean(type: Delete) { +tasks.register("clean", Delete) { delete rootProject.buildDir } From 103bcb623521b10303e05724feb82649ceee9e78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Fri, 9 Feb 2024 12:58:05 +0100 Subject: [PATCH 147/164] Release version 3.1.0 --- CHANGELOG.md | 5 +++++ README.md | 2 +- example/pubspec.lock | 2 +- pubspec.yaml | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 540af589..a7122cd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## [3.1.0] + +* Upgraded to Dart 3 +* Updated intl version to 0.19.0 + ## [3.0.9] * Updated intl version to 0.18.0 diff --git a/README.md b/README.md index 5c7f72a2..3c51c800 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Add the following line to `pubspec.yaml`: ```yaml dependencies: - table_calendar: ^3.0.9 + table_calendar: ^3.1.0 ``` ### Basic setup diff --git a/example/pubspec.lock b/example/pubspec.lock index 586843ad..9ba72e01 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -158,7 +158,7 @@ packages: path: ".." relative: true source: path - version: "3.0.9" + version: "3.1.0" term_glyph: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 8e42b1ce..d237b937 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: table_calendar description: Highly customizable, feature-packed calendar widget for Flutter. -version: 3.0.9 +version: 3.1.0 author: Aleksander Woźniak homepage: https://github.com/aleksanderwozniak/table_calendar From 8d98b23cacee98a88aed2ecb065fc405706f3785 Mon Sep 17 00:00:00 2001 From: Michael Soliman Date: Sat, 24 Feb 2024 20:01:59 +0200 Subject: [PATCH 148/164] feat: cell day localization (#763) --- example/pubspec.lock | 20 ++++++++++++++++ lib/src/widgets/cell_content.dart | 2 +- pubspec.lock | 20 ++++++++++++++++ test/cell_content_test.dart | 38 +++++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 1 deletion(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index 9ba72e01..50c66a1f 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -15,8 +15,11 @@ packages: name: boolean_selector sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" url: "https://pub.dev" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" source: hosted version: "2.1.1" + version: "2.1.1" characters: dependency: transitive description: @@ -31,6 +34,8 @@ packages: name: clock sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf url: "https://pub.dev" + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" source: hosted version: "1.1.1" collection: @@ -55,6 +60,8 @@ packages: name: fake_async sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" url: "https://pub.dev" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" source: hosted version: "1.3.1" flutter: @@ -113,6 +120,8 @@ packages: name: simple_gesture_detector sha256: "86d08f85f1f58583b7b4b941d989f48ea6ce08c1724a1d10954a277c2ec36592" url: "https://pub.dev" + sha256: "86d08f85f1f58583b7b4b941d989f48ea6ce08c1724a1d10954a277c2ec36592" + url: "https://pub.dev" source: hosted version: "0.2.0" sky_engine: @@ -134,24 +143,33 @@ packages: name: stack_trace sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 url: "https://pub.dev" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" source: hosted version: "1.11.0" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" url: "https://pub.dev" + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" source: hosted version: "2.1.1" + version: "2.1.1" string_scanner: dependency: transitive description: name: string_scanner sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" url: "https://pub.dev" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" source: hosted version: "1.2.0" + version: "1.2.0" table_calendar: dependency: "direct main" description: @@ -165,6 +183,8 @@ packages: name: term_glyph sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 url: "https://pub.dev" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" source: hosted version: "1.2.1" test_api: diff --git a/lib/src/widgets/cell_content.dart b/lib/src/widgets/cell_content.dart index eb75716c..a3276d5b 100644 --- a/lib/src/widgets/cell_content.dart +++ b/lib/src/widgets/cell_content.dart @@ -60,7 +60,7 @@ class CellContent extends StatelessWidget { ); } - final text = '${day.day}'; + final text = DateFormat.d(locale).format(day); final margin = calendarStyle.cellMargin; final padding = calendarStyle.cellPadding; final alignment = calendarStyle.cellAlignment; diff --git a/pubspec.lock b/pubspec.lock index 36b6452e..5db1767a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -15,8 +15,11 @@ packages: name: boolean_selector sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" url: "https://pub.dev" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" source: hosted version: "2.1.1" + version: "2.1.1" characters: dependency: transitive description: @@ -31,6 +34,8 @@ packages: name: clock sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf url: "https://pub.dev" + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" source: hosted version: "1.1.1" collection: @@ -47,6 +52,8 @@ packages: name: fake_async sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" url: "https://pub.dev" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" source: hosted version: "1.3.1" flutter: @@ -105,6 +112,8 @@ packages: name: simple_gesture_detector sha256: "86d08f85f1f58583b7b4b941d989f48ea6ce08c1724a1d10954a277c2ec36592" url: "https://pub.dev" + sha256: "86d08f85f1f58583b7b4b941d989f48ea6ce08c1724a1d10954a277c2ec36592" + url: "https://pub.dev" source: hosted version: "0.2.0" sky_engine: @@ -126,30 +135,41 @@ packages: name: stack_trace sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 url: "https://pub.dev" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" source: hosted version: "1.11.0" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" url: "https://pub.dev" + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" source: hosted version: "2.1.1" + version: "2.1.1" string_scanner: dependency: transitive description: name: string_scanner sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" url: "https://pub.dev" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" source: hosted version: "1.2.0" + version: "1.2.0" term_glyph: dependency: transitive description: name: term_glyph sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 url: "https://pub.dev" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" source: hosted version: "1.2.1" test_api: diff --git a/test/cell_content_test.dart b/test/cell_content_test.dart index 514c129d..40ade637 100644 --- a/test/cell_content_test.dart +++ b/test/cell_content_test.dart @@ -3,6 +3,8 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:intl/date_symbol_data_local.dart'; +import 'package:intl/intl.dart' hide TextDirection; import 'package:table_calendar/src/widgets/cell_content.dart'; import 'package:table_calendar/table_calendar.dart'; @@ -19,6 +21,7 @@ Widget setupTestWidget( bool isWithinRange = false, bool isHoliday = false, bool isTodayHighlighted = true, + String? locale, }) { final calendarStyle = CalendarStyle(); @@ -39,6 +42,7 @@ Widget setupTestWidget( isWithinRange: isWithinRange, isHoliday: isHoliday, isTodayHighlighted: isTodayHighlighted, + locale: locale, ), ); } @@ -310,4 +314,38 @@ void main() { }, ); }); + + group('CalendarBuilders Locale test:', () { + testWidgets('en locale', (tester) async { + final locale = 'en'; + initializeDateFormatting(locale, null); + + final cellDay = DateTime.utc(2021, 7, 15); + await tester.pumpWidget( + setupTestWidget( + cellDay, + locale: locale, + ), + ); + + final dayFinder = find.text(DateFormat.d(locale).format(cellDay)); + expect(dayFinder, findsOneWidget); + }); + + testWidgets('ar locale', (tester) async { + final locale = 'ar'; + initializeDateFormatting(locale, null); + + final cellDay = DateTime.utc(2021, 7, 15); + await tester.pumpWidget( + setupTestWidget( + cellDay, + locale: locale, + ), + ); + + final dayFinder = find.text(DateFormat.d(locale).format(cellDay)); + expect(dayFinder, findsOneWidget); + }); + }); } From ea2c6c148bb3dfa97d33cc2b600103668e3a04b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sat, 24 Feb 2024 19:11:40 +0100 Subject: [PATCH 149/164] Upgrade dependencies, add launch.json config --- .vscode/launch.json | 28 ++++++++++++++++++ example/android/app/build.gradle | 2 +- example/pubspec.lock | 50 ++++++++++---------------------- pubspec.lock | 50 ++++++++++---------------------- 4 files changed, 59 insertions(+), 71 deletions(-) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..4566494e --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,28 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "example", + "cwd": "example", + "request": "launch", + "type": "dart" + }, + { + "name": "example (profile mode)", + "cwd": "example", + "request": "launch", + "type": "dart", + "flutterMode": "profile" + }, + { + "name": "example (release mode)", + "cwd": "example", + "request": "launch", + "type": "dart", + "flutterMode": "release" + } + ] +} \ No newline at end of file diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index f95d0d7f..c90f8af2 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -39,7 +39,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.example.example" - minSdkVersion 16 + minSdkVersion flutter.minSdkVersion targetSdkVersion 31 versionCode flutterVersionCode.toInteger() versionName flutterVersionName diff --git a/example/pubspec.lock b/example/pubspec.lock index 50c66a1f..678a61d9 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -15,11 +15,8 @@ packages: name: boolean_selector sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" url: "https://pub.dev" - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" - url: "https://pub.dev" source: hosted version: "2.1.1" - version: "2.1.1" characters: dependency: transitive description: @@ -34,18 +31,16 @@ packages: name: clock sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf url: "https://pub.dev" - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf - url: "https://pub.dev" source: hosted version: "1.1.1" collection: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" cupertino_icons: dependency: "direct main" description: @@ -60,8 +55,6 @@ packages: name: fake_async sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" url: "https://pub.dev" - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" - url: "https://pub.dev" source: hosted version: "1.3.1" flutter: @@ -102,10 +95,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" path: dependency: transitive description: @@ -118,12 +111,10 @@ packages: dependency: transitive description: name: simple_gesture_detector - sha256: "86d08f85f1f58583b7b4b941d989f48ea6ce08c1724a1d10954a277c2ec36592" - url: "https://pub.dev" - sha256: "86d08f85f1f58583b7b4b941d989f48ea6ce08c1724a1d10954a277c2ec36592" + sha256: ba2cd5af24ff20a0b8d609cec3f40e5b0744d2a71804a2616ae086b9c19d19a3 url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.2.1" sky_engine: dependency: transitive description: flutter @@ -141,35 +132,26 @@ packages: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 - url: "https://pub.dev" - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" - url: "https://pub.dev" - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" - version: "2.1.1" + version: "2.1.2" string_scanner: dependency: transitive description: name: string_scanner sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" url: "https://pub.dev" - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" - url: "https://pub.dev" source: hosted version: "1.2.0" - version: "1.2.0" table_calendar: dependency: "direct main" description: @@ -183,18 +165,16 @@ packages: name: term_glyph sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 url: "https://pub.dev" - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 - url: "https://pub.dev" source: hosted version: "1.2.1" test_api: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.6.1" vector_math: dependency: transitive description: @@ -207,10 +187,10 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" + dart: ">=3.2.0-194.0.dev <4.0.0" flutter: ">=1.17.0" diff --git a/pubspec.lock b/pubspec.lock index 5db1767a..115e68f9 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -15,11 +15,8 @@ packages: name: boolean_selector sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" url: "https://pub.dev" - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" - url: "https://pub.dev" source: hosted version: "2.1.1" - version: "2.1.1" characters: dependency: transitive description: @@ -34,26 +31,22 @@ packages: name: clock sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf url: "https://pub.dev" - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf - url: "https://pub.dev" source: hosted version: "1.1.1" collection: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" fake_async: dependency: transitive description: name: fake_async sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" url: "https://pub.dev" - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" - url: "https://pub.dev" source: hosted version: "1.3.1" flutter: @@ -94,10 +87,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" path: dependency: transitive description: @@ -110,12 +103,10 @@ packages: dependency: "direct main" description: name: simple_gesture_detector - sha256: "86d08f85f1f58583b7b4b941d989f48ea6ce08c1724a1d10954a277c2ec36592" - url: "https://pub.dev" - sha256: "86d08f85f1f58583b7b4b941d989f48ea6ce08c1724a1d10954a277c2ec36592" + sha256: ba2cd5af24ff20a0b8d609cec3f40e5b0744d2a71804a2616ae086b9c19d19a3 url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.2.1" sky_engine: dependency: transitive description: flutter @@ -133,53 +124,42 @@ packages: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 - url: "https://pub.dev" - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" - url: "https://pub.dev" - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" - version: "2.1.1" + version: "2.1.2" string_scanner: dependency: transitive description: name: string_scanner sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" url: "https://pub.dev" - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" - url: "https://pub.dev" source: hosted version: "1.2.0" - version: "1.2.0" term_glyph: dependency: transitive description: name: term_glyph sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 url: "https://pub.dev" - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 - url: "https://pub.dev" source: hosted version: "1.2.1" test_api: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.6.1" vector_math: dependency: transitive description: @@ -192,10 +172,10 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" + dart: ">=3.2.0-194.0.dev <4.0.0" flutter: ">=1.17.0" From b8cbb450e62bf4b52a87549647427856058e4946 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 24 Mar 2024 14:51:17 +0100 Subject: [PATCH 150/164] Release version 3.1.1 --- CHANGELOG.md | 4 ++++ README.md | 2 +- example/pubspec.lock | 2 +- pubspec.yaml | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7122cd7..05acda51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## [3.1.1] + +* Added cell text localization based on current locale + ## [3.1.0] * Upgraded to Dart 3 diff --git a/README.md b/README.md index 3c51c800..f89a6fbb 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Add the following line to `pubspec.yaml`: ```yaml dependencies: - table_calendar: ^3.1.0 + table_calendar: ^3.1.1 ``` ### Basic setup diff --git a/example/pubspec.lock b/example/pubspec.lock index 678a61d9..618525e4 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -158,7 +158,7 @@ packages: path: ".." relative: true source: path - version: "3.1.0" + version: "3.1.1" term_glyph: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index d237b937..d7999699 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: table_calendar description: Highly customizable, feature-packed calendar widget for Flutter. -version: 3.1.0 +version: 3.1.1 author: Aleksander Woźniak homepage: https://github.com/aleksanderwozniak/table_calendar From c4f22830f8ae59f7390e4e38211131d5f170cab8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 9 Jun 2024 22:34:55 +0200 Subject: [PATCH 151/164] Migrate Gradle plugins --- example/android/app/build.gradle | 18 ++++------- example/android/build.gradle | 17 ++--------- .../gradle/wrapper/gradle-wrapper.properties | 2 +- example/android/settings.gradle | 30 ++++++++++++++----- 4 files changed, 31 insertions(+), 36 deletions(-) diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index c90f8af2..b6b288b8 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -1,3 +1,9 @@ +plugins { + id "com.android.application" + id "kotlin-android" + id "dev.flutter.flutter-gradle-plugin" +} + def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { @@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) { } } -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { flutterVersionCode = '1' @@ -21,10 +22,6 @@ if (flutterVersionName == null) { flutterVersionName = '1.0' } -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - android { compileSdkVersion 31 @@ -58,6 +55,3 @@ flutter { source '../..' } -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -} diff --git a/example/android/build.gradle b/example/android/build.gradle index 05744a00..8f31e8ca 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,20 +1,7 @@ -buildscript { - ext.kotlin_version = '1.6.10' - repositories { - google() - jcenter() - } - - dependencies { - classpath 'com.android.tools.build:gradle:4.1.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - allprojects { repositories { google() - jcenter() + mavenCentral() } } @@ -28,4 +15,4 @@ subprojects { tasks.register("clean", Delete) { delete rootProject.buildDir -} +} \ No newline at end of file diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index bc6a58af..cfe88f69 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip diff --git a/example/android/settings.gradle b/example/android/settings.gradle index 44e62bcf..960ad4a9 100644 --- a/example/android/settings.gradle +++ b/example/android/settings.gradle @@ -1,11 +1,25 @@ -include ':app' +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "7.0.0" apply false + id "org.jetbrains.kotlin.android" version "1.6.10" apply false +} + +include ":app" \ No newline at end of file From 9e952b8914371e3c3309e0f2e67eddb792c3975b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 9 Jun 2024 22:36:08 +0200 Subject: [PATCH 152/164] Update dependencies --- example/pubspec.lock | 60 +++++++++++++++++++++++++++++++------------- pubspec.lock | 56 +++++++++++++++++++++++++++++------------ 2 files changed, 82 insertions(+), 34 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index 618525e4..276285b8 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -45,10 +45,10 @@ packages: dependency: "direct main" description: name: cupertino_icons - sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 url: "https://pub.dev" source: hosted - version: "1.0.6" + version: "1.0.8" fake_async: dependency: transitive description: @@ -75,38 +75,62 @@ packages: url: "https://pub.dev" source: hosted version: "0.19.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + url: "https://pub.dev" + source: hosted + version: "10.0.4" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + url: "https://pub.dev" + source: hosted + version: "3.0.3" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" matcher: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.8.0" meta: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.12.0" path: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.0" simple_gesture_detector: dependency: transitive description: @@ -171,10 +195,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.0" vector_math: dependency: transitive description: @@ -183,14 +207,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - web: + vm_service: dependency: transitive description: - name: web - sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + name: vm_service + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "14.2.1" sdks: - dart: ">=3.2.0-194.0.dev <4.0.0" - flutter: ">=1.17.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/pubspec.lock b/pubspec.lock index 115e68f9..19725456 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -67,38 +67,62 @@ packages: url: "https://pub.dev" source: hosted version: "0.19.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + url: "https://pub.dev" + source: hosted + version: "10.0.4" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + url: "https://pub.dev" + source: hosted + version: "3.0.3" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" matcher: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.8.0" meta: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.12.0" path: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.0" simple_gesture_detector: dependency: "direct main" description: @@ -156,10 +180,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.0" vector_math: dependency: transitive description: @@ -168,14 +192,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - web: + vm_service: dependency: transitive description: - name: web - sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + name: vm_service + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "14.2.1" sdks: - dart: ">=3.2.0-194.0.dev <4.0.0" - flutter: ">=1.17.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" From 25ead153b30e2b29e9531ff47c69b37bb4adfa46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 9 Jun 2024 22:39:25 +0200 Subject: [PATCH 153/164] Add dayTextFormatter property to CalendarStyle, revert to original '${date.day}' day cell text formatting --- lib/src/customization/calendar_style.dart | 11 +++++++++++ lib/src/widgets/cell_content.dart | 3 ++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/src/customization/calendar_style.dart b/lib/src/customization/calendar_style.dart index c052540b..abc00151 100644 --- a/lib/src/customization/calendar_style.dart +++ b/lib/src/customization/calendar_style.dart @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import 'package:flutter/widgets.dart'; +import 'package:table_calendar/table_calendar.dart'; /// Class containing styling and configuration for `TableCalendar`'s content. class CalendarStyle { @@ -151,6 +152,15 @@ class CalendarStyle { /// Padding for the internal `Table` widget. final EdgeInsets tablePadding; + /// Use to customize the text within each day cell. + /// Defaults to `'${date.day}'`, to show just the day number. + /// + /// Example usage: + /// ```dart + /// dayTextFormatter: (date, locale) => DateFormat.d(locale).format(date), + /// ``` + final TextFormatter? dayTextFormatter; + /// Creates a `CalendarStyle` used by `TableCalendar` widget. const CalendarStyle({ this.isTodayHighlighted = true, @@ -227,6 +237,7 @@ class CalendarStyle { this.rowDecoration = const BoxDecoration(), this.tableBorder = const TableBorder(), this.tablePadding = const EdgeInsets.all(0), + this.dayTextFormatter, }); } diff --git a/lib/src/widgets/cell_content.dart b/lib/src/widgets/cell_content.dart index a3276d5b..ba4d4ce7 100644 --- a/lib/src/widgets/cell_content.dart +++ b/lib/src/widgets/cell_content.dart @@ -60,7 +60,8 @@ class CellContent extends StatelessWidget { ); } - final text = DateFormat.d(locale).format(day); + final text = + calendarStyle.dayTextFormatter?.call(day, locale) ?? '${day.day}'; final margin = calendarStyle.cellMargin; final padding = calendarStyle.cellPadding; final alignment = calendarStyle.cellAlignment; From 4f784cfed686bd39e7b239231d84a062a4d67d40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 9 Jun 2024 22:52:25 +0200 Subject: [PATCH 154/164] Update CellContent locale tests --- test/cell_content_test.dart | 47 +++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/test/cell_content_test.dart b/test/cell_content_test.dart index 40ade637..a147707e 100644 --- a/test/cell_content_test.dart +++ b/test/cell_content_test.dart @@ -11,6 +11,7 @@ import 'package:table_calendar/table_calendar.dart'; Widget setupTestWidget( DateTime cellDay, { CalendarBuilders calendarBuilders = const CalendarBuilders(), + CalendarStyle calendarStyle = const CalendarStyle(), bool isDisabled = false, bool isToday = false, bool isWeekend = false, @@ -23,8 +24,6 @@ Widget setupTestWidget( bool isTodayHighlighted = true, String? locale, }) { - final calendarStyle = CalendarStyle(); - return Directionality( textDirection: TextDirection.ltr, child: CellContent( @@ -316,7 +315,23 @@ void main() { }); group('CalendarBuilders Locale test:', () { - testWidgets('en locale', (tester) async { + testWidgets('en locale with default dayTextFormatter', (tester) async { + final locale = 'en'; + initializeDateFormatting(locale, null); + + final cellDay = DateTime.utc(2021, 7, 15); + await tester.pumpWidget( + setupTestWidget( + cellDay, + locale: locale, + ), + ); + + final dayFinder = find.text('${cellDay.day}'); + expect(dayFinder, findsOneWidget); + }); + + testWidgets('en locale with custom dayTextFormatter', (tester) async { final locale = 'en'; initializeDateFormatting(locale, null); @@ -325,6 +340,10 @@ void main() { setupTestWidget( cellDay, locale: locale, + calendarStyle: CalendarStyle( + dayTextFormatter: (date, locale) => + DateFormat.d(locale).format(date), + ), ), ); @@ -332,7 +351,7 @@ void main() { expect(dayFinder, findsOneWidget); }); - testWidgets('ar locale', (tester) async { + testWidgets('ar locale with default dayTextFormatter', (tester) async { final locale = 'ar'; initializeDateFormatting(locale, null); @@ -344,6 +363,26 @@ void main() { ), ); + final dayFinder = find.text('${cellDay.day}'); + expect(dayFinder, findsOneWidget); + }); + + testWidgets('ar locale with custom dayTextFormatter', (tester) async { + final locale = 'ar'; + initializeDateFormatting(locale, null); + + final cellDay = DateTime.utc(2021, 7, 15); + await tester.pumpWidget( + setupTestWidget( + cellDay, + locale: locale, + calendarStyle: CalendarStyle( + dayTextFormatter: (date, locale) => + DateFormat.d(locale).format(date), + ), + ), + ); + final dayFinder = find.text(DateFormat.d(locale).format(cellDay)); expect(dayFinder, findsOneWidget); }); From ca01aa6265772dfb475fd2923a68aeaec3185200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Sun, 9 Jun 2024 23:13:25 +0200 Subject: [PATCH 155/164] Release version 3.1.2 --- CHANGELOG.md | 5 +++++ README.md | 2 +- example/pubspec.lock | 2 +- pubspec.yaml | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05acda51..8c8fa09e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## [3.1.2] + +* Added dayTextFormatter property to CalendarStyle that allows to customize the text within day cells +* Reverted the default day cell's text formatting to just the day's number + ## [3.1.1] * Added cell text localization based on current locale diff --git a/README.md b/README.md index f89a6fbb..dd8f2293 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Add the following line to `pubspec.yaml`: ```yaml dependencies: - table_calendar: ^3.1.1 + table_calendar: ^3.1.2 ``` ### Basic setup diff --git a/example/pubspec.lock b/example/pubspec.lock index 276285b8..5505275a 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -182,7 +182,7 @@ packages: path: ".." relative: true source: path - version: "3.1.1" + version: "3.1.2" term_glyph: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index d7999699..6c032a95 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: table_calendar description: Highly customizable, feature-packed calendar widget for Flutter. -version: 3.1.1 +version: 3.1.2 author: Aleksander Woźniak homepage: https://github.com/aleksanderwozniak/table_calendar From 36a4a2492634687bbfaa629682046950ad1593c0 Mon Sep 17 00:00:00 2001 From: Nikolas Rimikis <25266387+Leptopoda@users.noreply.github.com> Date: Thu, 12 Dec 2024 13:56:22 +0100 Subject: [PATCH 156/164] Linting (#777) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add linting * fix lints * format code --------- Co-authored-by: Aleksander Woźniak --- analysis_options.yaml | 25 + lib/src/customization/calendar_builders.dart | 18 +- lib/src/customization/calendar_style.dart | 36 +- lib/src/customization/days_of_week_style.dart | 6 +- lib/src/customization/header_style.dart | 8 +- lib/src/shared/utils.dart | 5 +- lib/src/table_calendar.dart | 88 +-- lib/src/table_calendar_base.dart | 47 +- lib/src/widgets/calendar_core.dart | 55 +- lib/src/widgets/calendar_header.dart | 14 +- lib/src/widgets/calendar_page.dart | 37 +- lib/src/widgets/cell_content.dart | 10 +- lib/src/widgets/custom_icon_button.dart | 6 +- lib/src/widgets/format_button.dart | 6 +- pubspec.yaml | 1 + test/calendar_header_test.dart | 6 +- test/calendar_page_test.dart | 64 +- test/common.dart | 2 +- test/custom_icon_button_test.dart | 2 +- test/format_button_test.dart | 16 +- test/table_calendar_base_test.dart | 51 +- test/table_calendar_test.dart | 644 ++++++++++-------- 22 files changed, 627 insertions(+), 520 deletions(-) create mode 100644 analysis_options.yaml diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 00000000..d7855022 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,25 @@ +# This file configures the analyzer to use the lint rule set from `package:lint` + +# include: package:lint/strict.yaml # For production apps +# include: package:lint/casual.yaml # For code samples, hackathons and other non-production code +include: package:lint/package.yaml # Use this for packages with public API + +# You might want to exclude auto-generated files from dart analysis +analyzer: + exclude: + #- '**.freezed.dart' + #- '**.g.dart' + +# You can customize the lint rules set to your own liking. A list of all rules +# can be found at https://dart-lang.github.io/linter/lints/options/options.html +linter: + rules: + # Util classes are awesome! + # avoid_classes_with_only_static_members: false + + # Make constructors the first thing in every class + # sort_constructors_first: true + + # Choose wisely, but you don't have to + # prefer_double_quotes: true + # prefer_single_quotes: true diff --git a/lib/src/customization/calendar_builders.dart b/lib/src/customization/calendar_builders.dart index af69c23e..6649e0fc 100644 --- a/lib/src/customization/calendar_builders.dart +++ b/lib/src/customization/calendar_builders.dart @@ -3,24 +3,34 @@ import 'package:flutter/widgets.dart'; -import '../shared/utils.dart' show DayBuilder, FocusedDayBuilder; +import 'package:table_calendar/src/shared/utils.dart' + show DayBuilder, FocusedDayBuilder; /// Signature for a function that creates a single event marker for a given `day`. /// Contains a single `event` associated with that `day`. typedef SingleMarkerBuilder = Widget? Function( - BuildContext context, DateTime day, T event); + BuildContext context, + DateTime day, + T event, +); /// Signature for a function that creates an event marker for a given `day`. /// Contains a list of `events` associated with that `day`. typedef MarkerBuilder = Widget? Function( - BuildContext context, DateTime day, List events); + BuildContext context, + DateTime day, + List events, +); /// Signature for a function that creates a background highlight for a given `day`. /// /// Used for highlighting current range selection. /// Contains a value determining if the given `day` falls within the selected range. typedef HighlightBuilder = Widget? Function( - BuildContext context, DateTime day, bool isWithinRange); + BuildContext context, + DateTime day, + bool isWithinRange, +); /// Class containing all custom builders for `TableCalendar`. class CalendarBuilders { diff --git a/lib/src/customization/calendar_style.dart b/lib/src/customization/calendar_style.dart index abc00151..ecfa578e 100644 --- a/lib/src/customization/calendar_style.dart +++ b/lib/src/customization/calendar_style.dart @@ -175,68 +175,68 @@ class CalendarStyle { this.markersAlignment = Alignment.bottomCenter, this.markersMaxCount = 4, this.cellMargin = const EdgeInsets.all(6.0), - this.cellPadding = const EdgeInsets.all(0), + this.cellPadding = EdgeInsets.zero, this.cellAlignment = Alignment.center, this.markersOffset = const PositionedOffset(), this.rangeHighlightColor = const Color(0xFFBBDDFF), this.markerDecoration = const BoxDecoration( - color: const Color(0xFF263238), + color: Color(0xFF263238), shape: BoxShape.circle, ), this.todayTextStyle = const TextStyle( - color: const Color(0xFFFAFAFA), + color: Color(0xFFFAFAFA), fontSize: 16.0, ), // this.todayDecoration = const BoxDecoration( - color: const Color(0xFF9FA8DA), + color: Color(0xFF9FA8DA), shape: BoxShape.circle, ), this.selectedTextStyle = const TextStyle( - color: const Color(0xFFFAFAFA), + color: Color(0xFFFAFAFA), fontSize: 16.0, ), this.selectedDecoration = const BoxDecoration( - color: const Color(0xFF5C6BC0), + color: Color(0xFF5C6BC0), shape: BoxShape.circle, ), this.rangeStartTextStyle = const TextStyle( - color: const Color(0xFFFAFAFA), + color: Color(0xFFFAFAFA), fontSize: 16.0, ), this.rangeStartDecoration = const BoxDecoration( - color: const Color(0xFF6699FF), + color: Color(0xFF6699FF), shape: BoxShape.circle, ), this.rangeEndTextStyle = const TextStyle( - color: const Color(0xFFFAFAFA), + color: Color(0xFFFAFAFA), fontSize: 16.0, ), this.rangeEndDecoration = const BoxDecoration( - color: const Color(0xFF6699FF), + color: Color(0xFF6699FF), shape: BoxShape.circle, ), this.withinRangeTextStyle = const TextStyle(), this.withinRangeDecoration = const BoxDecoration(shape: BoxShape.circle), - this.outsideTextStyle = const TextStyle(color: const Color(0xFFAEAEAE)), + this.outsideTextStyle = const TextStyle(color: Color(0xFFAEAEAE)), this.outsideDecoration = const BoxDecoration(shape: BoxShape.circle), - this.disabledTextStyle = const TextStyle(color: const Color(0xFFBFBFBF)), + this.disabledTextStyle = const TextStyle(color: Color(0xFFBFBFBF)), this.disabledDecoration = const BoxDecoration(shape: BoxShape.circle), - this.holidayTextStyle = const TextStyle(color: const Color(0xFF5C6BC0)), + this.holidayTextStyle = const TextStyle(color: Color(0xFF5C6BC0)), this.holidayDecoration = const BoxDecoration( - border: const Border.fromBorderSide( - const BorderSide(color: const Color(0xFF9FA8DA), width: 1.4), + border: Border.fromBorderSide( + BorderSide(color: Color(0xFF9FA8DA), width: 1.4), ), shape: BoxShape.circle, ), - this.weekendTextStyle = const TextStyle(color: const Color(0xFF5A5A5A)), + this.weekendTextStyle = const TextStyle(color: Color(0xFF5A5A5A)), this.weekendDecoration = const BoxDecoration(shape: BoxShape.circle), this.weekNumberTextStyle = - const TextStyle(fontSize: 12, color: const Color(0xFFBFBFBF)), + const TextStyle(fontSize: 12, color: Color(0xFFBFBFBF)), this.defaultTextStyle = const TextStyle(), this.defaultDecoration = const BoxDecoration(shape: BoxShape.circle), this.rowDecoration = const BoxDecoration(), this.tableBorder = const TableBorder(), - this.tablePadding = const EdgeInsets.all(0), + this.tablePadding = EdgeInsets.zero, this.dayTextFormatter, }); } diff --git a/lib/src/customization/days_of_week_style.dart b/lib/src/customization/days_of_week_style.dart index b379ba94..a3ac65d9 100644 --- a/lib/src/customization/days_of_week_style.dart +++ b/lib/src/customization/days_of_week_style.dart @@ -3,7 +3,7 @@ import 'package:flutter/widgets.dart'; -import '../shared/utils.dart' show TextFormatter; +import 'package:table_calendar/src/shared/utils.dart' show TextFormatter; /// Class containing styling for `TableCalendar`'s days of week panel. class DaysOfWeekStyle { @@ -30,7 +30,7 @@ class DaysOfWeekStyle { const DaysOfWeekStyle({ this.dowTextFormatter, this.decoration = const BoxDecoration(), - this.weekdayStyle = const TextStyle(color: const Color(0xFF4F4F4F)), - this.weekendStyle = const TextStyle(color: const Color(0xFF6A6A6A)), + this.weekdayStyle = const TextStyle(color: Color(0xFF4F4F4F)), + this.weekendStyle = const TextStyle(color: Color(0xFF6A6A6A)), }); } diff --git a/lib/src/customization/header_style.dart b/lib/src/customization/header_style.dart index 3fcc1089..3ebf47e4 100644 --- a/lib/src/customization/header_style.dart +++ b/lib/src/customization/header_style.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; -import '../shared/utils.dart' show TextFormatter; +import 'package:table_calendar/src/shared/utils.dart' show TextFormatter; /// Class containing styling and configuration of `TableCalendar`'s header. class HeaderStyle { @@ -88,10 +88,10 @@ class HeaderStyle { this.titleTextStyle = const TextStyle(fontSize: 17.0), this.formatButtonTextStyle = const TextStyle(fontSize: 14.0), this.formatButtonDecoration = const BoxDecoration( - border: const Border.fromBorderSide(BorderSide()), - borderRadius: const BorderRadius.all(Radius.circular(12.0)), + border: Border.fromBorderSide(BorderSide()), + borderRadius: BorderRadius.all(Radius.circular(12.0)), ), - this.headerMargin = const EdgeInsets.all(0.0), + this.headerMargin = EdgeInsets.zero, this.headerPadding = const EdgeInsets.symmetric(vertical: 8.0), this.formatButtonPadding = const EdgeInsets.symmetric(horizontal: 10.0, vertical: 4.0), diff --git a/lib/src/shared/utils.dart b/lib/src/shared/utils.dart index accc4497..7e63f1ec 100644 --- a/lib/src/shared/utils.dart +++ b/lib/src/shared/utils.dart @@ -9,7 +9,10 @@ typedef DayBuilder = Widget? Function(BuildContext context, DateTime day); /// Signature for a function that creates a widget for a given `day`. /// Additionally, contains the currently focused day. typedef FocusedDayBuilder = Widget? Function( - BuildContext context, DateTime day, DateTime focusedDay); + BuildContext context, + DateTime day, + DateTime focusedDay, +); /// Signature for a function returning text that can be localized and formatted with `DateFormat`. typedef TextFormatter = String Function(DateTime date, dynamic locale); diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index bcd52656..67a0a2ca 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -1,29 +1,36 @@ // Copyright 2019 Aleksander Woźniak // SPDX-License-Identifier: Apache-2.0 +// ignore_for_file: library_private_types_in_public_api + import 'dart:math'; import 'package:flutter/widgets.dart'; import 'package:intl/intl.dart'; import 'package:simple_gesture_detector/simple_gesture_detector.dart'; -import 'customization/calendar_builders.dart'; -import 'customization/calendar_style.dart'; -import 'customization/days_of_week_style.dart'; -import 'customization/header_style.dart'; -import 'shared/utils.dart'; -import 'table_calendar_base.dart'; -import 'widgets/calendar_header.dart'; -import 'widgets/cell_content.dart'; +import 'package:table_calendar/src/customization/calendar_builders.dart'; +import 'package:table_calendar/src/customization/calendar_style.dart'; +import 'package:table_calendar/src/customization/days_of_week_style.dart'; +import 'package:table_calendar/src/customization/header_style.dart'; +import 'package:table_calendar/src/shared/utils.dart'; +import 'package:table_calendar/src/table_calendar_base.dart'; +import 'package:table_calendar/src/widgets/calendar_header.dart'; +import 'package:table_calendar/src/widgets/cell_content.dart'; /// Signature for `onDaySelected` callback. Contains the selected day and focused day. typedef OnDaySelected = void Function( - DateTime selectedDay, DateTime focusedDay); + DateTime selectedDay, + DateTime focusedDay, +); /// Signature for `onRangeSelected` callback. /// Contains start and end of the selected range, as well as currently focused day. typedef OnRangeSelected = void Function( - DateTime? start, DateTime? end, DateTime focusedDay); + DateTime? start, + DateTime? end, + DateTime focusedDay, +); /// Modes that range selection can operate in. enum RangeSelectionMode { disabled, toggledOff, toggledOn, enforced } @@ -206,7 +213,7 @@ class TableCalendar extends StatefulWidget { /// Creates a `TableCalendar` widget. TableCalendar({ - Key? key, + super.key, required DateTime focusedDay, required DateTime firstDay, required DateTime lastDay, @@ -262,15 +269,16 @@ class TableCalendar extends StatefulWidget { this.onCalendarCreated, }) : assert(availableCalendarFormats.keys.contains(calendarFormat)), assert(availableCalendarFormats.length <= CalendarFormat.values.length), - assert(weekendDays.isNotEmpty - ? weekendDays.every( - (day) => day >= DateTime.monday && day <= DateTime.sunday) - : true), + assert( + weekendDays.isNotEmpty && + weekendDays.every( + (day) => day >= DateTime.monday && day <= DateTime.sunday, + ), + ), focusedDay = normalizeDate(focusedDay), firstDay = normalizeDate(firstDay), lastDay = normalizeDate(lastDay), - currentDay = currentDay ?? DateTime.now(), - super(key: key); + currentDay = currentDay ?? DateTime.now(); @override _TableCalendarState createState() => _TableCalendarState(); @@ -512,22 +520,19 @@ class _TableCalendarState extends State> { weekNumbersVisible: widget.weekNumbersVisible, weekNumberBuilder: (BuildContext context, DateTime day) { final weekNumber = _calculateWeekNumber(day); - Widget? cell = widget.calendarBuilders.weekNumberBuilder + final cell = widget.calendarBuilders.weekNumberBuilder ?.call(context, weekNumber); - if (cell == null) { - cell = Padding( - padding: const EdgeInsets.symmetric(horizontal: 4), - child: Center( - child: Text( - weekNumber.toString(), - style: widget.calendarStyle.weekNumberTextStyle, + return cell ?? + Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: Center( + child: Text( + weekNumber.toString(), + style: widget.calendarStyle.weekNumberTextStyle, + ), ), - ), - ); - } - - return cell; + ); }, dowBuilder: (BuildContext context, DateTime day) { Widget? dowCell = @@ -619,7 +624,7 @@ class _TableCalendarState extends State> { final isDisabled = _isDayDisabled(day); final isWeekend = _isWeekend(day, weekendDays: widget.weekendDays); - Widget content = CellContent( + final content = CellContent( key: ValueKey('CellContent-${day.year}-${day.month}-${day.day}'), day: day, focusedDay: focusedDay, @@ -686,10 +691,10 @@ class _TableCalendarState extends State> { return Stack( alignment: widget.calendarStyle.markersAlignment, - children: children, clipBehavior: widget.calendarStyle.canMarkersOverflow ? Clip.none : Clip.hardEdge, + children: children, ); }, ); @@ -714,10 +719,7 @@ class _TableCalendarState extends State> { } int _dayOfYear(DateTime date) { - return normalizeDate(date) - .difference(DateTime.utc(date.year, 1, 1)) - .inDays + - 1; + return normalizeDate(date).difference(DateTime.utc(date.year)).inDays + 1; } bool _isWithinRange(DateTime day, DateTime start, DateTime end) { @@ -739,19 +741,21 @@ class _TableCalendarState extends State> { } bool _isDayAvailable(DateTime day) { - return widget.enabledDayPredicate == null - ? true - : widget.enabledDayPredicate!(day); + if (widget.enabledDayPredicate == null) { + return true; + } + + return widget.enabledDayPredicate!(day); } DateTime _firstDayOfMonth(DateTime month) { - return DateTime.utc(month.year, month.month, 1); + return DateTime.utc(month.year, month.month); } DateTime _lastDayOfMonth(DateTime month) { final date = month.month < 12 - ? DateTime.utc(month.year, month.month + 1, 1) - : DateTime.utc(month.year + 1, 1, 1); + ? DateTime.utc(month.year, month.month + 1) + : DateTime.utc(month.year + 1); return date.subtract(const Duration(days: 1)); } diff --git a/lib/src/table_calendar_base.dart b/lib/src/table_calendar_base.dart index 659a43a7..c0a56cbc 100644 --- a/lib/src/table_calendar_base.dart +++ b/lib/src/table_calendar_base.dart @@ -1,11 +1,13 @@ // Copyright 2019 Aleksander Woźniak // SPDX-License-Identifier: Apache-2.0 +// ignore_for_file: library_private_types_in_public_api + import 'package:flutter/material.dart'; import 'package:simple_gesture_detector/simple_gesture_detector.dart'; -import 'shared/utils.dart'; -import 'widgets/calendar_core.dart'; +import 'package:table_calendar/src/shared/utils.dart'; +import 'package:table_calendar/src/widgets/calendar_core.dart'; class TableCalendarBase extends StatefulWidget { final DateTime firstDay; @@ -38,7 +40,7 @@ class TableCalendarBase extends StatefulWidget { final void Function(PageController pageController)? onCalendarCreated; TableCalendarBase({ - Key? key, + super.key, required this.firstDay, required this.lastDay, required this.focusedDay, @@ -76,8 +78,7 @@ class TableCalendarBase extends StatefulWidget { this.onCalendarCreated, }) : assert(!dowVisible || (dowHeight != null && dowBuilder != null)), assert(isSameDay(focusedDay, firstDay) || focusedDay.isAfter(firstDay)), - assert(isSameDay(focusedDay, lastDay) || focusedDay.isBefore(lastDay)), - super(key: key); + assert(isSameDay(focusedDay, lastDay) || focusedDay.isBefore(lastDay)); @override _TableCalendarBaseState createState() => _TableCalendarBaseState(); @@ -99,7 +100,10 @@ class _TableCalendarBaseState extends State { _pageHeight = ValueNotifier(_getPageHeight(rowCount)); final initialPage = _calculateFocusedPage( - widget.calendarFormat, widget.firstDay, _focusedDay); + widget.calendarFormat, + widget.firstDay, + _focusedDay, + ); _pageController = PageController(initialPage: initialPage); widget.onCalendarCreated?.call(_pageController); @@ -147,10 +151,16 @@ class _TableCalendarBaseState extends State { void _updatePage({bool shouldAnimate = false}) { final currentIndex = _calculateFocusedPage( - widget.calendarFormat, widget.firstDay, _focusedDay); + widget.calendarFormat, + widget.firstDay, + _focusedDay, + ); final endIndex = _calculateFocusedPage( - widget.calendarFormat, widget.firstDay, widget.lastDay); + widget.calendarFormat, + widget.firstDay, + widget.lastDay, + ); if (currentIndex != _previousIndex || currentIndex == 0 || @@ -209,8 +219,8 @@ class _TableCalendarBaseState extends State { constraints: constraints, pageController: _pageController, scrollPhysics: _canScrollHorizontally - ? PageScrollPhysics() - : NeverScrollableScrollPhysics(), + ? const PageScrollPhysics() + : const NeverScrollableScrollPhysics(), firstDay: widget.firstDay, lastDay: widget.lastDay, startingDayOfWeek: widget.startingDayOfWeek, @@ -265,7 +275,10 @@ class _TableCalendarBaseState extends State { } int _calculateFocusedPage( - CalendarFormat format, DateTime startDay, DateTime focusedDay) { + CalendarFormat format, + DateTime startDay, + DateTime focusedDay, + ) { switch (format) { case CalendarFormat.month: return _getMonthCount(startDay, focusedDay); @@ -319,12 +332,12 @@ class _TableCalendarBaseState extends State { } int _getDaysAfter(DateTime lastDay) { - int invertedStartingWeekday = + final invertedStartingWeekday = 8 - getWeekdayNumber(widget.startingDayOfWeek); - int daysAfter = 7 - ((lastDay.weekday + invertedStartingWeekday) % 7); + final daysAfter = 7 - ((lastDay.weekday + invertedStartingWeekday) % 7); if (daysAfter == 7) { - daysAfter = 0; + return 0; } return daysAfter; @@ -336,13 +349,13 @@ class _TableCalendarBaseState extends State { } DateTime _firstDayOfMonth(DateTime month) { - return DateTime.utc(month.year, month.month, 1); + return DateTime.utc(month.year, month.month); } DateTime _lastDayOfMonth(DateTime month) { final date = month.month < 12 - ? DateTime.utc(month.year, month.month + 1, 1) - : DateTime.utc(month.year + 1, 1, 1); + ? DateTime.utc(month.year, month.month + 1) + : DateTime.utc(month.year + 1); return date.subtract(const Duration(days: 1)); } } diff --git a/lib/src/widgets/calendar_core.dart b/lib/src/widgets/calendar_core.dart index d196296f..e2ef0fb9 100644 --- a/lib/src/widgets/calendar_core.dart +++ b/lib/src/widgets/calendar_core.dart @@ -3,11 +3,8 @@ import 'package:flutter/material.dart'; -import '../shared/utils.dart'; -import 'calendar_page.dart'; - -typedef _OnCalendarPageChanged = void Function( - int pageIndex, DateTime focusedDay); +import 'package:table_calendar/src/shared/utils.dart'; +import 'package:table_calendar/src/widgets/calendar_page.dart'; class CalendarCore extends StatelessWidget { final DateTime? focusedDay; @@ -31,10 +28,10 @@ class CalendarCore extends StatelessWidget { final StartingDayOfWeek startingDayOfWeek; final PageController? pageController; final ScrollPhysics? scrollPhysics; - final _OnCalendarPageChanged onPageChanged; + final void Function(int, DateTime) onPageChanged; const CalendarCore({ - Key? key, + super.key, this.dowBuilder, required this.dayBuilder, required this.onPageChanged, @@ -57,8 +54,7 @@ class CalendarCore extends StatelessWidget { this.tableBorder, this.tablePadding, this.scrollPhysics, - }) : assert(!dowVisible || (dowHeight != null && dowBuilder != null)), - super(key: key); + }) : assert(!dowVisible || (dowHeight != null && dowBuilder != null)); @override Widget build(BuildContext context) { @@ -158,7 +154,10 @@ class CalendarCore extends StatelessWidget { } DateTime _getFocusedDay( - CalendarFormat format, DateTime prevFocusedDay, int pageIndex) { + CalendarFormat format, + DateTime prevFocusedDay, + int pageIndex, + ) { if (pageIndex == previousIndex) { return prevFocusedDay; } @@ -171,12 +170,18 @@ class CalendarCore extends StatelessWidget { day = DateTime.utc(prevFocusedDay.year, prevFocusedDay.month + pageDif); break; case CalendarFormat.twoWeeks: - day = DateTime.utc(prevFocusedDay.year, prevFocusedDay.month, - prevFocusedDay.day + pageDif * 14); + day = DateTime.utc( + prevFocusedDay.year, + prevFocusedDay.month, + prevFocusedDay.day + pageDif * 14, + ); break; case CalendarFormat.week: - day = DateTime.utc(prevFocusedDay.year, prevFocusedDay.month, - prevFocusedDay.day + pageDif * 7); + day = DateTime.utc( + prevFocusedDay.year, + prevFocusedDay.month, + prevFocusedDay.day + pageDif * 7, + ); break; } @@ -198,11 +203,17 @@ class CalendarCore extends StatelessWidget { break; case CalendarFormat.twoWeeks: day = DateTime.utc( - firstDay.year, firstDay.month, firstDay.day + pageIndex * 14); + firstDay.year, + firstDay.month, + firstDay.day + pageIndex * 14, + ); break; case CalendarFormat.week: day = DateTime.utc( - firstDay.year, firstDay.month, firstDay.day + pageIndex * 7); + firstDay.year, + firstDay.month, + firstDay.day + pageIndex * 7, + ); break; } @@ -273,13 +284,13 @@ class CalendarCore extends StatelessWidget { } DateTime _firstDayOfMonth(DateTime month) { - return DateTime.utc(month.year, month.month, 1); + return DateTime.utc(month.year, month.month); } DateTime _lastDayOfMonth(DateTime month) { final date = month.month < 12 - ? DateTime.utc(month.year, month.month + 1, 1) - : DateTime.utc(month.year + 1, 1, 1); + ? DateTime.utc(month.year, month.month + 1) + : DateTime.utc(month.year + 1); return date.subtract(const Duration(days: 1)); } @@ -308,11 +319,11 @@ class CalendarCore extends StatelessWidget { } int _getDaysAfter(DateTime lastDay) { - int invertedStartingWeekday = 8 - getWeekdayNumber(startingDayOfWeek); + final invertedStartingWeekday = 8 - getWeekdayNumber(startingDayOfWeek); - int daysAfter = 7 - ((lastDay.weekday + invertedStartingWeekday) % 7); + final daysAfter = 7 - ((lastDay.weekday + invertedStartingWeekday) % 7); if (daysAfter == 7) { - daysAfter = 0; + return 0; } return daysAfter; diff --git a/lib/src/widgets/calendar_header.dart b/lib/src/widgets/calendar_header.dart index cce74329..e6c8047a 100644 --- a/lib/src/widgets/calendar_header.dart +++ b/lib/src/widgets/calendar_header.dart @@ -4,10 +4,11 @@ import 'package:flutter/widgets.dart'; import 'package:intl/intl.dart'; -import '../customization/header_style.dart'; -import '../shared/utils.dart' show CalendarFormat, DayBuilder; -import 'custom_icon_button.dart'; -import 'format_button.dart'; +import 'package:table_calendar/src/customization/header_style.dart'; +import 'package:table_calendar/src/shared/utils.dart' + show CalendarFormat, DayBuilder; +import 'package:table_calendar/src/widgets/custom_icon_button.dart'; +import 'package:table_calendar/src/widgets/format_button.dart'; class CalendarHeader extends StatelessWidget { final dynamic locale; @@ -23,7 +24,7 @@ class CalendarHeader extends StatelessWidget { final DayBuilder? headerTitleBuilder; const CalendarHeader({ - Key? key, + super.key, this.locale, required this.focusedMonth, required this.calendarFormat, @@ -35,7 +36,7 @@ class CalendarHeader extends StatelessWidget { required this.onFormatButtonTap, required this.availableCalendarFormats, this.headerTitleBuilder, - }) : super(key: key); + }); @override Widget build(BuildContext context) { @@ -47,7 +48,6 @@ class CalendarHeader extends StatelessWidget { margin: headerStyle.headerMargin, padding: headerStyle.headerPadding, child: Row( - mainAxisSize: MainAxisSize.max, children: [ if (headerStyle.leftChevronVisible) CustomIconButton( diff --git a/lib/src/widgets/calendar_page.dart b/lib/src/widgets/calendar_page.dart index a13cb62b..b9663fdd 100644 --- a/lib/src/widgets/calendar_page.dart +++ b/lib/src/widgets/calendar_page.dart @@ -17,7 +17,7 @@ class CalendarPage extends StatelessWidget { final double? dowHeight; const CalendarPage({ - Key? key, + super.key, required this.visibleDays, this.dowBuilder, required this.dayBuilder, @@ -30,8 +30,7 @@ class CalendarPage extends StatelessWidget { this.weekNumberVisible = false, this.dowHeight, }) : assert(!dowVisible || (dowHeight != null && dowBuilder != null)), - assert(!weekNumberVisible || weekNumberBuilder != null), - super(key: key); + assert(!weekNumberVisible || weekNumberBuilder != null); @override Widget build(BuildContext context) { @@ -61,11 +60,12 @@ class CalendarPage extends StatelessWidget { return Column( children: [ if (dowVisible) SizedBox(height: dowHeight ?? 0), - ...List.generate(rowAmount, (index) => index * 7) - .map((index) => Expanded( - child: weekNumberBuilder!(context, visibleDays[index]), - )) - .toList() + ...List.generate( + rowAmount, + (index) => Expanded( + child: weekNumberBuilder!(context, visibleDays[index * 7]), + ), + ) ], ); } @@ -76,21 +76,22 @@ class CalendarPage extends StatelessWidget { children: List.generate( 7, (index) => dowBuilder!(context, visibleDays[index]), - ).toList(), + ), ); } List _buildCalendarDays(BuildContext context) { final rowAmount = visibleDays.length ~/ 7; - return List.generate(rowAmount, (index) => index * 7) - .map((index) => TableRow( - decoration: rowDecoration, - children: List.generate( - 7, - (id) => dayBuilder(context, visibleDays[index + id]), - ), - )) - .toList(); + return List.generate( + rowAmount, + (index) => TableRow( + decoration: rowDecoration, + children: List.generate( + 7, + (id) => dayBuilder(context, visibleDays[index * 7 + id]), + ), + ), + ); } } diff --git a/lib/src/widgets/cell_content.dart b/lib/src/widgets/cell_content.dart index ba4d4ce7..9708ea40 100644 --- a/lib/src/widgets/cell_content.dart +++ b/lib/src/widgets/cell_content.dart @@ -4,8 +4,8 @@ import 'package:flutter/widgets.dart'; import 'package:intl/intl.dart'; -import '../customization/calendar_builders.dart'; -import '../customization/calendar_style.dart'; +import 'package:table_calendar/src/customization/calendar_builders.dart'; +import 'package:table_calendar/src/customization/calendar_style.dart'; class CellContent extends StatelessWidget { final DateTime day; @@ -25,7 +25,7 @@ class CellContent extends StatelessWidget { final CalendarBuilders calendarBuilders; const CellContent({ - Key? key, + super.key, required this.day, required this.focusedDay, required this.calendarStyle, @@ -41,7 +41,7 @@ class CellContent extends StatelessWidget { required this.isHoliday, required this.isWeekend, this.locale, - }) : super(key: key); + }); @override Widget build(BuildContext context) { @@ -65,7 +65,7 @@ class CellContent extends StatelessWidget { final margin = calendarStyle.cellMargin; final padding = calendarStyle.cellPadding; final alignment = calendarStyle.cellAlignment; - final duration = const Duration(milliseconds: 250); + const duration = Duration(milliseconds: 250); if (isDisabled) { cell = calendarBuilders.disabledBuilder?.call(context, day, focusedDay) ?? diff --git a/lib/src/widgets/custom_icon_button.dart b/lib/src/widgets/custom_icon_button.dart index 9e652c60..0b31b98a 100644 --- a/lib/src/widgets/custom_icon_button.dart +++ b/lib/src/widgets/custom_icon_button.dart @@ -12,12 +12,12 @@ class CustomIconButton extends StatelessWidget { final EdgeInsets padding; const CustomIconButton({ - Key? key, + super.key, required this.icon, required this.onTap, - this.margin = const EdgeInsets.all(0.0), + this.margin = EdgeInsets.zero, this.padding = const EdgeInsets.all(8.0), - }) : super(key: key); + }); @override Widget build(BuildContext context) { diff --git a/lib/src/widgets/format_button.dart b/lib/src/widgets/format_button.dart index 1c11c643..e8b03f49 100644 --- a/lib/src/widgets/format_button.dart +++ b/lib/src/widgets/format_button.dart @@ -5,7 +5,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import '../shared/utils.dart' show CalendarFormat; +import 'package:table_calendar/src/shared/utils.dart' show CalendarFormat; class FormatButton extends StatelessWidget { final CalendarFormat calendarFormat; @@ -17,7 +17,7 @@ class FormatButton extends StatelessWidget { final Map availableCalendarFormats; const FormatButton({ - Key? key, + super.key, required this.calendarFormat, required this.onTap, required this.textStyle, @@ -25,7 +25,7 @@ class FormatButton extends StatelessWidget { required this.padding, required this.showsNextFormat, required this.availableCalendarFormats, - }) : super(key: key); + }); @override Widget build(BuildContext context) { diff --git a/pubspec.yaml b/pubspec.yaml index 6c032a95..99ae64d5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,5 +17,6 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter + lint: ^2.0.1 flutter: diff --git a/test/calendar_header_test.dart b/test/calendar_header_test.dart index ad91c8af..0df34d6d 100644 --- a/test/calendar_header_test.dart +++ b/test/calendar_header_test.dart @@ -124,7 +124,7 @@ void main() { (tester) async { await tester.pumpWidget( setupTestWidget( - headerStyle: HeaderStyle( + headerStyle: const HeaderStyle( leftChevronVisible: false, ), ), @@ -150,7 +150,7 @@ void main() { (tester) async { await tester.pumpWidget( setupTestWidget( - headerStyle: HeaderStyle( + headerStyle: const HeaderStyle( rightChevronVisible: false, ), ), @@ -190,7 +190,7 @@ void main() { (tester) async { await tester.pumpWidget( setupTestWidget( - headerStyle: HeaderStyle(formatButtonVisible: false), + headerStyle: const HeaderStyle(formatButtonVisible: false), ), ); diff --git a/test/calendar_page_test.dart b/test/calendar_page_test.dart index f9d15020..19fc8267 100644 --- a/test/calendar_page_test.dart +++ b/test/calendar_page_test.dart @@ -57,7 +57,6 @@ void main() { dayBuilder: (context, day) { return Text('${day.day}'); }, - dowVisible: true, dowBuilder: (context, day) { return Text('${day.weekday}'); }, @@ -67,7 +66,7 @@ void main() { ); final expectedCellCount = visibleDays.length; - final expectedDowLabels = 7; + const expectedDowLabels = 7; expect( find.byType(Text), @@ -79,19 +78,21 @@ void main() { testWidgets( 'Throw AssertionError when CalendarPage is built with dowVisible set to true, but dowBuilder is absent', (tester) async { - expect(() async { - await tester.pumpWidget( - setupTestWidget( - CalendarPage( - visibleDays: visibleDays, - dayBuilder: (context, day) { - return Text('${day.day}'); - }, - dowVisible: true, + expect( + () async { + await tester.pumpWidget( + setupTestWidget( + CalendarPage( + visibleDays: visibleDays, + dayBuilder: (context, day) { + return Text('${day.day}'); + }, + ), ), - ), - ); - }, throwsAssertionError); + ); + }, + throwsAssertionError, + ); }, ); @@ -105,7 +106,6 @@ void main() { dayBuilder: (context, day) { return Text('${day.day}'); }, - dowVisible: true, dowBuilder: (context, day) { return Text('${day.weekday}'); }, @@ -124,23 +124,24 @@ void main() { testWidgets( 'Week numbers are visible', (tester) async { - await tester.pumpWidget(setupTestWidget( - CalendarPage( - visibleDays: visibleDays, - dayBuilder: (context, day) { - return Text('${day.day}'); - }, - dowVisible: true, - dowBuilder: (context, day) { - return Text('${day.weekday}'); - }, - dowHeight: 5, - weekNumberVisible: true, - weekNumberBuilder: (BuildContext context, DateTime day) { - return Text(day.weekday.toString()); - }, + await tester.pumpWidget( + setupTestWidget( + CalendarPage( + visibleDays: visibleDays, + dayBuilder: (context, day) { + return Text('${day.day}'); + }, + dowBuilder: (context, day) { + return Text('${day.weekday}'); + }, + dowHeight: 5, + weekNumberVisible: true, + weekNumberBuilder: (BuildContext context, DateTime day) { + return Text(day.weekday.toString()); + }, + ), ), - )); + ); expect( find.byType(Column), @@ -148,5 +149,4 @@ void main() { ); }, ); - } diff --git a/test/common.dart b/test/common.dart index e351cea2..7e869196 100644 --- a/test/common.dart +++ b/test/common.dart @@ -8,7 +8,7 @@ ValueKey dateToKey(DateTime date, {String prefix = ''}) { return ValueKey('$prefix${date.year}-${date.month}-${date.day}'); } -const calendarFormatMap = const { +const calendarFormatMap = { CalendarFormat.month: 'Month', CalendarFormat.twoWeeks: 'Two weeks', CalendarFormat.week: 'week', diff --git a/test/custom_icon_button_test.dart b/test/custom_icon_button_test.dart index b608f978..de53539b 100644 --- a/test/custom_icon_button_test.dart +++ b/test/custom_icon_button_test.dart @@ -21,7 +21,7 @@ void main() { await tester.pumpWidget( setupTestWidget( CustomIconButton( - icon: Icon(Icons.chevron_left), + icon: const Icon(Icons.chevron_left), onTap: () { buttonTapped = true; }, diff --git a/test/format_button_test.dart b/test/format_button_test.dart index d562260d..61c4bdeb 100644 --- a/test/format_button_test.dart +++ b/test/format_button_test.dart @@ -20,7 +20,7 @@ void main() { testWidgets( 'Initial format month returns twoWeeks when tapped', (tester) async { - final headerStyle = HeaderStyle(); + const headerStyle = HeaderStyle(); CalendarFormat? calendarFormat; await tester.pumpWidget( @@ -51,7 +51,7 @@ void main() { testWidgets( 'Initial format twoWeeks returns week when tapped', (tester) async { - final headerStyle = HeaderStyle(); + const headerStyle = HeaderStyle(); CalendarFormat? calendarFormat; await tester.pumpWidget( @@ -82,7 +82,7 @@ void main() { testWidgets( 'Initial format week return month when tapped', (tester) async { - final headerStyle = HeaderStyle(); + const headerStyle = HeaderStyle(); CalendarFormat? calendarFormat; await tester.pumpWidget( @@ -115,15 +115,15 @@ void main() { testWidgets( 'true - display next calendar format', (tester) async { - final headerStyle = HeaderStyle(formatButtonShowsNext: true); + const headerStyle = HeaderStyle(); - final currentFormatIndex = 0; + const currentFormatIndex = 0; final currentFormat = calendarFormatMap.keys.elementAt(currentFormatIndex); final currentFormatText = calendarFormatMap.values.elementAt(currentFormatIndex); - final nextFormatIndex = 1; + const nextFormatIndex = 1; final nextFormatText = calendarFormatMap.values.elementAt(nextFormatIndex); @@ -152,9 +152,9 @@ void main() { testWidgets( 'false - display current calendar format', (tester) async { - final headerStyle = HeaderStyle(formatButtonShowsNext: false); + const headerStyle = HeaderStyle(formatButtonShowsNext: false); - final currentFormatIndex = 0; + const currentFormatIndex = 0; final currentFormat = calendarFormatMap.keys.elementAt(currentFormatIndex); final currentFormatText = diff --git a/test/table_calendar_base_test.dart b/test/table_calendar_base_test.dart index 4076a540..c0fddf66 100644 --- a/test/table_calendar_base_test.dart +++ b/test/table_calendar_base_test.dart @@ -37,8 +37,6 @@ void main() { }, rowHeight: 52, dowVisible: false, - calendarFormat: CalendarFormat.month, - startingDayOfWeek: StartingDayOfWeek.sunday, ), ), ); @@ -84,7 +82,6 @@ void main() { rowHeight: 52, dowVisible: false, calendarFormat: CalendarFormat.twoWeeks, - startingDayOfWeek: StartingDayOfWeek.sunday, ), ), ); @@ -130,7 +127,6 @@ void main() { rowHeight: 52, dowVisible: false, calendarFormat: CalendarFormat.week, - startingDayOfWeek: StartingDayOfWeek.sunday, ), ), ); @@ -175,14 +171,13 @@ void main() { }, rowHeight: 52, dowVisible: false, - calendarFormat: CalendarFormat.month, startingDayOfWeek: StartingDayOfWeek.monday, ), ), ); final firstVisibleDay = DateTime.utc(2021, 6, 28); - final lastVisibleDay = DateTime.utc(2021, 8, 1); + final lastVisibleDay = DateTime.utc(2021, 8); final focusedDayKey = dateToKey(focusedDay); final firstVisibleDayKey = dateToKey(firstVisibleDay); @@ -354,28 +349,30 @@ void main() { testWidgets( 'Throw AssertionError when TableCalendarBase is built with dowVisible and dowBuilder, but dowHeight is absent', (tester) async { - expect(() async { - await tester.pumpWidget( - setupTestWidget( - TableCalendarBase( - firstDay: DateTime.utc(2021, 5, 15), - lastDay: DateTime.utc(2021, 8, 18), - focusedDay: DateTime.utc(2021, 7, 15), - dayBuilder: (context, day, focusedDay) { - return Text( - '${day.day}', - key: dateToKey(day), - ); - }, - rowHeight: 52, - dowVisible: true, - dowBuilder: (context, day) { - return Text('${day.weekday}'); - }, + expect( + () async { + await tester.pumpWidget( + setupTestWidget( + TableCalendarBase( + firstDay: DateTime.utc(2021, 5, 15), + lastDay: DateTime.utc(2021, 8, 18), + focusedDay: DateTime.utc(2021, 7, 15), + dayBuilder: (context, day, focusedDay) { + return Text( + '${day.day}', + key: dateToKey(day), + ); + }, + rowHeight: 52, + dowBuilder: (context, day) { + return Text('${day.weekday}'); + }, + ), ), - ), - ); - }, throwsAssertionError); + ); + }, + throwsAssertionError, + ); }, ); } diff --git a/test/table_calendar_test.dart b/test/table_calendar_test.dart index 3361255b..69418e2a 100644 --- a/test/table_calendar_test.dart +++ b/test/table_calendar_test.dart @@ -255,7 +255,7 @@ void main() { ), ); - var dayCells = tester.widgetList(find.byType(CellContent)); + final dayCells = tester.widgetList(find.byType(CellContent)); expect(dayCells.length, 7); }, ); @@ -269,7 +269,7 @@ void main() { ), ); - var dayCells = tester.widgetList(find.byType(CellContent)); + final dayCells = tester.widgetList(find.byType(CellContent)); expect(dayCells.length, 14); }, ); @@ -278,12 +278,10 @@ void main() { '35 day cells in month format for July 2021', (tester) async { await tester.pumpWidget( - createTableCalendar( - calendarFormat: CalendarFormat.month, - ), + createTableCalendar(), ); - var dayCells = tester.widgetList(find.byType(CellContent)); + final dayCells = tester.widgetList(find.byType(CellContent)); expect(dayCells.length, 35); }, ); @@ -293,12 +291,11 @@ void main() { (tester) async { await tester.pumpWidget( createTableCalendar( - calendarFormat: CalendarFormat.month, sixWeekMonthsEnforced: true, ), ); - var dayCells = tester.widgetList(find.byType(CellContent)); + final dayCells = tester.widgetList(find.byType(CellContent)); expect(dayCells.length, 42); }, ); @@ -423,21 +420,23 @@ void main() { (tester) async { final eventDay = DateTime.utc(2021, 7, 20); - await tester.pumpWidget(setupTestWidget( - TableCalendar( - focusedDay: initialFocusedDay, - firstDay: firstDay, - lastDay: lastDay, - currentDay: today, - eventLoader: (day) { - if (day.day == eventDay.day && day.month == eventDay.month) { - return ['Event 1', 'Event 2', 'Event 3']; - } - - return []; - }, + await tester.pumpWidget( + setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + eventLoader: (day) { + if (day.day == eventDay.day && day.month == eventDay.month) { + return ['Event 1', 'Event 2', 'Event 3']; + } + + return []; + }, + ), ), - )); + ); final eventDayKey = cellContentKey(eventDay); final eventDayCellContent = find.byKey(eventDayKey); @@ -463,14 +462,16 @@ void main() { testWidgets( 'currentDay correctly marks given day as today', (tester) async { - await tester.pumpWidget(setupTestWidget( - TableCalendar( - focusedDay: initialFocusedDay, - firstDay: firstDay, - lastDay: lastDay, - currentDay: today, + await tester.pumpWidget( + setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + ), ), - )); + ); final currentDayKey = cellContentKey(today); final currentDayCellContent = @@ -487,13 +488,15 @@ void main() { final firstDay = DateTime.utc(now.year, now.month - 3, now.day); final lastDay = DateTime.utc(now.year, now.month + 3, now.day); - await tester.pumpWidget(setupTestWidget( - TableCalendar( - focusedDay: now, - firstDay: firstDay, - lastDay: lastDay, + await tester.pumpWidget( + setupTestWidget( + TableCalendar( + focusedDay: now, + firstDay: firstDay, + lastDay: lastDay, + ), ), - )); + ); final currentDayKey = cellContentKey(now); final currentDayCellContent = @@ -508,17 +511,19 @@ void main() { (tester) async { final selectedDay = DateTime.utc(2021, 7, 20); - await tester.pumpWidget(setupTestWidget( - TableCalendar( - focusedDay: initialFocusedDay, - firstDay: firstDay, - lastDay: lastDay, - currentDay: today, - selectedDayPredicate: (day) { - return isSameDay(day, selectedDay); - }, + await tester.pumpWidget( + setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + selectedDayPredicate: (day) { + return isSameDay(day, selectedDay); + }, + ), ), - )); + ); final selectedDayKey = cellContentKey(selectedDay); final selectedDayCellContent = @@ -533,17 +538,19 @@ void main() { (tester) async { final holiday = DateTime.utc(2021, 7, 20); - await tester.pumpWidget(setupTestWidget( - TableCalendar( - focusedDay: initialFocusedDay, - firstDay: firstDay, - lastDay: lastDay, - currentDay: today, - holidayPredicate: (day) { - return isSameDay(day, holiday); - }, + await tester.pumpWidget( + setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + holidayPredicate: (day) { + return isSameDay(day, holiday); + }, + ), ), - )); + ); final holidayKey = cellContentKey(holiday); final holidayCellContent = @@ -634,18 +641,20 @@ void main() { testWidgets('when initial format is month', (tester) async { CalendarFormat calendarFormat = CalendarFormat.month; - await tester.pumpWidget(setupTestWidget( - TableCalendar( - focusedDay: today, - firstDay: firstDay, - lastDay: lastDay, - currentDay: today, - calendarFormat: calendarFormat, - onFormatChanged: (format) { - calendarFormat = format; - }, + await tester.pumpWidget( + setupTestWidget( + TableCalendar( + focusedDay: today, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + calendarFormat: calendarFormat, + onFormatChanged: (format) { + calendarFormat = format; + }, + ), ), - )); + ); await tester.drag(find.byType(CellContent).first, const Offset(0, -500)); await tester.pumpAndSettle(); @@ -659,18 +668,20 @@ void main() { testWidgets('when initial format is two weeks', (tester) async { CalendarFormat calendarFormat = CalendarFormat.twoWeeks; - await tester.pumpWidget(setupTestWidget( - TableCalendar( - focusedDay: today, - firstDay: firstDay, - lastDay: lastDay, - currentDay: today, - calendarFormat: calendarFormat, - onFormatChanged: (format) { - calendarFormat = format; - }, + await tester.pumpWidget( + setupTestWidget( + TableCalendar( + focusedDay: today, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + calendarFormat: calendarFormat, + onFormatChanged: (format) { + calendarFormat = format; + }, + ), ), - )); + ); await tester.drag(find.byType(CellContent).first, const Offset(0, -500)); await tester.pumpAndSettle(); @@ -684,18 +695,20 @@ void main() { testWidgets('when initial format is week', (tester) async { CalendarFormat calendarFormat = CalendarFormat.week; - await tester.pumpWidget(setupTestWidget( - TableCalendar( - focusedDay: today, - firstDay: firstDay, - lastDay: lastDay, - currentDay: today, - calendarFormat: calendarFormat, - onFormatChanged: (format) { - calendarFormat = format; - }, + await tester.pumpWidget( + setupTestWidget( + TableCalendar( + focusedDay: today, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + calendarFormat: calendarFormat, + onFormatChanged: (format) { + calendarFormat = format; + }, + ), ), - )); + ); await tester.drag(find.byType(CellContent).first, const Offset(0, -500)); await tester.pumpAndSettle(); @@ -713,17 +726,19 @@ void main() { (tester) async { DateTime? selectedDay; - await tester.pumpWidget(setupTestWidget( - TableCalendar( - focusedDay: initialFocusedDay, - firstDay: firstDay, - lastDay: lastDay, - currentDay: today, - onDaySelected: (selected, focused) { - selectedDay = selected; - }, + await tester.pumpWidget( + setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + onDaySelected: (selected, focused) { + selectedDay = selected; + }, + ), ), - )); + ); expect(selectedDay, isNull); @@ -741,17 +756,19 @@ void main() { (tester) async { DateTime? focusedDay; - await tester.pumpWidget(setupTestWidget( - TableCalendar( - focusedDay: initialFocusedDay, - firstDay: firstDay, - lastDay: lastDay, - currentDay: today, - onDaySelected: (selected, focused) { - focusedDay = focused; - }, + await tester.pumpWidget( + setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + onDaySelected: (selected, focused) { + focusedDay = focused; + }, + ), ), - )); + ); expect(focusedDay, isNull); @@ -770,18 +787,20 @@ void main() { DateTime? selectedDay; DateTime? focusedDay; - await tester.pumpWidget(setupTestWidget( - TableCalendar( - focusedDay: initialFocusedDay, - firstDay: firstDay, - lastDay: lastDay, - currentDay: today, - onDaySelected: (selected, focused) { - selectedDay = selected; - focusedDay = focused; - }, + await tester.pumpWidget( + setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + onDaySelected: (selected, focused) { + selectedDay = selected; + focusedDay = focused; + }, + ), ), - )); + ); expect(selectedDay, isNull); expect(focusedDay, isNull); @@ -789,7 +808,7 @@ void main() { final tappedDay = DateTime.utc(2021, 6, 30); final tappedDayKey = cellContentKey(tappedDay); - final expectedFocusedDay = DateTime.utc(2021, 7, 1); + final expectedFocusedDay = DateTime.utc(2021, 7); await tester.tap(find.byKey(tappedDayKey)); await tester.pumpAndSettle(); @@ -804,23 +823,25 @@ void main() { DateTime? selectedDay; DateTime? focusedDay; - await tester.pumpWidget(setupTestWidget( - TableCalendar( - focusedDay: DateTime.utc(2021, 8, 16), - firstDay: firstDay, - lastDay: lastDay, - currentDay: DateTime.utc(2021, 8, 16), - onDaySelected: (selected, focused) { - selectedDay = selected; - focusedDay = focused; - }, + await tester.pumpWidget( + setupTestWidget( + TableCalendar( + focusedDay: DateTime.utc(2021, 8, 16), + firstDay: firstDay, + lastDay: lastDay, + currentDay: DateTime.utc(2021, 8, 16), + onDaySelected: (selected, focused) { + selectedDay = selected; + focusedDay = focused; + }, + ), ), - )); + ); expect(selectedDay, isNull); expect(focusedDay, isNull); - final tappedDay = DateTime.utc(2021, 9, 1); + final tappedDay = DateTime.utc(2021, 9); final tappedDayKey = cellContentKey(tappedDay); final expectedFocusedDay = DateTime.utc(2021, 8, 31); @@ -839,17 +860,19 @@ void main() { (tester) async { DateTime? selectedDay; - await tester.pumpWidget(setupTestWidget( - TableCalendar( - focusedDay: initialFocusedDay, - firstDay: firstDay, - lastDay: lastDay, - currentDay: today, - onDayLongPressed: (selected, focused) { - selectedDay = selected; - }, + await tester.pumpWidget( + setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + onDayLongPressed: (selected, focused) { + selectedDay = selected; + }, + ), ), - )); + ); expect(selectedDay, isNull); @@ -867,17 +890,19 @@ void main() { (tester) async { DateTime? focusedDay; - await tester.pumpWidget(setupTestWidget( - TableCalendar( - focusedDay: initialFocusedDay, - firstDay: firstDay, - lastDay: lastDay, - currentDay: today, - onDayLongPressed: (selected, focused) { - focusedDay = focused; - }, + await tester.pumpWidget( + setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + onDayLongPressed: (selected, focused) { + focusedDay = focused; + }, + ), ), - )); + ); expect(focusedDay, isNull); @@ -896,18 +921,20 @@ void main() { DateTime? selectedDay; DateTime? focusedDay; - await tester.pumpWidget(setupTestWidget( - TableCalendar( - focusedDay: initialFocusedDay, - firstDay: firstDay, - lastDay: lastDay, - currentDay: today, - onDayLongPressed: (selected, focused) { - selectedDay = selected; - focusedDay = focused; - }, + await tester.pumpWidget( + setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + onDayLongPressed: (selected, focused) { + selectedDay = selected; + focusedDay = focused; + }, + ), ), - )); + ); expect(selectedDay, isNull); expect(focusedDay, isNull); @@ -915,7 +942,7 @@ void main() { final longPressedDay = DateTime.utc(2021, 6, 30); final longPressedDayKey = cellContentKey(longPressedDay); - final expectedFocusedDay = DateTime.utc(2021, 7, 1); + final expectedFocusedDay = DateTime.utc(2021, 7); await tester.longPress(find.byKey(longPressedDayKey)); await tester.pumpAndSettle(); @@ -930,23 +957,25 @@ void main() { DateTime? selectedDay; DateTime? focusedDay; - await tester.pumpWidget(setupTestWidget( - TableCalendar( - focusedDay: DateTime.utc(2021, 8, 16), - firstDay: firstDay, - lastDay: lastDay, - currentDay: DateTime.utc(2021, 8, 16), - onDayLongPressed: (selected, focused) { - selectedDay = selected; - focusedDay = focused; - }, + await tester.pumpWidget( + setupTestWidget( + TableCalendar( + focusedDay: DateTime.utc(2021, 8, 16), + firstDay: firstDay, + lastDay: lastDay, + currentDay: DateTime.utc(2021, 8, 16), + onDayLongPressed: (selected, focused) { + selectedDay = selected; + focusedDay = focused; + }, + ), ), - )); + ); expect(selectedDay, isNull); expect(focusedDay, isNull); - final longPressedDay = DateTime.utc(2021, 9, 1); + final longPressedDay = DateTime.utc(2021, 9); final longPressedDayKey = cellContentKey(longPressedDay); final expectedFocusedDay = DateTime.utc(2021, 8, 31); @@ -967,20 +996,22 @@ void main() { DateTime? rangeEnd; DateTime? focusedDay; - await tester.pumpWidget(setupTestWidget( - TableCalendar( - focusedDay: initialFocusedDay, - firstDay: firstDay, - lastDay: lastDay, - currentDay: today, - rangeSelectionMode: RangeSelectionMode.enforced, - onRangeSelected: (start, end, focused) { - rangeStart = start; - rangeEnd = end; - focusedDay = focused; - }, + await tester.pumpWidget( + setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + rangeSelectionMode: RangeSelectionMode.enforced, + onRangeSelected: (start, end, focused) { + rangeStart = start; + rangeEnd = end; + focusedDay = focused; + }, + ), ), - )); + ); expect(rangeStart, isNull); expect(rangeEnd, isNull); @@ -1011,20 +1042,22 @@ void main() { DateTime? rangeEnd; DateTime? focusedDay; - await tester.pumpWidget(setupTestWidget( - TableCalendar( - focusedDay: initialFocusedDay, - firstDay: firstDay, - lastDay: lastDay, - currentDay: today, - rangeSelectionMode: RangeSelectionMode.enforced, - onRangeSelected: (start, end, focused) { - rangeStart = start; - rangeEnd = end; - focusedDay = focused; - }, + await tester.pumpWidget( + setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + rangeSelectionMode: RangeSelectionMode.enforced, + onRangeSelected: (start, end, focused) { + rangeStart = start; + rangeEnd = end; + focusedDay = focused; + }, + ), ), - )); + ); expect(rangeStart, isNull); expect(rangeEnd, isNull); @@ -1056,25 +1089,25 @@ void main() { DateTime? focusedDay; DateTime? selectedDay; - await tester.pumpWidget(setupTestWidget( - TableCalendar( - focusedDay: initialFocusedDay, - firstDay: firstDay, - lastDay: lastDay, - currentDay: today, - rangeSelectionMode: RangeSelectionMode.toggledOff, - onDaySelected: (selected, focused) { - selectedDay = selected; - focusedDay = focused; - }, - onRangeSelected: (start, end, focused) { - rangeStart = start; - rangeEnd = end; - focusedDay = focused; - }, - onDayLongPressed: null, + await tester.pumpWidget( + setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + onDaySelected: (selected, focused) { + selectedDay = selected; + focusedDay = focused; + }, + onRangeSelected: (start, end, focused) { + rangeStart = start; + rangeEnd = end; + focusedDay = focused; + }, + ), ), - )); + ); expect(rangeStart, isNull); expect(rangeEnd, isNull); @@ -1111,25 +1144,26 @@ void main() { DateTime? focusedDay; DateTime? selectedDay; - await tester.pumpWidget(setupTestWidget( - TableCalendar( - focusedDay: initialFocusedDay, - firstDay: firstDay, - lastDay: lastDay, - currentDay: today, - rangeSelectionMode: RangeSelectionMode.toggledOn, - onDaySelected: (selected, focused) { - selectedDay = selected; - focusedDay = focused; - }, - onRangeSelected: (start, end, focused) { - rangeStart = start; - rangeEnd = end; - focusedDay = focused; - }, - onDayLongPressed: null, + await tester.pumpWidget( + setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + rangeSelectionMode: RangeSelectionMode.toggledOn, + onDaySelected: (selected, focused) { + selectedDay = selected; + focusedDay = focused; + }, + onRangeSelected: (start, end, focused) { + rangeStart = start; + rangeEnd = end; + focusedDay = focused; + }, + ), ), - )); + ); expect(rangeStart, isNull); expect(rangeEnd, isNull); @@ -1166,25 +1200,26 @@ void main() { DateTime? focusedDay; DateTime? selectedDay; - await tester.pumpWidget(setupTestWidget( - TableCalendar( - focusedDay: initialFocusedDay, - firstDay: firstDay, - lastDay: lastDay, - currentDay: today, - rangeSelectionMode: RangeSelectionMode.enforced, - onDaySelected: (selected, focused) { - selectedDay = selected; - focusedDay = focused; - }, - onRangeSelected: (start, end, focused) { - rangeStart = start; - rangeEnd = end; - focusedDay = focused; - }, - onDayLongPressed: null, + await tester.pumpWidget( + setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + rangeSelectionMode: RangeSelectionMode.enforced, + onDaySelected: (selected, focused) { + selectedDay = selected; + focusedDay = focused; + }, + onRangeSelected: (start, end, focused) { + rangeStart = start; + rangeEnd = end; + focusedDay = focused; + }, + ), ), - )); + ); expect(rangeStart, isNull); expect(rangeEnd, isNull); @@ -1221,25 +1256,26 @@ void main() { DateTime? focusedDay; DateTime? selectedDay; - await tester.pumpWidget(setupTestWidget( - TableCalendar( - focusedDay: initialFocusedDay, - firstDay: firstDay, - lastDay: lastDay, - currentDay: today, - rangeSelectionMode: RangeSelectionMode.disabled, - onDaySelected: (selected, focused) { - selectedDay = selected; - focusedDay = focused; - }, - onRangeSelected: (start, end, focused) { - rangeStart = start; - rangeEnd = end; - focusedDay = focused; - }, - onDayLongPressed: null, + await tester.pumpWidget( + setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + rangeSelectionMode: RangeSelectionMode.disabled, + onDaySelected: (selected, focused) { + selectedDay = selected; + focusedDay = focused; + }, + onRangeSelected: (start, end, focused) { + rangeStart = start; + rangeEnd = end; + focusedDay = focused; + }, + ), ), - )); + ); expect(rangeStart, isNull); expect(rangeEnd, isNull); @@ -1276,16 +1312,18 @@ void main() { final rangeStart = DateTime.utc(2021, 7, 8); final rangeEnd = DateTime.utc(2021, 7, 21); - await tester.pumpWidget(setupTestWidget( - TableCalendar( - focusedDay: initialFocusedDay, - firstDay: firstDay, - lastDay: lastDay, - currentDay: today, - rangeStartDay: rangeStart, - rangeEndDay: rangeEnd, + await tester.pumpWidget( + setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + rangeStartDay: rangeStart, + rangeEndDay: rangeEnd, + ), ), - )); + ); final rangeStartKey = cellContentKey(rangeStart); final rangeStartCellContent = @@ -1311,16 +1349,18 @@ void main() { final rangeStart = DateTime.utc(2021, 7, 8); final rangeEnd = DateTime.utc(2021, 7, 13); - await tester.pumpWidget(setupTestWidget( - TableCalendar( - focusedDay: initialFocusedDay, - firstDay: firstDay, - lastDay: lastDay, - currentDay: today, - rangeStartDay: rangeStart, - rangeEndDay: rangeEnd, + await tester.pumpWidget( + setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + rangeStartDay: rangeStart, + rangeEndDay: rangeEnd, + ), ), - )); + ); final dayCount = rangeEnd.difference(rangeStart).inDays - 1; expect(dayCount, 4); @@ -1346,16 +1386,18 @@ void main() { final rangeStart = DateTime.utc(2021, 7, 8); final rangeEnd = DateTime.utc(2021, 7, 13); - await tester.pumpWidget(setupTestWidget( - TableCalendar( - focusedDay: initialFocusedDay, - firstDay: firstDay, - lastDay: lastDay, - currentDay: today, - rangeStartDay: rangeStart, - rangeEndDay: rangeEnd, + await tester.pumpWidget( + setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + rangeStartDay: rangeStart, + rangeEndDay: rangeEnd, + ), ), - )); + ); final oobStart = rangeStart.subtract(const Duration(days: 1)); final oobEnd = rangeEnd.add(const Duration(days: 1)); From 92236c252c3e5670e06e4361dbf3274f345a48e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Thu, 12 Dec 2024 14:02:53 +0100 Subject: [PATCH 157/164] Remove covered "default" clauses --- lib/src/table_calendar_base.dart | 3 --- lib/src/widgets/calendar_core.dart | 7 +------ 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/lib/src/table_calendar_base.dart b/lib/src/table_calendar_base.dart index c0a56cbc..502929ac 100644 --- a/lib/src/table_calendar_base.dart +++ b/lib/src/table_calendar_base.dart @@ -5,7 +5,6 @@ import 'package:flutter/material.dart'; import 'package:simple_gesture_detector/simple_gesture_detector.dart'; - import 'package:table_calendar/src/shared/utils.dart'; import 'package:table_calendar/src/widgets/calendar_core.dart'; @@ -286,8 +285,6 @@ class _TableCalendarBaseState extends State { return _getTwoWeekCount(startDay, focusedDay); case CalendarFormat.week: return _getWeekCount(startDay, focusedDay); - default: - return _getMonthCount(startDay, focusedDay); } } diff --git a/lib/src/widgets/calendar_core.dart b/lib/src/widgets/calendar_core.dart index e2ef0fb9..c868657f 100644 --- a/lib/src/widgets/calendar_core.dart +++ b/lib/src/widgets/calendar_core.dart @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 import 'package:flutter/material.dart'; - import 'package:table_calendar/src/shared/utils.dart'; import 'package:table_calendar/src/widgets/calendar_page.dart'; @@ -54,7 +53,7 @@ class CalendarCore extends StatelessWidget { this.tableBorder, this.tablePadding, this.scrollPhysics, - }) : assert(!dowVisible || (dowHeight != null && dowBuilder != null)); + }) : assert(!dowVisible || (dowHeight != null && dowBuilder != null)); @override Widget build(BuildContext context) { @@ -133,8 +132,6 @@ class CalendarCore extends StatelessWidget { return _getTwoWeekCount(first, last) + 1; case CalendarFormat.week: return _getWeekCount(first, last) + 1; - default: - return _getMonthCount(first, last) + 1; } } @@ -234,8 +231,6 @@ class CalendarCore extends StatelessWidget { return _daysInTwoWeeks(focusedDay); case CalendarFormat.week: return _daysInWeek(focusedDay); - default: - return _daysInMonth(focusedDay); } } From 1ba4abcf2355bc0c0c2ae26a060f54951b96aae7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Thu, 12 Dec 2024 14:21:29 +0100 Subject: [PATCH 158/164] Apply lint rules, format code --- example/lib/main.dart | 43 ++++++++++--------- example/lib/pages/basics_example.dart | 9 ++-- example/lib/pages/complex_example.dart | 28 ++++++------ example/lib/pages/events_example.dart | 13 +++--- example/lib/pages/multi_example.dart | 13 +++--- example/lib/pages/range_example.dart | 9 ++-- example/lib/utils.dart | 16 ++++--- example/pubspec.yaml | 7 +-- lib/src/customization/calendar_builders.dart | 1 - lib/src/customization/days_of_week_style.dart | 1 - lib/src/customization/header_style.dart | 1 - lib/src/table_calendar.dart | 5 +-- lib/src/table_calendar_base.dart | 4 +- lib/src/widgets/calendar_core.dart | 6 --- lib/src/widgets/calendar_header.dart | 1 - lib/src/widgets/calendar_page.dart | 2 +- lib/src/widgets/cell_content.dart | 1 - lib/src/widgets/format_button.dart | 1 - test/cell_content_test.dart | 16 +++---- 19 files changed, 86 insertions(+), 91 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index ae2bc273..6cb6ced5 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -3,18 +3,19 @@ import 'package:flutter/material.dart'; import 'package:intl/date_symbol_data_local.dart'; - -import 'pages/basics_example.dart'; -import 'pages/complex_example.dart'; -import 'pages/events_example.dart'; -import 'pages/multi_example.dart'; -import 'pages/range_example.dart'; +import 'package:table_calendar_example/pages/basics_example.dart'; +import 'package:table_calendar_example/pages/complex_example.dart'; +import 'package:table_calendar_example/pages/events_example.dart'; +import 'package:table_calendar_example/pages/multi_example.dart'; +import 'package:table_calendar_example/pages/range_example.dart'; void main() { - initializeDateFormatting().then((_) => runApp(MyApp())); + initializeDateFormatting().then((_) => runApp(const MyApp())); } class MyApp extends StatelessWidget { + const MyApp({super.key}); + @override Widget build(BuildContext context) { return MaterialApp( @@ -22,14 +23,16 @@ class MyApp extends StatelessWidget { theme: ThemeData( primarySwatch: Colors.blue, ), - home: StartPage(), + home: const StartPage(), ); } } class StartPage extends StatefulWidget { + const StartPage({super.key}); + @override - _StartPageState createState() => _StartPageState(); + State createState() => _StartPageState(); } class _StartPageState extends State { @@ -37,7 +40,7 @@ class _StartPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text('TableCalendar Example'), + title: const Text('TableCalendar Example'), ), body: Center( child: Column( @@ -45,42 +48,42 @@ class _StartPageState extends State { children: [ const SizedBox(height: 20.0), ElevatedButton( - child: Text('Basics'), + child: const Text('Basics'), onPressed: () => Navigator.push( context, - MaterialPageRoute(builder: (_) => TableBasicsExample()), + MaterialPageRoute(builder: (_) => const TableBasicsExample()), ), ), const SizedBox(height: 12.0), ElevatedButton( - child: Text('Range Selection'), + child: const Text('Range Selection'), onPressed: () => Navigator.push( context, - MaterialPageRoute(builder: (_) => TableRangeExample()), + MaterialPageRoute(builder: (_) => const TableRangeExample()), ), ), const SizedBox(height: 12.0), ElevatedButton( - child: Text('Events'), + child: const Text('Events'), onPressed: () => Navigator.push( context, - MaterialPageRoute(builder: (_) => TableEventsExample()), + MaterialPageRoute(builder: (_) => const TableEventsExample()), ), ), const SizedBox(height: 12.0), ElevatedButton( - child: Text('Multiple Selection'), + child: const Text('Multiple Selection'), onPressed: () => Navigator.push( context, - MaterialPageRoute(builder: (_) => TableMultiExample()), + MaterialPageRoute(builder: (_) => const TableMultiExample()), ), ), const SizedBox(height: 12.0), ElevatedButton( - child: Text('Complex'), + child: const Text('Complex'), onPressed: () => Navigator.push( context, - MaterialPageRoute(builder: (_) => TableComplexExample()), + MaterialPageRoute(builder: (_) => const TableComplexExample()), ), ), const SizedBox(height: 20.0), diff --git a/example/lib/pages/basics_example.dart b/example/lib/pages/basics_example.dart index 6a8f91b6..fda26a75 100644 --- a/example/lib/pages/basics_example.dart +++ b/example/lib/pages/basics_example.dart @@ -3,12 +3,13 @@ import 'package:flutter/material.dart'; import 'package:table_calendar/table_calendar.dart'; - -import '../utils.dart'; +import 'package:table_calendar_example/utils.dart'; class TableBasicsExample extends StatefulWidget { + const TableBasicsExample({super.key}); + @override - _TableBasicsExampleState createState() => _TableBasicsExampleState(); + State createState() => _TableBasicsExampleState(); } class _TableBasicsExampleState extends State { @@ -20,7 +21,7 @@ class _TableBasicsExampleState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text('TableCalendar - Basics'), + title: const Text('TableCalendar - Basics'), ), body: TableCalendar( firstDay: kFirstDay, diff --git a/example/lib/pages/complex_example.dart b/example/lib/pages/complex_example.dart index 61635b3c..81c6e4a8 100644 --- a/example/lib/pages/complex_example.dart +++ b/example/lib/pages/complex_example.dart @@ -1,17 +1,20 @@ // Copyright 2019 Aleksander Woźniak // SPDX-License-Identifier: Apache-2.0 +// ignore_for_file: avoid_print + import 'dart:collection'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:table_calendar/table_calendar.dart'; - -import '../utils.dart'; +import 'package:table_calendar_example/utils.dart'; class TableComplexExample extends StatefulWidget { + const TableComplexExample({super.key}); + @override - _TableComplexExampleState createState() => _TableComplexExampleState(); + State createState() => _TableComplexExampleState(); } class _TableComplexExampleState extends State { @@ -100,7 +103,7 @@ class _TableComplexExampleState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text('TableCalendar - Complex'), + title: const Text('TableCalendar - Complex'), ), body: Column( children: [ @@ -123,13 +126,13 @@ class _TableComplexExampleState extends State { }, onLeftArrowTap: () { _pageController.previousPage( - duration: Duration(milliseconds: 300), + duration: const Duration(milliseconds: 300), curve: Curves.easeOut, ); }, onRightArrowTap: () { _pageController.nextPage( - duration: Duration(milliseconds: 300), + duration: const Duration(milliseconds: 300), curve: Curves.easeOut, ); }, @@ -203,14 +206,13 @@ class _CalendarHeader extends StatelessWidget { final bool clearButtonVisible; const _CalendarHeader({ - Key? key, required this.focusedDay, required this.onLeftArrowTap, required this.onRightArrowTap, required this.onTodayButtonTap, required this.onClearButtonTap, required this.clearButtonVisible, - }) : super(key: key); + }); @override Widget build(BuildContext context) { @@ -225,27 +227,27 @@ class _CalendarHeader extends StatelessWidget { width: 120.0, child: Text( headerText, - style: TextStyle(fontSize: 26.0), + style: const TextStyle(fontSize: 26.0), ), ), IconButton( - icon: Icon(Icons.calendar_today, size: 20.0), + icon: const Icon(Icons.calendar_today, size: 20.0), visualDensity: VisualDensity.compact, onPressed: onTodayButtonTap, ), if (clearButtonVisible) IconButton( - icon: Icon(Icons.clear, size: 20.0), + icon: const Icon(Icons.clear, size: 20.0), visualDensity: VisualDensity.compact, onPressed: onClearButtonTap, ), const Spacer(), IconButton( - icon: Icon(Icons.chevron_left), + icon: const Icon(Icons.chevron_left), onPressed: onLeftArrowTap, ), IconButton( - icon: Icon(Icons.chevron_right), + icon: const Icon(Icons.chevron_right), onPressed: onRightArrowTap, ), ], diff --git a/example/lib/pages/events_example.dart b/example/lib/pages/events_example.dart index ce00dca2..0d5d8143 100644 --- a/example/lib/pages/events_example.dart +++ b/example/lib/pages/events_example.dart @@ -1,14 +1,17 @@ // Copyright 2019 Aleksander Woźniak // SPDX-License-Identifier: Apache-2.0 +// ignore_for_file: avoid_print + import 'package:flutter/material.dart'; import 'package:table_calendar/table_calendar.dart'; - -import '../utils.dart'; +import 'package:table_calendar_example/utils.dart'; class TableEventsExample extends StatefulWidget { + const TableEventsExample({super.key}); + @override - _TableEventsExampleState createState() => _TableEventsExampleState(); + State createState() => _TableEventsExampleState(); } class _TableEventsExampleState extends State { @@ -86,7 +89,7 @@ class _TableEventsExampleState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text('TableCalendar - Events'), + title: const Text('TableCalendar - Events'), ), body: Column( children: [ @@ -101,7 +104,7 @@ class _TableEventsExampleState extends State { rangeSelectionMode: _rangeSelectionMode, eventLoader: _getEventsForDay, startingDayOfWeek: StartingDayOfWeek.monday, - calendarStyle: CalendarStyle( + calendarStyle: const CalendarStyle( // Use `CalendarStyle` to customize the UI outsideDaysVisible: false, ), diff --git a/example/lib/pages/multi_example.dart b/example/lib/pages/multi_example.dart index 0be4a10d..bf3b570a 100644 --- a/example/lib/pages/multi_example.dart +++ b/example/lib/pages/multi_example.dart @@ -1,16 +1,19 @@ // Copyright 2019 Aleksander Woźniak // SPDX-License-Identifier: Apache-2.0 +// ignore_for_file: avoid_print + import 'dart:collection'; import 'package:flutter/material.dart'; import 'package:table_calendar/table_calendar.dart'; - -import '../utils.dart'; +import 'package:table_calendar_example/utils.dart'; class TableMultiExample extends StatefulWidget { + const TableMultiExample({super.key}); + @override - _TableMultiExampleState createState() => _TableMultiExampleState(); + State createState() => _TableMultiExampleState(); } class _TableMultiExampleState extends State { @@ -62,7 +65,7 @@ class _TableMultiExampleState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text('TableCalendar - Multi'), + title: const Text('TableCalendar - Multi'), ), body: Column( children: [ @@ -90,7 +93,7 @@ class _TableMultiExampleState extends State { }, ), ElevatedButton( - child: Text('Clear selection'), + child: const Text('Clear selection'), onPressed: () { setState(() { _selectedDays.clear(); diff --git a/example/lib/pages/range_example.dart b/example/lib/pages/range_example.dart index 7cd7d771..6243fa5f 100644 --- a/example/lib/pages/range_example.dart +++ b/example/lib/pages/range_example.dart @@ -3,12 +3,13 @@ import 'package:flutter/material.dart'; import 'package:table_calendar/table_calendar.dart'; - -import '../utils.dart'; +import 'package:table_calendar_example/utils.dart'; class TableRangeExample extends StatefulWidget { + const TableRangeExample({super.key}); + @override - _TableRangeExampleState createState() => _TableRangeExampleState(); + State createState() => _TableRangeExampleState(); } class _TableRangeExampleState extends State { @@ -24,7 +25,7 @@ class _TableRangeExampleState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text('TableCalendar - Range'), + title: const Text('TableCalendar - Range'), ), body: TableCalendar( firstDay: kFirstDay, diff --git a/example/lib/utils.dart b/example/lib/utils.dart index c3f6e7d0..f75484c9 100644 --- a/example/lib/utils.dart +++ b/example/lib/utils.dart @@ -23,14 +23,16 @@ final kEvents = LinkedHashMap>( hashCode: getHashCode, )..addAll(_kEventSource); -final _kEventSource = Map.fromIterable(List.generate(50, (index) => index), - key: (item) => DateTime.utc(kFirstDay.year, kFirstDay.month, item * 5), - value: (item) => List.generate( - item % 4 + 1, (index) => Event('Event $item | ${index + 1}'))) - ..addAll({ +final _kEventSource = { + for (var item in List.generate(50, (index) => index)) + DateTime.utc(kFirstDay.year, kFirstDay.month, item * 5): List.generate( + item % 4 + 1, + (index) => Event('Event $item | ${index + 1}'), + ), +}..addAll({ kToday: [ - Event('Today\'s Event 1'), - Event('Today\'s Event 2'), + const Event("Today's Event 1"), + const Event("Today's Event 2"), ], }); diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 85145c18..cd6a009f 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -23,11 +23,8 @@ environment: dependencies: flutter: sdk: flutter - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.2 - + + intl: any table_calendar: path: ../ diff --git a/lib/src/customization/calendar_builders.dart b/lib/src/customization/calendar_builders.dart index 6649e0fc..cf21d2e4 100644 --- a/lib/src/customization/calendar_builders.dart +++ b/lib/src/customization/calendar_builders.dart @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 import 'package:flutter/widgets.dart'; - import 'package:table_calendar/src/shared/utils.dart' show DayBuilder, FocusedDayBuilder; diff --git a/lib/src/customization/days_of_week_style.dart b/lib/src/customization/days_of_week_style.dart index a3ac65d9..8ec79212 100644 --- a/lib/src/customization/days_of_week_style.dart +++ b/lib/src/customization/days_of_week_style.dart @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 import 'package:flutter/widgets.dart'; - import 'package:table_calendar/src/shared/utils.dart' show TextFormatter; /// Class containing styling for `TableCalendar`'s days of week panel. diff --git a/lib/src/customization/header_style.dart b/lib/src/customization/header_style.dart index 3ebf47e4..71ac67df 100644 --- a/lib/src/customization/header_style.dart +++ b/lib/src/customization/header_style.dart @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 import 'package:flutter/material.dart'; - import 'package:table_calendar/src/shared/utils.dart' show TextFormatter; /// Class containing styling and configuration of `TableCalendar`'s header. diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index 67a0a2ca..83d9e997 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -1,14 +1,11 @@ // Copyright 2019 Aleksander Woźniak // SPDX-License-Identifier: Apache-2.0 -// ignore_for_file: library_private_types_in_public_api - import 'dart:math'; import 'package:flutter/widgets.dart'; import 'package:intl/intl.dart'; import 'package:simple_gesture_detector/simple_gesture_detector.dart'; - import 'package:table_calendar/src/customization/calendar_builders.dart'; import 'package:table_calendar/src/customization/calendar_style.dart'; import 'package:table_calendar/src/customization/days_of_week_style.dart'; @@ -281,7 +278,7 @@ class TableCalendar extends StatefulWidget { currentDay = currentDay ?? DateTime.now(); @override - _TableCalendarState createState() => _TableCalendarState(); + State> createState() => _TableCalendarState(); } class _TableCalendarState extends State> { diff --git a/lib/src/table_calendar_base.dart b/lib/src/table_calendar_base.dart index 502929ac..a5727160 100644 --- a/lib/src/table_calendar_base.dart +++ b/lib/src/table_calendar_base.dart @@ -1,8 +1,6 @@ // Copyright 2019 Aleksander Woźniak // SPDX-License-Identifier: Apache-2.0 -// ignore_for_file: library_private_types_in_public_api - import 'package:flutter/material.dart'; import 'package:simple_gesture_detector/simple_gesture_detector.dart'; import 'package:table_calendar/src/shared/utils.dart'; @@ -80,7 +78,7 @@ class TableCalendarBase extends StatefulWidget { assert(isSameDay(focusedDay, lastDay) || focusedDay.isBefore(lastDay)); @override - _TableCalendarBaseState createState() => _TableCalendarBaseState(); + State createState() => _TableCalendarBaseState(); } class _TableCalendarBaseState extends State { diff --git a/lib/src/widgets/calendar_core.dart b/lib/src/widgets/calendar_core.dart index c868657f..573ef532 100644 --- a/lib/src/widgets/calendar_core.dart +++ b/lib/src/widgets/calendar_core.dart @@ -165,21 +165,18 @@ class CalendarCore extends StatelessWidget { switch (format) { case CalendarFormat.month: day = DateTime.utc(prevFocusedDay.year, prevFocusedDay.month + pageDif); - break; case CalendarFormat.twoWeeks: day = DateTime.utc( prevFocusedDay.year, prevFocusedDay.month, prevFocusedDay.day + pageDif * 14, ); - break; case CalendarFormat.week: day = DateTime.utc( prevFocusedDay.year, prevFocusedDay.month, prevFocusedDay.day + pageDif * 7, ); - break; } if (day.isBefore(firstDay)) { @@ -197,21 +194,18 @@ class CalendarCore extends StatelessWidget { switch (format) { case CalendarFormat.month: day = DateTime.utc(firstDay.year, firstDay.month + pageIndex); - break; case CalendarFormat.twoWeeks: day = DateTime.utc( firstDay.year, firstDay.month, firstDay.day + pageIndex * 14, ); - break; case CalendarFormat.week: day = DateTime.utc( firstDay.year, firstDay.month, firstDay.day + pageIndex * 7, ); - break; } if (day.isBefore(firstDay)) { diff --git a/lib/src/widgets/calendar_header.dart b/lib/src/widgets/calendar_header.dart index e6c8047a..6a54336e 100644 --- a/lib/src/widgets/calendar_header.dart +++ b/lib/src/widgets/calendar_header.dart @@ -3,7 +3,6 @@ import 'package:flutter/widgets.dart'; import 'package:intl/intl.dart'; - import 'package:table_calendar/src/customization/header_style.dart'; import 'package:table_calendar/src/shared/utils.dart' show CalendarFormat, DayBuilder; diff --git a/lib/src/widgets/calendar_page.dart b/lib/src/widgets/calendar_page.dart index b9663fdd..0b3cb08d 100644 --- a/lib/src/widgets/calendar_page.dart +++ b/lib/src/widgets/calendar_page.dart @@ -65,7 +65,7 @@ class CalendarPage extends StatelessWidget { (index) => Expanded( child: weekNumberBuilder!(context, visibleDays[index * 7]), ), - ) + ), ], ); } diff --git a/lib/src/widgets/cell_content.dart b/lib/src/widgets/cell_content.dart index 9708ea40..c2b52b4a 100644 --- a/lib/src/widgets/cell_content.dart +++ b/lib/src/widgets/cell_content.dart @@ -3,7 +3,6 @@ import 'package:flutter/widgets.dart'; import 'package:intl/intl.dart'; - import 'package:table_calendar/src/customization/calendar_builders.dart'; import 'package:table_calendar/src/customization/calendar_style.dart'; diff --git a/lib/src/widgets/format_button.dart b/lib/src/widgets/format_button.dart index e8b03f49..76bbc523 100644 --- a/lib/src/widgets/format_button.dart +++ b/lib/src/widgets/format_button.dart @@ -4,7 +4,6 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; - import 'package:table_calendar/src/shared/utils.dart' show CalendarFormat; class FormatButton extends StatelessWidget { diff --git a/test/cell_content_test.dart b/test/cell_content_test.dart index a147707e..9f43e2b6 100644 --- a/test/cell_content_test.dart +++ b/test/cell_content_test.dart @@ -316,8 +316,8 @@ void main() { group('CalendarBuilders Locale test:', () { testWidgets('en locale with default dayTextFormatter', (tester) async { - final locale = 'en'; - initializeDateFormatting(locale, null); + const locale = 'en'; + initializeDateFormatting(locale); final cellDay = DateTime.utc(2021, 7, 15); await tester.pumpWidget( @@ -332,8 +332,8 @@ void main() { }); testWidgets('en locale with custom dayTextFormatter', (tester) async { - final locale = 'en'; - initializeDateFormatting(locale, null); + const locale = 'en'; + initializeDateFormatting(locale); final cellDay = DateTime.utc(2021, 7, 15); await tester.pumpWidget( @@ -352,8 +352,8 @@ void main() { }); testWidgets('ar locale with default dayTextFormatter', (tester) async { - final locale = 'ar'; - initializeDateFormatting(locale, null); + const locale = 'ar'; + initializeDateFormatting(locale); final cellDay = DateTime.utc(2021, 7, 15); await tester.pumpWidget( @@ -368,8 +368,8 @@ void main() { }); testWidgets('ar locale with custom dayTextFormatter', (tester) async { - final locale = 'ar'; - initializeDateFormatting(locale, null); + const locale = 'ar'; + initializeDateFormatting(locale); final cellDay = DateTime.utc(2021, 7, 15); await tester.pumpWidget( From d5510b8749e9759e48ddf378f9c6232000c1e88e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Thu, 12 Dec 2024 18:04:07 +0100 Subject: [PATCH 159/164] Example - update gradle config, bump compileSdkVersion to 34 --- example/android/app/build.gradle | 14 ++++++++++++-- .../gradle/wrapper/gradle-wrapper.properties | 2 +- example/android/settings.gradle | 4 ++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index b6b288b8..60a550dc 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -23,7 +23,8 @@ if (flutterVersionName == null) { } android { - compileSdkVersion 31 + compileSdkVersion 34 + namespace "com.example.example" sourceSets { main.java.srcDirs += 'src/main/kotlin' @@ -37,7 +38,7 @@ android { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.example.example" minSdkVersion flutter.minSdkVersion - targetSdkVersion 31 + targetSdkVersion 34 versionCode flutterVersionCode.toInteger() versionName flutterVersionName } @@ -49,6 +50,15 @@ android { signingConfig signingConfigs.debug } } + + kotlinOptions { + jvmTarget = "1.8" + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } } flutter { diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index cfe88f69..fe63c6c4 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-all.zip diff --git a/example/android/settings.gradle b/example/android/settings.gradle index 960ad4a9..9301c074 100644 --- a/example/android/settings.gradle +++ b/example/android/settings.gradle @@ -18,8 +18,8 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "7.0.0" apply false - id "org.jetbrains.kotlin.android" version "1.6.10" apply false + id "com.android.application" version "8.4.1" apply false + id "org.jetbrains.kotlin.android" version "2.0.0" apply false } include ":app" \ No newline at end of file From 9638fe215ec2cbe4770e97005567e4da7ad1ae36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Thu, 12 Dec 2024 18:09:21 +0100 Subject: [PATCH 160/164] Release version 3.1.3 --- CHANGELOG.md | 5 +++++ README.md | 2 +- example/pubspec.lock | 36 ++++++++++++++---------------------- pubspec.lock | 32 ++++++++++++++++++++------------ pubspec.yaml | 2 +- 5 files changed, 41 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c8fa09e..72e112ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## [3.1.3] + +* Updated gradle config for example project +* Added and applied lint rules, refactored code + ## [3.1.2] * Added dayTextFormatter property to CalendarStyle that allows to customize the text within day cells diff --git a/README.md b/README.md index dd8f2293..df164049 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Add the following line to `pubspec.yaml`: ```yaml dependencies: - table_calendar: ^3.1.2 + table_calendar: ^3.1.3 ``` ### Basic setup diff --git a/example/pubspec.lock b/example/pubspec.lock index 5505275a..69440148 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -41,14 +41,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.18.0" - cupertino_icons: - dependency: "direct main" - description: - name: cupertino_icons - sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 - url: "https://pub.dev" - source: hosted - version: "1.0.8" fake_async: dependency: transitive description: @@ -68,7 +60,7 @@ packages: source: sdk version: "0.0.0" intl: - dependency: transitive + dependency: "direct main" description: name: intl sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf @@ -79,18 +71,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.4" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -111,18 +103,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.15.0" path: dependency: transitive description: @@ -182,7 +174,7 @@ packages: path: ".." relative: true source: path - version: "3.1.2" + version: "3.1.3" term_glyph: dependency: transitive description: @@ -195,10 +187,10 @@ packages: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.2" vector_math: dependency: transitive description: @@ -211,10 +203,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.2.1" + version: "14.2.5" sdks: dart: ">=3.3.0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/pubspec.lock b/pubspec.lock index 19725456..1c8d5181 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -71,18 +71,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.4" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -91,6 +91,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.1" + lint: + dependency: "direct dev" + description: + name: lint + sha256: d758a5211fce7fd3f5e316f804daefecdc34c7e53559716125e6da7388ae8565 + url: "https://pub.dev" + source: hosted + version: "2.3.0" matcher: dependency: transitive description: @@ -103,18 +111,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.15.0" path: dependency: transitive description: @@ -180,10 +188,10 @@ packages: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.2" vector_math: dependency: transitive description: @@ -196,10 +204,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.2.1" + version: "14.2.5" sdks: dart: ">=3.3.0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/pubspec.yaml b/pubspec.yaml index 99ae64d5..a212f61b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: table_calendar description: Highly customizable, feature-packed calendar widget for Flutter. -version: 3.1.2 +version: 3.1.3 author: Aleksander Woźniak homepage: https://github.com/aleksanderwozniak/table_calendar From baaa3f9ebb50d775c2c718be7194c43d6560ef15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Wed, 8 Jan 2025 18:18:10 +0100 Subject: [PATCH 161/164] Fix code formatting --- test/table_calendar_test.dart | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/test/table_calendar_test.dart b/test/table_calendar_test.dart index 7551f0c0..2889b947 100644 --- a/test/table_calendar_test.dart +++ b/test/table_calendar_test.dart @@ -464,23 +464,25 @@ void main() { (tester) async { final eventDay = DateTime.utc(2021, 7, 20); - await tester.pumpWidget(setupTestWidget( - TableCalendar( - focusedDay: initialFocusedDay, - loadEventsForDisabledDays: true, - firstDay: firstDay, - lastDay: lastDay, - currentDay: today, - eventLoader: (day) { - if (day.day == eventDay.day && day.month == eventDay.month) { - return ['Event 1', 'Event 2', 'Event 3']; - } + await tester.pumpWidget( + setupTestWidget( + TableCalendar( + focusedDay: initialFocusedDay, + loadEventsForDisabledDays: true, + firstDay: firstDay, + lastDay: lastDay, + currentDay: today, + eventLoader: (day) { + if (day.day == eventDay.day && day.month == eventDay.month) { + return ['Event 1', 'Event 2', 'Event 3']; + } - return []; - }, - enabledDayPredicate: (day) => false, + return []; + }, + enabledDayPredicate: (day) => false, + ), ), - )); + ); final eventDayKey = cellContentKey(eventDay); final eventDayCellContent = find.byKey(eventDayKey); From 665dd69d4b0072ddd33e887e12310ee33f0d90c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Wed, 8 Jan 2025 18:36:11 +0100 Subject: [PATCH 162/164] Upgrade intl to ^0.20.0 --- example/pubspec.lock | 38 +++++++++++++++++++++++++++++++++++--- pubspec.lock | 38 +++++++++++++++++++++++++++++++++++--- pubspec.yaml | 2 +- 3 files changed, 71 insertions(+), 7 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index 69440148..0dc447c5 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -59,14 +59,30 @@ packages: description: flutter source: sdk version: "0.0.0" + http: + dependency: transitive + description: + name: http + sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 + url: "https://pub.dev" + source: hosted + version: "1.2.2" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" intl: dependency: "direct main" description: name: intl - sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + sha256: "00f33b908655e606b86d2ade4710a231b802eec6f11e87e4ea3783fd72077a50" url: "https://pub.dev" source: hosted - version: "0.19.0" + version: "0.20.1" leak_tracker: dependency: transitive description: @@ -191,6 +207,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.2" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" vector_math: dependency: transitive description: @@ -207,6 +231,14 @@ packages: url: "https://pub.dev" source: hosted version: "14.2.5" + web: + dependency: transitive + description: + name: web + sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb + url: "https://pub.dev" + source: hosted + version: "1.1.0" sdks: - dart: ">=3.3.0 <4.0.0" + dart: ">=3.5.0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/pubspec.lock b/pubspec.lock index 1c8d5181..d4a0886f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -59,14 +59,30 @@ packages: description: flutter source: sdk version: "0.0.0" + http: + dependency: transitive + description: + name: http + sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 + url: "https://pub.dev" + source: hosted + version: "1.2.2" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" intl: dependency: "direct main" description: name: intl - sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + sha256: "00f33b908655e606b86d2ade4710a231b802eec6f11e87e4ea3783fd72077a50" url: "https://pub.dev" source: hosted - version: "0.19.0" + version: "0.20.1" leak_tracker: dependency: transitive description: @@ -192,6 +208,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.2" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" vector_math: dependency: transitive description: @@ -208,6 +232,14 @@ packages: url: "https://pub.dev" source: hosted version: "14.2.5" + web: + dependency: transitive + description: + name: web + sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb + url: "https://pub.dev" + source: hosted + version: "1.1.0" sdks: - dart: ">=3.3.0 <4.0.0" + dart: ">=3.5.0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/pubspec.yaml b/pubspec.yaml index a212f61b..8637027b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ dependencies: flutter: sdk: flutter - intl: ^0.19.0 + intl: ^0.20.0 simple_gesture_detector: ^0.2.0 dev_dependencies: From b5ad81bb491fad9ddc6957ffe35deea4ce13b9a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Wed, 8 Jan 2025 18:51:05 +0100 Subject: [PATCH 163/164] Fix empty weekendDays assertion bug --- lib/src/table_calendar.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/table_calendar.dart b/lib/src/table_calendar.dart index 9af770f0..aca0a03c 100644 --- a/lib/src/table_calendar.dart +++ b/lib/src/table_calendar.dart @@ -273,7 +273,7 @@ class TableCalendar extends StatefulWidget { }) : assert(availableCalendarFormats.keys.contains(calendarFormat)), assert(availableCalendarFormats.length <= CalendarFormat.values.length), assert( - weekendDays.isNotEmpty && + weekendDays.isEmpty || weekendDays.every( (day) => day >= DateTime.monday && day <= DateTime.sunday, ), From 5dc128b387204837d0937a0b16a00094957ed523 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20Wo=C5=BAniak?= Date: Wed, 8 Jan 2025 18:53:00 +0100 Subject: [PATCH 164/164] Release version 3.2.0 --- CHANGELOG.md | 6 ++++++ README.md | 2 +- example/pubspec.lock | 2 +- pubspec.yaml | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72e112ea..af61d0b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## [3.2.0] + +* Added loadEventsForDisabledDays property to enable loading events for disabled days as well +* Fixed empty weekendDays assertion issue +* Updated intl version to 0.20.0 + ## [3.1.3] * Updated gradle config for example project diff --git a/README.md b/README.md index df164049..67f8fe98 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Add the following line to `pubspec.yaml`: ```yaml dependencies: - table_calendar: ^3.1.3 + table_calendar: ^3.2.0 ``` ### Basic setup diff --git a/example/pubspec.lock b/example/pubspec.lock index 0dc447c5..ede0d144 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -190,7 +190,7 @@ packages: path: ".." relative: true source: path - version: "3.1.3" + version: "3.2.0" term_glyph: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 8637027b..c1fb94c0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: table_calendar description: Highly customizable, feature-packed calendar widget for Flutter. -version: 3.1.3 +version: 3.2.0 author: Aleksander Woźniak homepage: https://github.com/aleksanderwozniak/table_calendar