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
|
||||||