diff --git a/flutter_date_pickers-master/CHANGELOG.md b/flutter_date_pickers-master/CHANGELOG.md new file mode 100644 index 00000000..3231855b --- /dev/null +++ b/flutter_date_pickers-master/CHANGELOG.md @@ -0,0 +1,58 @@ +## [0.0.6] - 21 November 2019 +Added support for custom day decoration. +Added support for custom disabled days. + +## [0.1.0] - 31 May 2020 +Fixed i18n issue for MonthPicker in case no locale was set. +Fixed selection periods with unselectable dates issue in RangePicker. +Minor changes and fixes. + +## [0.1.1] - 20 June 2020 +Added scrollPhysics property to DatePickerLayoutSettings. + +## [0.1.3] - 23 June 2020 +Added day headers style customization. +Added prev/next icon customization. +Added selected period text styles customization. + +## [0.1.4] - 2 July 2020 +Added firstDayOfWeekIndex customization. + +## [0.1.5] - 29 July 2020 +Added support of the CupertinoApp ancestor (fixed #29). + +## [0.1.6] - 21 August 2020 +Added two customizable fields to DatePickerLayoutSettings: showNextMonthStart, showPrevMonthEnd (implemented #28). + +## [0.1.7] - 25 August 2020 +Added onMonthChange callback for all day based pickers. +Added newPeriod field to UnselectablePeriodError class. + +## [0.1.8] - 26 October 2020 +Fixed selection in RangePicker which is on the edge of date when time changes (#44). + +## [0.1.9] - 23 December 2020 +Increased intl dependency version. +Minor changes. + +## [0.1.10] - 23 December 2020 +Increased intl dependency version according to one used on pub.dev. + +## [0.2.0] - 7 March 2021 +Migrated to null-safety. +Added DatePickerLayoutSettings.hideMonthNavigationRow option. + +## [0.2.1] - 16 March 2021 +Used intl for getting short month name for MonthPicker (fixed #54) + +## [0.2.2] - 20 March 2021 +Added **initiallyShowDate** optional property for DayPicker, WeekPicker and RangePicker. + +## [0.2.3] - 05 April 2021 +Fixed nextTooltip initializing (#57). + +## [0.2.3+1] - 11 April 2021 +Fixed defining DayHeaderStyle in DatePickerStyles.fulfillWithTheme. + +## [0.2.4] - 29 April 2021 +Fixed incorrect new month calculations (#56). \ No newline at end of file diff --git a/flutter_date_pickers-master/LICENSE b/flutter_date_pickers-master/LICENSE new file mode 100644 index 00000000..d67d1c7a --- /dev/null +++ b/flutter_date_pickers-master/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Maria Melnik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/flutter_date_pickers-master/README.md b/flutter_date_pickers-master/README.md new file mode 100644 index 00000000..196492b5 --- /dev/null +++ b/flutter_date_pickers-master/README.md @@ -0,0 +1,120 @@ +# flutter_date_pickers + +[![Actions Status](https://github.com/MariaMelnik/flutter_date_pickers/workflows/Test/badge.svg)](https://github.com/MariaMelnik/flutter_date_pickers/actions) +[![Pub](https://img.shields.io/pub/v/flutter_date_pickers.svg)](https://pub.dev/packages/flutter_date_pickers) +[![Likes](https://img.shields.io/badge/dynamic/json?color=blue&label=likes&query=likes&url=http://www.pubscore.gq/likes?package=flutter_date_pickers&style=flat-square&cacheSeconds=90000)](https://pub.dev/packages/flutter_date_pickers) +[![Health](https://img.shields.io/badge/dynamic/json?color=blue&label=health&query=pub_points&url=http://www.pubscore.gq/pub-points?package=flutter_date_pickers&style=flat-square&cacheSeconds=90000)](https://pub.dev/packages/flutter_date_pickers/score) +[![Code size](https://img.shields.io/github/languages/code-size/MariaMelnik/flutter_date_pickers?logo=github&logoColor=white)](https://github.com/MariaMelnik/flutter_date_pickers) +[![License: MIT](https://img.shields.io/badge/license-MIT-purple.svg)](https://opensource.org/licenses/MIT) +[![GitHub stars](https://img.shields.io/github/stars/MariaMelnik/flutter_date_pickers?style=social)](https://github.com/MariaMelnik/flutter_date_pickers/) + + + +Allows to use date pickers without dialog. +Provides some customizable styles for date pickers. + +A set of date pickers: +* `DayPicker` for one day +* `WeekPicker` for whole week +* `RangePicker` for random range +* `MonthPicker` for month + +![](demoDatePickers2.gif) + +## How to style date picker +Every date picker constructor take a style object as a parameter (if no styles passed - defaults will be used). + +For single value pickers (DayPicker, MonthPicker) it is DatePickerStyles object; + +For range pickers (WeekPicker, RangePickers) it is DatePickerRangeStyles object; + +Customizable styles: +for all date pickers + +| Property | Description | +|---|---| +| TextStyle displayedPeriodTitle | title of the date picker | +| TextStyle currentDateStyle | style for current date | +| TextStyle disabledDateStyle | style for disabled dates (before first and after last date user can pick) | +| TextStyle selectedDateStyle | style for selected date | +| BoxDecoration selectedSingleDateDecoration | decoration for selected date in case single value is selected | +| TextStyle defaultDateTextStyle | style for date which is neither current nor disabled nor selected | + +only for range date pickers (WeekPicker, RangePicker) + +| Property | Description | +|---|---| +| BoxDecoration selectedPeriodStartDecoration | decoration for the first date of the selected range | +| BoxDecoration selectedPeriodLastDecoration | decoration for the first date of the selected range | +| BoxDecoration selectedPeriodMiddleDecoration | Decoration for the date of the selected range which is not first date and not end date of this range | + +## How to make some dates not selectable date picker +By default only dates before `firstDate` and after `lastDate` are not selectable. But you can set custom disabled days. +`DayPicker`, `WeekPicker` and `RangePicker` take a `SelectableDayPredicate selectableDayPredicate` +where you can specify function which returns if some date is disabled or not. + +If some date is disabled for selection it gets `disabledDateStyle`. + +If selected range or week pretends to include such disabled date `UnselectablePeriodException` occurs. +To handle it - pass `onSelectionError` callback to date picker. + +## How to make special decorations for some dates +By default cells are decorated with `datePickerStyles` slyles (or default if no styles was passed to date picker). +If you need special decoration for some days use `eventDecorationBuilder`. +Currently only for `DayPicker`, `WeekPicker` and `RangePicker`. + +- If date is not selected basic styles will be merged with styles from `eventDecorationBuilder`. +- If date is current date styles from `eventDecorationBuilder` win (if there are). +- Otherwise basic styles (`datePickerStyles`) win. + +## What time I will get after selection? +If one day selected: + you will get start of the day (00:00:00) by default. If selected `firstDate` - you will get time of it. + +If range/week selected: + for start you will get start of the day (00:00:00) by default. If selected `firstDate` - you will get time of it. + for end you will get end of the day (23:59:59.999) by default. If selected `lastDate` - you will get time of it. + +If month selected: + you will get start (00:00:00) of the 1 day of month by default. + If selected month same as month of the `firstDate` - you will get `firstDate`. + +## Usage + +```dart +// Create week date picker with passed parameters +Widget buildWeekDatePicker (DateTime selectedDate, DateTime firstAllowedDate, DateTime lastAllowedDate, ValueChanged onNewSelected) { + + // add some colors to default settings + DatePickerRangeStyles styles = DatePickerRangeStyles( + selectedPeriodLastDecoration: BoxDecoration( + color: Colors.red, + borderRadius: BorderRadius.only( + topRight: Radius.circular(10.0), + bottomRight: Radius.circular(10.0))), + selectedPeriodStartDecoration: BoxDecoration( + color: Colors.green, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10.0), bottomLeft: Radius.circular(10.0)), + ), + selectedPeriodMiddleDecoration: BoxDecoration( + color: Colors.yellow, shape: BoxShape.rectangle), + ); + + return WeekPicker( + selectedDate: selectedDate, + onChanged: onNewSelected, + firstDate: firstAllowedDate, + lastDate: lastAllowedDate, + datePickerStyles: styles + ); +} +``` + +## Example app +Please checkout [example](https://github.com/MariaMelnik/flutter_date_pickers/tree/master/example). + +For help getting started with Flutter, view our +[online documentation](https://flutter.io/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/flutter_date_pickers-master/analysis_options.yaml b/flutter_date_pickers-master/analysis_options.yaml new file mode 100644 index 00000000..d8569d72 --- /dev/null +++ b/flutter_date_pickers-master/analysis_options.yaml @@ -0,0 +1,94 @@ +linter: + rules: + # STYLE + camel_case_types: true + camel_case_extensions: true + library_names: true + file_names: true + library_prefixes: true + non_constant_identifier_names: true + constant_identifier_names: true + directives_ordering: true + lines_longer_than_80_chars: true + curly_braces_in_flow_control_structures: true + + # DOCUMENTATION + slash_for_doc_comments: true + package_api_docs: true + public_member_api_docs: true + comment_references: true + + # USAGE + implementation_imports: true + avoid_relative_lib_imports: true + prefer_relative_imports: true + prefer_adjacent_string_concatenation: true + prefer_interpolation_to_compose_strings: true + unnecessary_brace_in_string_interps: true + prefer_collection_literals: true + prefer_is_empty: true + prefer_is_not_empty: true + avoid_function_literals_in_foreach_calls: true + prefer_iterable_whereType: true + prefer_function_declarations_over_variables: true + unnecessary_lambdas: true + prefer_equal_for_default_values: true + avoid_init_to_null: true + unnecessary_getters_setters: true + unnecessary_getters: true + prefer_expression_function_bodies: true + unnecessary_this: true + unnecessary_const: true + avoid_catches_without_on_clauses: true + avoid_catching_errors: true + use_rethrow_when_possible: true + + # DESIGN + use_to_and_as_if_applicable: true + one_member_abstracts: true + avoid_classes_with_only_static_members: true + prefer_mixin: true + prefer_final_fields: true + use_setters_to_change_properties: true + avoid_setters_without_getters: true + avoid_returning_null: true + avoid_returning_this: true + type_annotate_public_apis: true + prefer_typing_uninitialized_variables: true +# omit_local_variable_types: true + avoid_types_on_closure_parameters: true + avoid_return_types_on_setters: true + prefer_generic_function_type_aliases: true + avoid_private_typedef_functions: true + use_function_type_syntax_for_parameters: true + avoid_positional_boolean_parameters: true + hash_and_equals: true + avoid_equals_and_hash_code_on_mutable_classes: true + avoid_null_checks_in_equality_operators: true + + + # PEDANTIC + # (duplicated rules are removed) + always_declare_return_types: true + always_require_non_null_named_parameters: true + annotate_overrides: true + avoid_empty_else: true + avoid_shadowing_type_parameters: true + avoid_types_as_parameter_names: true + empty_catches: true + empty_constructor_bodies: true + no_duplicate_case_values: true + null_closures: true + prefer_conditional_assignment: true + prefer_contains: true + prefer_for_elements_to_map_fromIterable: true + prefer_if_null_operators: true +# prefer_single_quotes: true + prefer_spread_collections: true + recursive_getters: true + type_init_formals: true + unawaited_futures: true + unnecessary_new: true + unnecessary_null_in_if_null_operators: true + unrelated_type_equality_checks: true + valid_regexps: true \ No newline at end of file diff --git a/flutter_date_pickers-master/demoDatePickers.gif b/flutter_date_pickers-master/demoDatePickers.gif new file mode 100644 index 00000000..4e927d5d Binary files /dev/null and b/flutter_date_pickers-master/demoDatePickers.gif differ diff --git a/flutter_date_pickers-master/demoDatePickers2.gif b/flutter_date_pickers-master/demoDatePickers2.gif new file mode 100644 index 00000000..68482fec Binary files /dev/null and b/flutter_date_pickers-master/demoDatePickers2.gif differ diff --git a/flutter_date_pickers-master/example/android/app/build.gradle b/flutter_date_pickers-master/example/android/app/build.gradle new file mode 100644 index 00000000..3a57252b --- /dev/null +++ b/flutter_date_pickers-master/example/android/app/build.gradle @@ -0,0 +1,61 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +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' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 28 + + lintOptions { + disable 'InvalidPackage' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.mariamelnik.flutter_date_picker" + minSdkVersion 16 + targetSdkVersion 28 + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +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' +} diff --git a/flutter_date_pickers-master/example/android/app/src/debug/AndroidManifest.xml b/flutter_date_pickers-master/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 00000000..73c267b0 --- /dev/null +++ b/flutter_date_pickers-master/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/flutter_date_pickers-master/example/android/app/src/main/AndroidManifest.xml b/flutter_date_pickers-master/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..0ff98a1a --- /dev/null +++ b/flutter_date_pickers-master/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + diff --git a/flutter_date_pickers-master/example/android/app/src/main/java/com/mariamelnik/flutter_date_picker/MainActivity.java b/flutter_date_pickers-master/example/android/app/src/main/java/com/mariamelnik/flutter_date_picker/MainActivity.java new file mode 100644 index 00000000..7831724f --- /dev/null +++ b/flutter_date_pickers-master/example/android/app/src/main/java/com/mariamelnik/flutter_date_picker/MainActivity.java @@ -0,0 +1,13 @@ +package com.mariamelnik.flutter_date_picker; + +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/flutter_date_pickers-master/example/android/app/src/main/res/drawable/launch_background.xml b/flutter_date_pickers-master/example/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 00000000..304732f8 --- /dev/null +++ b/flutter_date_pickers-master/example/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/flutter_date_pickers-master/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/flutter_date_pickers-master/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 00000000..db77bb4b Binary files /dev/null and b/flutter_date_pickers-master/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/flutter_date_pickers-master/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/flutter_date_pickers-master/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 00000000..17987b79 Binary files /dev/null and b/flutter_date_pickers-master/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/flutter_date_pickers-master/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/flutter_date_pickers-master/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 00000000..09d43914 Binary files /dev/null and b/flutter_date_pickers-master/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/flutter_date_pickers-master/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/flutter_date_pickers-master/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 00000000..d5f1c8d3 Binary files /dev/null and b/flutter_date_pickers-master/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/flutter_date_pickers-master/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/flutter_date_pickers-master/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 00000000..4d6372ee Binary files /dev/null and b/flutter_date_pickers-master/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/flutter_date_pickers-master/example/android/app/src/main/res/values/styles.xml b/flutter_date_pickers-master/example/android/app/src/main/res/values/styles.xml new file mode 100644 index 00000000..00fa4417 --- /dev/null +++ b/flutter_date_pickers-master/example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,8 @@ + + + + diff --git a/flutter_date_pickers-master/example/android/app/src/profile/AndroidManifest.xml b/flutter_date_pickers-master/example/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 00000000..73c267b0 --- /dev/null +++ b/flutter_date_pickers-master/example/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/flutter_date_pickers-master/example/android/build.gradle b/flutter_date_pickers-master/example/android/build.gradle new file mode 100644 index 00000000..bb8a3038 --- /dev/null +++ b/flutter_date_pickers-master/example/android/build.gradle @@ -0,0 +1,29 @@ +buildscript { + repositories { + google() + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.2.1' + } +} + +allprojects { + repositories { + google() + jcenter() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/flutter_date_pickers-master/example/android/gradle.properties b/flutter_date_pickers-master/example/android/gradle.properties new file mode 100644 index 00000000..7be3d8b4 --- /dev/null +++ b/flutter_date_pickers-master/example/android/gradle.properties @@ -0,0 +1,2 @@ +org.gradle.jvmargs=-Xmx1536M +android.enableR8=true diff --git a/flutter_date_pickers-master/example/android/gradle/wrapper/gradle-wrapper.properties b/flutter_date_pickers-master/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..2819f022 --- /dev/null +++ b/flutter_date_pickers-master/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Jun 23 08:50:38 CEST 2017 +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 diff --git a/flutter_date_pickers-master/example/android/settings.gradle b/flutter_date_pickers-master/example/android/settings.gradle new file mode 100644 index 00000000..5a2f14fb --- /dev/null +++ b/flutter_date_pickers-master/example/android/settings.gradle @@ -0,0 +1,15 @@ +include ':app' + +def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + +def plugins = new Properties() +def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') +if (pluginsFile.exists()) { + pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } +} + +plugins.each { name, path -> + def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() + include ":$name" + project(":$name").projectDir = pluginDirectory +} diff --git a/flutter_date_pickers-master/example/ios/Flutter/AppFrameworkInfo.plist b/flutter_date_pickers-master/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 00000000..9367d483 --- /dev/null +++ b/flutter_date_pickers-master/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 8.0 + + diff --git a/flutter_date_pickers-master/example/ios/Flutter/Debug.xcconfig b/flutter_date_pickers-master/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 00000000..592ceee8 --- /dev/null +++ b/flutter_date_pickers-master/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/flutter_date_pickers-master/example/ios/Flutter/Release.xcconfig b/flutter_date_pickers-master/example/ios/Flutter/Release.xcconfig new file mode 100644 index 00000000..592ceee8 --- /dev/null +++ b/flutter_date_pickers-master/example/ios/Flutter/Release.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/flutter_date_pickers-master/example/ios/Runner.xcodeproj/project.pbxproj b/flutter_date_pickers-master/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 00000000..2785fc1b --- /dev/null +++ b/flutter_date_pickers-master/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,491 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 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 */; }; + 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 */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* 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 = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; 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 = ""; }; + 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 = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + CF3B75C9A7D2FA2A4C99F110 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 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 */, + ); + path = Runner; + sourceTree = ""; + }; + 97C146F11CF9000F007C117D /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 97C146F21CF9000F007C117D /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0910; + ORGANIZATIONNAME = "The Chromium Authors"; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, + 97C146F31CF9000F007C117D /* main.m in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = 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_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = S8QB4VV633; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.mariamelnik.flutterDatePicker; + PRODUCT_NAME = "$(TARGET_NAME)"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = 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_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = 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_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.mariamelnik.flutterDatePicker; + PRODUCT_NAME = "$(TARGET_NAME)"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.mariamelnik.flutterDatePicker; + PRODUCT_NAME = "$(TARGET_NAME)"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/flutter_date_pickers-master/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/flutter_date_pickers-master/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/flutter_date_pickers-master/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/flutter_date_pickers-master/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/flutter_date_pickers-master/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 00000000..786d6aad --- /dev/null +++ b/flutter_date_pickers-master/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/flutter_date_pickers-master/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/flutter_date_pickers-master/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..1d526a16 --- /dev/null +++ b/flutter_date_pickers-master/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/flutter_date_pickers-master/example/ios/Runner/AppDelegate.h b/flutter_date_pickers-master/example/ios/Runner/AppDelegate.h new file mode 100644 index 00000000..36e21bbf --- /dev/null +++ b/flutter_date_pickers-master/example/ios/Runner/AppDelegate.h @@ -0,0 +1,6 @@ +#import +#import + +@interface AppDelegate : FlutterAppDelegate + +@end diff --git a/flutter_date_pickers-master/example/ios/Runner/AppDelegate.m b/flutter_date_pickers-master/example/ios/Runner/AppDelegate.m new file mode 100644 index 00000000..59a72e90 --- /dev/null +++ b/flutter_date_pickers-master/example/ios/Runner/AppDelegate.m @@ -0,0 +1,13 @@ +#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/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..d36b1fab --- /dev/null +++ b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 00000000..3d43d11e Binary files /dev/null and b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 00000000..28c6bf03 Binary files /dev/null and b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 00000000..2ccbfd96 Binary files /dev/null and b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 00000000..f091b6b0 Binary files /dev/null and b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 00000000..4cde1211 Binary files /dev/null and b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 00000000..d0ef06e7 Binary files /dev/null and b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 00000000..dcdc2306 Binary files /dev/null and b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 00000000..2ccbfd96 Binary files /dev/null and b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 00000000..c8f9ed8f Binary files /dev/null and b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 00000000..a6d6b860 Binary files /dev/null and b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 00000000..a6d6b860 Binary files /dev/null and b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 00000000..75b2d164 Binary files /dev/null and b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 00000000..c4df70d3 Binary files /dev/null and b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 00000000..6a84f41e Binary files /dev/null and b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 00000000..d0e1f585 Binary files /dev/null and b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 00000000..0bedcf2f --- /dev/null +++ b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 00000000..9da19eac Binary files /dev/null and b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 00000000..9da19eac Binary files /dev/null and b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 00000000..9da19eac Binary files /dev/null and b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 00000000..89c2725b --- /dev/null +++ b/flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/flutter_date_pickers-master/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/flutter_date_pickers-master/example/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..f2e259c7 --- /dev/null +++ b/flutter_date_pickers-master/example/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/flutter_date_pickers-master/example/ios/Runner/Base.lproj/Main.storyboard b/flutter_date_pickers-master/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 00000000..f3c28516 --- /dev/null +++ b/flutter_date_pickers-master/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/flutter_date_pickers-master/example/ios/Runner/Info.plist b/flutter_date_pickers-master/example/ios/Runner/Info.plist new file mode 100644 index 00000000..588cd65b --- /dev/null +++ b/flutter_date_pickers-master/example/ios/Runner/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + flutter_date_picker + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/flutter_date_pickers-master/example/ios/Runner/main.m b/flutter_date_pickers-master/example/ios/Runner/main.m new file mode 100644 index 00000000..dff6597e --- /dev/null +++ b/flutter_date_pickers-master/example/ios/Runner/main.m @@ -0,0 +1,9 @@ +#import +#import +#import "AppDelegate.h" + +int main(int argc, char* argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/flutter_date_pickers-master/example/lib/color_picker_dialog.dart b/flutter_date_pickers-master/example/lib/color_picker_dialog.dart new file mode 100644 index 00000000..1e834d7d --- /dev/null +++ b/flutter_date_pickers-master/example/lib/color_picker_dialog.dart @@ -0,0 +1,71 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_material_color_picker/flutter_material_color_picker.dart'; + +/// Dialog with some Material colors ([materialColors]) to pick one of them. +class ColorPickerDialog extends StatefulWidget { + + /// Initially selected color. + /// + /// If pre-selected color is not from [materialColors] [Colors.blue] will be + /// used. + final Color selectedColor; + + /// + const ColorPickerDialog({ + Key? key, + required this.selectedColor + }) : super(key: key); + + @override + State createState() => _ColorPickerDialogState(); +} + +class _ColorPickerDialogState extends State { + Color _mainColor = Colors.blue; + + @override + void initState() { + super.initState(); + + bool isSelectedMaterial = materialColors.contains(widget.selectedColor); + if (isSelectedMaterial) { + _mainColor = widget.selectedColor; + } + } + + @override + // ignore: prefer_expression_function_bodies + Widget build(BuildContext context) { + return AlertDialog( + contentPadding: const EdgeInsets.all(6.0), + title: const Text("Color picker"), + content: MaterialColorPicker( + selectedColor: _mainColor, + allowShades: false, + onMainColorChange: _onMainColorChange, + ), + actions: [ + TextButton( + child: const Text('CANCEL'), + onPressed: () { + Navigator.of(context).pop(null); + }, + ), + TextButton( + child: const Text('SUBMIT'), + onPressed: () { + Navigator.of(context).pop(_mainColor); + }, + ), + ], + ); + } + + void _onMainColorChange (Color? newColor) { + if (newColor == null) return; + + setState(() { + _mainColor = newColor; + }); + } +} diff --git a/flutter_date_pickers-master/example/lib/color_selector_btn.dart b/flutter_date_pickers-master/example/lib/color_selector_btn.dart new file mode 100644 index 00000000..f33a83b7 --- /dev/null +++ b/flutter_date_pickers-master/example/lib/color_selector_btn.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; + +// default with and height for the button container +const double _kBtnSize = 24.0; + +/// Round colored button with title to select some style color. +class ColorSelectorBtn extends StatelessWidget { + /// Title near color button. + final String title; + + /// Color of the button. + final Color color; + + /// onTap callback. + final VoidCallback showDialogFunction; + + /// Size of the circle. + /// + /// By default is [_kBtnSize]. + final double colorBtnSize; + + /// + const ColorSelectorBtn( + {Key? key, + required this.title, + required this.color, + required this.showDialogFunction, + this.colorBtnSize = _kBtnSize}) + : super(key: key); + + @override + // ignore: prefer_expression_function_bodies + Widget build(BuildContext context) { + return Expanded( + child: Row( + children: [ + GestureDetector( + onTap: showDialogFunction, + child: Container( + height: colorBtnSize, + width: colorBtnSize, + decoration: BoxDecoration(color: color, shape: BoxShape.circle), + ), + ), + const SizedBox( + width: 8.0, + ), + Expanded( + child: Text( + title, + overflow: TextOverflow.ellipsis, + )), + ], + ), + ); + } +} diff --git a/flutter_date_pickers-master/example/lib/date_pickers_widgets/day_picker_page.dart b/flutter_date_pickers-master/example/lib/date_pickers_widgets/day_picker_page.dart new file mode 100644 index 00000000..7ae2a10f --- /dev/null +++ b/flutter_date_pickers-master/example/lib/date_pickers_widgets/day_picker_page.dart @@ -0,0 +1,188 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_date_pickers/flutter_date_pickers.dart' as dp; +import 'package:flutter_date_pickers/flutter_date_pickers.dart'; + +import '../color_picker_dialog.dart'; +import '../color_selector_btn.dart'; +import '../event.dart'; + +/// Page with [dp.DayPicker]. +class DayPickerPage extends StatefulWidget { + /// Custom events. + final List events; + + /// + const DayPickerPage({ + Key? key, + this.events = const [] + }) : super(key: key); + + @override + State createState() => _DayPickerPageState(); +} + +class _DayPickerPageState extends State { + DateTime _selectedDate = DateTime.now(); + + DateTime _firstDate = DateTime.now().subtract(Duration(days: 45)); + DateTime _lastDate = DateTime.now().add(Duration(days: 45)); + + Color selectedDateStyleColor = Colors.blue; + Color selectedSingleDateDecorationColor = Colors.red; + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + + Color? bodyTextColor = Theme.of(context).accentTextTheme.bodyText1?.color; + if (bodyTextColor != null) selectedDateStyleColor = bodyTextColor; + + selectedSingleDateDecorationColor = Theme.of(context).accentColor; + } + + @override + Widget build(BuildContext context) { + // add selected colors to default settings + dp.DatePickerRangeStyles styles = dp.DatePickerRangeStyles( + selectedDateStyle: Theme.of(context) + .accentTextTheme + .bodyText1 + ?.copyWith(color: selectedDateStyleColor), + selectedSingleDateDecoration: BoxDecoration( + color: selectedSingleDateDecorationColor, + shape: BoxShape.circle + ), + dayHeaderStyle: DayHeaderStyle( + textStyle: TextStyle( + color: Colors.red + ) + ) + ); + + return Flex( + direction: MediaQuery.of(context).orientation == Orientation.portrait + ? Axis.vertical + : Axis.horizontal, + children: [ + Expanded( + child: dp.DayPicker.single( + selectedDate: _selectedDate, + onChanged: _onSelectedDateChanged, + firstDate: _firstDate, + lastDate: _lastDate, + datePickerStyles: styles, + datePickerLayoutSettings: dp.DatePickerLayoutSettings( + maxDayPickerRowCount: 2, + showPrevMonthEnd: true, + showNextMonthStart: true + ), + selectableDayPredicate: _isSelectableCustom, + eventDecorationBuilder: _eventDecorationBuilder, + ), + ), + Container( + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 12.0, vertical: 12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Selected date styles", + style: Theme.of(context).textTheme.subtitle1, + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 12.0), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + ColorSelectorBtn( + title: "Text", + color: selectedDateStyleColor, + showDialogFunction: _showSelectedDateDialog, + colorBtnSize: 42.0, + ), + SizedBox( + width: 12.0, + ), + ColorSelectorBtn( + title: "Background", + color: selectedSingleDateDecorationColor, + showDialogFunction: _showSelectedBackgroundColorDialog, + colorBtnSize: 42.0, + ), + ], + ), + ), + Text("Selected: $_selectedDate") + ], + ), + ), + ), + ], + ); + } + + // select text color of the selected date + void _showSelectedDateDialog() async { + Color? newSelectedColor = await showDialog( + context: context, + builder: (_) => ColorPickerDialog( + selectedColor: selectedDateStyleColor, + )); + + if (newSelectedColor != null) { + setState(() { + selectedDateStyleColor = newSelectedColor; + }); + } + } + + // select background color of the selected date + void _showSelectedBackgroundColorDialog() async { + Color? newSelectedColor = await showDialog( + context: context, + builder: (_) => ColorPickerDialog( + selectedColor: selectedSingleDateDecorationColor, + )); + + if (newSelectedColor != null) { + setState(() { + selectedSingleDateDecorationColor = newSelectedColor; + }); + } + } + + void _onSelectedDateChanged(DateTime newDate) { + setState(() { + _selectedDate = newDate; + }); + } + + // ignore: prefer_expression_function_bodies + bool _isSelectableCustom (DateTime day) { + return day.weekday < 6; + } + + dp.EventDecoration? _eventDecorationBuilder(DateTime date) { + List eventsDates = widget.events + .map((Event e) => e.date) + .toList(); + + bool isEventDate = eventsDates.any((DateTime d) => + date.year == d.year + && date.month == d.month + && d.day == date.day); + + BoxDecoration roundedBorder = BoxDecoration( + border: Border.all( + color: Colors.deepOrange, + ), + borderRadius: BorderRadius.all(Radius.circular(3.0)) + ); + + return isEventDate + ? dp.EventDecoration(boxDecoration: roundedBorder) + : null; + } +} diff --git a/flutter_date_pickers-master/example/lib/date_pickers_widgets/days_picker_page.dart b/flutter_date_pickers-master/example/lib/date_pickers_widgets/days_picker_page.dart new file mode 100644 index 00000000..05fb1448 --- /dev/null +++ b/flutter_date_pickers-master/example/lib/date_pickers_widgets/days_picker_page.dart @@ -0,0 +1,191 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_date_pickers/flutter_date_pickers.dart' as dp; + +import '../color_picker_dialog.dart'; +import '../color_selector_btn.dart'; +import '../event.dart'; + +/// Page with [dp.DayPicker] where many single days can be selected. +class DaysPickerPage extends StatefulWidget { + /// Custom events. + final List events; + + /// + const DaysPickerPage({ + Key? key, + this.events = const [] + }) : super(key: key); + + @override + State createState() => _DaysPickerPageState(); +} + +class _DaysPickerPageState extends State { + List _selectedDates = []; + DateTime _firstDate = DateTime.now().subtract(Duration(days: 45)); + DateTime _lastDate = DateTime.now().add(Duration(days: 45)); + + Color selectedDateStyleColor = Colors.blue; + Color selectedSingleDateDecorationColor = Colors.red; + + @override + void initState() { + super.initState(); + + final now = DateTime.now(); + + _selectedDates = [ + now, + now.subtract(Duration(days: 10)), + now.add(Duration(days: 7)) + ]; + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + + Color? bodyTextColor = Theme.of(context).accentTextTheme.bodyText1?.color; + if (bodyTextColor != null) selectedDateStyleColor = bodyTextColor; + + selectedSingleDateDecorationColor = Theme.of(context).accentColor; + } + + @override + Widget build(BuildContext context) { + // add selected colors to default settings + dp.DatePickerRangeStyles styles = dp.DatePickerRangeStyles( + selectedDateStyle: Theme.of(context) + .accentTextTheme + .bodyText1 + ?.copyWith(color: selectedDateStyleColor), + selectedSingleDateDecoration: BoxDecoration( + color: selectedSingleDateDecorationColor, shape: BoxShape.circle)); + + return Flex( + direction: MediaQuery.of(context).orientation == Orientation.portrait + ? Axis.vertical + : Axis.horizontal, + children: [ + Expanded( + child: dp.DayPicker.multi( + selectedDates: _selectedDates, + onChanged: _onSelectedDateChanged, + firstDate: _firstDate, + lastDate: _lastDate, + datePickerStyles: styles, + datePickerLayoutSettings: dp.DatePickerLayoutSettings( + maxDayPickerRowCount: 2, + showPrevMonthEnd: true, + showNextMonthStart: true + ), + selectableDayPredicate: _isSelectableCustom, + eventDecorationBuilder: _eventDecorationBuilder, + ), + ), + Container( + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 12.0, vertical: 12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Selected date styles", + style: Theme.of(context).textTheme.subtitle1, + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 12.0), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + ColorSelectorBtn( + title: "Text", + color: selectedDateStyleColor, + showDialogFunction: _showSelectedDateDialog, + colorBtnSize: 42.0, + ), + SizedBox( + width: 12.0, + ), + ColorSelectorBtn( + title: "Background", + color: selectedSingleDateDecorationColor, + showDialogFunction: _showSelectedBackgroundColorDialog, + colorBtnSize: 42.0, + ), + ], + ), + ), + Text("Selected: $_selectedDates") + ], + ), + ), + ), + ], + ); + } + + // select text color of the selected date + void _showSelectedDateDialog() async { + Color? newSelectedColor = await showDialog( + context: context, + builder: (_) => ColorPickerDialog( + selectedColor: selectedDateStyleColor, + )); + + if (newSelectedColor != null) { + setState(() { + selectedDateStyleColor = newSelectedColor; + }); + } + } + + // select background color of the selected date + void _showSelectedBackgroundColorDialog() async { + Color? newSelectedColor = await showDialog( + context: context, + builder: (_) => ColorPickerDialog( + selectedColor: selectedSingleDateDecorationColor, + )); + + if (newSelectedColor != null) { + setState(() { + selectedSingleDateDecorationColor = newSelectedColor; + }); + } + } + + void _onSelectedDateChanged(List newDates) { + setState(() { + _selectedDates = newDates; + }); + } + + // ignore: prefer_expression_function_bodies + bool _isSelectableCustom (DateTime day) { + return day.weekday < 6; + } + + dp.EventDecoration? _eventDecorationBuilder(DateTime date) { + List eventsDates = widget.events + .map((Event e) => e.date) + .toList(); + + bool isEventDate = eventsDates.any((DateTime d) => + date.year == d.year + && date.month == d.month + && d.day == date.day); + + BoxDecoration roundedBorder = BoxDecoration( + border: Border.all( + color: Colors.deepOrange, + ), + borderRadius: BorderRadius.all(Radius.circular(3.0)) + ); + + return isEventDate + ? dp.EventDecoration(boxDecoration: roundedBorder) + : null; + } +} diff --git a/flutter_date_pickers-master/example/lib/date_pickers_widgets/month_picker_page.dart b/flutter_date_pickers-master/example/lib/date_pickers_widgets/month_picker_page.dart new file mode 100644 index 00000000..c6960de5 --- /dev/null +++ b/flutter_date_pickers-master/example/lib/date_pickers_widgets/month_picker_page.dart @@ -0,0 +1,134 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_date_pickers/flutter_date_pickers.dart' as dp; + +import '../color_picker_dialog.dart'; +import '../color_selector_btn.dart'; + +/// Page with the [dp.MonthPicker]. +class MonthPickerPage extends StatefulWidget { + @override + State createState() => _MonthPickerPageState(); +} + +class _MonthPickerPageState extends State { + DateTime _firstDate = DateTime.now().subtract(Duration(days: 350)); + DateTime _lastDate = DateTime.now().add(Duration(days: 350)); + DateTime _selectedDate = DateTime.now(); + + Color selectedDateStyleColor = Colors.blue; + Color selectedSingleDateDecorationColor = Colors.red; + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + + Color? bodyTextColor = Theme.of(context).accentTextTheme.bodyText1?.color; + if (bodyTextColor != null) selectedDateStyleColor = bodyTextColor; + + selectedSingleDateDecorationColor = Theme.of(context).accentColor; + } + + @override + Widget build(BuildContext context) { + // add selected colors to default settings + dp.DatePickerStyles styles = dp.DatePickerStyles( + selectedDateStyle: Theme.of(context) + .accentTextTheme + .bodyText1 + ?.copyWith(color: selectedDateStyleColor), + selectedSingleDateDecoration: BoxDecoration( + color: selectedSingleDateDecorationColor, shape: BoxShape.circle)); + + return Flex( + direction: MediaQuery.of(context).orientation == Orientation.portrait + ? Axis.vertical + : Axis.horizontal, + children: [ + Expanded( + child: dp.MonthPicker( + selectedDate: _selectedDate, + onChanged: _onSelectedDateChanged, + firstDate: _firstDate, + lastDate: _lastDate, + datePickerStyles: styles, + ), + ), + Container( + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 12.0, vertical: 12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Selected date styles", + style: Theme.of(context).textTheme.subtitle1, + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 12.0), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + ColorSelectorBtn( + title: "Text", + color: selectedDateStyleColor, + showDialogFunction: _showSelectedDateDialog, + colorBtnSize: 42.0, + ), + SizedBox( + width: 12.0, + ), + ColorSelectorBtn( + title: "Background", + color: selectedSingleDateDecorationColor, + showDialogFunction: _showSelectedBackgroundColorDialog, + colorBtnSize: 42.0, + ), + ], + ), + ), + Text("Selected: $_selectedDate") + ], + ), + ), + ), + ], + ); + } + + // select text color of the selected date + void _showSelectedDateDialog() async { + Color? newSelectedColor = await showDialog( + context: context, + builder: (_) => ColorPickerDialog( + selectedColor: selectedDateStyleColor, + )); + + if (newSelectedColor != null) { + setState(() { + selectedDateStyleColor = newSelectedColor; + }); + } + } + + // select background color of the selected date + void _showSelectedBackgroundColorDialog() async { + Color? newSelectedColor = await showDialog( + context: context, + builder: (_) => ColorPickerDialog( + selectedColor: selectedSingleDateDecorationColor, + )); + + if (newSelectedColor != null) { + setState(() { + selectedSingleDateDecorationColor = newSelectedColor; + }); + } + } + + void _onSelectedDateChanged(DateTime newDate) { + setState(() { + _selectedDate = newDate; + }); + } +} diff --git a/flutter_date_pickers-master/example/lib/date_pickers_widgets/range_picker_page.dart b/flutter_date_pickers-master/example/lib/date_pickers_widgets/range_picker_page.dart new file mode 100644 index 00000000..887e7e30 --- /dev/null +++ b/flutter_date_pickers-master/example/lib/date_pickers_widgets/range_picker_page.dart @@ -0,0 +1,273 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_date_pickers/flutter_date_pickers.dart'; + +import '../color_picker_dialog.dart'; +import '../color_selector_btn.dart'; +import '../event.dart'; + +/// Page with the [RangePicker]. +class RangePickerPage extends StatefulWidget { + + /// Custom events. + final List events; + + /// + const RangePickerPage({ + Key? key, + this.events = const [] + }) : super(key: key); + + @override + State createState() => _RangePickerPageState(); +} + +class _RangePickerPageState extends State { + DateTime _firstDate = DateTime.now().subtract(Duration(days: 345)); + DateTime _lastDate = DateTime.now().add(Duration(days: 345)); + DatePeriod _selectedPeriod = DatePeriod( + DateTime.now().subtract(Duration(days: 30)), + DateTime.now().subtract(Duration(days: 12)) + ); + + Color selectedPeriodStartColor = Colors.blue; + Color selectedPeriodLastColor = Colors.blue; + Color selectedPeriodMiddleColor = Colors.blue; + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + + selectedPeriodLastColor = Theme.of(context).accentColor; + selectedPeriodMiddleColor = Theme.of(context).accentColor; + selectedPeriodStartColor = Theme.of(context).accentColor; + } + + @override + Widget build(BuildContext context) { + // add selected colors to default settings + DatePickerRangeStyles styles = DatePickerRangeStyles( + selectedPeriodLastDecoration: BoxDecoration( + color: selectedPeriodLastColor, + borderRadius: const BorderRadius.only( + topRight: Radius.circular(24.0), + bottomRight: Radius.circular(24.0))), + selectedPeriodStartDecoration: BoxDecoration( + color: selectedPeriodStartColor, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(24.0), + bottomLeft: Radius.circular(24.0) + ), + ), + selectedPeriodMiddleDecoration: BoxDecoration( + color: selectedPeriodMiddleColor, shape: BoxShape.rectangle), + nextIcon: const Icon(Icons.arrow_right), + prevIcon: const Icon(Icons.arrow_left), + dayHeaderStyleBuilder: _dayHeaderStyleBuilder + ); + + return Flex( + direction: MediaQuery.of(context).orientation == Orientation.portrait + ? Axis.vertical + : Axis.horizontal, + children: [ + Expanded( + child: RangePicker( + initiallyShowDate: DateTime.now(), + selectedPeriod: _selectedPeriod, + onChanged: _onSelectedDateChanged, + firstDate: _firstDate, + lastDate: _lastDate, + datePickerStyles: styles, + eventDecorationBuilder: _eventDecorationBuilder, + selectableDayPredicate: _isSelectableCustom, + onSelectionError: _onSelectionError, + ), + ), + Container( + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 12.0, vertical: 12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Selected date styles", + style: Theme.of(context).textTheme.subtitle1, + ), + _stylesBlock(), + _selectedBlock() + ], + ), + ), + ), + ], + ); + } + + // Block with show information about selected date + // and boundaries of the selected period. + Widget _selectedBlock() => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _selectedPeriod != null + ? Column(children: [ + Padding( + padding: const EdgeInsets.only(top: 8.0, bottom: 4.0), + child: Text("Selected period boundaries:"), + ), + Text(_selectedPeriod.start.toString()), + Text(_selectedPeriod.end.toString()), + ]) + : Container() + ], + ); + + // block with color buttons inside + Widget _stylesBlock() => Padding( + padding: const EdgeInsets.symmetric(vertical: 12.0), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + ColorSelectorBtn( + title: "Start", + color: selectedPeriodStartColor, + showDialogFunction: _showSelectedStartColorDialog), + SizedBox( + width: 12.0, + ), + ColorSelectorBtn( + title: "Middle", + color: selectedPeriodMiddleColor, + showDialogFunction: _showSelectedMiddleColorDialog), + SizedBox( + width: 12.0, + ), + ColorSelectorBtn( + title: "End", + color: selectedPeriodLastColor, + showDialogFunction: _showSelectedEndColorDialog), + ], + ), + ); + + // select background color for the first date of the selected period + void _showSelectedStartColorDialog() async { + Color? newSelectedColor = await showDialog( + context: context, + builder: (_) => ColorPickerDialog( + selectedColor: selectedPeriodStartColor, + )); + + if (newSelectedColor != null) { + setState(() { + selectedPeriodStartColor = newSelectedColor; + }); + } + } + + // select background color for the last date of the selected period + void _showSelectedEndColorDialog() async { + Color? newSelectedColor = await showDialog( + context: context, + builder: (_) => ColorPickerDialog( + selectedColor: selectedPeriodLastColor, + )); + + if (newSelectedColor != null) { + setState(() { + selectedPeriodLastColor = newSelectedColor; + }); + } + } + + // select background color for the middle dates of the selected period + void _showSelectedMiddleColorDialog() async { + Color? newSelectedColor = await showDialog( + context: context, + builder: (_) => ColorPickerDialog( + selectedColor: selectedPeriodMiddleColor, + )); + + if (newSelectedColor != null) { + setState(() { + selectedPeriodMiddleColor = newSelectedColor; + }); + } + } + + void _onSelectedDateChanged(DatePeriod newPeriod) { + setState(() { + _selectedPeriod = newPeriod; + }); + } + + EventDecoration? _eventDecorationBuilder(DateTime date) { + List eventsDates = widget.events + .map((Event e) => e.date) + .toList(); + + bool isEventDate = eventsDates.any((DateTime d) => + date.year == d.year + && date.month == d.month + && d.day == date.day); + + BoxDecoration roundedBorder = BoxDecoration( + border: Border.all( + color: Colors.green, + ), + borderRadius: BorderRadius.all(Radius.circular(3.0)) + ); + + return isEventDate + ? EventDecoration(boxDecoration: roundedBorder) + : null; + } + + // ignore: prefer_expression_function_bodies + bool _isSelectableCustom (DateTime day) { + DateTime now = DateTime.now(); + DateTime yesterday = now.subtract(Duration(days: 1)); + DateTime tomorrow = now.add(Duration(days: 1)); + bool isYesterday = sameDate(day, yesterday); + bool isTomorrow = sameDate(day, tomorrow); + + return !isYesterday && !isTomorrow; + + // return true; +// return day.weekday < 6; +// return day.day != DateTime.now().add(Duration(days: 7)).day ; + } + + void _onSelectionError(UnselectablePeriodException exception) { + DatePeriod errorPeriod = exception.period; + + // If user supposed to set another start of the period. + bool selectStart = _selectedPeriod.start != errorPeriod.start; + + DateTime newSelection = selectStart + ? errorPeriod.start + : errorPeriod.end; + + DatePeriod newPeriod = DatePeriod(newSelection, newSelection); + + setState(() { + _selectedPeriod = newPeriod; + }); + } + + // 0 is Sunday, 6 is Saturday + DayHeaderStyle _dayHeaderStyleBuilder(int weekday) { + bool isWeekend = weekday == 0 || weekday == 6; + + return DayHeaderStyle( + textStyle: TextStyle( + color: isWeekend ? Colors.red : Colors.teal + ) + ); + } +} + + +bool sameDate(DateTime first, DateTime second) { + return first.year == second.year && first.month == second.month && first.day == second.day; +} \ No newline at end of file diff --git a/flutter_date_pickers-master/example/lib/date_pickers_widgets/range_picker_styled.dart b/flutter_date_pickers-master/example/lib/date_pickers_widgets/range_picker_styled.dart new file mode 100644 index 00000000..99e32874 --- /dev/null +++ b/flutter_date_pickers-master/example/lib/date_pickers_widgets/range_picker_styled.dart @@ -0,0 +1,228 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_date_pickers/flutter_date_pickers.dart'; + +import '../event.dart'; + +/// Page with the [RangePicker] styled according to issue: +/// https://github.com/MariaMelnik/flutter_date_pickers/issues/49 +class RangePickerPageStyled extends StatefulWidget { + + /// Custom events. + final List events; + + /// + const RangePickerPageStyled({ + Key? key, + this.events = const [] + }) : super(key: key); + + @override + State createState() => _RangePickerPageStyledState(); +} + +class _RangePickerPageStyledState extends State { + DateTime _firstDate = DateTime.now().subtract(Duration(days: 345)); + DateTime _lastDate = DateTime.now().add(Duration(days: 345)); + DatePeriod _selectedPeriod = DatePeriod( + DateTime.now().subtract(Duration(days: 4)), + DateTime.now().add(Duration(days: 8)) + ); + + Color selectedPeriodStartColor = Colors.blue; + Color selectedPeriodLastColor = Colors.blue; + Color selectedPeriodMiddleColor = Colors.blue; + + @override + void initState() { + super.initState(); + + DateTime selectedPeriodStart = DateTime.now().subtract(Duration(days: 4)); + DateTime selectedPeriodEnd = DateTime.now().add(Duration(days: 8)); + _selectedPeriod = DatePeriod(selectedPeriodStart, selectedPeriodEnd); + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + + // defaults for styles + selectedPeriodLastColor = Theme.of(context).accentColor; + selectedPeriodMiddleColor = Theme.of(context).accentColor; + selectedPeriodStartColor = Theme.of(context).accentColor; + } + + @override + Widget build(BuildContext context) { + Color middleBgColor = Color.fromRGBO(237, 237, 250, 1); + DecorationImage circleImg = DecorationImage( + image: AssetImage('images/bg.png'), + fit: BoxFit.contain + ); + + // add selected colors to default settings + DatePickerRangeStyles styles = DatePickerRangeStyles( + selectedPeriodLastDecoration: BoxDecoration( + color: middleBgColor, + gradient: LinearGradient( + colors: [middleBgColor, Colors.transparent], + stops: [0.5, 0.5] + ), + image: circleImg, + borderRadius: const BorderRadius.only( + topRight: Radius.circular(24.0), + bottomRight: Radius.circular(24.0)) + ), + selectedPeriodStartDecoration: BoxDecoration( + color: middleBgColor, + gradient: LinearGradient( + colors: [Colors.transparent, middleBgColor,], + stops: [0.5, 0.5] + ), + image: circleImg, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(24.0), + bottomLeft: Radius.circular(24.0) + ), + ), + selectedPeriodMiddleDecoration: BoxDecoration( + color: middleBgColor, + shape: BoxShape.rectangle + ), + ); + + return Flex( + direction: MediaQuery.of(context).orientation == Orientation.portrait + ? Axis.vertical + : Axis.horizontal, + children: [ + Expanded( + child: RangePicker( + selectedPeriod: _selectedPeriod, + onChanged: _onSelectedDateChanged, + firstDate: _firstDate, + lastDate: _lastDate, + datePickerStyles: styles, + eventDecorationBuilder: _eventDecorationBuilder, + selectableDayPredicate: _isSelectableCustom, + onSelectionError: _onSelectionError, + datePickerLayoutSettings: DatePickerLayoutSettings( + showNextMonthStart: true, + showPrevMonthEnd: true + ), + ), + ), + Container( + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 12.0, vertical: 12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Selected date styles", + style: Theme.of(context).textTheme.subtitle1, + ), + _selectedBlock() + ], + ), + ), + ), + ], + ); + } + + // Block with show information about selected date + // and boundaries of the selected period. + Widget _selectedBlock() => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _selectedPeriod != null + ? Column(children: [ + Padding( + padding: const EdgeInsets.only(top: 8.0, bottom: 4.0), + child: Text("Selected period boundaries:"), + ), + Text(_selectedPeriod.start.toString()), + Text(_selectedPeriod.end.toString()), + ]) + : Container() + ], + ); + + void _onSelectedDateChanged(DatePeriod newPeriod) { + setState(() { + _selectedPeriod = newPeriod; + }); + } + + EventDecoration? _eventDecorationBuilder(DateTime date) { + List eventsDates = widget.events + .map((Event e) => e.date) + .toList(); + + bool isEventDate = eventsDates.any((DateTime d) => + date.year == d.year + && date.month == d.month + && d.day == date.day); + + BoxDecoration roundedBorder = BoxDecoration( + border: Border.all( + color: Colors.green, + ), + borderRadius: BorderRadius.all(Radius.circular(3.0)) + ); + + return isEventDate + ? EventDecoration(boxDecoration: roundedBorder) + : null; + } + + // ignore: prefer_expression_function_bodies + bool _isSelectableCustom (DateTime day) { + DateTime now = DateTime.now(); + DateTime yesterday = now.subtract(Duration(days: 1)); + DateTime tomorrow = now.add(Duration(days: 1)); + bool isYesterday = _sameDate(day, yesterday); + bool isTomorrow = _sameDate(day, tomorrow); + + return !isYesterday && !isTomorrow; + + // return true; +// return day.weekday < 6; +// return day.day != DateTime.now().add(Duration(days: 7)).day ; + } + + void _onSelectionError(UnselectablePeriodException exception) { + DatePeriod errorPeriod = exception.period; + + // If user supposed to set another start of the period. + bool selectStart = _selectedPeriod.start != errorPeriod.start; + + DateTime newSelection = selectStart + ? errorPeriod.start + : errorPeriod.end; + + DatePeriod newPeriod = DatePeriod(newSelection, newSelection); + + setState(() { + _selectedPeriod = newPeriod; + }); + } + + // 0 is Sunday, 6 is Saturday + DayHeaderStyle _dayHeaderStyleBuilder(int weekday) { + bool isWeekend = weekday == 0 || weekday == 6; + + return DayHeaderStyle( + textStyle: TextStyle( + color: isWeekend ? Colors.red : Colors.teal + ) + ); + } +} + + +bool _sameDate(DateTime first, DateTime second) => + first.year == second.year + && first.month == second.month + && first.day == second.day; \ No newline at end of file diff --git a/flutter_date_pickers-master/example/lib/date_pickers_widgets/week_picker_page.dart b/flutter_date_pickers-master/example/lib/date_pickers_widgets/week_picker_page.dart new file mode 100644 index 00000000..276aad16 --- /dev/null +++ b/flutter_date_pickers-master/example/lib/date_pickers_widgets/week_picker_page.dart @@ -0,0 +1,239 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_date_pickers/flutter_date_pickers.dart'; + +import '../color_picker_dialog.dart'; +import '../color_selector_btn.dart'; +import '../event.dart'; + +/// Page with the [WeekPicker]. +class WeekPickerPage extends StatefulWidget { + /// Custom events. + final List events; + + /// + const WeekPickerPage({ + Key? key, + this.events = const [] + }) : super(key: key); + + @override + State createState() => _WeekPickerPageState(); +} + +class _WeekPickerPageState extends State { + DateTime _selectedDate = DateTime.now(); + DateTime _firstDate = DateTime.now().subtract(Duration(days: 45)); + DateTime _lastDate = DateTime.now().add(Duration(days: 45)); + DatePeriod? _selectedPeriod; + + Color selectedPeriodStartColor = Colors.blue; + Color selectedPeriodLastColor = Colors.blue; + Color selectedPeriodMiddleColor = Colors.blue; + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + + // defaults for styles + selectedPeriodLastColor = Theme.of(context).accentColor; + selectedPeriodMiddleColor = Theme.of(context).accentColor; + selectedPeriodStartColor = Theme.of(context).accentColor; + } + + @override + Widget build(BuildContext context) { + // add selected colors to default settings + DatePickerRangeStyles styles = DatePickerRangeStyles( + selectedPeriodLastDecoration: BoxDecoration( + color: selectedPeriodLastColor, + borderRadius: BorderRadius.only( + topRight: Radius.circular(10.0), + bottomRight: Radius.circular(10.0))), + selectedPeriodStartDecoration: BoxDecoration( + color: selectedPeriodStartColor, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10.0), bottomLeft: Radius.circular(10.0)), + ), + selectedPeriodMiddleDecoration: BoxDecoration( + color: selectedPeriodMiddleColor, shape: BoxShape.rectangle), + ); + + return Flex( + direction: MediaQuery.of(context).orientation == Orientation.portrait + ? Axis.vertical + : Axis.horizontal, + children: [ + Expanded( + child: WeekPicker( + selectedDate: _selectedDate, + onChanged: _onSelectedDateChanged, + firstDate: _firstDate, + lastDate: _lastDate, + datePickerStyles: styles, + onSelectionError: _onSelectionError, + selectableDayPredicate: _isSelectableCustom, + eventDecorationBuilder: _eventDecorationBuilder, + ), + ), + Container( + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 12.0, vertical: 12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Selected date styles", + style: Theme.of(context).textTheme.subtitle1, + ), + _stylesBlock(), + _selectedBlock() + ], + ), + ), + ), + ], + ); + } + + // block witt color buttons insede + Widget _stylesBlock() => Padding( + padding: const EdgeInsets.symmetric(vertical: 12.0), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + ColorSelectorBtn( + title: "Start", + color: selectedPeriodStartColor, + showDialogFunction: _showSelectedStartColorDialog), + SizedBox( + width: 12.0, + ), + ColorSelectorBtn( + title: "Middle", + color: selectedPeriodMiddleColor, + showDialogFunction: _showSelectedMiddleColorDialog), + SizedBox( + width: 12.0, + ), + ColorSelectorBtn( + title: "End", + color: selectedPeriodLastColor, + showDialogFunction: _showSelectedEndColorDialog), + ], + ), + ); + + // Block with information about selected date + // and boundaries of the selected period. + Widget _selectedBlock() => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Text("Selected: $_selectedDate"), + ), + _selectedPeriod != null + ? Column(children: [ + Padding( + padding: const EdgeInsets.only(top: 8.0, bottom: 4.0), + child: Text("Selected period boundaries:"), + ), + Text(_selectedPeriod!.start.toString()), + Text(_selectedPeriod!.end.toString()), + ]) + : Container() + ], + ); + + // select background color for the first date of the selected period + void _showSelectedStartColorDialog() async { + Color? newSelectedColor = await showDialog( + context: context, + builder: (_) => ColorPickerDialog( + selectedColor: selectedPeriodStartColor, + )); + + if (newSelectedColor != null) { + setState(() { + selectedPeriodStartColor = newSelectedColor; + }); + } + } + + // select background color for the last date of the selected period + void _showSelectedEndColorDialog() async { + Color? newSelectedColor = await showDialog( + context: context, + builder: (_) => ColorPickerDialog( + selectedColor: selectedPeriodLastColor, + )); + + if (newSelectedColor != null) { + setState(() { + selectedPeriodLastColor = newSelectedColor; + }); + } + } + + // select background color for the middle dates of the selected period + void _showSelectedMiddleColorDialog() async { + Color newSelectedColor = await showDialog( + context: context, + builder: (_) => ColorPickerDialog( + selectedColor: selectedPeriodMiddleColor, + )); + + if (newSelectedColor != null) { + setState(() { + selectedPeriodMiddleColor = newSelectedColor; + }); + } + } + + void _onSelectedDateChanged(DatePeriod newPeriod) { + setState(() { + _selectedDate = newPeriod.start; + _selectedPeriod = newPeriod; + }); + } + + void _onSelectionError(Object e){ + if (e is UnselectablePeriodException) print("catch error: $e"); + } + +// ignore: prefer_expression_function_bodies + bool _isSelectableCustom (DateTime day) { +// return day.weekday < 6; + return day.day != DateTime.now().add(Duration(days: 7)).day ; + } + + EventDecoration? _eventDecorationBuilder(DateTime date) { + List eventsDates = widget.events + .map((Event e) => e.date) + .toList(); + + bool isEventDate = eventsDates.any((DateTime d) => date.year == d.year + && date.month == d.month + && d.day == date.day); + + if (!isEventDate) return null; + + BoxDecoration roundedBorder = BoxDecoration( + color: Colors.blue, + border: Border.all( + color: Colors.blue, + ), + borderRadius: BorderRadius.all(Radius.circular(3.0)) + ); + + TextStyle? whiteText = Theme.of(context) + .textTheme + .bodyText2 + ?.copyWith(color: Colors.white); + + return isEventDate + ? EventDecoration(boxDecoration: roundedBorder, textStyle: whiteText) + : null; + } +} diff --git a/flutter_date_pickers-master/example/lib/event.dart b/flutter_date_pickers-master/example/lib/event.dart new file mode 100644 index 00000000..a2ec6745 --- /dev/null +++ b/flutter_date_pickers-master/example/lib/event.dart @@ -0,0 +1,14 @@ +/// Event for the date pickers. +class Event { + + /// Event's date. + final DateTime date; + + /// Event's title. + final String dis; + + /// + Event(this.date, this.dis) + : assert(date != null), + assert(dis != null); +} diff --git a/flutter_date_pickers-master/example/lib/main.dart b/flutter_date_pickers-master/example/lib/main.dart new file mode 100644 index 00000000..83112f3a --- /dev/null +++ b/flutter_date_pickers-master/example/lib/main.dart @@ -0,0 +1,121 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; + +import 'date_pickers_widgets/day_picker_page.dart'; +import 'date_pickers_widgets/days_picker_page.dart'; +import 'date_pickers_widgets/month_picker_page.dart'; +import 'date_pickers_widgets/range_picker_page.dart'; +import 'date_pickers_widgets/week_picker_page.dart'; + +import 'event.dart'; + +void main() { + runApp(MyApp()); +} + +/// +class MyApp extends StatelessWidget { + + @override + // ignore: prefer_expression_function_bodies + Widget build(BuildContext context) { + return MaterialApp( + localizationsDelegates: GlobalMaterialLocalizations.delegates, + supportedLocales: [ + const Locale('en', 'US'), // American English + const Locale('ru', 'RU'), // Russian + const Locale("pt") // Portuguese + ], + debugShowCheckedModeBanner: false, + title: 'Date pickers demo', + theme: ThemeData( + primarySwatch: Colors.blueGrey, + ), + home: MyHomePage( + title: 'flutter_date_pickers Demo', + ), + ); + } +} + +/// Start page. +class MyHomePage extends StatefulWidget { + /// Page title. + final String title; + + /// + MyHomePage({ + required this.title, + Key? key, + }) : super(key: key); + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State with TickerProviderStateMixin { + DateTime startOfPeriod = DateTime.now().subtract(Duration(days: 10)); + DateTime endOfPeriod = DateTime.now().add(Duration(days: 10)); + int _selectedTab = 0; + + final List datePickers = [ + DayPickerPage(events: events,), + DaysPickerPage(), + WeekPickerPage(events: events,), + RangePickerPage(events: events,), + MonthPickerPage() + ]; + + @override + // ignore: prefer_expression_function_bodies + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text( + widget.title, + style: TextStyle(letterSpacing: 1.15), + ), + ), + body: datePickers[_selectedTab], + bottomNavigationBar: Theme( + data: Theme.of(context).copyWith( + canvasColor: Colors.blueGrey, + textTheme: Theme.of(context).textTheme.copyWith( + caption: TextStyle(color: Colors.white.withOpacity(0.5)))), + child: BottomNavigationBar( + type: BottomNavigationBarType.fixed, + items: [ + BottomNavigationBarItem( + icon: Icon(Icons.date_range), label: "Day"), + BottomNavigationBarItem( + icon: Icon(Icons.date_range), label: "Days"), + BottomNavigationBarItem( + icon: Icon(Icons.date_range), label: "Week"), + BottomNavigationBarItem( + icon: Icon(Icons.date_range), label: "Range"), + BottomNavigationBarItem( + icon: Icon(Icons.date_range), label: "Month"), + ], + fixedColor: Colors.yellow, + currentIndex: _selectedTab, + onTap: (newIndex) { + setState(() { + _selectedTab = newIndex; + }); + }, + ), + ), + ); + } +} + +/// Mock events. +final List events = [ + Event(DateTime.now(), "Today event"), + Event(DateTime.now().subtract(Duration(days: 3)), "Ev1"), + Event(DateTime.now().subtract(Duration(days: 13)), "Ev2"), + Event(DateTime.now().subtract(Duration(days: 30)), "Ev3"), + Event(DateTime.now().add(Duration(days: 3)), "Ev4"), + Event(DateTime.now().add(Duration(days: 13)), "Ev5"), + Event(DateTime.now().add(Duration(days: 30)), "Ev6"), +]; \ No newline at end of file diff --git a/flutter_date_pickers-master/example/pubspec.lock b/flutter_date_pickers-master/example/pubspec.lock new file mode 100644 index 00000000..b9e279b6 --- /dev/null +++ b/flutter_date_pickers-master/example/pubspec.lock @@ -0,0 +1,181 @@ +# 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" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.15.0" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_date_pickers: + dependency: "direct main" + description: + path: ".." + relative: true + source: path + version: "0.2.3+1" + flutter_localizations: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_material_color_picker: + dependency: "direct main" + description: + path: "." + ref: HEAD + resolved-ref: "286596b1e0167877bcdb87011bff77364338ba57" + url: "git://github.com/Bwolfs2/flutter_material_color_picker.git" + source: git + version: "1.0.5-nullsafety.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.17.0" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.10" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0" + 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" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.19" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" +sdks: + dart: ">=2.12.0 <3.0.0" diff --git a/flutter_date_pickers-master/example/pubspec.yaml b/flutter_date_pickers-master/example/pubspec.yaml new file mode 100644 index 00000000..3581d637 --- /dev/null +++ b/flutter_date_pickers-master/example/pubspec.yaml @@ -0,0 +1,80 @@ +name: flutter_date_picker +description: A new Flutter application. + +# 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 +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# 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.12.0 <3.0.0" + +dependencies: + flutter: + sdk: flutter + flutter_localizations: + sdk: flutter + flutter_material_color_picker: + # null-safety fork + git: + url: git://github.com/Bwolfs2/flutter_material_color_picker.git + flutter_date_pickers: + path: ../ + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + + +# For information on the generic Dart part of this file, see the +# following page: https://www.dartlang.org/tools/pub/pubspec + +# The following section is specific to Flutter. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + assets: + - images/bg.png + # - 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. + + # For details regarding adding assets from package dependencies, see + # https://flutter.io/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 + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.io/custom-fonts/#from-packages diff --git a/flutter_date_pickers-master/flutter_date_pickers.iml b/flutter_date_pickers-master/flutter_date_pickers.iml new file mode 100644 index 00000000..8c1effee --- /dev/null +++ b/flutter_date_pickers-master/flutter_date_pickers.iml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/flutter_date_pickers-master/lib/flutter_date_pickers.dart b/flutter_date_pickers-master/lib/flutter_date_pickers.dart new file mode 100644 index 00000000..4358ab79 --- /dev/null +++ b/flutter_date_pickers-master/lib/flutter_date_pickers.dart @@ -0,0 +1,12 @@ +export 'package:flutter_date_pickers/src/date_period.dart'; +export 'package:flutter_date_pickers/src/date_picker_keys.dart'; +export 'package:flutter_date_pickers/src/layout_settings.dart'; +export 'package:flutter_date_pickers/src/date_picker_styles.dart'; +export 'package:flutter_date_pickers/src/event_decoration.dart'; + +export 'package:flutter_date_pickers/src/day_picker.dart'; +export 'package:flutter_date_pickers/src/week_picker.dart'; +export 'package:flutter_date_pickers/src/month_picker.dart'; +export 'package:flutter_date_pickers/src/range_picker.dart'; + +export 'package:flutter_date_pickers/src/unselectable_period_error.dart'; diff --git a/flutter_date_pickers-master/lib/src/basic_day_based_widget.dart b/flutter_date_pickers-master/lib/src/basic_day_based_widget.dart new file mode 100644 index 00000000..1722777f --- /dev/null +++ b/flutter_date_pickers-master/lib/src/basic_day_based_widget.dart @@ -0,0 +1,347 @@ +import 'package:flutter/material.dart'; + +import 'date_picker_mixin.dart'; +import 'date_picker_styles.dart'; +import 'day_type.dart'; +import 'event_decoration.dart'; +import 'i_selectable_picker.dart'; +import 'layout_settings.dart'; +import 'utils.dart'; + +/// Widget for date pickers based on days and cover entire month. +/// Each cell of this picker is day. +class DayBasedPicker extends StatelessWidget with CommonDatePickerFunctions { + /// Selection logic. + final ISelectablePicker selectablePicker; + + /// The current date at the time the picker is displayed. + final DateTime currentDate; + + /// The earliest date the user is permitted to pick. + /// (only year, month and day matter, time doesn't matter) + final DateTime firstDate; + + /// The latest date the user is permitted to pick. + /// (only year, month and day matter, time doesn't matter) + final DateTime lastDate; + + /// The month whose days are displayed by this picker. + final DateTime displayedMonth; + + /// Layout settings what can be customized by user + final DatePickerLayoutSettings datePickerLayoutSettings; + + /// Key fo selected month (useful for integration tests) + final Key? selectedPeriodKey; + + /// Styles what can be customized by user + final DatePickerRangeStyles datePickerStyles; + + /// Builder to get event decoration for each date. + /// + /// All event styles are overridden by selected styles + /// except days with dayType is [DayType.notSelected]. + final EventDecorationBuilder? eventDecorationBuilder; + + /// Localizations used to get strings for prev/next button tooltips, + /// weekday headers and display values for days numbers. + final MaterialLocalizations localizations; + + /// Creates main date picker view where every cell is day. + DayBasedPicker( + {Key? key, + required this.currentDate, + required this.firstDate, + required this.lastDate, + required this.displayedMonth, + required this.datePickerLayoutSettings, + required this.datePickerStyles, + required this.selectablePicker, + required this.localizations, + this.selectedPeriodKey, + this.eventDecorationBuilder}) + : assert(!firstDate.isAfter(lastDate)), + super(key: key); + + @override + Widget build(BuildContext context) { + final List labels = []; + + List headers = _buildHeaders(localizations); + List daysBeforeMonthStart = _buildCellsBeforeStart(localizations); + List monthDays = _buildMonthCells(localizations); + List daysAfterMonthEnd = _buildCellsAfterEnd(localizations); + + labels.addAll(headers); + labels.addAll(daysBeforeMonthStart); + labels.addAll(monthDays); + labels.addAll(daysAfterMonthEnd); + + return Padding( + padding: datePickerLayoutSettings.contentPadding, + child: Column( + children: [ + Flexible( + child: GridView.custom( + physics: datePickerLayoutSettings.scrollPhysics, + gridDelegate: datePickerLayoutSettings.dayPickerGridDelegate, + childrenDelegate: + SliverChildListDelegate(labels, addRepaintBoundaries: false), + ), + ), + ], + ), + ); + } + + List _buildHeaders(MaterialLocalizations localizations) { + final int firstDayOfWeekIndex = datePickerStyles.firstDayOfeWeekIndex ?? + localizations.firstDayOfWeekIndex; + + DayHeaderStyleBuilder dayHeaderStyleBuilder = + datePickerStyles.dayHeaderStyleBuilder ?? + // ignore: avoid_types_on_closure_parameters + (int i) => datePickerStyles.dayHeaderStyle; + + List headers = getDayHeaders(dayHeaderStyleBuilder, + localizations.narrowWeekdays, firstDayOfWeekIndex); + + return headers; + } + + List _buildCellsBeforeStart(MaterialLocalizations localizations) { + List result = []; + + final int year = displayedMonth.year; + final int month = displayedMonth.month; + final int firstDayOfWeekIndex = datePickerStyles.firstDayOfeWeekIndex ?? + localizations.firstDayOfWeekIndex; + final int firstDayOffset = + computeFirstDayOffset(year, month, firstDayOfWeekIndex); + + final bool showDates = datePickerLayoutSettings.showPrevMonthEnd; + if (showDates) { + int prevMonth = month - 1; + if (prevMonth < 1) prevMonth = 12; + int prevYear = prevMonth == 12 ? year - 1 : year; + + int daysInPrevMonth = DatePickerUtils.getDaysInMonth(prevYear, prevMonth); + List days = List.generate(firstDayOffset, (index) => index) + .reversed + .map((i) => daysInPrevMonth - i) + .map((day) => _buildCell(prevYear, prevMonth, day)) + .toList(); + + result = days; + } else { + result = List.generate(firstDayOffset, (_) => const SizedBox.shrink()); + } + + return result; + } + + List _buildMonthCells(MaterialLocalizations localizations) { + List result = []; + + final int year = displayedMonth.year; + final int month = displayedMonth.month; + final int daysInMonth = DatePickerUtils.getDaysInMonth(year, month); + + for (int i = 1; i <= daysInMonth; i += 1) { + Widget dayWidget = _buildCell(year, month, i); + result.add(dayWidget); + } + + return result; + } + + List _buildCellsAfterEnd(MaterialLocalizations localizations) { + List result = []; + final bool showDates = datePickerLayoutSettings.showNextMonthStart; + if (!showDates) return result; + + final int year = displayedMonth.year; + final int month = displayedMonth.month; + final int firstDayOfWeekIndex = datePickerStyles.firstDayOfeWeekIndex ?? + localizations.firstDayOfWeekIndex; + final int firstDayOffset = + computeFirstDayOffset(year, month, firstDayOfWeekIndex); + final int daysInMonth = DatePickerUtils.getDaysInMonth(year, month); + final int totalFilledDays = firstDayOffset + daysInMonth; + + int reminder = totalFilledDays % 7; + if (reminder == 0) return result; + final int emptyCellsNum = 7 - reminder; + + int nextMonth = month + 1; + result = List.generate(emptyCellsNum, (i) => i + 1) + .map((day) => _buildCell(year, nextMonth, day)) + .toList(); + + return result; + } + + Widget _buildCell(int year, int month, int day) { + DateTime dayToBuild = DateTime(year, month, day); + dayToBuild = _checkDateTime(dayToBuild); + + DayType dayType = selectablePicker.getDayType(dayToBuild); + + Widget dayWidget = _DayCell( + day: dayToBuild, + currentDate: currentDate, + selectablePicker: selectablePicker, + datePickerStyles: datePickerStyles, + eventDecorationBuilder: eventDecorationBuilder, + localizations: localizations, + ); + + if (dayType != DayType.disabled) { + dayWidget = GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => selectablePicker.onDayTapped(dayToBuild), + child: dayWidget, + ); + } + + return dayWidget; + } + + /// Checks if [DateTime] is same day as [lastDate] or [firstDate] + /// and returns dt corrected (with time of [lastDate] or [firstDate]). + DateTime _checkDateTime(DateTime dt) { + DateTime result = dt; + + // If dayToBuild is the first day we need to save original time for it. + if (DatePickerUtils.sameDate(dt, firstDate)) result = firstDate; + + // If dayToBuild is the last day we need to save original time for it. + if (DatePickerUtils.sameDate(dt, lastDate)) result = lastDate; + + return result; + } +} + +class _DayCell extends StatelessWidget { + /// Day for this cell. + final DateTime day; + + /// Selection logic. + final ISelectablePicker selectablePicker; + + /// Styles what can be customized by user + final DatePickerRangeStyles datePickerStyles; + + /// The current date at the time the picker is displayed. + final DateTime currentDate; + + /// Builder to get event decoration for each date. + /// + /// All event styles are overridden by selected styles + /// except days with dayType is [DayType.notSelected]. + final EventDecorationBuilder? eventDecorationBuilder; + + final MaterialLocalizations localizations; + + const _DayCell( + {Key? key, + required this.day, + required this.selectablePicker, + required this.datePickerStyles, + required this.currentDate, + required this.localizations, + this.eventDecorationBuilder}) + : super(key: key); + + @override + Widget build(BuildContext context) { + DayType dayType = selectablePicker.getDayType(day); + + BoxDecoration? decoration; + TextStyle? itemStyle; + + if (dayType != DayType.disabled && dayType != DayType.notSelected) { + itemStyle = _getSelectedTextStyle(dayType); + decoration = _getSelectedDecoration(dayType); + } else if (dayType == DayType.disabled) { + itemStyle = datePickerStyles.disabledDateStyle; + } else if (DatePickerUtils.sameDate(currentDate, day)) { + itemStyle = datePickerStyles.currentDateStyle; + } else { + itemStyle = datePickerStyles.defaultDateTextStyle; + } + + // Merges decoration and textStyle with [EventDecoration]. + // + // Merges only in cases if [dayType] is DayType.notSelected. + // If day is current day it is also gets event decoration + // instead of decoration for current date. + if (dayType == DayType.notSelected && eventDecorationBuilder != null) { + EventDecoration? eDecoration = eventDecorationBuilder != null + ? eventDecorationBuilder!.call(day) + : null; + + decoration = eDecoration?.boxDecoration ?? decoration; + itemStyle = eDecoration?.textStyle ?? itemStyle; + } + + String semanticLabel = '${localizations.formatDecimal(day.day)}, ' + '${localizations.formatFullDate(day)}'; + + bool daySelected = + dayType != DayType.disabled && dayType != DayType.notSelected; + + Widget dayWidget = Container( + decoration: decoration, + child: Center( + child: Semantics( + // We want the day of month to be spoken first irrespective of the + // locale-specific preferences or TextDirection. This is because + // an accessibility user is more likely to be interested in the + // day of month before the rest of the date, as they are looking + // for the day of month. To do that we prepend day of month to the + // formatted full date. + label: semanticLabel, + selected: daySelected, + child: ExcludeSemantics( + child: Text(localizations.formatDecimal(day.day), style: itemStyle), + ), + ), + ), + ); + + return dayWidget; + } + + BoxDecoration? _getSelectedDecoration(DayType dayType) { + BoxDecoration? result; + + if (dayType == DayType.single) { + result = datePickerStyles.selectedSingleDateDecoration; + } else if (dayType == DayType.start) { + result = datePickerStyles.selectedPeriodStartDecoration; + } else if (dayType == DayType.end) { + result = datePickerStyles.selectedPeriodLastDecoration; + } else { + result = datePickerStyles.selectedPeriodMiddleDecoration; + } + + return result; + } + + TextStyle? _getSelectedTextStyle(DayType dayType) { + TextStyle? result; + + if (dayType == DayType.single) { + result = datePickerStyles.selectedDateStyle; + } else if (dayType == DayType.start) { + result = datePickerStyles.selectedPeriodStartTextStyle; + } else if (dayType == DayType.end) { + result = datePickerStyles.selectedPeriodEndTextStyle; + } else { + result = datePickerStyles.selectedPeriodMiddleTextStyle; + } + + return result; + } +} diff --git a/flutter_date_pickers-master/lib/src/date_period.dart b/flutter_date_pickers-master/lib/src/date_period.dart new file mode 100644 index 00000000..39df6b2a --- /dev/null +++ b/flutter_date_pickers-master/lib/src/date_period.dart @@ -0,0 +1,13 @@ +/// Date period. +class DatePeriod { + /// Start of this period. + final DateTime start; + + /// End of this period. + final DateTime end; + + /// + const DatePeriod(this.start, this.end) + : assert(start != null), + assert(end != null); +} diff --git a/flutter_date_pickers-master/lib/src/date_picker_keys.dart b/flutter_date_pickers-master/lib/src/date_picker_keys.dart new file mode 100644 index 00000000..6638b4cb --- /dev/null +++ b/flutter_date_pickers-master/lib/src/date_picker_keys.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; + +/// Keys for some date picker's widgets. +/// +/// Useful for integration tests to find widgets. +class DatePickerKeys { + /// Key for the previous page icon widget. + final Key previousPageIconKey; + + /// Key for the next page icon widget. + final Key nextPageIconKey; + + /// Key for showing month. + final Key selectedPeriodKeys; + + /// + DatePickerKeys( + this.previousPageIconKey, this.nextPageIconKey, this.selectedPeriodKeys); +} diff --git a/flutter_date_pickers-master/lib/src/date_picker_mixin.dart b/flutter_date_pickers-master/lib/src/date_picker_mixin.dart new file mode 100644 index 00000000..7c5668af --- /dev/null +++ b/flutter_date_pickers-master/lib/src/date_picker_mixin.dart @@ -0,0 +1,99 @@ +import 'package:flutter/material.dart'; + +import 'date_picker_styles.dart'; + +/// +mixin CommonDatePickerFunctions { + + /// Builds widgets showing abbreviated days of week. The first widget in the + /// returned list corresponds to the first day of week for the current locale. + /// + /// Examples: + /// + /// ``` + /// ┌ Sunday is the first day of week in the US (en_US) + /// | + /// S M T W T F S <-- the returned list contains these widgets + /// _ _ _ _ _ 1 2 + /// 3 4 5 6 7 8 9 + /// + /// ┌ But it's Monday in the UK (en_GB) + /// | + /// M T W T F S S <-- the returned list contains these widgets + /// _ _ _ _ 1 2 3 + /// 4 5 6 7 8 9 10 + /// ``` + List getDayHeaders( + DayHeaderStyleBuilder headerStyleBuilder, + List narrowWeekdays, + int firstDayOfWeekIndex) { + + final List result = []; + + for (int i = firstDayOfWeekIndex; true; i = (i + 1) % 7) { + DayHeaderStyle? headerStyle = headerStyleBuilder(i); + final String weekday = narrowWeekdays[i]; + + Widget header = ExcludeSemantics( + child: Container( + decoration: headerStyle?.decoration, + child: Center( + child: Text( + weekday, + style: headerStyle?.textStyle + ) + ), + ), + ); + + result.add(header); + if (i == (firstDayOfWeekIndex - 1) % 7) { + break; + } + } + return result; + } + + /// Computes the offset from the first day of week that the first day of the + /// [month] falls on. + /// + /// For example, September 1, 2017 falls on a Friday, which in the calendar + /// localized for United States English appears as: + /// + /// ``` + /// S M T W T F S + /// _ _ _ _ _ 1 2 + /// ``` + /// + /// The offset for the first day of the months is the number of leading blanks + /// in the calendar, i.e. 5. + /// + /// The same date localized for the Russian calendar has a different offset, + /// because the first day of week is Monday rather than Sunday: + /// + /// ``` + /// M T W T F S S + /// _ _ _ _ 1 2 3 + /// ``` + /// + /// So the offset is 4, rather than 5. + /// + /// This code consolidates the following: + /// + /// - [DateTime.weekday] provides a 1-based index into days of week, with 1 + /// falling on Monday. + /// - [MaterialLocalizations.firstDayOfWeekIndex] provides a 0-based index + /// into the [MaterialLocalizations.narrowWeekdays] list. + /// - [MaterialLocalizations.narrowWeekdays] list provides localized names of + /// days of week, always starting with Sunday and ending with Saturday. + int computeFirstDayOffset( + int year, int month, int firstDayOfWeekFromSunday) { + // 0-based day of week, with 0 representing Monday. + final int weekdayFromMonday = DateTime(year, month).weekday - 1; + // firstDayOfWeekFromSunday recomputed to be Monday-based + final int firstDayOfWeekFromMonday = (firstDayOfWeekFromSunday - 1) % 7; + // Number of days between the first day of week appearing on the calendar, + // and the day corresponding to the 1-st of the month. + return (weekdayFromMonday - firstDayOfWeekFromMonday) % 7; + } +} \ No newline at end of file diff --git a/flutter_date_pickers-master/lib/src/date_picker_styles.dart b/flutter_date_pickers-master/lib/src/date_picker_styles.dart new file mode 100644 index 00000000..4f50599c --- /dev/null +++ b/flutter_date_pickers-master/lib/src/date_picker_styles.dart @@ -0,0 +1,386 @@ +import 'package:flutter/material.dart'; +import 'range_picker.dart'; +import 'week_picker.dart'; + +/// 0 points to Sunday, and 6 points to Saturday. +typedef DayHeaderStyleBuilder = DayHeaderStyle? Function(int dayOfTheWeek); + +/// Common styles for date pickers. +/// +/// To define more styles for date pickers which allow select some range +/// (e.g. [RangePicker], [WeekPicker]) use [DatePickerRangeStyles]. +@immutable +class DatePickerStyles { + /// Styles for title of displayed period + /// (e.g. month for day picker and year for month picker). + final TextStyle? displayedPeriodTitle; + + /// Style for the number of current date. + final TextStyle? currentDateStyle; + + /// Style for the numbers of disabled dates. + final TextStyle? disabledDateStyle; + + /// Style for the number of selected date. + final TextStyle? selectedDateStyle; + + /// Used for date which is neither current nor disabled nor selected. + final TextStyle? defaultDateTextStyle; + + /// Day cell decoration for selected date in case only one date is selected. + final BoxDecoration? selectedSingleDateDecoration; + + /// Style for the day header. + /// + /// If you need to customize day header's style depends on day of the week + /// use [dayHeaderStyleBuilder] instead. + final DayHeaderStyle? dayHeaderStyle; + + /// Builder to customize styles for day headers depends on day of the week. + /// Where 0 points to Sunday and 6 points to Saturday. + /// + /// Builder must return not null value for every weekday from 0 to 6. + /// + /// If styles should be the same for any day of the week + /// use [dayHeaderStyle] instead. + final DayHeaderStyleBuilder? dayHeaderStyleBuilder; + + /// Widget which will be shown left side of the shown page title. + /// User goes to previous data period by click on it. + final Widget prevIcon; + + /// Widget which will be shown right side of the shown page title. + /// User goes to next data period by click on it. + final Widget nextIcon; + + /// Index of the first day of week, where 0 points to Sunday, and 6 points to + /// Saturday. Must not be less 0 or more then 6. + /// + /// Can be null. In this case value from current locale will be used. + final int? firstDayOfeWeekIndex; + + /// Styles for date picker. + DatePickerStyles({ + this.displayedPeriodTitle, + this.currentDateStyle, + this.disabledDateStyle, + this.selectedDateStyle, + this.selectedSingleDateDecoration, + this.defaultDateTextStyle, + this.dayHeaderStyleBuilder, + this.dayHeaderStyle, + this.firstDayOfeWeekIndex, + this.prevIcon = const Icon(Icons.chevron_left), + this.nextIcon = const Icon(Icons.chevron_right) + }) : assert(!(dayHeaderStyle != null && dayHeaderStyleBuilder != null), + "Should be only one from: dayHeaderStyleBuilder, dayHeaderStyle."), + assert(dayHeaderStyleBuilder == null + || _validateDayHeaderStyleBuilder(dayHeaderStyleBuilder), + "dayHeaderStyleBuilder must return not null value from every weekday " + "(from 0 to 6)."), + assert(_validateFirstDayOfWeek(firstDayOfeWeekIndex), + "firstDayOfeWeekIndex must be null or in correct range (from 0 to 6)."); + + /// Return new [DatePickerStyles] object where fields + /// with null values set with defaults from theme. + DatePickerStyles fulfillWithTheme(ThemeData theme) { + Color accentColor = theme.accentColor; + + TextStyle? _displayedPeriodTitle = + displayedPeriodTitle ?? theme.textTheme.subtitle1; + TextStyle? _currentDateStyle = currentDateStyle ?? + theme.textTheme.bodyText1?.copyWith(color: theme.accentColor); + TextStyle? _disabledDateStyle = disabledDateStyle ?? + theme.textTheme.bodyText2?.copyWith(color: theme.disabledColor); + TextStyle? _selectedDateStyle = + selectedDateStyle ?? theme.accentTextTheme.bodyText1; + TextStyle? _defaultDateTextStyle = + defaultDateTextStyle ?? theme.textTheme.bodyText2; + BoxDecoration _selectedSingleDateDecoration = + selectedSingleDateDecoration ?? + BoxDecoration( + color: accentColor, + borderRadius: const BorderRadius.all(Radius.circular(10.0))); + + DayHeaderStyle? _dayHeaderStyle = dayHeaderStyle; + if (dayHeaderStyleBuilder == null && _dayHeaderStyle == null) { + _dayHeaderStyle = DayHeaderStyle(textStyle: theme.textTheme.caption); + } + + return DatePickerStyles( + disabledDateStyle: _disabledDateStyle, + currentDateStyle: _currentDateStyle, + displayedPeriodTitle: _displayedPeriodTitle, + selectedDateStyle: _selectedDateStyle, + selectedSingleDateDecoration: _selectedSingleDateDecoration, + defaultDateTextStyle: _defaultDateTextStyle, + dayHeaderStyle: _dayHeaderStyle, + dayHeaderStyleBuilder: dayHeaderStyleBuilder, + nextIcon: nextIcon, + prevIcon: prevIcon + ); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + if (other.runtimeType != runtimeType) return false; + + return other is DatePickerStyles + && other.displayedPeriodTitle == displayedPeriodTitle + && other.currentDateStyle == currentDateStyle + && other.disabledDateStyle == disabledDateStyle + && other.selectedDateStyle == selectedDateStyle + && other.defaultDateTextStyle == defaultDateTextStyle + && other.selectedSingleDateDecoration == selectedSingleDateDecoration + && other.dayHeaderStyle == dayHeaderStyle + && other.dayHeaderStyleBuilder == dayHeaderStyleBuilder + && other.prevIcon == prevIcon + && other.nextIcon == nextIcon + && other.firstDayOfeWeekIndex == firstDayOfeWeekIndex; + } + + @override + int get hashCode => + hashValues( + displayedPeriodTitle, + currentDateStyle, + disabledDateStyle, + selectedDateStyle, + defaultDateTextStyle, + selectedSingleDateDecoration, + dayHeaderStyle, + dayHeaderStyleBuilder, + prevIcon, + nextIcon, + firstDayOfeWeekIndex + ); + + static bool _validateDayHeaderStyleBuilder(DayHeaderStyleBuilder builder) { + List weekdays = const [0, 1, 2, 3, 4, 5, 6]; + + // ignore: avoid_types_on_closure_parameters + bool valid = weekdays.every((int weekday) => builder(weekday) != null); + + return valid; + } + + static bool _validateFirstDayOfWeek(int? index) { + if (index == null) return true; + + bool valid = index >= 0 && index <= 6; + + return valid; + } +} + +/// Styles for date pickers which allow select some range +/// (e.g. RangePicker, WeekPicker). +@immutable +class DatePickerRangeStyles extends DatePickerStyles { + /// Decoration for the first date of the selected range. + final BoxDecoration? selectedPeriodStartDecoration; + + /// Text style for the first date of the selected range. + /// + /// If null - default [DatePickerStyles.selectedDateStyle] will be used. + final TextStyle? selectedPeriodStartTextStyle; + + /// Decoration for the last date of the selected range. + final BoxDecoration? selectedPeriodLastDecoration; + + /// Text style for the last date of the selected range. + /// + /// If null - default [DatePickerStyles.selectedDateStyle] will be used. + final TextStyle? selectedPeriodEndTextStyle; + + /// Decoration for the date of the selected range + /// which is not first date and not end date of this range. + /// + /// If there is only one date selected + /// [DatePickerStyles.selectedSingleDateDecoration] will be used. + final BoxDecoration? selectedPeriodMiddleDecoration; + + /// Text style for the middle date of the selected range. + /// + /// If null - default [DatePickerStyles.selectedDateStyle] will be used. + final TextStyle? selectedPeriodMiddleTextStyle; + + /// Return new [DatePickerRangeStyles] object + /// where fields with null values set with defaults from given theme. + @override + DatePickerRangeStyles fulfillWithTheme(ThemeData theme) { + Color accentColor = theme.accentColor; + + DatePickerStyles commonStyles = super.fulfillWithTheme(theme); + + final BoxDecoration _selectedPeriodStartDecoration = + selectedPeriodStartDecoration ?? + BoxDecoration( + color: accentColor, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(10.0), + bottomLeft: Radius.circular(10.0)), + ); + + final BoxDecoration _selectedPeriodLastDecoration = + selectedPeriodLastDecoration ?? + BoxDecoration( + color: accentColor, + borderRadius: const BorderRadius.only( + topRight: Radius.circular(10.0), + bottomRight: Radius.circular(10.0)), + ); + + final BoxDecoration _selectedPeriodMiddleDecoration = + selectedPeriodMiddleDecoration ?? + BoxDecoration( + color: accentColor, + shape: BoxShape.rectangle, + ); + + final TextStyle? _selectedPeriodStartTextStyle = + selectedPeriodStartTextStyle ?? commonStyles.selectedDateStyle; + + final TextStyle? _selectedPeriodMiddleTextStyle = + selectedPeriodMiddleTextStyle ?? commonStyles.selectedDateStyle; + + final TextStyle? _selectedPeriodEndTextStyle = + selectedPeriodEndTextStyle ?? commonStyles.selectedDateStyle; + + return DatePickerRangeStyles( + disabledDateStyle: commonStyles.disabledDateStyle, + currentDateStyle: commonStyles.currentDateStyle, + displayedPeriodTitle: commonStyles.displayedPeriodTitle, + selectedDateStyle: commonStyles.selectedDateStyle, + selectedSingleDateDecoration: commonStyles.selectedSingleDateDecoration, + defaultDateTextStyle: commonStyles.defaultDateTextStyle, + dayHeaderStyle: commonStyles.dayHeaderStyle, + dayHeaderStyleBuilder: commonStyles.dayHeaderStyleBuilder, + firstDayOfWeekIndex: firstDayOfeWeekIndex, + selectedPeriodStartDecoration: _selectedPeriodStartDecoration, + selectedPeriodMiddleDecoration: _selectedPeriodMiddleDecoration, + selectedPeriodLastDecoration: _selectedPeriodLastDecoration, + selectedPeriodStartTextStyle: _selectedPeriodStartTextStyle, + selectedPeriodMiddleTextStyle: _selectedPeriodMiddleTextStyle, + selectedPeriodEndTextStyle: _selectedPeriodEndTextStyle, + ); + } + + /// Styles for the pickers that allows to select range ([RangePicker], + /// [WeekPicker]). + DatePickerRangeStyles({ + displayedPeriodTitle, + currentDateStyle, + disabledDateStyle, + selectedDateStyle, + selectedSingleDateDecoration, + defaultDateTextStyle, + dayHeaderStyle, + dayHeaderStyleBuilder, + Widget nextIcon = const Icon(Icons.chevron_right), + Widget prevIcon = const Icon(Icons.chevron_left), + firstDayOfWeekIndex, + this.selectedPeriodLastDecoration, + this.selectedPeriodMiddleDecoration, + this.selectedPeriodStartDecoration, + this.selectedPeriodStartTextStyle, + this.selectedPeriodMiddleTextStyle, + this.selectedPeriodEndTextStyle, + }) : super( + displayedPeriodTitle: displayedPeriodTitle, + currentDateStyle: currentDateStyle, + disabledDateStyle: disabledDateStyle, + selectedDateStyle: selectedDateStyle, + selectedSingleDateDecoration: selectedSingleDateDecoration, + defaultDateTextStyle: defaultDateTextStyle, + dayHeaderStyle: dayHeaderStyle, + dayHeaderStyleBuilder: dayHeaderStyleBuilder, + nextIcon: nextIcon, + prevIcon: prevIcon, + firstDayOfeWeekIndex: firstDayOfWeekIndex + ); + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + if (other.runtimeType != runtimeType) return false; + + return other is DatePickerRangeStyles + && other.selectedPeriodStartDecoration == selectedPeriodStartDecoration + && other.selectedPeriodStartTextStyle == selectedPeriodStartTextStyle + && other.selectedPeriodLastDecoration == selectedPeriodLastDecoration + && other.selectedPeriodEndTextStyle == selectedPeriodEndTextStyle + && other.selectedPeriodMiddleDecoration ==selectedPeriodMiddleDecoration + && other.selectedPeriodMiddleTextStyle == selectedPeriodMiddleTextStyle + && other.displayedPeriodTitle == displayedPeriodTitle + && other.currentDateStyle == currentDateStyle + && other.disabledDateStyle == disabledDateStyle + && other.selectedDateStyle == selectedDateStyle + && other.defaultDateTextStyle == defaultDateTextStyle + && other.selectedSingleDateDecoration == selectedSingleDateDecoration + && other.dayHeaderStyle == dayHeaderStyle + && other.dayHeaderStyleBuilder == dayHeaderStyleBuilder + && other.prevIcon == prevIcon + && other.nextIcon == nextIcon + && other.firstDayOfeWeekIndex == firstDayOfeWeekIndex; + } + + @override + int get hashCode => + hashValues( + selectedPeriodStartDecoration, + selectedPeriodStartTextStyle, + selectedPeriodLastDecoration, + selectedPeriodEndTextStyle, + selectedPeriodMiddleDecoration, + selectedPeriodMiddleTextStyle, + displayedPeriodTitle, + currentDateStyle, + disabledDateStyle, + selectedDateStyle, + defaultDateTextStyle, + selectedSingleDateDecoration, + dayHeaderStyle, + dayHeaderStyleBuilder, + prevIcon, + nextIcon, + firstDayOfeWeekIndex + ); +} + + +/// User styles for the day header in date picker. +@immutable +class DayHeaderStyle { + /// If null - textTheme.caption from the Theme will be used. + final TextStyle? textStyle; + + /// If null - no decoration will be applied for the day header; + final BoxDecoration? decoration; + + /// Creates styles for the day headers in date pickers. + /// + /// See also: + /// * [DatePickerStyles.dayHeaderStyleBuilder] + const DayHeaderStyle({ + this.textStyle, + this.decoration + }); + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + if (other.runtimeType != runtimeType) return false; + + return other is DayHeaderStyle + && other.textStyle == textStyle + && other.decoration == decoration; + } + + @override + int get hashCode => hashValues( + textStyle, + decoration + ); +} \ No newline at end of file diff --git a/flutter_date_pickers-master/lib/src/day_based_changable_picker.dart b/flutter_date_pickers-master/lib/src/day_based_changable_picker.dart new file mode 100644 index 00000000..6aa6726e --- /dev/null +++ b/flutter_date_pickers-master/lib/src/day_based_changable_picker.dart @@ -0,0 +1,363 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; + +import 'package:intl/intl.dart' as intl; + +import 'basic_day_based_widget.dart'; +import 'date_picker_keys.dart'; +import 'date_picker_styles.dart'; +import 'day_based_changeable_picker_presenter.dart'; +import 'day_picker_selection.dart'; +import 'day_type.dart'; +import 'event_decoration.dart'; +import 'i_selectable_picker.dart'; +import 'layout_settings.dart'; +import 'month_navigation_row.dart'; +import 'semantic_sorting.dart'; +import 'typedefs.dart'; +import 'utils.dart'; + + +const Locale _defaultLocale = Locale('en', 'US'); + +/// Date picker based on [DayBasedPicker] picker (for days, weeks, ranges). +/// Allows select previous/next month. +class DayBasedChangeablePicker extends StatefulWidget { + /// The currently selected date. + /// + /// This date is highlighted in the picker. + final DayPickerSelection selection; + + /// Called when the user picks a new T. + final ValueChanged onChanged; + + /// Called when the error was thrown after user selection. + final OnSelectionError? onSelectionError; + + /// The earliest date the user is permitted to pick. + final DateTime firstDate; + + /// The latest date the user is permitted to pick. + final DateTime lastDate; + + /// Date for defining what month should be shown initially. + /// + /// Default value is [selection.earliest]. + final DateTime initiallyShowDate; + + /// Layout settings what can be customized by user + final DatePickerLayoutSettings datePickerLayoutSettings; + + /// Styles what can be customized by user + final DatePickerRangeStyles datePickerStyles; + + /// Some keys useful for integration tests + final DatePickerKeys? datePickerKeys; + + /// Logic for date selections. + final ISelectablePicker selectablePicker; + + /// Builder to get event decoration for each date. + /// + /// All event styles are overridden by selected styles + /// except days with dayType is [DayType.notSelected]. + final EventDecorationBuilder? eventDecorationBuilder; + + /// Called when the user changes the month + final ValueChanged? onMonthChanged; + + /// Create picker with option to change month. + DayBasedChangeablePicker( + {Key? key, + required this.selection, + required this.onChanged, + required this.firstDate, + required this.lastDate, + required this.datePickerLayoutSettings, + required this.datePickerStyles, + required this.selectablePicker, + DateTime? initiallyShownDate, + this.datePickerKeys, + this.onSelectionError, + this.eventDecorationBuilder, + this.onMonthChanged}) + : initiallyShowDate = initiallyShownDate ?? selection.earliest, + super(key: key); + + @override + State> createState() => + _DayBasedChangeablePickerState(); +} + +// todo: Check initial selection and call onSelectionError in case it has error +// todo: (ISelectablePicker.curSelectionIsCorrupted); +class _DayBasedChangeablePickerState + extends State> { + DateTime _todayDate = DateTime.now(); + + Locale curLocale = _defaultLocale; + + MaterialLocalizations localizations = _defaultLocalizations; + + PageController _dayPickerController = PageController(); + + // Styles from widget fulfilled with current Theme. + DatePickerRangeStyles _resultStyles = DatePickerRangeStyles(); + + DayBasedChangeablePickerPresenter _presenter = _defaultPresenter; + + Timer? _timer; + StreamSubscription? _changesSubscription; + + @override + void initState() { + super.initState(); + + // Initially display the pre-selected date. + final int monthPage = _getInitPage(); + _dayPickerController = PageController(initialPage: monthPage); + + _changesSubscription = widget.selectablePicker.onUpdate + .listen((newSelectedDate) => widget.onChanged(newSelectedDate)) + ..onError((e) => widget.onSelectionError != null + ? widget.onSelectionError!.call(e) + : print(e.toString())); + + _updateCurrentDate(); + _initPresenter(); + } + + @override + void didUpdateWidget(DayBasedChangeablePicker oldWidget) { + super.didUpdateWidget(oldWidget); + + if (widget.datePickerStyles != oldWidget.datePickerStyles) { + final ThemeData theme = Theme.of(context); + _resultStyles = widget.datePickerStyles.fulfillWithTheme(theme); + } + + if (widget.selectablePicker != oldWidget.selectablePicker) { + _changesSubscription = widget.selectablePicker.onUpdate + .listen((newSelectedDate) => widget.onChanged(newSelectedDate)) + ..onError((e) => widget.onSelectionError != null + ? widget.onSelectionError!.call(e) + : print(e.toString())); + } + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + curLocale = Localizations.localeOf(context); + + MaterialLocalizations? curLocalizations = + Localizations.of(context, MaterialLocalizations); + if (curLocalizations != null && localizations != curLocalizations) { + localizations = curLocalizations; + _initPresenter(); + } + + final ThemeData theme = Theme.of(context); + _resultStyles = widget.datePickerStyles.fulfillWithTheme(theme); + } + + @override + // ignore: prefer_expression_function_bodies + Widget build(BuildContext context) { + return SizedBox( + width: widget.datePickerLayoutSettings.monthPickerPortraitWidth, + height: widget.datePickerLayoutSettings.maxDayPickerHeight, + child: Column( + children: [ + widget.datePickerLayoutSettings.hideMonthNavigationRow + ? const SizedBox() + : SizedBox( + height: widget.datePickerLayoutSettings.dayPickerRowHeight, + child: Padding( + //match _DayPicker main layout padding + padding: widget.datePickerLayoutSettings.contentPadding, + child: _buildMonthNavigationRow()), + ), + Expanded( + child: Semantics( + sortKey: MonthPickerSortKey.calendar, + child: _buildDayPickerPageView(), + ), + ), + ], + )); + } + + @override + void dispose() { + _timer?.cancel(); + _dayPickerController.dispose(); + _changesSubscription?.cancel(); + widget.selectablePicker.dispose(); + _presenter.dispose(); + super.dispose(); + } + + void _updateCurrentDate() { + _todayDate = DateTime.now(); + final DateTime tomorrow = + DateTime(_todayDate.year, _todayDate.month, _todayDate.day + 1); + Duration timeUntilTomorrow = tomorrow.difference(_todayDate); + timeUntilTomorrow += + const Duration(seconds: 1); // so we don't miss it by rounding + _timer?.cancel(); + _timer = Timer(timeUntilTomorrow, () { + setState(_updateCurrentDate); + }); + } + + // ignore: prefer_expression_function_bodies + Widget _buildMonthNavigationRow() { + return StreamBuilder( + stream: _presenter.data, + initialData: _presenter.lastVal, + builder: (context, snapshot) { + if (!snapshot.hasData) { + return const SizedBox(); + } + + DayBasedChangeablePickerState state = snapshot.data!; + + return MonthNavigationRow( + previousPageIconKey: widget.datePickerKeys?.previousPageIconKey, + nextPageIconKey: widget.datePickerKeys?.nextPageIconKey, + previousMonthTooltip: state.prevTooltip, + nextMonthTooltip: state.nextTooltip, + onPreviousMonthTapped: + state.isFirstMonth ? null : _presenter.gotoPrevMonth, + onNextMonthTapped: + state.isLastMonth ? null : _presenter.gotoNextMonth, + title: Text( + state.curMonthDis, + key: widget.datePickerKeys?.selectedPeriodKeys, + style: _resultStyles.displayedPeriodTitle, + ), + nextIcon: widget.datePickerStyles.nextIcon, + prevIcon: widget.datePickerStyles.prevIcon, + ); + }); + } + + Widget _buildDayPickerPageView() => PageView.builder( + controller: _dayPickerController, + scrollDirection: Axis.horizontal, + itemCount: + DatePickerUtils.monthDelta(widget.firstDate, widget.lastDate) + 1, + itemBuilder: _buildCalendar, + onPageChanged: _handleMonthPageChanged, + ); + + Widget _buildCalendar(BuildContext context, int index) { + final DateTime targetDate = + DatePickerUtils.addMonthsToMonthDate(widget.firstDate, index); + + return DayBasedPicker( + key: ValueKey(targetDate), + selectablePicker: widget.selectablePicker, + currentDate: _todayDate, + firstDate: widget.firstDate, + lastDate: widget.lastDate, + displayedMonth: targetDate, + datePickerLayoutSettings: widget.datePickerLayoutSettings, + selectedPeriodKey: widget.datePickerKeys?.selectedPeriodKeys, + datePickerStyles: _resultStyles, + eventDecorationBuilder: widget.eventDecorationBuilder, + localizations: localizations, + ); + } + + // Returns appropriate date to be shown for init. + // If [widget.initiallyShowDate] is out of bounds [widget.firstDate] + // - [widget.lastDate], nearest bound will be used. + DateTime _getCheckedInitialDate() { + DateTime initiallyShowDateChecked = widget.initiallyShowDate; + if (initiallyShowDateChecked.isBefore(widget.firstDate)) { + initiallyShowDateChecked = widget.firstDate; + } + + if (initiallyShowDateChecked.isAfter(widget.lastDate)) { + initiallyShowDateChecked = widget.lastDate; + } + + return initiallyShowDateChecked; + } + + int _getInitPage() { + final initialDate = _getCheckedInitialDate(); + int initPage = DatePickerUtils.monthDelta( + widget.firstDate, initialDate + ); + + return initPage; + } + + void _initPresenter() { + _presenter.dispose(); + + _presenter = DayBasedChangeablePickerPresenter( + firstDate: widget.firstDate, + lastDate: widget.lastDate, + localizations: localizations, + showPrevMonthDates: widget.datePickerLayoutSettings.showPrevMonthEnd, + showNextMonthDates: widget.datePickerLayoutSettings.showNextMonthStart, + firstDayOfWeekIndex: widget.datePickerStyles.firstDayOfeWeekIndex); + _presenter.data.listen(_onStateChanged); + + // date used to define what month should be shown + DateTime initSelection = _getCheckedInitialDate(); + + // Give information about initial selection to presenter. + // It should be done after first frame when PageView is already created. + // Otherwise event from presenter will cause a error. + WidgetsBinding.instance!.addPostFrameCallback((_) { + _presenter.setSelectedDate(initSelection); + }); + } + + void _onStateChanged(DayBasedChangeablePickerState newState) { + DateTime newMonth = newState.currentMonth; + final int monthPage = + DatePickerUtils.monthDelta(widget.firstDate, newMonth); + _dayPickerController.animateToPage(monthPage, + duration: const Duration(milliseconds: 200), curve: Curves.easeInOut); + } + + void _handleMonthPageChanged(int monthPage) { + DateTime firstMonth = widget.firstDate; + DateTime newMonth = DateTime(firstMonth.year, firstMonth.month + monthPage); + _presenter.changeMonth(newMonth); + + widget.onMonthChanged?.call(newMonth); + } + + static MaterialLocalizations get _defaultLocalizations => + MaterialLocalizationEn( + twoDigitZeroPaddedFormat: + intl.NumberFormat('00', _defaultLocale.toString()), + fullYearFormat: intl.DateFormat.y(_defaultLocale.toString()), + longDateFormat: intl.DateFormat.yMMMMEEEEd(_defaultLocale.toString()), + shortMonthDayFormat: intl.DateFormat.MMMd(_defaultLocale.toString()), + decimalFormat: + intl.NumberFormat.decimalPattern(_defaultLocale.toString()), + shortDateFormat: intl.DateFormat.yMMMd(_defaultLocale.toString()), + mediumDateFormat: intl.DateFormat.MMMEd(_defaultLocale.toString()), + compactDateFormat: intl.DateFormat.yMd(_defaultLocale.toString()), + yearMonthFormat: intl.DateFormat.yMMMM(_defaultLocale.toString()), + ); + + static DayBasedChangeablePickerPresenter get _defaultPresenter => + DayBasedChangeablePickerPresenter( + firstDate: DateTime.now(), + lastDate: DateTime.now(), + localizations: _defaultLocalizations, + showPrevMonthDates: false, + showNextMonthDates: false, + firstDayOfWeekIndex: 1); +} diff --git a/flutter_date_pickers-master/lib/src/day_based_changeable_picker_presenter.dart b/flutter_date_pickers-master/lib/src/day_based_changeable_picker_presenter.dart new file mode 100644 index 00000000..b608aa29 --- /dev/null +++ b/flutter_date_pickers-master/lib/src/day_based_changeable_picker_presenter.dart @@ -0,0 +1,177 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; + +import 'day_based_changable_picker.dart'; +import 'utils.dart'; + +/// Presenter for [DayBasedChangeablePicker] to handle month changes. +class DayBasedChangeablePickerPresenter { + /// First date user can select. + final DateTime firstDate; + + /// Last date user can select. + final DateTime lastDate; + + /// Localization. + final MaterialLocalizations localizations; + + /// If empty day cells before 1st day of showing month should be filled with + /// date from the last week of the previous month. + final bool showPrevMonthDates; + + /// If empty day cells after last day of showing month should be filled with + /// date from the first week of the next month. + final bool showNextMonthDates; + + /// Index of the first day in week. + /// 0 is Sunday, 6 is Saturday. + final int firstDayOfWeekIndex; + + /// View model stream for the [DayBasedChangeablePicker]. + Stream get data => _controller.stream; + + /// Last view model state of the [DayBasedChangeablePicker]. + DayBasedChangeablePickerState? get lastVal => _lastVal; + + /// Creates presenter to use for [DayBasedChangeablePicker]. + DayBasedChangeablePickerPresenter({ + required this.firstDate, + required this.lastDate, + required this.localizations, + required this.showPrevMonthDates, + required this.showNextMonthDates, + int? firstDayOfWeekIndex + }): firstDayOfWeekIndex = firstDayOfWeekIndex + ?? localizations.firstDayOfWeekIndex; + + /// Update state according to the [selectedDate] if it needs. + void setSelectedDate(DateTime selectedDate) { + // bool firstAndLastNotNull = _firstShownDate != null + // && _lastShownDate != null; + // + // bool selectedOnCurPage = firstAndLastNotNull + // && !selectedDate.isBefore(_firstShownDate) + // && !selectedDate.isAfter(_lastShownDate); + // if (selectedOnCurPage) return; + + changeMonth(selectedDate); + } + + /// Update state to show previous month. + void gotoPrevMonth() { + DateTime oldCur = _lastVal!.currentMonth; + DateTime newCurDate = DateTime(oldCur.year, oldCur.month - 1); + + changeMonth(newCurDate); + } + + /// Update state to show next month. + void gotoNextMonth() { + DateTime oldCur = _lastVal!.currentMonth; + DateTime newCurDate = DateTime(oldCur.year, oldCur.month + 1); + + changeMonth(newCurDate); + } + + /// Update state to change month to the [newMonth]. + void changeMonth(DateTime newMonth) { + bool sameMonth = _lastVal != null + && DatePickerUtils.sameMonth(_lastVal!.currentMonth, newMonth); + if (sameMonth) return; + + int monthPage = DatePickerUtils.monthDelta(firstDate, newMonth); + DateTime prevMonth = DatePickerUtils + .addMonthsToMonthDate(firstDate, monthPage - 1); + + DateTime curMonth = DatePickerUtils + .addMonthsToMonthDate(firstDate, monthPage); + + DateTime nextMonth = DatePickerUtils + .addMonthsToMonthDate(firstDate, monthPage + 1); + + String prevMonthStr = localizations.formatMonthYear(prevMonth); + String curMonthStr = localizations.formatMonthYear(curMonth); + String nextMonthStr = localizations.formatMonthYear(nextMonth); + + bool isFirstMonth = DatePickerUtils.sameMonth(curMonth, firstDate); + bool isLastMonth = DatePickerUtils.sameMonth(curMonth, lastDate); + + String? prevTooltip = isFirstMonth + ? null + : "${localizations.previousMonthTooltip} $prevMonthStr"; + + String? nextTooltip = isLastMonth + ? null + : "${localizations.nextMonthTooltip} $nextMonthStr"; + + DayBasedChangeablePickerState newState = DayBasedChangeablePickerState( + currentMonth: curMonth, + curMonthDis: curMonthStr, + prevMonthDis: prevMonthStr, + nextMonthDis: nextMonthStr, + prevTooltip: prevTooltip, + nextTooltip: nextTooltip, + isFirstMonth: isFirstMonth, + isLastMonth: isLastMonth + ); + + _updateState(newState); + } + + /// Closes controller. + void dispose () { + _controller.close(); + } + + void _updateState(DayBasedChangeablePickerState newState) { + _lastVal = newState; + _controller.add(newState); + } + + final StreamController _controller = + StreamController.broadcast(); + + DayBasedChangeablePickerState? _lastVal; +} + + +/// View Model for the [DayBasedChangeablePicker]. +class DayBasedChangeablePickerState { + + /// Display name of the current month. + final String curMonthDis; + + /// Display name of the previous month. + final String prevMonthDis; + + /// Display name of the next month. + final String nextMonthDis; + + /// Tooltip for the previous month icon. + final String? prevTooltip; + + /// Tooltip for the next month icon. + final String? nextTooltip; + + /// Tooltip for the current month icon. + final DateTime currentMonth; + + /// If selected month is the month contains last date user can select. + final bool isLastMonth; + + /// If selected month is the month contains first date user can select. + final bool isFirstMonth; + + /// Creates view model for the [DayBasedChangeablePicker]. + DayBasedChangeablePickerState({ + required this.curMonthDis, + required this.prevMonthDis, + required this.nextMonthDis, + required this.currentMonth, + required this.isLastMonth, + required this.isFirstMonth, + this.prevTooltip, + this.nextTooltip, + }); +} \ No newline at end of file diff --git a/flutter_date_pickers-master/lib/src/day_picker.dart b/flutter_date_pickers-master/lib/src/day_picker.dart new file mode 100644 index 00000000..cffa331f --- /dev/null +++ b/flutter_date_pickers-master/lib/src/day_picker.dart @@ -0,0 +1,189 @@ +import 'package:flutter/material.dart'; + +import 'date_picker_keys.dart'; +import 'date_picker_styles.dart'; +import 'day_based_changable_picker.dart'; +import 'day_picker_selection.dart'; +import 'day_type.dart'; +import 'event_decoration.dart'; +import 'i_selectable_picker.dart'; +import 'layout_settings.dart'; + +/// Date picker for selection one day. +class DayPicker extends StatelessWidget { + DayPicker._({Key? key, + required this.onChanged, + required this.firstDate, + required this.lastDate, + required this.selectionLogic, + required this.selection, + this.initiallyShowDate, + this.datePickerLayoutSettings = const DatePickerLayoutSettings(), + this.datePickerStyles, + this.datePickerKeys, + this.selectableDayPredicate, + this.eventDecorationBuilder, + this.onMonthChanged}) : super(key: key); + + /// Creates a day picker where only one single day can be selected. + /// + /// See also: + /// * [DayPicker.multi] - day picker where many single days can be selected. + static DayPicker single({ + Key? key, + required DateTime selectedDate, + required ValueChanged onChanged, + required DateTime firstDate, + required DateTime lastDate, + DatePickerLayoutSettings datePickerLayoutSettings + = const DatePickerLayoutSettings(), + DateTime? initiallyShowDate, + DatePickerRangeStyles? datePickerStyles, + DatePickerKeys? datePickerKeys, + SelectableDayPredicate? selectableDayPredicate, + EventDecorationBuilder? eventDecorationBuilder, + ValueChanged? onMonthChanged + }) + { + assert(!firstDate.isAfter(lastDate)); + assert(!lastDate.isBefore(firstDate)); + assert(!selectedDate.isBefore(firstDate)); + assert(!selectedDate.isAfter(lastDate)); + assert(initiallyShowDate == null + || !initiallyShowDate.isAfter(lastDate)); + assert(initiallyShowDate == null + || !initiallyShowDate.isBefore(firstDate)); + + final selection = DayPickerSingleSelection(selectedDate); + final selectionLogic = DaySelectable( + selectedDate, firstDate, lastDate, + selectableDayPredicate: selectableDayPredicate); + + return DayPicker._( + onChanged: onChanged, + firstDate: firstDate, + lastDate: lastDate, + initiallyShowDate: initiallyShowDate, + selectionLogic: selectionLogic, + selection: selection, + eventDecorationBuilder: eventDecorationBuilder, + onMonthChanged: onMonthChanged, + selectableDayPredicate: selectableDayPredicate, + datePickerKeys: datePickerKeys, + datePickerStyles: datePickerStyles, + datePickerLayoutSettings: datePickerLayoutSettings, + ); + } + + + /// Creates a day picker where many single days can be selected. + /// + /// See also: + /// * [DayPicker.single] - day picker where only one single day + /// can be selected. + static DayPicker> multi({Key? key, + required List selectedDates, + required ValueChanged> onChanged, + required DateTime firstDate, + required DateTime lastDate, + DatePickerLayoutSettings datePickerLayoutSettings + = const DatePickerLayoutSettings(), + DateTime? initiallyShowDate, + DatePickerRangeStyles? datePickerStyles, + DatePickerKeys? datePickerKeys, + SelectableDayPredicate? selectableDayPredicate, + EventDecorationBuilder? eventDecorationBuilder, + ValueChanged? onMonthChanged}) + { + assert(!firstDate.isAfter(lastDate)); + assert(!lastDate.isBefore(firstDate)); + assert(initiallyShowDate == null + || !initiallyShowDate.isAfter(lastDate)); + assert(initiallyShowDate == null + || !initiallyShowDate.isBefore(lastDate)); + + final selection = DayPickerMultiSelection(selectedDates); + final selectionLogic = DayMultiSelectable( + selectedDates, firstDate, lastDate, + selectableDayPredicate: selectableDayPredicate); + + return DayPicker>._( + onChanged: onChanged, + firstDate: firstDate, + lastDate: lastDate, + initiallyShowDate: initiallyShowDate, + selectionLogic: selectionLogic, + selection: selection, + eventDecorationBuilder: eventDecorationBuilder, + onMonthChanged: onMonthChanged, + selectableDayPredicate: selectableDayPredicate, + datePickerKeys: datePickerKeys, + datePickerStyles: datePickerStyles, + datePickerLayoutSettings: datePickerLayoutSettings, + ); + } + + /// The currently selected date. + /// + /// This date is highlighted in the picker. + final DayPickerSelection selection; + + /// Called when the user picks a day. + final ValueChanged onChanged; + + /// The earliest date the user is permitted to pick. + final DateTime firstDate; + + /// The latest date the user is permitted to pick. + final DateTime lastDate; + + /// Date for defining what month should be shown initially. + /// + /// In case of null earliest of the [selection] will be shown. + final DateTime? initiallyShowDate; + + /// Layout settings what can be customized by user + final DatePickerLayoutSettings datePickerLayoutSettings; + + /// Styles what can be customized by user + final DatePickerRangeStyles? datePickerStyles; + + /// Some keys useful for integration tests + final DatePickerKeys? datePickerKeys; + + /// Function returns if day can be selected or not. + /// + /// If null + final SelectableDayPredicate? selectableDayPredicate; + + /// Builder to get event decoration for each date. + /// + /// All event styles are overriden by selected styles + /// except days with dayType is [DayType.notSelected]. + final EventDecorationBuilder? eventDecorationBuilder; + + // Called when the user changes the month. + /// New DateTime object represents first day of new month and 00:00 time. + final ValueChanged? onMonthChanged; + + /// Logic to handle user's selections. + final ISelectablePicker selectionLogic; + + @override + // ignore: prefer_expression_function_bodies + Widget build(BuildContext context) { + return DayBasedChangeablePicker( + selectablePicker: selectionLogic, + selection: selection, + firstDate: firstDate, + lastDate: lastDate, + initiallyShownDate: initiallyShowDate, + onChanged: onChanged, + datePickerLayoutSettings: datePickerLayoutSettings, + datePickerStyles: datePickerStyles ?? DatePickerRangeStyles(), + datePickerKeys: datePickerKeys, + eventDecorationBuilder: eventDecorationBuilder, + onMonthChanged: onMonthChanged, + ); + } +} \ No newline at end of file diff --git a/flutter_date_pickers-master/lib/src/day_picker_selection.dart b/flutter_date_pickers-master/lib/src/day_picker_selection.dart new file mode 100644 index 00000000..16cdf3fe --- /dev/null +++ b/flutter_date_pickers-master/lib/src/day_picker_selection.dart @@ -0,0 +1,120 @@ +import 'date_period.dart'; +import 'utils.dart'; + +/// Base class for day based pickers selection. +abstract class DayPickerSelection { + + /// If this is before [dateTime]. + bool isBefore(DateTime dateTime); + + /// If this is after [dateTime]. + bool isAfter(DateTime dateTime); + + /// Returns earliest [DateTime] in this selection. + DateTime get earliest; + + /// If this selection is empty. + bool get isEmpty; + + /// If this selection is not empty. + bool get isNotEmpty; + + /// Constructor to allow children to have constant constructor. + const DayPickerSelection(); +} + +/// Selection with only one selected date. +/// +/// See also: +/// * [DayPickerMultiSelection] - selection with one or many single dates. +/// * [DayPickerRangeSelection] - date period selection. +class DayPickerSingleSelection extends DayPickerSelection { + + /// Selected date. + final DateTime selectedDate; + + /// Creates selection with only one selected date. + const DayPickerSingleSelection(this.selectedDate) + : assert(selectedDate != null); + + @override + bool isAfter(DateTime dateTime) => selectedDate.isAfter(dateTime); + + @override + bool isBefore(DateTime dateTime) => selectedDate.isAfter(dateTime); + + @override + DateTime get earliest => selectedDate; + + @override + bool get isEmpty => selectedDate == null; + + @override + bool get isNotEmpty => selectedDate != null; +} + + +/// Selection with one or many single dates. +/// +/// See also: +/// * [DayPickerSingleSelection] - selection with only one selected date. +/// * [DayPickerRangeSelection] - date period selection. +class DayPickerMultiSelection extends DayPickerSelection { + + /// List of the selected dates. + final List selectedDates; + + /// Selection with one or many single dates. + DayPickerMultiSelection(this.selectedDates) + : assert(selectedDates != null); + + + @override + bool isAfter(DateTime dateTime) + => selectedDates.every((d) => d.isAfter(dateTime)); + + @override + bool isBefore(DateTime dateTime) + => selectedDates.every((d) => d.isBefore(dateTime)); + + @override + DateTime get earliest => DatePickerUtils.getEarliestFromList(selectedDates); + + @override + bool get isEmpty => selectedDates.isEmpty; + + @override + bool get isNotEmpty => selectedDates.isNotEmpty; +} + + + +/// Date period selection. +/// +/// See also: +/// * [DayPickerSingleSelection] - selection with only one selected date. +/// * [DayPickerMultiSelection] - selection with one or many single dates. +class DayPickerRangeSelection extends DayPickerSelection { + + /// Selected period. + final DatePeriod selectedRange; + + /// Date period selection. + const DayPickerRangeSelection(this.selectedRange) + : assert(selectedRange != null); + + @override + DateTime get earliest => selectedRange.start; + + @override + bool isAfter(DateTime dateTime) => selectedRange.start.isAfter(dateTime); + + @override + bool isBefore(DateTime dateTime) => selectedRange.end.isBefore(dateTime); + + @override + bool get isEmpty => selectedRange == null; + + @override + bool get isNotEmpty => selectedRange != null; +} \ No newline at end of file diff --git a/flutter_date_pickers-master/lib/src/day_type.dart b/flutter_date_pickers-master/lib/src/day_type.dart new file mode 100644 index 00000000..86ba762e --- /dev/null +++ b/flutter_date_pickers-master/lib/src/day_type.dart @@ -0,0 +1,20 @@ +/// Type of the day in day based date picker. +enum DayType { + /// start of the selected period + start, + + /// middle of the selected period + middle, + + /// end of the selected period + end, + + /// selected single day + single, + + /// disabled day + disabled, + + /// not selected day (but not disabled) + notSelected +} \ No newline at end of file diff --git a/flutter_date_pickers-master/lib/src/event_decoration.dart b/flutter_date_pickers-master/lib/src/event_decoration.dart new file mode 100644 index 00000000..d206e375 --- /dev/null +++ b/flutter_date_pickers-master/lib/src/event_decoration.dart @@ -0,0 +1,35 @@ +import 'package:flutter/widgets.dart'; + +import 'day_picker.dart'; +import 'range_picker.dart'; +import 'week_picker.dart'; + + +/// Signature for function which is used to set set specific decoration for +/// some days in [DayPicker], [WeekPicker] and [RangePicker]. +/// +/// See also: +/// * [DayPicker.eventDecorationBuilder] +/// * [WeekPicker.eventDecorationBuilder] +/// * [RangePicker.eventDecorationBuilder] +typedef EventDecorationBuilder = EventDecoration? Function(DateTime date); + + +/// Class to store styles for event (specific day in the date picker). +@immutable +class EventDecoration { + + /// Cell decoration for the specific day in the date picker (event). + final BoxDecoration? boxDecoration; + + /// Style for number of the specific day in the date picker (event). + final TextStyle? textStyle; + + /// Creates decoration for special day. + /// + /// Used for [EventDecorationBuilder] function which is usually passed to + /// [DayPicker.eventDecorationBuilder], [WeekPicker.eventDecorationBuilder] + /// and [RangePicker.eventDecorationBuilder] to set specific decoration for + /// some days. + const EventDecoration({this.boxDecoration, this.textStyle}); +} \ No newline at end of file diff --git a/flutter_date_pickers-master/lib/src/i_selectable_picker.dart b/flutter_date_pickers-master/lib/src/i_selectable_picker.dart new file mode 100644 index 00000000..f945258e --- /dev/null +++ b/flutter_date_pickers-master/lib/src/i_selectable_picker.dart @@ -0,0 +1,537 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; + +import 'date_period.dart'; +import 'day_picker.dart' as day_picker; +import 'day_type.dart'; +import 'range_picker.dart'; +import 'unselectable_period_error.dart'; +import 'utils.dart'; + +/// Interface for selection logic of the different date pickers. +/// +/// T - is selection type. +abstract class ISelectablePicker { + /// The earliest date the user is permitted to pick. + /// (only year, month and day matter, time doesn't matter) + final DateTime firstDate; + + /// The latest date the user is permitted to pick. + /// (only year, month and day matter, time doesn't matter) + final DateTime lastDate; + + /// Function returns if day can be selected or not. + final SelectableDayPredicate _selectableDayPredicate; + + /// StreamController for new selection (T). + @protected + StreamController onUpdateController = StreamController.broadcast(); + + /// Stream with new selected (T) event. + /// + /// Throws [UnselectablePeriodException] + /// if there is any custom disabled date in selected. + Stream get onUpdate => onUpdateController.stream; + + /// Constructor with required fields that used in non-abstract methods + /// ([isDisabled]). + ISelectablePicker(this.firstDate, this.lastDate, + {SelectableDayPredicate? selectableDayPredicate}) + : _selectableDayPredicate = + selectableDayPredicate ?? _defaultSelectableDayPredicate; + + /// If current selection exists and includes day/days that can't be selected + /// according to the [_selectableDayPredicate]' + bool get curSelectionIsCorrupted; + + /// Returns [DayType] for given [day]. + DayType getDayType(DateTime day); + + /// Call when user tap on the day cell. + void onDayTapped(DateTime selectedDate); + + /// Returns if given day is disabled. + /// + /// Returns weather given day before the beginning of the [firstDate] + /// or after the end of the [lastDate]. + /// + /// If [_selectableDayPredicate] is set checks it as well. + @protected + bool isDisabled(DateTime day) { + final DateTime beginOfTheFirstDay = + DatePickerUtils.startOfTheDay(firstDate); + final DateTime endOfTheLastDay = DatePickerUtils.endOfTheDay(lastDate); + final bool customDisabled = + _selectableDayPredicate != null ? !_selectableDayPredicate(day) : false; + + return day.isAfter(endOfTheLastDay) || + day.isBefore(beginOfTheFirstDay) || + customDisabled; + } + + /// Closes [onUpdateController]. + /// After it [onUpdateController] can't get new events. + void dispose() { + onUpdateController.close(); + } + + static bool _defaultSelectableDayPredicate(_) => true; +} + +/// Selection logic for WeekPicker. +class WeekSelectable extends ISelectablePicker { + /// Initialized in ctor body. + late DateTime _firstDayOfSelectedWeek; + + /// Initialized in ctor body. + late DateTime _lastDayOfSelectedWeek; + + // It is int from 0 to 6 where 0 points to Sunday and 6 points to Saturday. + // According to MaterialLocalization.firstDayOfWeekIndex. + final int _firstDayOfWeekIndex; + + @override + bool get curSelectionIsCorrupted => _checkCurSelection(); + + /// Creates selection logic for WeekPicker. + /// + /// Entire week will be selected if + /// * it is between [firstDate] and [lastDate] + /// * it doesn't include unselectable days according to the + /// [selectableDayPredicate] + /// + /// If one or more days of the week are before [firstDate] + /// first selection date will be the same as [firstDate]. + /// + /// If one or more days of the week are after [lastDate] + /// last selection date will be the same as [lastDate]. + /// + /// If one or more days of week are not selectable according to the + /// [selectableDayPredicate] nothing will be returned as selection + /// but [UnselectablePeriodException] will be thrown. + WeekSelectable(DateTime selectedDate, this._firstDayOfWeekIndex, + DateTime firstDate, DateTime lastDate, + {SelectableDayPredicate? selectableDayPredicate}) + : super(firstDate, lastDate, + selectableDayPredicate: selectableDayPredicate) { + DatePeriod selectedWeek = _getNewSelectedPeriod(selectedDate); + _firstDayOfSelectedWeek = selectedWeek.start; + _lastDayOfSelectedWeek = selectedWeek.end; + _checkCurSelection(); + } + + @override + DayType getDayType(DateTime date) { + DayType result; + + DatePeriod selectedPeriod = + DatePeriod(_firstDayOfSelectedWeek, _lastDayOfSelectedWeek); + bool selectedPeriodIsBroken = + _disabledDatesInPeriod(selectedPeriod).isNotEmpty; + + if (isDisabled(date)) { + result = DayType.disabled; + } else if (_isDaySelected(date) && !selectedPeriodIsBroken) { + DateTime firstNotDisabledDayOfSelectedWeek = + _firstDayOfSelectedWeek.isBefore(firstDate) + ? firstDate + : _firstDayOfSelectedWeek; + + DateTime lastNotDisabledDayOfSelectedWeek = + _lastDayOfSelectedWeek.isAfter(lastDate) + ? lastDate + : _lastDayOfSelectedWeek; + + if (DatePickerUtils.sameDate(date, firstNotDisabledDayOfSelectedWeek) && + DatePickerUtils.sameDate(date, lastNotDisabledDayOfSelectedWeek)) { + result = DayType.single; + } else if (DatePickerUtils.sameDate(date, _firstDayOfSelectedWeek) || + DatePickerUtils.sameDate(date, firstDate)) { + result = DayType.start; + } else if (DatePickerUtils.sameDate(date, _lastDayOfSelectedWeek) || + DatePickerUtils.sameDate(date, lastDate)) { + result = DayType.end; + } else { + result = DayType.middle; + } + } else { + result = DayType.notSelected; + } + + return result; + } + + @override + void onDayTapped(DateTime selectedDate) { + DatePeriod newPeriod = _getNewSelectedPeriod(selectedDate); + List customDisabledDays = _disabledDatesInPeriod(newPeriod); + + customDisabledDays.isEmpty + ? onUpdateController.add(newPeriod) + : onUpdateController.addError( + UnselectablePeriodException(customDisabledDays, newPeriod)); + } + + // Returns new selected period according to tapped date. + // Doesn't check custom disabled days. + // You have to check it separately if it needs. + DatePeriod _getNewSelectedPeriod(DateTime tappedDay) { + DatePeriod newPeriod; + + DateTime firstDayOfTappedWeek = + DatePickerUtils.getFirstDayOfWeek(tappedDay, _firstDayOfWeekIndex); + DateTime lastDayOfTappedWeek = + DatePickerUtils.getLastDayOfWeek(tappedDay, _firstDayOfWeekIndex); + + DateTime firstNotDisabledDayOfSelectedWeek = + firstDayOfTappedWeek.isBefore(firstDate) + ? firstDate + : firstDayOfTappedWeek; + + DateTime lastNotDisabledDayOfSelectedWeek = + lastDayOfTappedWeek.isAfter(lastDate) ? lastDate : lastDayOfTappedWeek; + + newPeriod = DatePeriod( + firstNotDisabledDayOfSelectedWeek, lastNotDisabledDayOfSelectedWeek); + return newPeriod; + } + + bool _isDaySelected(DateTime date) { + DateTime startOfTheStartDay = + DatePickerUtils.startOfTheDay(_firstDayOfSelectedWeek); + DateTime endOfTheLastDay = + DatePickerUtils.endOfTheDay(_lastDayOfSelectedWeek); + return !(date.isBefore(startOfTheStartDay) || + date.isAfter(endOfTheLastDay)); + } + + List _disabledDatesInPeriod(DatePeriod period) { + List result = []; + + var date = period.start; + + while (!date.isAfter(period.end)) { + if (isDisabled(date)) result.add(date); + + date = date.add(Duration(days: 1)); + } + + return result; + } + + // Returns if current selection contains disabled dates. + // Returns false if there is no any selection. + bool _checkCurSelection() { + bool noSelection = + _firstDayOfSelectedWeek == null || _lastDayOfSelectedWeek == null; + + if (noSelection) return false; + + DatePeriod selectedPeriod = + DatePeriod(_firstDayOfSelectedWeek, _lastDayOfSelectedWeek); + List disabledDates = _disabledDatesInPeriod(selectedPeriod); + + bool selectedPeriodIsBroken = disabledDates.isNotEmpty; + return selectedPeriodIsBroken; + } +} + +/// Selection logic for [day_picker.DayPicker]. +class DaySelectable extends ISelectablePicker { + /// Currently selected date. + DateTime selectedDate; + + @override + bool get curSelectionIsCorrupted => _checkCurSelection(); + + /// Creates selection logic for [day_picker.DayPicker]. + /// + /// Every day can be selected if it is between [firstDate] and [lastDate] + /// and not unselectable according to the [selectableDayPredicate]. + /// + /// If day is not selectable according to the [selectableDayPredicate] + /// nothing will be returned as selection + /// but [UnselectablePeriodException] will be thrown. + DaySelectable(this.selectedDate, DateTime firstDate, DateTime lastDate, + {SelectableDayPredicate? selectableDayPredicate}) + : super(firstDate, lastDate, + selectableDayPredicate: selectableDayPredicate); + + @override + DayType getDayType(DateTime date) { + DayType result; + + if (isDisabled(date)) { + result = DayType.disabled; + } else if (_isDaySelected(date)) { + result = DayType.single; + } else { + result = DayType.notSelected; + } + + return result; + } + + @override + void onDayTapped(DateTime selectedDate) { + DateTime newSelected = DatePickerUtils.sameDate(firstDate, selectedDate) + ? selectedDate + : DateTime(selectedDate.year, selectedDate.month, selectedDate.day); + onUpdateController.add(newSelected); + } + + bool _isDaySelected(DateTime date) => + DatePickerUtils.sameDate(date, selectedDate); + + // Returns if current selection is disabled + // according to the [_selectableDayPredicate]. + // + // Returns false if there is no any selection. + bool _checkCurSelection() { + if (selectedDate == null) return false; + bool selectedIsBroken = _selectableDayPredicate(selectedDate); + + return selectedIsBroken; + } +} + +/// Selection logic for [day_picker.DayPicker] where many single days can be +/// selected. +class DayMultiSelectable extends ISelectablePicker> { + /// Currently selected dates. + List selectedDates; + + /// Creates selection logic for [day_picker.DayPicker]. + /// + /// Every day can be selected if it is between [firstDate] and [lastDate] + /// and not unselectable according to the [selectableDayPredicate]. + /// + /// If day is not selectable according to the [selectableDayPredicate] + /// nothing will be returned as selection + /// but [UnselectablePeriodException] will be thrown. + DayMultiSelectable(this.selectedDates, DateTime firstDate, DateTime lastDate, + {SelectableDayPredicate? selectableDayPredicate}) + : super(firstDate, lastDate, + selectableDayPredicate: selectableDayPredicate); + + @override + bool get curSelectionIsCorrupted => _checkCurSelection(); + + @override + DayType getDayType(DateTime date) { + DayType result; + + if (isDisabled(date)) { + result = DayType.disabled; + } else if (_isDaySelected(date)) { + result = DayType.single; + } else { + result = DayType.notSelected; + } + + return result; + } + + @override + void onDayTapped(DateTime selectedDate) { + bool alreadyExist = + selectedDates.any((d) => DatePickerUtils.sameDate(d, selectedDate)); + + if (alreadyExist) { + List newSelectedDates = List.from(selectedDates) + ..removeWhere((d) => DatePickerUtils.sameDate(d, selectedDate)); + + onUpdateController.add(newSelectedDates); + } else { + DateTime newSelected = DatePickerUtils.sameDate(firstDate, selectedDate) + ? selectedDate + : DateTime(selectedDate.year, selectedDate.month, selectedDate.day); + + List newSelectedDates = List.from(selectedDates) + ..add(newSelected); + + onUpdateController.add(newSelectedDates); + } + } + + bool _isDaySelected(DateTime date) => + selectedDates.any((d) => DatePickerUtils.sameDate(date, d)); + + // Returns if current selection is disabled + // according to the [_selectableDayPredicate]. + // + // Returns false if there is no any selection. + bool _checkCurSelection() { + if (selectedDates == null || selectedDates.isEmpty) return false; + bool selectedIsBroken = selectedDates.every(_selectableDayPredicate); + + return selectedIsBroken; + } +} + +/// Selection logic for [RangePicker]. +class RangeSelectable extends ISelectablePicker { + /// Initially selected period. + DatePeriod selectedPeriod; + + @override + bool get curSelectionIsCorrupted => _checkCurSelection(); + + /// Creates selection logic for [RangePicker]. + /// + /// Period can be selected if + /// * it is between [firstDate] and [lastDate] + /// * it doesn't include unselectable days according to the + /// [selectableDayPredicate] + /// + /// + /// If one or more days of the period are not selectable according to the + /// [selectableDayPredicate] nothing will be returned as selection + /// but [UnselectablePeriodException] will be thrown. + RangeSelectable(this.selectedPeriod, DateTime firstDate, DateTime lastDate, + {SelectableDayPredicate? selectableDayPredicate}) + : super(firstDate, lastDate, + selectableDayPredicate: selectableDayPredicate); + + @override + DayType getDayType(DateTime date) { + DayType result; + + bool selectedPeriodIsBroken = + _disabledDatesInPeriod(selectedPeriod).isNotEmpty; + + if (isDisabled(date)) { + result = DayType.disabled; + } else if (_isDaySelected(date) && !selectedPeriodIsBroken) { + if (DatePickerUtils.sameDate(date, selectedPeriod.start) && + DatePickerUtils.sameDate(date, selectedPeriod.end)) { + result = DayType.single; + } else if (DatePickerUtils.sameDate(date, selectedPeriod.start) || + DatePickerUtils.sameDate(date, firstDate)) { + result = DayType.start; + } else if (DatePickerUtils.sameDate(date, selectedPeriod.end) || + DatePickerUtils.sameDate(date, lastDate)) { + result = DayType.end; + } else { + result = DayType.middle; + } + } else { + result = DayType.notSelected; + } + + return result; + } + + @override + void onDayTapped(DateTime selectedDate) { + DatePeriod newPeriod = _getNewSelectedPeriod(selectedDate); + List customDisabledDays = _disabledDatesInPeriod(newPeriod); + + customDisabledDays.isEmpty + ? onUpdateController.add(newPeriod) + : onUpdateController.addError( + UnselectablePeriodException(customDisabledDays, newPeriod)); + } + + // Returns new selected period according to tapped date. + DatePeriod _getNewSelectedPeriod(DateTime tappedDate) { + // check if was selected only one date and we should generate period + bool sameDate = + DatePickerUtils.sameDate(selectedPeriod.start, selectedPeriod.end); + DatePeriod newPeriod; + + // Was selected one-day-period. + // With new user tap will be generated 2 dates as a period. + if (sameDate) { + // if user tap on the already selected single day + bool selectedAlreadySelectedDay = + DatePickerUtils.sameDate(tappedDate, selectedPeriod.end); + bool isSelectedFirstDay = DatePickerUtils.sameDate(tappedDate, firstDate); + bool isSelectedLastDay = DatePickerUtils.sameDate(tappedDate, lastDate); + + if (selectedAlreadySelectedDay) { + if (isSelectedFirstDay && isSelectedLastDay) { + newPeriod = DatePeriod(firstDate, lastDate); + } else if (isSelectedFirstDay) { + newPeriod = + DatePeriod(firstDate, DatePickerUtils.endOfTheDay(firstDate)); + } else if (isSelectedLastDay) { + newPeriod = + DatePeriod(DatePickerUtils.startOfTheDay(lastDate), lastDate); + } else { + newPeriod = DatePeriod(DatePickerUtils.startOfTheDay(tappedDate), + DatePickerUtils.endOfTheDay(tappedDate)); + } + } else { + DateTime startOfTheSelectedDay = + DatePickerUtils.startOfTheDay(selectedPeriod.start); + + if (!tappedDate.isAfter(startOfTheSelectedDay)) { + newPeriod = DatePickerUtils.sameDate(tappedDate, firstDate) + ? DatePeriod(firstDate, selectedPeriod.end) + : DatePeriod(DatePickerUtils.startOfTheDay(tappedDate), + selectedPeriod.end); + } else { + newPeriod = DatePickerUtils.sameDate(tappedDate, lastDate) + ? DatePeriod(selectedPeriod.start, lastDate) + : DatePeriod(selectedPeriod.start, + DatePickerUtils.endOfTheDay(tappedDate)); + } + } + + // Was selected 2 dates as a period. + // With new user tap new one-day-period will be generated. + } else { + bool sameAsFirst = DatePickerUtils.sameDate(tappedDate, firstDate); + bool sameAsLast = DatePickerUtils.sameDate(tappedDate, lastDate); + + if (sameAsFirst && sameAsLast) { + newPeriod = DatePeriod(firstDate, lastDate); + } else if (sameAsFirst) { + newPeriod = + DatePeriod(firstDate, DatePickerUtils.endOfTheDay(firstDate)); + } else if (sameAsLast) { + newPeriod = + DatePeriod(DatePickerUtils.startOfTheDay(tappedDate), lastDate); + } else { + newPeriod = DatePeriod(DatePickerUtils.startOfTheDay(tappedDate), + DatePickerUtils.endOfTheDay(tappedDate)); + } + } + + return newPeriod; + } + + // Returns if current selection contains disabled dates. + // Returns false if there is no any selection. + bool _checkCurSelection() { + if (selectedPeriod == null) return false; + List disabledDates = _disabledDatesInPeriod(selectedPeriod); + + bool selectedPeriodIsBroken = disabledDates.isNotEmpty; + return selectedPeriodIsBroken; + } + + List _disabledDatesInPeriod(DatePeriod period) { + List result = []; + + var date = period.start; + + while (!date.isAfter(period.end)) { + if (isDisabled(date)) result.add(date); + + date = date.add(Duration(days: 1)); + } + + return result; + } + + bool _isDaySelected(DateTime date) { + DateTime startOfTheStartDay = + DatePickerUtils.startOfTheDay(selectedPeriod.start); + DateTime endOfTheLastDay = DatePickerUtils.endOfTheDay(selectedPeriod.end); + return !(date.isBefore(startOfTheStartDay) || + date.isAfter(endOfTheLastDay)); + } +} diff --git a/flutter_date_pickers-master/lib/src/icon_btn.dart b/flutter_date_pickers-master/lib/src/icon_btn.dart new file mode 100644 index 00000000..09efe3bf --- /dev/null +++ b/flutter_date_pickers-master/lib/src/icon_btn.dart @@ -0,0 +1,54 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +/// Icon button widget built different +/// depends on [MaterialApp] or [CupertinoApp] ancestor. +class IconBtn extends StatelessWidget { + /// Widget to use inside button. + /// + /// Typically [Icon] widget. + final Widget icon; + + /// Function called when user tap on the button. + /// + /// Can be null. In this case button will be disabled. + final VoidCallback? onTap; + + /// Tooltip for button. + /// + /// Applied only for material style buttons. + /// It means only if widget has [MaterialApp] ancestor. + final String? tooltip; + + /// Creates button with [icon] different + /// depends on [MaterialApp] or [CupertinoApp] ancestor. + const IconBtn({ + Key? key, + required this.icon, + this.onTap, + this.tooltip + }) : super(key: key); + + @override + Widget build(BuildContext context) { + bool isMaterial = Material.of(context) != null; + + return isMaterial + ? _materialBtn() + : _cupertinoBtn(); + } + + Widget _cupertinoBtn() => + CupertinoButton( + padding: const EdgeInsets.all(0.0), + child: icon, + onPressed: onTap, + ); + + Widget _materialBtn() => + IconButton( + icon: icon, + tooltip: tooltip ?? "", + onPressed: onTap, + ); +} diff --git a/flutter_date_pickers-master/lib/src/layout_settings.dart b/flutter_date_pickers-master/lib/src/layout_settings.dart new file mode 100644 index 00000000..4f8b6f33 --- /dev/null +++ b/flutter_date_pickers-master/lib/src/layout_settings.dart @@ -0,0 +1,115 @@ +import 'dart:math' as math; + +import 'package:flutter/rendering.dart'; +import 'package:flutter/widgets.dart'; +import 'day_picker.dart'; +import 'month_picker.dart'; +import 'range_picker.dart'; +import 'week_picker.dart'; + +// layout defaults +const Duration _kPageScrollDuration = Duration(milliseconds: 200); +const double _kDayPickerRowHeight = 42.0; +const int _kMaxDayPickerRowCount = 6; // A 31 day month that starts on Saturday. +const double _kMonthPickerPortraitWidth = 330.0; +const EdgeInsetsGeometry _kContentPadding = + EdgeInsets.symmetric(horizontal: 8.0); + +/// Settings for the layout of the [DayPicker], [WeekPicker], [RangePicker] +/// and [MonthPicker]. +class DatePickerLayoutSettings { + /// Duration for scroll to previous or next page. + final Duration pagesScrollDuration; + + /// Determines the scroll physics of a date picker widget. + /// + /// Can be null. In this case default physics for [ScrollView] will be used. + final ScrollPhysics? scrollPhysics; + + /// Height of the one row in picker including headers. + /// + /// Default is [_kDayPickerRowHeight]. + final double dayPickerRowHeight; + + /// Width of the day based pickers. + final double monthPickerPortraitWidth; + + /// + final int maxDayPickerRowCount; + + /// Padding for the entire picker. + final EdgeInsetsGeometry contentPadding; + + /// If the first dates from the next month should be shown + /// to complete last week of the selected month. + /// + /// false by default. + final bool showNextMonthStart; + + /// If the last dates from the previous month should be shown + /// to complete first week of the selected month. + /// + /// false by default. + final bool showPrevMonthEnd; + + /// Hide Month navigation row + /// false by default. + final bool hideMonthNavigationRow; + + /// Grid delegate for the picker according to [dayPickerRowHeight] and + /// [maxDayPickerRowCount]. + SliverGridDelegate get dayPickerGridDelegate => + _DayPickerGridDelegate(dayPickerRowHeight, maxDayPickerRowCount); + + /// Maximum height of the day based picker according to [dayPickerRowHeight] + /// and [maxDayPickerRowCount]. + /// + /// Two extra rows: + /// one for the day-of-week header and one for the month header. + double get maxDayPickerHeight => + dayPickerRowHeight * (maxDayPickerRowCount + 2); + + /// Creates layout settings for the date picker. + /// + /// Usually used in [DayPicker], [WeekPicker], [RangePicker] + /// and [MonthPicker]. + const DatePickerLayoutSettings({ + this.pagesScrollDuration = _kPageScrollDuration, + this.dayPickerRowHeight = _kDayPickerRowHeight, + this.monthPickerPortraitWidth = _kMonthPickerPortraitWidth, + this.maxDayPickerRowCount = _kMaxDayPickerRowCount, + this.contentPadding = _kContentPadding, + this.showNextMonthStart = false, + this.showPrevMonthEnd = false, + this.hideMonthNavigationRow = false, + this.scrollPhysics + }); +} + + +class _DayPickerGridDelegate extends SliverGridDelegate { + final double _dayPickerRowHeight; + final int _maxDayPickerRowCount; + + const _DayPickerGridDelegate( + this._dayPickerRowHeight, this._maxDayPickerRowCount); + + @override + SliverGridLayout getLayout(SliverConstraints constraints) { + const int columnCount = DateTime.daysPerWeek; + final double tileWidth = constraints.crossAxisExtent / columnCount; + final double tileHeight = math.min(_dayPickerRowHeight, + constraints.viewportMainAxisExtent / (_maxDayPickerRowCount + 1)); + return SliverGridRegularTileLayout( + crossAxisCount: columnCount, + mainAxisStride: tileHeight, + crossAxisStride: tileWidth, + childMainAxisExtent: tileHeight, + childCrossAxisExtent: tileWidth, + reverseCrossAxis: axisDirectionIsReversed(constraints.crossAxisDirection), + ); + } + + @override + bool shouldRelayout(SliverGridDelegate oldDelegate) => false; +} diff --git a/flutter_date_pickers-master/lib/src/month_navigation_row.dart b/flutter_date_pickers-master/lib/src/month_navigation_row.dart new file mode 100644 index 00000000..188f7231 --- /dev/null +++ b/flutter_date_pickers-master/lib/src/month_navigation_row.dart @@ -0,0 +1,99 @@ +import 'package:flutter/material.dart'; +import 'day_picker.dart' as day_picker; +import 'icon_btn.dart'; +import 'range_picker.dart'; +import 'semantic_sorting.dart'; +import 'week_picker.dart'; + +/// Month navigation widget for day based date pickers like +/// [day_picker.DayPicker], +/// [WeekPicker], +/// [RangePicker]. +/// +/// It is row with [title] of showing month in the center and icons to selects +/// previous and next month around it. +class MonthNavigationRow extends StatelessWidget { + /// Key for previous page icon. + /// + /// Can be useful in integration tests to find icon. + final Key? previousPageIconKey; + + /// Key for next page icon. + /// + /// Can be useful in integration tests to find icon. + final Key? nextPageIconKey; + + /// Function called when [nextIcon] is tapped. + final VoidCallback? onNextMonthTapped; + + /// Function called when [prevIcon] is tapped. + final VoidCallback? onPreviousMonthTapped; + + /// Tooltip for the [nextIcon]. + final String? nextMonthTooltip; + + /// Tooltip for the [prevIcon]. + final String? previousMonthTooltip; + + /// Widget to use at the end of this row (after title). + final Widget nextIcon; + + /// Widget to use at the beginning of this row (before title). + final Widget prevIcon; + + /// Usually [Text] widget. + final Widget? title; + + /// Creates month navigation row. + const MonthNavigationRow({ + Key? key, + this.previousPageIconKey, + this.nextPageIconKey, + this.onNextMonthTapped, + this.onPreviousMonthTapped, + this.nextMonthTooltip, + this.previousMonthTooltip, + this.title, + required this.nextIcon, + required this.prevIcon + }) : super(key: key); + + @override + // ignore: prefer_expression_function_bodies + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Semantics( + sortKey: MonthPickerSortKey.previousMonth, + child: IconBtn( + key: previousPageIconKey, + icon: prevIcon, + tooltip: previousMonthTooltip, + onTap: onPreviousMonthTapped, + ), + ), + Expanded( + child: Container( + alignment: Alignment.center, + child: Center( + child: ExcludeSemantics( + child: title, + ), + ), + ), + ), + Semantics( + sortKey: MonthPickerSortKey.nextMonth, + child: IconBtn( + key: nextPageIconKey, + icon: nextIcon, + tooltip: nextMonthTooltip, + onTap: onNextMonthTapped, + ), + ), + ], + ); + } +} diff --git a/flutter_date_pickers-master/lib/src/month_picker.dart b/flutter_date_pickers-master/lib/src/month_picker.dart new file mode 100644 index 00000000..1df80740 --- /dev/null +++ b/flutter_date_pickers-master/lib/src/month_picker.dart @@ -0,0 +1,440 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:intl/intl.dart' as intl; + +import 'date_picker_keys.dart'; +import 'date_picker_styles.dart'; +import 'layout_settings.dart'; +import 'semantic_sorting.dart'; +import 'utils.dart'; + +const Locale _defaultLocale = Locale('en', 'US'); + +/// Month picker widget. +class MonthPicker extends StatefulWidget { + /// Month picker widget. + MonthPicker( + {Key? key, + required this.selectedDate, + required this.onChanged, + required this.firstDate, + required this.lastDate, + this.datePickerLayoutSettings = const DatePickerLayoutSettings(), + this.datePickerKeys, + required this.datePickerStyles}) + : assert(!firstDate.isAfter(lastDate)), + assert(!selectedDate.isBefore(firstDate)), + assert(!selectedDate.isAfter(lastDate)), + super(key: key); + + /// The currently selected date. + /// + /// This date is highlighted in the picker. + final DateTime selectedDate; + + /// Called when the user picks a month. + final ValueChanged onChanged; + + /// The earliest date the user is permitted to pick. + final DateTime firstDate; + + /// The latest date the user is permitted to pick. + final DateTime lastDate; + + /// Layout settings what can be customized by user + final DatePickerLayoutSettings datePickerLayoutSettings; + + /// Some keys useful for integration tests + final DatePickerKeys? datePickerKeys; + + /// Styles what can be customized by user + final DatePickerStyles datePickerStyles; + + @override + State createState() => _MonthPickerState(); +} + +class _MonthPickerState extends State { + PageController _monthPickerController = PageController(); + + Locale locale = _defaultLocale; + MaterialLocalizations localizations = _defaultLocalizations; + + TextDirection textDirection = TextDirection.ltr; + + DateTime _todayDate = DateTime.now(); + DateTime _previousYearDate = DateTime(DateTime.now().year - 1); + DateTime _nextYearDate = DateTime(DateTime.now().year + 1); + + DateTime _currentDisplayedYearDate = DateTime.now(); + + Timer? _timer; + + /// True if the earliest allowable year is displayed. + bool get _isDisplayingFirstYear => + !_currentDisplayedYearDate.isAfter(DateTime(widget.firstDate.year)); + + /// True if the latest allowable year is displayed. + bool get _isDisplayingLastYear => + !_currentDisplayedYearDate.isBefore(DateTime(widget.lastDate.year)); + + @override + void initState() { + super.initState(); + // Initially display the pre-selected date. + final int yearPage = + DatePickerUtils.yearDelta(widget.firstDate, widget.selectedDate); + + _monthPickerController.dispose(); + _monthPickerController = PageController(initialPage: yearPage); + _handleYearPageChanged(yearPage); + _updateCurrentDate(); + } + + @override + void didUpdateWidget(MonthPicker oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.selectedDate != oldWidget.selectedDate) { + final int yearPage = + DatePickerUtils.yearDelta(widget.firstDate, widget.selectedDate); + _monthPickerController = PageController(initialPage: yearPage); + _handleYearPageChanged(yearPage); + } + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + + try { + locale = Localizations.localeOf(context); + + MaterialLocalizations? curLocalizations = + Localizations.of( + context, MaterialLocalizations); + if (curLocalizations != null && localizations != curLocalizations) { + localizations = curLocalizations; + } + + textDirection = Directionality.of(context); + + // No MaterialLocalizations or Directionality or Locale was found + // and ".of" method throws error + // trying to cast null to MaterialLocalizations. + } on TypeError catch (_) {} + } + + void _updateCurrentDate() { + _todayDate = DateTime.now(); + final DateTime tomorrow = + DateTime(_todayDate.year, _todayDate.month, _todayDate.day + 1); + Duration timeUntilTomorrow = tomorrow.difference(_todayDate); + timeUntilTomorrow += + const Duration(seconds: 1); // so we don't miss it by rounding + _timer?.cancel(); + _timer = Timer(timeUntilTomorrow, () { + setState(_updateCurrentDate); + }); + } + + /// Add years to a year truncated date. + DateTime _addYearsToYearDate(DateTime yearDate, int yearsToAdd) => + DateTime(yearDate.year + yearsToAdd); + + Widget _buildItems(BuildContext context, int index) { + final DateTime year = _addYearsToYearDate(widget.firstDate, index); + + final ThemeData theme = Theme.of(context); + DatePickerStyles styles = widget.datePickerStyles; + styles = styles.fulfillWithTheme(theme); + + return _MonthPicker( + key: ValueKey(year), + selectedDate: widget.selectedDate, + currentDate: _todayDate, + onChanged: widget.onChanged, + firstDate: widget.firstDate, + lastDate: widget.lastDate, + datePickerLayoutSettings: widget.datePickerLayoutSettings, + displayedYear: year, + selectedPeriodKey: widget.datePickerKeys?.selectedPeriodKeys, + datePickerStyles: styles, + locale: locale, + localizations: localizations, + ); + } + + void _handleNextYear() { + if (!_isDisplayingLastYear) { + String yearStr = localizations.formatYear(_nextYearDate); + SemanticsService.announce(yearStr, textDirection); + _monthPickerController.nextPage( + duration: widget.datePickerLayoutSettings.pagesScrollDuration, + curve: Curves.ease); + } + } + + void _handlePreviousYear() { + if (!_isDisplayingFirstYear) { + String yearStr = localizations.formatYear(_previousYearDate); + SemanticsService.announce(yearStr, textDirection); + _monthPickerController.previousPage( + duration: widget.datePickerLayoutSettings.pagesScrollDuration, + curve: Curves.ease); + } + } + + void _handleYearPageChanged(int yearPage) { + setState(() { + _previousYearDate = _addYearsToYearDate(widget.firstDate, yearPage - 1); + _currentDisplayedYearDate = + _addYearsToYearDate(widget.firstDate, yearPage); + _nextYearDate = _addYearsToYearDate(widget.firstDate, yearPage + 1); + }); + } + + @override + Widget build(BuildContext context) { + int yearsCount = + DatePickerUtils.yearDelta(widget.firstDate, widget.lastDate) + 1; + + return SizedBox( + width: widget.datePickerLayoutSettings.monthPickerPortraitWidth, + height: widget.datePickerLayoutSettings.maxDayPickerHeight, + child: Stack( + children: [ + Semantics( + sortKey: YearPickerSortKey.calendar, + child: PageView.builder( + key: ValueKey(widget.selectedDate), + controller: _monthPickerController, + scrollDirection: Axis.horizontal, + itemCount: yearsCount, + itemBuilder: _buildItems, + onPageChanged: _handleYearPageChanged, + ), + ), + PositionedDirectional( + top: 0.0, + start: 8.0, + child: Semantics( + sortKey: YearPickerSortKey.previousYear, + child: IconButton( + key: widget.datePickerKeys?.previousPageIconKey, + icon: widget.datePickerStyles.prevIcon, + tooltip: _isDisplayingFirstYear + ? null + : '${localizations.formatYear(_previousYearDate)}', + onPressed: _isDisplayingFirstYear ? null : _handlePreviousYear, + ), + ), + ), + PositionedDirectional( + top: 0.0, + end: 8.0, + child: Semantics( + sortKey: YearPickerSortKey.nextYear, + child: IconButton( + key: widget.datePickerKeys?.nextPageIconKey, + icon: widget.datePickerStyles.nextIcon, + tooltip: _isDisplayingLastYear + ? null + : '${localizations.formatYear(_nextYearDate)}', + onPressed: _isDisplayingLastYear ? null : _handleNextYear, + ), + ), + ), + ], + ), + ); + } + + static MaterialLocalizations get _defaultLocalizations => + MaterialLocalizationEn( + twoDigitZeroPaddedFormat: + intl.NumberFormat('00', _defaultLocale.toString()), + fullYearFormat: intl.DateFormat.y(_defaultLocale.toString()), + longDateFormat: intl.DateFormat.yMMMMEEEEd(_defaultLocale.toString()), + shortMonthDayFormat: intl.DateFormat.MMMd(_defaultLocale.toString()), + decimalFormat: + intl.NumberFormat.decimalPattern(_defaultLocale.toString()), + shortDateFormat: intl.DateFormat.yMMMd(_defaultLocale.toString()), + mediumDateFormat: intl.DateFormat.MMMEd(_defaultLocale.toString()), + compactDateFormat: intl.DateFormat.yMd(_defaultLocale.toString()), + yearMonthFormat: intl.DateFormat.yMMMM(_defaultLocale.toString()), + ); +} + +class _MonthPicker extends StatelessWidget { + /// The month whose days are displayed by this picker. + final DateTime displayedYear; + + /// The earliest date the user is permitted to pick. + final DateTime firstDate; + + /// The latest date the user is permitted to pick. + final DateTime lastDate; + + /// The currently selected date. + /// + /// This date is highlighted in the picker. + final DateTime selectedDate; + + /// The current date at the time the picker is displayed. + final DateTime currentDate; + + /// Layout settings what can be customized by user + final DatePickerLayoutSettings datePickerLayoutSettings; + + /// Called when the user picks a day. + final ValueChanged onChanged; + + /// Key fo selected month (useful for integration tests) + final Key? selectedPeriodKey; + + /// Styles what can be customized by user + final DatePickerStyles datePickerStyles; + + final MaterialLocalizations localizations; + + final Locale locale; + + _MonthPicker( + {required this.displayedYear, + required this.firstDate, + required this.lastDate, + required this.selectedDate, + required this.currentDate, + required this.onChanged, + required this.datePickerLayoutSettings, + required this.datePickerStyles, + required this.localizations, + required this.locale, + this.selectedPeriodKey, + Key? key}) + : assert(!firstDate.isAfter(lastDate)), + assert(selectedDate.isAfter(firstDate) || + selectedDate.isAtSameMomentAs(firstDate)), + super(key: key); + + // We only need to know if month of passed day + // before the month of the firstDate or after the month of the lastDate. + // + // Don't need to compare day and time. + bool _isDisabled(DateTime month) { + DateTime beginningOfTheFirstDateMonth = + DateTime(firstDate.year, firstDate.month); + DateTime endOfTheLastDateMonth = DateTime(lastDate.year, lastDate.month + 1) + .subtract(Duration(microseconds: 1)); + + return month.isAfter(endOfTheLastDateMonth) || + month.isBefore(beginningOfTheFirstDateMonth); + } + + @override + Widget build(BuildContext context) { + final ThemeData themeData = Theme.of(context); + final int monthsInYear = 12; + final int year = displayedYear.year; + final int day = 1; + + final List labels = []; + + for (int i = 0; i < monthsInYear; i += 1) { + final int month = i + 1; + final DateTime monthToBuild = DateTime(year, month, day); + + final bool disabled = _isDisabled(monthToBuild); + final bool isSelectedMonth = + selectedDate.year == year && selectedDate.month == month; + + BoxDecoration? decoration; + TextStyle? itemStyle = themeData.textTheme.bodyText2; + + if (isSelectedMonth) { + itemStyle = datePickerStyles.selectedDateStyle; + decoration = datePickerStyles.selectedSingleDateDecoration; + } else if (disabled) { + itemStyle = datePickerStyles.disabledDateStyle; + } else if (currentDate.year == year && currentDate.month == month) { + // The current month gets a different text color. + itemStyle = datePickerStyles.currentDateStyle; + } else { + itemStyle = datePickerStyles.defaultDateTextStyle; + } + + String monthStr = _getMonthStr(monthToBuild); + + Widget monthWidget = Container( + decoration: decoration, + child: Center( + child: Semantics( + // We want the day of month to be spoken first irrespective of the + // locale-specific preferences or TextDirection. This is because + // an accessibility user is more likely to be interested in the + // day of month before the rest of the date, as they are looking + // for the day of month. To do that we prepend day of month to the + // formatted full date. + label: '${localizations.formatDecimal(month)}, ' + '${localizations.formatFullDate(monthToBuild)}', + selected: isSelectedMonth, + child: ExcludeSemantics( + child: Text(monthStr, style: itemStyle), + ), + ), + ), + ); + + if (!disabled) { + monthWidget = GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + DatePickerUtils.sameMonth(firstDate, monthToBuild) + ? onChanged(firstDate) + : onChanged(monthToBuild); + }, + child: monthWidget, + ); + } + labels.add(monthWidget); + } + + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: Column( + children: [ + Container( + height: datePickerLayoutSettings.dayPickerRowHeight, + child: Center( + child: ExcludeSemantics( + child: Text( + localizations.formatYear(displayedYear), + key: selectedPeriodKey, + style: datePickerStyles.displayedPeriodTitle, + ), + ), + ), + ), + Flexible( + child: GridView.count( + physics: datePickerLayoutSettings.scrollPhysics, + crossAxisCount: 4, + children: labels, + ), + ), + ], + ), + ); + } + + // Returns only month made with intl.DateFormat.MMM() for current [locale]. + // We can'r use [localizations] here because MaterialLocalizations doesn't + // provide short month string. + String _getMonthStr(DateTime date) { + String month = intl.DateFormat.MMM(locale.toString()).format(date); + return month; + } +} diff --git a/flutter_date_pickers-master/lib/src/range_picker.dart b/flutter_date_pickers-master/lib/src/range_picker.dart new file mode 100644 index 00000000..a9221aaa --- /dev/null +++ b/flutter_date_pickers-master/lib/src/range_picker.dart @@ -0,0 +1,109 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; + +import 'date_period.dart'; +import 'date_picker_keys.dart'; +import 'date_picker_styles.dart'; +import 'day_based_changable_picker.dart'; +import 'day_picker_selection.dart'; +import 'day_type.dart'; +import 'event_decoration.dart'; +import 'i_selectable_picker.dart'; +import 'layout_settings.dart'; +import 'typedefs.dart'; + +/// Date picker for range selection. +class RangePicker extends StatelessWidget { + /// Creates a range picker. + RangePicker( + {Key? key, + required this.selectedPeriod, + required this.onChanged, + required this.firstDate, + required this.lastDate, + this.initiallyShowDate, + this.datePickerLayoutSettings = const DatePickerLayoutSettings(), + this.datePickerStyles, + this.datePickerKeys, + this.selectableDayPredicate, + this.onSelectionError, + this.eventDecorationBuilder, + this.onMonthChanged}) + : assert(!firstDate.isAfter(lastDate)), + assert(!lastDate.isBefore(firstDate)), + assert(!selectedPeriod.start.isBefore(firstDate)), + assert(!selectedPeriod.end.isAfter(lastDate)), + assert(initiallyShowDate == null + || !initiallyShowDate.isAfter(lastDate)), + assert(initiallyShowDate == null + || !initiallyShowDate.isBefore(firstDate)), + super(key: key); + + /// The currently selected period. + /// + /// This date is highlighted in the picker. + final DatePeriod selectedPeriod; + + /// Called when the user picks a week. + final ValueChanged onChanged; + + /// Called when the error was thrown after user selection. + /// (e.g. when user selected a range with one or more days + /// that can't be selected) + final OnSelectionError? onSelectionError; + + /// The earliest date the user is permitted to pick. + final DateTime firstDate; + + /// The latest date the user is permitted to pick. + final DateTime lastDate; + + /// Date for defining what month should be shown initially. + /// + /// In case of null start of the [selectedPeriod] will be shown. + final DateTime? initiallyShowDate; + + /// Layout settings what can be customized by user + final DatePickerLayoutSettings datePickerLayoutSettings; + + /// Some keys useful for integration tests + final DatePickerKeys? datePickerKeys; + + /// Styles what can be customized by user + final DatePickerRangeStyles? datePickerStyles; + + /// Function returns if day can be selected or not. + final SelectableDayPredicate? selectableDayPredicate; + + /// Builder to get event decoration for each date. + /// + /// All event styles are overridden by selected styles + /// except days with dayType is [DayType.notSelected]. + final EventDecorationBuilder? eventDecorationBuilder; + + /// Called when the user changes the month. + /// New DateTime object represents first day of new month and 00:00 time. + final ValueChanged? onMonthChanged; + + @override + Widget build(BuildContext context) { + ISelectablePicker rangeSelectablePicker = RangeSelectable( + selectedPeriod, firstDate, lastDate, + selectableDayPredicate: selectableDayPredicate); + + return DayBasedChangeablePicker( + selectablePicker: rangeSelectablePicker, + selection: DayPickerRangeSelection(selectedPeriod), + firstDate: firstDate, + lastDate: lastDate, + initiallyShownDate: initiallyShowDate, + onChanged: onChanged, + onSelectionError: onSelectionError, + datePickerLayoutSettings: datePickerLayoutSettings, + datePickerStyles: datePickerStyles ?? DatePickerRangeStyles(), + datePickerKeys: datePickerKeys, + eventDecorationBuilder: eventDecorationBuilder, + onMonthChanged: onMonthChanged, + ); + } +} diff --git a/flutter_date_pickers-master/lib/src/semantic_sorting.dart b/flutter_date_pickers-master/lib/src/semantic_sorting.dart new file mode 100644 index 00000000..1036926a --- /dev/null +++ b/flutter_date_pickers-master/lib/src/semantic_sorting.dart @@ -0,0 +1,33 @@ +import 'package:flutter/semantics.dart'; + +/// Defines semantic traversal order of the top-level widgets +/// inside the day or week picker. +class MonthPickerSortKey extends OrdinalSortKey { + /// Previous month key. + static const MonthPickerSortKey previousMonth = MonthPickerSortKey(1.0); + + /// Next month key. + static const MonthPickerSortKey nextMonth = MonthPickerSortKey(2.0); + + /// Calendar key. + static const MonthPickerSortKey calendar = MonthPickerSortKey(3.0); + + /// + const MonthPickerSortKey(double order) : super(order); +} + +/// Defines semantic traversal order of the top-level widgets +/// inside the month picker. +class YearPickerSortKey extends OrdinalSortKey { + /// Previous year key. + static const YearPickerSortKey previousYear = YearPickerSortKey(1.0); + + /// Next year key. + static const YearPickerSortKey nextYear = YearPickerSortKey(2.0); + + /// Calendar key. + static const YearPickerSortKey calendar = YearPickerSortKey(3.0); + + /// + const YearPickerSortKey(double order) : super(order); +} diff --git a/flutter_date_pickers-master/lib/src/typedefs.dart b/flutter_date_pickers-master/lib/src/typedefs.dart new file mode 100644 index 00000000..d361ea12 --- /dev/null +++ b/flutter_date_pickers-master/lib/src/typedefs.dart @@ -0,0 +1,11 @@ +import 'range_picker.dart'; +import 'unselectable_period_error.dart'; +import 'week_picker.dart'; + + +/// Signature for function that can be used to handle incorrect selections. +/// +/// See also: +/// * [WeekPicker.onSelectionError] +/// * [RangePicker.onSelectionError] +typedef OnSelectionError = void Function(UnselectablePeriodException e); diff --git a/flutter_date_pickers-master/lib/src/unselectable_period_error.dart b/flutter_date_pickers-master/lib/src/unselectable_period_error.dart new file mode 100644 index 00000000..938b09e3 --- /dev/null +++ b/flutter_date_pickers-master/lib/src/unselectable_period_error.dart @@ -0,0 +1,30 @@ +import 'date_period.dart'; +import 'range_picker.dart'; +import 'week_picker.dart'; + + +/// Exception thrown when selected period contains custom disabled days. +class UnselectablePeriodException implements Exception { + /// Dates inside selected period what can't be selected + /// according custom rules. + final List customDisabledDates; + + /// Selected period wanted by the user. + final DatePeriod period; + + /// Creates exception that stores dates that can not be selected. + /// + /// See also: + /// *[WeekPicker.onSelectionError] + /// *[RangePicker.onSelectionError] + UnselectablePeriodException(this.customDisabledDates, this.period); + + @override + String toString() => + "UnselectablePeriodException:" + " ${customDisabledDates.length} dates inside selected period " + "(${period.start} - ${period.end}) " + "can't be selected according custom rules (selectable pridicate). " + "Check 'customDisabledDates' property " + "to get entire list of such dates."; +} diff --git a/flutter_date_pickers-master/lib/src/utils.dart b/flutter_date_pickers-master/lib/src/utils.dart new file mode 100644 index 00000000..b8c1c756 --- /dev/null +++ b/flutter_date_pickers-master/lib/src/utils.dart @@ -0,0 +1,251 @@ +/// Bunch of useful functions for date pickers. +class DatePickerUtils { + /// Returns if two objects have same year, month and day. + /// Time doesn't matter. + static bool sameDate(DateTime dateTimeOne, DateTime dateTimeTwo) => + dateTimeOne.year == dateTimeTwo.year && + dateTimeOne.month == dateTimeTwo.month && + dateTimeOne.day == dateTimeTwo.day; + + /// Returns if two objects have same year and month. + /// Day and time don't matter/ + static bool sameMonth(DateTime dateTimeOne, DateTime dateTimeTwo) => + dateTimeOne.year == dateTimeTwo.year + && dateTimeOne.month == dateTimeTwo.month; + + + // Do not use this directly - call getDaysInMonth instead. + static const List _daysInMonth = [ + 31, + -1, + 31, + 30, + 31, + 30, + 31, + 31, + 30, + 31, + 30, + 31 + ]; + + + /// Returns the number of days in a month, according to the proleptic + /// Gregorian calendar. + /// + /// This applies the leap year logic introduced by the Gregorian reforms of + /// 1582. It will not give valid results for dates prior to that time. + static int getDaysInMonth(int year, int month) { + if (month == DateTime.february) { + final bool isLeapYear = + (year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0); + return isLeapYear ? 29 : 28; + } + return _daysInMonth[month - 1]; + } + + + /// Returns number of months between [startDate] and [endDate] + static int monthDelta(DateTime startDate, DateTime endDate) => + (endDate.year - startDate.year) * 12 + + endDate.month - + startDate.month; + + + /// Add months to a month truncated date. + static DateTime addMonthsToMonthDate(DateTime monthDate, int monthsToAdd) => + // year is switched automatically if new month > 12 + DateTime(monthDate.year, monthDate.month + monthsToAdd); + + + /// Returns number of years between [startDate] and [endDate] + static int yearDelta(DateTime startDate, DateTime endDate) => + endDate.year - startDate.year; + + + /// Returns start of the first day of the week with given day. + /// + /// Start of the week calculated using firstDayIndex which is int from 0 to 6 + /// where 0 points to Sunday and 6 points to Saturday. + /// (according to MaterialLocalization.firstDayIfWeekIndex) + static DateTime getFirstDayOfWeek(DateTime day, int firstDayIndex) { + // from 1 to 7 where 1 points to Monday and 7 points to Sunday + int weekday = day.weekday; + + // to match weekdays where Sunday is 7 not 0 + if (firstDayIndex == 0) firstDayIndex = 7; + + int diff = weekday - firstDayIndex; + if (diff < 0) diff = 7 + diff; + + DateTime firstDayOfWeek = day.subtract(Duration(days: diff)); + firstDayOfWeek = startOfTheDay(firstDayOfWeek); + return firstDayOfWeek; + } + + /// Returns end of the last day of the week with given day. + /// + /// Start of the week calculated using firstDayIndex which is int from 0 to 6 + /// where 0 points to Sunday and 6 points to Saturday. + /// (according to MaterialLocalization.firstDayIfWeekIndex) + static DateTime getLastDayOfWeek(DateTime day, int firstDayIndex) { + // from 1 to 7 where 1 points to Monday and 7 points to Sunday + int weekday = day.weekday; + + // to match weekdays where Sunday is 7 not 0 + if (firstDayIndex == 0) firstDayIndex = 7; + + int lastDayIndex = firstDayIndex - 1; + if (lastDayIndex == 0) lastDayIndex = 7; + + int diff = lastDayIndex - weekday; + if (diff < 0) diff = 7 + diff; + + DateTime lastDayOfWeek = day.add(Duration(days: diff)); + lastDayOfWeek = endOfTheDay(lastDayOfWeek); + return lastDayOfWeek; + } + + /// Returns end of the given day. + /// + /// End time is 1 millisecond before start of the next day. + static DateTime endOfTheDay(DateTime date) { + DateTime tomorrowStart = DateTime(date.year, date.month, date.day + 1); + DateTime result = tomorrowStart.subtract(const Duration(milliseconds: 1)); + + return result; + } + + + /// Returns start of the given day. + /// + /// Start time is 00:00:00. + static DateTime startOfTheDay(DateTime date) => + DateTime(date.year, date.month, date.day); + + /// Returns first shown date for the [curMonth]. + /// + /// First shown date is not always 1st day of the [curMonth]. + /// It can be day from previous month if [showEndOfPrevMonth] is true. + /// + /// If [showEndOfPrevMonth] is true empty day cells before 1st [curMonth] + /// are filled with days from the previous month. + static DateTime firstShownDate({ + required DateTime curMonth, + required bool showEndOfPrevMonth, + required int firstDayOfWeekFromSunday}) { + + DateTime result = DateTime(curMonth.year, curMonth.month, 1); + + if (showEndOfPrevMonth) { + int firstDayOffset = computeFirstDayOffset(curMonth.year, curMonth.month, + firstDayOfWeekFromSunday); + if (firstDayOffset == 0) return result; + + + int prevMonth = curMonth.month - 1; + if (prevMonth < 1) prevMonth = 12; + + int prevYear = prevMonth == 12 + ? curMonth.year - 1 + : curMonth.year; + + int daysInPrevMonth = getDaysInMonth(prevYear, prevMonth); + int firstShownDay = daysInPrevMonth - firstDayOffset + 1; + result = DateTime(prevYear, prevMonth, firstShownDay); + } + + return result; + } + + + /// Returns last shown date for the [curMonth]. + /// + /// Last shown date is not always last day of the [curMonth]. + /// It can be day from next month if [showStartNextMonth] is true. + /// + /// If [showStartNextMonth] is true empty day cells after last day + /// of [curMonth] are filled with days from the next month. + static DateTime lastShownDate({ + required DateTime curMonth, + required bool showStartNextMonth, + required int firstDayOfWeekFromSunday}) { + + int daysInCurMonth = getDaysInMonth(curMonth.year, curMonth.month); + DateTime result = DateTime(curMonth.year, curMonth.month, daysInCurMonth); + + if (showStartNextMonth) { + int firstDayOffset = computeFirstDayOffset(curMonth.year, curMonth.month, + firstDayOfWeekFromSunday); + + int totalDays = firstDayOffset + daysInCurMonth; + int trailingDaysCount = 7 - totalDays % 7; + bool fullWeekTrailing = trailingDaysCount == 7; + if (fullWeekTrailing) return result; + + result = DateTime(curMonth.year, curMonth.month + 1, trailingDaysCount); + } + + return result; + } + + /// Computes the offset from the first day of week that the first day of the + /// [month] falls on. + /// + /// For example, September 1, 2017 falls on a Friday, which in the calendar + /// localized for United States English appears as: + /// + /// ``` + /// S M T W T F S + /// _ _ _ _ _ 1 2 + /// ``` + /// + /// The offset for the first day of the months is the number of leading blanks + /// in the calendar, i.e. 5. + /// + /// The same date localized for the Russian calendar has a different offset, + /// because the first day of week is Monday rather than Sunday: + /// + /// ``` + /// M T W T F S S + /// _ _ _ _ 1 2 3 + /// ``` + /// + /// So the offset is 4, rather than 5. + /// + /// This code consolidates the following: + /// + /// - [DateTime.weekday] provides a 1-based index into days of week, with 1 + /// falling on Monday. + /// - MaterialLocalizations.firstDayOfWeekIndex provides a 0-based index + /// into the MaterialLocalizations.narrowWeekdays list. + /// - MaterialLocalizations.narrowWeekdays list provides localized names of + /// days of week, always starting with Sunday and ending with Saturday. + static int computeFirstDayOffset( + int year, int month, int firstDayOfWeekFromSunday) { + // 0-based day of week, with 0 representing Monday. + final int weekdayFromMonday = DateTime(year, month).weekday - 1; + // firstDayOfWeekFromSunday recomputed to be Monday-based + final int firstDayOfWeekFromMonday = (firstDayOfWeekFromSunday - 1) % 7; + // Number of days between the first day of week appearing on the calendar, + // and the day corresponding to the 1-st of the month. + return (weekdayFromMonday - firstDayOfWeekFromMonday) % 7; + } + + /// Returns earliest [DateTime] from the list. + /// + /// [dates] must not be null. + /// In case it is null, [ArgumentError] will be thrown. + static DateTime getEarliestFromList(List dates) { + ArgumentError.checkNotNull(dates, "dates"); + + return dates.fold(dates[0], getEarliest); + } + + /// Returns earliest [DateTime] from two. + /// + /// If two [DateTime]s is the same moment first ([a]) will be return. + static DateTime getEarliest(DateTime a, DateTime b) + => a.isBefore(b) ? a : b; +} diff --git a/flutter_date_pickers-master/lib/src/week_picker.dart b/flutter_date_pickers-master/lib/src/week_picker.dart new file mode 100644 index 00000000..c3712260 --- /dev/null +++ b/flutter_date_pickers-master/lib/src/week_picker.dart @@ -0,0 +1,117 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; + +import 'date_period.dart'; +import 'date_picker_keys.dart'; +import 'date_picker_styles.dart'; +import 'day_based_changable_picker.dart'; +import 'day_picker_selection.dart'; +import 'day_type.dart'; +import 'event_decoration.dart'; +import 'i_selectable_picker.dart'; +import 'layout_settings.dart'; +import 'typedefs.dart'; + +/// Date picker for selection a week. +class WeekPicker extends StatelessWidget { + /// Creates a month picker. + WeekPicker( + {Key? key, + required this.selectedDate, + required this.onChanged, + required this.firstDate, + required this.lastDate, + this.initiallyShowDate, + this.datePickerLayoutSettings = const DatePickerLayoutSettings(), + this.datePickerStyles, + this.datePickerKeys, + this.selectableDayPredicate, + this.onSelectionError, + this.eventDecorationBuilder, + this.onMonthChanged}) + : assert(!firstDate.isAfter(lastDate)), + assert(!lastDate.isBefore(firstDate)), + assert(!selectedDate.isBefore(firstDate)), + assert(!selectedDate.isAfter(lastDate)), + assert(initiallyShowDate == null + || !initiallyShowDate.isAfter(lastDate)), + assert(initiallyShowDate == null + || !initiallyShowDate.isBefore(firstDate)), + super(key: key); + + /// The currently selected date. + /// + /// This date is highlighted in the picker. + final DateTime selectedDate; + + /// Called when the user picks a week. + final ValueChanged onChanged; + + /// Called when the error was thrown after user selection. + /// (e.g. when user selected a week with one or more days + /// what can't be selected) + final OnSelectionError? onSelectionError; + + /// The earliest date the user is permitted to pick. + final DateTime firstDate; + + /// The latest date the user is permitted to pick. + final DateTime lastDate; + + /// Date for defining what month should be shown initially. + /// + /// In case of null month with earliest date of the selected week + /// will be shown. + final DateTime? initiallyShowDate; + + /// Layout settings what can be customized by user + final DatePickerLayoutSettings datePickerLayoutSettings; + + /// Some keys useful for integration tests + final DatePickerKeys? datePickerKeys; + + /// Styles what can be customized by user + final DatePickerRangeStyles? datePickerStyles; + + /// Function returns if day can be selected or not. + final SelectableDayPredicate? selectableDayPredicate; + + /// Builder to get event decoration for each date. + /// + /// All event styles are overriden by selected styles + /// except days with dayType is [DayType.notSelected]. + final EventDecorationBuilder? eventDecorationBuilder; + + /// Called when the user changes the month. + /// New DateTime object represents first day of new month and 00:00 time. + final ValueChanged? onMonthChanged; + + @override + Widget build(BuildContext context) { + MaterialLocalizations localizations = MaterialLocalizations.of(context); + + int firstDayOfWeekIndex = datePickerStyles?.firstDayOfeWeekIndex ?? + localizations.firstDayOfWeekIndex; + + ISelectablePicker weekSelectablePicker = WeekSelectable( + selectedDate, firstDayOfWeekIndex, firstDate, lastDate, + selectableDayPredicate: selectableDayPredicate); + + return DayBasedChangeablePicker( + selectablePicker: weekSelectablePicker, + // todo: maybe create selection for week + // todo: and change logic here to work with it + selection: DayPickerSingleSelection(selectedDate), + firstDate: firstDate, + lastDate: lastDate, + initiallyShownDate: initiallyShowDate, + onChanged: onChanged, + onSelectionError: onSelectionError, + datePickerLayoutSettings: datePickerLayoutSettings, + datePickerStyles: datePickerStyles ?? DatePickerRangeStyles(), + datePickerKeys: datePickerKeys, + eventDecorationBuilder: eventDecorationBuilder, + onMonthChanged: onMonthChanged, + ); + } +} diff --git a/flutter_date_pickers-master/pubspec.lock b/flutter_date_pickers-master/pubspec.lock new file mode 100644 index 00000000..84da0035 --- /dev/null +++ b/flutter_date_pickers-master/pubspec.lock @@ -0,0 +1,158 @@ +# 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" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.15.0" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_localizations: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + intl: + dependency: "direct main" + description: + name: intl + url: "https://pub.dartlang.org" + source: hosted + version: "0.17.0" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.10" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0" + 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" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.19" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" +sdks: + dart: ">=2.12.0 <3.0.0" diff --git a/flutter_date_pickers-master/pubspec.yaml b/flutter_date_pickers-master/pubspec.yaml new file mode 100644 index 00000000..ef0ee289 --- /dev/null +++ b/flutter_date_pickers-master/pubspec.yaml @@ -0,0 +1,20 @@ +name: flutter_date_pickers +description: Flutter package for day, week, range and month date pickers. +version: 0.2.4 +author: Maria Melnik +homepage: https://github.com/MariaMelnik/flutter_date_pickers +issue_tracker: https://github.com/MariaMelnik/flutter_date_pickers/issues + +environment: + sdk: '>=2.12.0 <3.0.0' + +dependencies: + flutter: + sdk: flutter + intl: ">=0.17.0 <1.0.0" + flutter_localizations: + sdk: flutter + +dev_dependencies: + flutter_test: + sdk: flutter diff --git a/flutter_date_pickers-master/test/date_time_utils.dart b/flutter_date_pickers-master/test/date_time_utils.dart new file mode 100644 index 00000000..7947596f --- /dev/null +++ b/flutter_date_pickers-master/test/date_time_utils.dart @@ -0,0 +1,152 @@ +/// Bunch of useful functions for date pickers. +class DateTimeUtils { + /// Returns if two objects have same year, month and day. + /// Time doesn't matter. + static bool sameDate(DateTime dateTimeOne, DateTime dateTimeTwo) => + dateTimeOne.year == dateTimeTwo.year && + dateTimeOne.month == dateTimeTwo.month && + dateTimeOne.day == dateTimeTwo.day; + + /// Returns if two objects have same year and month. + /// Day and time don't matter/ + static bool sameMonth(DateTime dateTimeOne, DateTime dateTimeTwo) => + dateTimeOne.year == dateTimeTwo.year + && dateTimeOne.month == dateTimeTwo.month; + + + + /// Returns number of months between [startDate] and [endDate] + static int monthDelta(DateTime startDate, DateTime endDate) => + (endDate.year - startDate.year) * 12 + + endDate.month - + startDate.month; + + + /// Add months to a month truncated date. + static DateTime addMonthsToMonthDate(DateTime monthDate, int monthsToAdd) => + // year is switched automatically if new month > 12 + DateTime(monthDate.year, monthDate.month + monthsToAdd); + + + /// Returns number of years between [startDate] and [endDate] + static int yearDelta(DateTime startDate, DateTime endDate) => + endDate.year - startDate.year; + + + /// Returns start of the first day of the week with given day. + /// + /// Start of the week calculated using firstDayIndex which is int from 0 to 6 + /// where 0 points to Sunday and 6 points to Saturday. + /// (according to MaterialLocalization.firstDayIfWeekIndex) + static DateTime getFirstDayOfWeek(DateTime day, int firstDayIndex) { + // from 1 to 7 where 1 points to Monday and 7 points to Sunday + int weekday = day.weekday; + + // to match weekdays where Sunday is 7 not 0 + if (firstDayIndex == 0) firstDayIndex = 7; + + int diff = weekday - firstDayIndex; + if (diff < 0) diff = 7 + diff; + + DateTime firstDayOfWeek = day.subtract(Duration(days: diff)); + firstDayOfWeek = startOfTheDay(firstDayOfWeek); + return firstDayOfWeek; + } + + /// Returns end of the last day of the week with given day. + /// + /// Start of the week calculated using firstDayIndex which is int from 0 to 6 + /// where 0 points to Sunday and 6 points to Saturday. + /// (according to MaterialLocalization.firstDayIfWeekIndex) + static DateTime getLastDayOfWeek(DateTime day, int firstDayIndex) { + // from 1 to 7 where 1 points to Monday and 7 points to Sunday + int weekday = day.weekday; + + // to match weekdays where Sunday is 7 not 0 + if (firstDayIndex == 0) firstDayIndex = 7; + + int lastDayIndex = firstDayIndex - 1; + if (lastDayIndex == 0) lastDayIndex = 7; + + int diff = lastDayIndex - weekday; + if (diff < 0) diff = 7 + diff; + + DateTime lastDayOfWeek = day.add(Duration(days: diff)); + lastDayOfWeek = endOfTheDay(lastDayOfWeek); + return lastDayOfWeek; + } + + /// Returns end of the given day. + /// + /// End time is 1 millisecond before start of the next day. + static DateTime endOfTheDay(DateTime date) => + DateTime(date.year, date.month, date.day) + .add(const Duration(days: 1)) + .subtract(const Duration(milliseconds: 1)); + + /// Returns start of the given day. + /// + /// Start time is 00:00:00. + static DateTime startOfTheDay(DateTime date) => + DateTime(date.year, date.month, date.day); + + + /// Computes the offset from the first day of week that the first day of the + /// [month] falls on. + /// + /// For example, September 1, 2017 falls on a Friday, which in the calendar + /// localized for United States English appears as: + /// + /// ``` + /// S M T W T F S + /// _ _ _ _ _ 1 2 + /// ``` + /// + /// The offset for the first day of the months is the number of leading blanks + /// in the calendar, i.e. 5. + /// + /// The same date localized for the Russian calendar has a different offset, + /// because the first day of week is Monday rather than Sunday: + /// + /// ``` + /// M T W T F S S + /// _ _ _ _ 1 2 3 + /// ``` + /// + /// So the offset is 4, rather than 5. + /// + /// This code consolidates the following: + /// + /// - [DateTime.weekday] provides a 1-based index into days of week, with 1 + /// falling on Monday. + /// - MaterialLocalizations.firstDayOfWeekIndex provides a 0-based index + /// into the MaterialLocalizations.narrowWeekdays list. + /// - MaterialLocalizations.narrowWeekdays list provides localized names of + /// days of week, always starting with Sunday and ending with Saturday. + static int computeFirstDayOffset( + int year, int month, int firstDayOfWeekFromSunday) { + // 0-based day of week, with 0 representing Monday. + final int weekdayFromMonday = DateTime(year, month).weekday - 1; + // firstDayOfWeekFromSunday recomputed to be Monday-based + final int firstDayOfWeekFromMonday = (firstDayOfWeekFromSunday - 1) % 7; + // Number of days between the first day of week appearing on the calendar, + // and the day corresponding to the 1-st of the month. + return (weekdayFromMonday - firstDayOfWeekFromMonday) % 7; + } + + /// Returns earliest [DateTime] from the list. + /// + /// [dates] must not be null. + /// In case it is null, [ArgumentError] will be thrown. + static DateTime getEarliestFromList(List dates) { + ArgumentError.checkNotNull(dates, "dates"); + + return dates.fold(dates[0], getEarliest); + } + + /// Returns earliest [DateTime] from two. + /// + /// If two [DateTime]s is the same moment first ([a]) will be return. + static DateTime getEarliest(DateTime a, DateTime b) + => a.isBefore(b) ? a : b; +} diff --git a/flutter_date_pickers-master/test/day_multi_selectable_test.dart b/flutter_date_pickers-master/test/day_multi_selectable_test.dart new file mode 100644 index 00000000..8f2b0a14 --- /dev/null +++ b/flutter_date_pickers-master/test/day_multi_selectable_test.dart @@ -0,0 +1,42 @@ +import 'package:flutter_date_pickers/src/day_type.dart'; +import 'package:flutter_date_pickers/src/i_selectable_picker.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'date_time_utils.dart'; + +void main() { + group("DayMultiSelectable test.", () { + test("getDayType() returns correct type for different dates", () { + final now = DateTime.now(); + final selectedDates = [ + now.subtract(const Duration(days: 1)), + now, + now.add(const Duration(days: 1)), + ]; + + final firstDate = now.subtract(const Duration(days: 10)); + final lastDate = now.add(const Duration(days: 10)); + final disabledDate = now.subtract(const Duration(days: 5)); + + // ignore: prefer_function_declarations_over_variables + final selectablePredicate = (DateTime d) + => !DateTimeUtils.sameDate(d, disabledDate); + + final selectableLogic = DayMultiSelectable( + selectedDates, firstDate, lastDate, + selectableDayPredicate: selectablePredicate); + + final notSelectedEnabledDateType = selectableLogic.getDayType(firstDate); + expect(notSelectedEnabledDateType, DayType.notSelected); + + final disabledDateType = selectableLogic.getDayType(disabledDate); + expect(disabledDateType, DayType.disabled); + + for (DateTime d in selectedDates) { + final selectedDateType = selectableLogic.getDayType(d); + expect(selectedDateType, DayType.single, + reason: "Incorrect DayType for the date ${d.toString()}"); + } + }); + }); +} \ No newline at end of file diff --git a/flutter_date_pickers-master/test/day_selectable_test.dart b/flutter_date_pickers-master/test/day_selectable_test.dart new file mode 100644 index 00000000..c6683ea2 --- /dev/null +++ b/flutter_date_pickers-master/test/day_selectable_test.dart @@ -0,0 +1,31 @@ +import 'package:flutter_date_pickers/src/day_type.dart'; +import 'package:flutter_date_pickers/src/i_selectable_picker.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'date_time_utils.dart'; + +void main() { + group("DaySelectable test.", () { + test("getDayType() returns correct type for different dates", () { + final selectedDate = DateTime.now(); + final firstDate = selectedDate.subtract(const Duration(days: 10)); + final lastDate = selectedDate.add(const Duration(days: 10)); + final disabledDate = selectedDate.subtract(const Duration(days: 1)); + + // ignore: prefer_function_declarations_over_variables + final selectablePredicate = (DateTime d) + => !DateTimeUtils.sameDate(d, disabledDate); + + final selectableLogic = DaySelectable( + selectedDate, firstDate, lastDate, + selectableDayPredicate: selectablePredicate); + final selectedDateType = selectableLogic.getDayType(selectedDate); + final notSelectedEnabledDateType = selectableLogic.getDayType(firstDate); + final disabledDateType = selectableLogic.getDayType(disabledDate); + + expect(selectedDateType, DayType.single); + expect(notSelectedEnabledDateType, DayType.notSelected); + expect(disabledDateType, DayType.disabled); + }); + }); +} \ No newline at end of file diff --git a/flutter_date_pickers-master/test/range_selectable_test.dart b/flutter_date_pickers-master/test/range_selectable_test.dart new file mode 100644 index 00000000..4502a498 --- /dev/null +++ b/flutter_date_pickers-master/test/range_selectable_test.dart @@ -0,0 +1,55 @@ +import 'package:flutter_date_pickers/flutter_date_pickers.dart'; +import 'package:flutter_date_pickers/src/day_type.dart'; +import 'package:flutter_date_pickers/src/i_selectable_picker.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'date_time_utils.dart'; + +void main() { + group("RangeSelectable test.", () { + test("getDayType() returns correct type for different dates", () { + final now = DateTime.now(); + final startPeriod = now.subtract(const Duration(days: 3)); + final endPeriod = now.add(const Duration(days: 3)); + final selectedPeriod = DatePeriod(startPeriod, endPeriod); + + final firstDate = now.subtract(const Duration(days: 10)); + final lastDate = now.add(const Duration(days: 10)); + final disabledDate = now.subtract(const Duration(days: 5)); + + // ignore: prefer_function_declarations_over_variables + final selectablePredicate = (DateTime d) + => !DateTimeUtils.sameDate(d, disabledDate); + + final selectableLogic = RangeSelectable( + selectedPeriod, firstDate, lastDate, + selectableDayPredicate: selectablePredicate); + + final notSelectedEnabledDateType = selectableLogic.getDayType(firstDate); + expect(notSelectedEnabledDateType, DayType.notSelected); + + final disabledDateType = selectableLogic.getDayType(disabledDate); + expect(disabledDateType, DayType.disabled); + + final startPeriodDateType = selectableLogic.getDayType(startPeriod); + expect(startPeriodDateType, DayType.start); + + final endPeriodDateType = selectableLogic.getDayType(endPeriod); + expect(endPeriodDateType, DayType.end); + + final periodDays = endPeriod.difference(startPeriod).inDays; + + // Count of the day period which is not start and not end. + final middleDaysCount = periodDays - 2; + final middleDates = List.generate(middleDaysCount, + (i) => startPeriod.add(Duration(days: i + 1))); + + for (DateTime date in middleDates) { + final middlePeriodDateType = selectableLogic.getDayType(date); + expect(middlePeriodDateType, DayType.middle, + reason: "Incorrect DayType for the date ${date.toString()} " + "in period ${startPeriod.toString()} - ${endPeriod.toString()}"); + } + }); + }); +} \ No newline at end of file diff --git a/flutter_date_pickers-master/test/week_selectabl_test.dart b/flutter_date_pickers-master/test/week_selectabl_test.dart new file mode 100644 index 00000000..eb0b2321 --- /dev/null +++ b/flutter_date_pickers-master/test/week_selectabl_test.dart @@ -0,0 +1,56 @@ +import 'package:flutter_date_pickers/src/day_type.dart'; +import 'package:flutter_date_pickers/src/i_selectable_picker.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'date_time_utils.dart'; + +void main() { + group("WeekSelectable test.", () { + test("getDayType() returns correct type for different dates", () { + final selectedDate = DateTime(2020, 10, 5); // Monday + final firstDayOfWeekIndex = 1; // Week starts from Monday + + final firstDate = selectedDate.subtract(const Duration(days: 10)); + final lastDate = selectedDate.add(const Duration(days: 10)); + final disabledDate = selectedDate.subtract(const Duration(days: 5)); + + // ignore: prefer_function_declarations_over_variables + final selectablePredicate = (DateTime d) + => !DateTimeUtils.sameDate(d, disabledDate); + + final selectableLogic = WeekSelectable( + selectedDate, firstDayOfWeekIndex, firstDate, lastDate, + selectableDayPredicate: selectablePredicate); + + final notSelectedEnabledDateType = selectableLogic.getDayType(firstDate); + expect(notSelectedEnabledDateType, DayType.notSelected); + + final disabledDateType = selectableLogic.getDayType(disabledDate); + expect(disabledDateType, DayType.disabled); + + final weekStart = DateTimeUtils + .getFirstDayOfWeek(selectedDate, firstDayOfWeekIndex); + + final weekEnd = DateTimeUtils + .getLastDayOfWeek(selectedDate, firstDayOfWeekIndex); + + final startPeriodDateType = selectableLogic.getDayType(weekStart); + expect(startPeriodDateType, DayType.start); + + final endPeriodDateType = selectableLogic.getDayType(weekEnd); + expect(endPeriodDateType, DayType.end); + + // Count of the week days which is not start and not end (7 - 2). + final middleDaysCount = 5; + final middleDates = List.generate(middleDaysCount, + (i) => weekStart.add(Duration(days: i + 1))); + + for (DateTime date in middleDates) { + final middlePeriodDateType = selectableLogic.getDayType(date); + expect(middlePeriodDateType, DayType.middle, + reason: "Incorrect DayType for the date ${date.toString()} " + "in week ${weekStart.toString()} - ${weekEnd.toString()}"); + } + }); + }); +} \ No newline at end of file diff --git a/flutter_date_pickers-master/tool/flutter_analyze.sh b/flutter_date_pickers-master/tool/flutter_analyze.sh new file mode 100644 index 00000000..4cd98c68 --- /dev/null +++ b/flutter_date_pickers-master/tool/flutter_analyze.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +OUTPUT="$(flutter analyze)" +echo "$OUTPUT" +echo +if grep -q "error •" echo "$OUTPUT"; then + echo "flutter analyze found errors" + exit 1 +else + echo "flutter analyze didn't find any errors" + exit 0 +fi \ No newline at end of file