New Example - Calendar (#91)
58
flutter_date_pickers-master/CHANGELOG.md
Normal file
@ -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).
|
21
flutter_date_pickers-master/LICENSE
Normal file
@ -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.
|
120
flutter_date_pickers-master/README.md
Normal file
@ -0,0 +1,120 @@
|
||||
# flutter_date_pickers
|
||||
|
||||
[](https://github.com/MariaMelnik/flutter_date_pickers/actions)
|
||||
[](https://pub.dev/packages/flutter_date_pickers)
|
||||
[](https://pub.dev/packages/flutter_date_pickers)
|
||||
[](https://pub.dev/packages/flutter_date_pickers/score)
|
||||
[](https://github.com/MariaMelnik/flutter_date_pickers)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://github.com/MariaMelnik/flutter_date_pickers/)
|
||||
<!--[](https://codecov.io/gh/MariaMelnik/flutter_date_pickers)
|
||||
[](https://github.com/tenhobi/effective_dart)-->
|
||||
|
||||
|
||||
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
|
||||
|
||||

|
||||
|
||||
## 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<DatePeriod> 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.
|
94
flutter_date_pickers-master/analysis_options.yaml
Normal file
@ -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
|
BIN
flutter_date_pickers-master/demoDatePickers.gif
Normal file
After Width: | Height: | Size: 1.5 MiB |
BIN
flutter_date_pickers-master/demoDatePickers2.gif
Normal file
After Width: | Height: | Size: 2.3 MiB |
61
flutter_date_pickers-master/example/android/app/build.gradle
Normal file
@ -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'
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.mariamelnik.flutter_date_picker">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</manifest>
|
@ -0,0 +1,33 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.mariamelnik.flutter_date_picker">
|
||||
|
||||
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
|
||||
calls FlutterMain.startInitialization(this); in its onCreate method.
|
||||
In most cases you can leave this as-is, but you if you want to provide
|
||||
additional functionality it is fine to subclass or reimplement
|
||||
FlutterApplication and put your custom class here. -->
|
||||
<application
|
||||
android:name="io.flutter.app.FlutterApplication"
|
||||
android:label="flutter_date_picker"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<!-- This keeps the window background of the activity showing
|
||||
until Flutter renders its first frame. It can be removed if
|
||||
there is no splash screen (such as the default splash screen
|
||||
defined in @style/LaunchTheme). -->
|
||||
<meta-data
|
||||
android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
|
||||
android:value="true" />
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
@ -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);
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Modify this file to customize your launch splash screen -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@android:color/white" />
|
||||
|
||||
<!-- You can insert your own image assets here -->
|
||||
<!-- <item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@mipmap/launch_image" />
|
||||
</item> -->
|
||||
</layer-list>
|
After Width: | Height: | Size: 544 B |
After Width: | Height: | Size: 442 B |
After Width: | Height: | Size: 721 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.4 KiB |
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
Flutter draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
</style>
|
||||
</resources>
|
@ -0,0 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.mariamelnik.flutter_date_picker">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</manifest>
|
29
flutter_date_pickers-master/example/android/build.gradle
Normal file
@ -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
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
org.gradle.jvmargs=-Xmx1536M
|
||||
android.enableR8=true
|
6
flutter_date_pickers-master/example/android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -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
|
15
flutter_date_pickers-master/example/android/settings.gradle
Normal file
@ -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
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>App</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>io.flutter.flutter.app</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>App</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>8.0</string>
|
||||
</dict>
|
||||
</plist>
|
@ -0,0 +1 @@
|
||||
#include "Generated.xcconfig"
|
@ -0,0 +1 @@
|
||||
#include "Generated.xcconfig"
|
@ -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 = "<group>"; };
|
||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
|
||||
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
||||
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
/* 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 = "<group>";
|
||||
};
|
||||
97C146E51CF9000F007C117D = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9740EEB11CF90186004384FC /* Flutter */,
|
||||
97C146F01CF9000F007C117D /* Runner */,
|
||||
97C146EF1CF9000F007C117D /* Products */,
|
||||
CF3B75C9A7D2FA2A4C99F110 /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
97C146EF1CF9000F007C117D /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
97C146EE1CF9000F007C117D /* Runner.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
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 = "<group>";
|
||||
};
|
||||
97C146F11CF9000F007C117D /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
97C146F21CF9000F007C117D /* main.m */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* 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 = "<group>";
|
||||
};
|
||||
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
97C147001CF9000F007C117D /* Base */,
|
||||
);
|
||||
name = LaunchScreen.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* 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 */;
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
@ -0,0 +1,93 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0910"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Profile"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
7
flutter_date_pickers-master/example/ios/Runner.xcworkspace/contents.xcworkspacedata
generated
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:Runner.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
@ -0,0 +1,6 @@
|
||||
#import <Flutter/Flutter.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface AppDelegate : FlutterAppDelegate
|
||||
|
||||
@end
|
13
flutter_date_pickers-master/example/ios/Runner/AppDelegate.m
Normal file
@ -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
|
@ -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"
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 564 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 3.5 KiB |
@ -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"
|
||||
}
|
||||
}
|
BIN
flutter_date_pickers-master/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
vendored
Normal file
After Width: | Height: | Size: 68 B |
After Width: | Height: | Size: 68 B |
After Width: | Height: | Size: 68 B |
@ -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.
|
@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="EHf-IW-A2E">
|
||||
<objects>
|
||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
|
||||
</imageView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
|
||||
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="53" y="375"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="LaunchImage" width="168" height="185"/>
|
||||
</resources>
|
||||
</document>
|
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Flutter View Controller-->
|
||||
<scene sceneID="tne-QT-ifu">
|
||||
<objects>
|
||||
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
45
flutter_date_pickers-master/example/ios/Runner/Info.plist
Normal file
@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>flutter_date_picker</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
9
flutter_date_pickers-master/example/ios/Runner/main.m
Normal file
@ -0,0 +1,9 @@
|
||||
#import <Flutter/Flutter.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "AppDelegate.h"
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
@autoreleasepool {
|
||||
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
|
||||
}
|
||||
}
|
@ -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<StatefulWidget> createState() => _ColorPickerDialogState();
|
||||
}
|
||||
|
||||
class _ColorPickerDialogState extends State<ColorPickerDialog> {
|
||||
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;
|
||||
});
|
||||
}
|
||||
}
|
@ -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: <Widget>[
|
||||
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,
|
||||
)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -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<Event> events;
|
||||
|
||||
///
|
||||
const DayPickerPage({
|
||||
Key? key,
|
||||
this.events = const []
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _DayPickerPageState();
|
||||
}
|
||||
|
||||
class _DayPickerPageState extends State<DayPickerPage> {
|
||||
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: <Widget>[
|
||||
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: <Widget>[
|
||||
Text(
|
||||
"Selected date styles",
|
||||
style: Theme.of(context).textTheme.subtitle1,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12.0),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
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<DateTime> eventsDates = widget.events
|
||||
.map<DateTime>((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;
|
||||
}
|
||||
}
|
@ -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<Event> events;
|
||||
|
||||
///
|
||||
const DaysPickerPage({
|
||||
Key? key,
|
||||
this.events = const []
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _DaysPickerPageState();
|
||||
}
|
||||
|
||||
class _DaysPickerPageState extends State<DaysPickerPage> {
|
||||
List<DateTime> _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: <Widget>[
|
||||
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: <Widget>[
|
||||
Text(
|
||||
"Selected date styles",
|
||||
style: Theme.of(context).textTheme.subtitle1,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12.0),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
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<DateTime> newDates) {
|
||||
setState(() {
|
||||
_selectedDates = newDates;
|
||||
});
|
||||
}
|
||||
|
||||
// ignore: prefer_expression_function_bodies
|
||||
bool _isSelectableCustom (DateTime day) {
|
||||
return day.weekday < 6;
|
||||
}
|
||||
|
||||
dp.EventDecoration? _eventDecorationBuilder(DateTime date) {
|
||||
List<DateTime> eventsDates = widget.events
|
||||
.map<DateTime>((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;
|
||||
}
|
||||
}
|
@ -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<StatefulWidget> createState() => _MonthPickerPageState();
|
||||
}
|
||||
|
||||
class _MonthPickerPageState extends State<MonthPickerPage> {
|
||||
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: <Widget>[
|
||||
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: <Widget>[
|
||||
Text(
|
||||
"Selected date styles",
|
||||
style: Theme.of(context).textTheme.subtitle1,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12.0),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
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;
|
||||
});
|
||||
}
|
||||
}
|
@ -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<Event> events;
|
||||
|
||||
///
|
||||
const RangePickerPage({
|
||||
Key? key,
|
||||
this.events = const []
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _RangePickerPageState();
|
||||
}
|
||||
|
||||
class _RangePickerPageState extends State<RangePickerPage> {
|
||||
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: <Widget>[
|
||||
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: <Widget>[
|
||||
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: <Widget>[
|
||||
_selectedPeriod != null
|
||||
? Column(children: <Widget>[
|
||||
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: <Widget>[
|
||||
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<DateTime> eventsDates = widget.events
|
||||
.map<DateTime>((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;
|
||||
}
|
@ -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<Event> events;
|
||||
|
||||
///
|
||||
const RangePickerPageStyled({
|
||||
Key? key,
|
||||
this.events = const []
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _RangePickerPageStyledState();
|
||||
}
|
||||
|
||||
class _RangePickerPageStyledState extends State<RangePickerPageStyled> {
|
||||
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: <Widget>[
|
||||
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: <Widget>[
|
||||
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: <Widget>[
|
||||
_selectedPeriod != null
|
||||
? Column(children: <Widget>[
|
||||
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<DateTime> eventsDates = widget.events
|
||||
.map<DateTime>((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;
|
@ -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<Event> events;
|
||||
|
||||
///
|
||||
const WeekPickerPage({
|
||||
Key? key,
|
||||
this.events = const []
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _WeekPickerPageState();
|
||||
}
|
||||
|
||||
class _WeekPickerPageState extends State<WeekPickerPage> {
|
||||
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: <Widget>[
|
||||
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: <Widget>[
|
||||
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: <Widget>[
|
||||
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: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Text("Selected: $_selectedDate"),
|
||||
),
|
||||
_selectedPeriod != null
|
||||
? Column(children: <Widget>[
|
||||
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<DateTime> eventsDates = widget.events
|
||||
.map<DateTime>((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;
|
||||
}
|
||||
}
|
14
flutter_date_pickers-master/example/lib/event.dart
Normal file
@ -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);
|
||||
}
|
121
flutter_date_pickers-master/example/lib/main.dart
Normal file
@ -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<MyHomePage> with TickerProviderStateMixin {
|
||||
DateTime startOfPeriod = DateTime.now().subtract(Duration(days: 10));
|
||||
DateTime endOfPeriod = DateTime.now().add(Duration(days: 10));
|
||||
int _selectedTab = 0;
|
||||
|
||||
final List<Widget> datePickers = <Widget>[
|
||||
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<Event> 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"),
|
||||
];
|
181
flutter_date_pickers-master/example/pubspec.lock
Normal file
@ -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"
|
80
flutter_date_pickers-master/example/pubspec.yaml
Normal file
@ -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
|
23
flutter_date_pickers-master/flutter_date_pickers.iml
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/lib" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.idea" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/ios/Flutter/App.framework/flutter_assets/packages" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Android API 25 Platform" jdkType="Android SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Dart Packages" level="project" />
|
||||
<orderEntry type="library" name="Dart SDK" level="project" />
|
||||
<orderEntry type="library" name="Flutter Plugins" level="project" />
|
||||
</component>
|
||||
</module>
|
12
flutter_date_pickers-master/lib/flutter_date_pickers.dart
Normal file
@ -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';
|
347
flutter_date_pickers-master/lib/src/basic_day_based_widget.dart
Normal file
@ -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<T> 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<Widget> labels = <Widget>[];
|
||||
|
||||
List<Widget> headers = _buildHeaders(localizations);
|
||||
List<Widget> daysBeforeMonthStart = _buildCellsBeforeStart(localizations);
|
||||
List<Widget> monthDays = _buildMonthCells(localizations);
|
||||
List<Widget> daysAfterMonthEnd = _buildCellsAfterEnd(localizations);
|
||||
|
||||
labels.addAll(headers);
|
||||
labels.addAll(daysBeforeMonthStart);
|
||||
labels.addAll(monthDays);
|
||||
labels.addAll(daysAfterMonthEnd);
|
||||
|
||||
return Padding(
|
||||
padding: datePickerLayoutSettings.contentPadding,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: GridView.custom(
|
||||
physics: datePickerLayoutSettings.scrollPhysics,
|
||||
gridDelegate: datePickerLayoutSettings.dayPickerGridDelegate,
|
||||
childrenDelegate:
|
||||
SliverChildListDelegate(labels, addRepaintBoundaries: false),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _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<Widget> headers = getDayHeaders(dayHeaderStyleBuilder,
|
||||
localizations.narrowWeekdays, firstDayOfWeekIndex);
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
List<Widget> _buildCellsBeforeStart(MaterialLocalizations localizations) {
|
||||
List<Widget> 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<Widget> 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<Widget> _buildMonthCells(MaterialLocalizations localizations) {
|
||||
List<Widget> 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<Widget> _buildCellsAfterEnd(MaterialLocalizations localizations) {
|
||||
List<Widget> 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;
|
||||
}
|
||||
}
|
13
flutter_date_pickers-master/lib/src/date_period.dart
Normal file
@ -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);
|
||||
}
|
19
flutter_date_pickers-master/lib/src/date_picker_keys.dart
Normal file
@ -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);
|
||||
}
|
99
flutter_date_pickers-master/lib/src/date_picker_mixin.dart
Normal file
@ -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<Widget> getDayHeaders(
|
||||
DayHeaderStyleBuilder headerStyleBuilder,
|
||||
List<String> narrowWeekdays,
|
||||
int firstDayOfWeekIndex) {
|
||||
|
||||
final List<Widget> result = <Widget>[];
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
386
flutter_date_pickers-master/lib/src/date_picker_styles.dart
Normal file
@ -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<int> 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
|
||||
);
|
||||
}
|
@ -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<T> 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<T> 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<T> 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<DateTime>? 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<DayBasedChangeablePicker<T>> createState() =>
|
||||
_DayBasedChangeablePickerState<T>();
|
||||
}
|
||||
|
||||
// todo: Check initial selection and call onSelectionError in case it has error
|
||||
// todo: (ISelectablePicker.curSelectionIsCorrupted);
|
||||
class _DayBasedChangeablePickerState<T>
|
||||
extends State<DayBasedChangeablePicker<T>> {
|
||||
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<T>? _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<T> 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<MaterialLocalizations>(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>[
|
||||
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<DayBasedChangeablePickerState>(
|
||||
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<DateTime>(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);
|
||||
}
|
@ -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<DayBasedChangeablePickerState> 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<DayBasedChangeablePickerState> _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,
|
||||
});
|
||||
}
|
189
flutter_date_pickers-master/lib/src/day_picker.dart
Normal file
@ -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<T extends Object> 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<DateTime> single({
|
||||
Key? key,
|
||||
required DateTime selectedDate,
|
||||
required ValueChanged<DateTime> onChanged,
|
||||
required DateTime firstDate,
|
||||
required DateTime lastDate,
|
||||
DatePickerLayoutSettings datePickerLayoutSettings
|
||||
= const DatePickerLayoutSettings(),
|
||||
DateTime? initiallyShowDate,
|
||||
DatePickerRangeStyles? datePickerStyles,
|
||||
DatePickerKeys? datePickerKeys,
|
||||
SelectableDayPredicate? selectableDayPredicate,
|
||||
EventDecorationBuilder? eventDecorationBuilder,
|
||||
ValueChanged<DateTime>? 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<DateTime>._(
|
||||
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<List<DateTime>> multi({Key? key,
|
||||
required List<DateTime> selectedDates,
|
||||
required ValueChanged<List<DateTime>> onChanged,
|
||||
required DateTime firstDate,
|
||||
required DateTime lastDate,
|
||||
DatePickerLayoutSettings datePickerLayoutSettings
|
||||
= const DatePickerLayoutSettings(),
|
||||
DateTime? initiallyShowDate,
|
||||
DatePickerRangeStyles? datePickerStyles,
|
||||
DatePickerKeys? datePickerKeys,
|
||||
SelectableDayPredicate? selectableDayPredicate,
|
||||
EventDecorationBuilder? eventDecorationBuilder,
|
||||
ValueChanged<DateTime>? 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<List<DateTime>>._(
|
||||
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<T> 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<DateTime>? onMonthChanged;
|
||||
|
||||
/// Logic to handle user's selections.
|
||||
final ISelectablePicker<T> selectionLogic;
|
||||
|
||||
@override
|
||||
// ignore: prefer_expression_function_bodies
|
||||
Widget build(BuildContext context) {
|
||||
return DayBasedChangeablePicker<T>(
|
||||
selectablePicker: selectionLogic,
|
||||
selection: selection,
|
||||
firstDate: firstDate,
|
||||
lastDate: lastDate,
|
||||
initiallyShownDate: initiallyShowDate,
|
||||
onChanged: onChanged,
|
||||
datePickerLayoutSettings: datePickerLayoutSettings,
|
||||
datePickerStyles: datePickerStyles ?? DatePickerRangeStyles(),
|
||||
datePickerKeys: datePickerKeys,
|
||||
eventDecorationBuilder: eventDecorationBuilder,
|
||||
onMonthChanged: onMonthChanged,
|
||||
);
|
||||
}
|
||||
}
|
120
flutter_date_pickers-master/lib/src/day_picker_selection.dart
Normal file
@ -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<DateTime> 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;
|
||||
}
|
20
flutter_date_pickers-master/lib/src/day_type.dart
Normal file
@ -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
|
||||
}
|
35
flutter_date_pickers-master/lib/src/event_decoration.dart
Normal file
@ -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});
|
||||
}
|
537
flutter_date_pickers-master/lib/src/i_selectable_picker.dart
Normal file
@ -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<T> {
|
||||
/// 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<T> onUpdateController = StreamController<T>.broadcast();
|
||||
|
||||
/// Stream with new selected (T) event.
|
||||
///
|
||||
/// Throws [UnselectablePeriodException]
|
||||
/// if there is any custom disabled date in selected.
|
||||
Stream<T> 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<DatePeriod> {
|
||||
/// 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<DateTime> 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<DateTime> _disabledDatesInPeriod(DatePeriod period) {
|
||||
List<DateTime> result = <DateTime>[];
|
||||
|
||||
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<DateTime> disabledDates = _disabledDatesInPeriod(selectedPeriod);
|
||||
|
||||
bool selectedPeriodIsBroken = disabledDates.isNotEmpty;
|
||||
return selectedPeriodIsBroken;
|
||||
}
|
||||
}
|
||||
|
||||
/// Selection logic for [day_picker.DayPicker].
|
||||
class DaySelectable extends ISelectablePicker<DateTime> {
|
||||
/// 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<List<DateTime>> {
|
||||
/// Currently selected dates.
|
||||
List<DateTime> 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<DateTime> 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<DateTime> 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<DatePeriod> {
|
||||
/// 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<DateTime> 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<DateTime> disabledDates = _disabledDatesInPeriod(selectedPeriod);
|
||||
|
||||
bool selectedPeriodIsBroken = disabledDates.isNotEmpty;
|
||||
return selectedPeriodIsBroken;
|
||||
}
|
||||
|
||||
List<DateTime> _disabledDatesInPeriod(DatePeriod period) {
|
||||
List<DateTime> result = <DateTime>[];
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
54
flutter_date_pickers-master/lib/src/icon_btn.dart
Normal file
@ -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,
|
||||
);
|
||||
}
|
115
flutter_date_pickers-master/lib/src/layout_settings.dart
Normal file
@ -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;
|
||||
}
|
@ -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: <Widget>[
|
||||
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,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
440
flutter_date_pickers-master/lib/src/month_picker.dart
Normal file
@ -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<DateTime> 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<StatefulWidget> createState() => _MonthPickerState();
|
||||
}
|
||||
|
||||
class _MonthPickerState extends State<MonthPicker> {
|
||||
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<MaterialLocalizations>(
|
||||
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<DateTime>(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: <Widget>[
|
||||
Semantics(
|
||||
sortKey: YearPickerSortKey.calendar,
|
||||
child: PageView.builder(
|
||||
key: ValueKey<DateTime>(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<DateTime> 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<Widget> labels = <Widget>[];
|
||||
|
||||
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: <Widget>[
|
||||
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;
|
||||
}
|
||||
}
|
109
flutter_date_pickers-master/lib/src/range_picker.dart
Normal file
@ -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<DatePeriod> 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<DateTime>? onMonthChanged;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
ISelectablePicker<DatePeriod> rangeSelectablePicker = RangeSelectable(
|
||||
selectedPeriod, firstDate, lastDate,
|
||||
selectableDayPredicate: selectableDayPredicate);
|
||||
|
||||
return DayBasedChangeablePicker<DatePeriod>(
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
33
flutter_date_pickers-master/lib/src/semantic_sorting.dart
Normal file
@ -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);
|
||||
}
|
11
flutter_date_pickers-master/lib/src/typedefs.dart
Normal file
@ -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);
|
@ -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<DateTime> 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.";
|
||||
}
|
251
flutter_date_pickers-master/lib/src/utils.dart
Normal file
@ -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<int> _daysInMonth = <int>[
|
||||
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<DateTime> 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;
|
||||
}
|
117
flutter_date_pickers-master/lib/src/week_picker.dart
Normal file
@ -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<DatePeriod> 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<DateTime>? onMonthChanged;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
MaterialLocalizations localizations = MaterialLocalizations.of(context);
|
||||
|
||||
int firstDayOfWeekIndex = datePickerStyles?.firstDayOfeWeekIndex ??
|
||||
localizations.firstDayOfWeekIndex;
|
||||
|
||||
ISelectablePicker<DatePeriod> weekSelectablePicker = WeekSelectable(
|
||||
selectedDate, firstDayOfWeekIndex, firstDate, lastDate,
|
||||
selectableDayPredicate: selectableDayPredicate);
|
||||
|
||||
return DayBasedChangeablePicker<DatePeriod>(
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
158
flutter_date_pickers-master/pubspec.lock
Normal file
@ -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"
|
20
flutter_date_pickers-master/pubspec.yaml
Normal file
@ -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 <melnikmk@gmail.com>
|
||||
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
|
152
flutter_date_pickers-master/test/date_time_utils.dart
Normal file
@ -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<DateTime> 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;
|
||||
}
|
@ -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()}");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
31
flutter_date_pickers-master/test/day_selectable_test.dart
Normal file
@ -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);
|
||||
});
|
||||
});
|
||||
}
|
55
flutter_date_pickers-master/test/range_selectable_test.dart
Normal file
@ -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()}");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
56
flutter_date_pickers-master/test/week_selectabl_test.dart
Normal file
@ -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()}");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
12
flutter_date_pickers-master/tool/flutter_analyze.sh
Normal file
@ -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
|