Bläddra i källkod

增加日期最大最小值的控制,
增加主题控制

chenrizhang 7 år sedan
förälder
incheckning
472d31742c
4 ändrade filer med 321 tillägg och 205 borttagningar
  1. 70 109
      .idea/workspace.xml
  2. 161 0
      lib/datetime_picker_theme_data.dart
  3. 78 64
      lib/flutter_datetime_picker.dart
  4. 12 32
      lib/src/date_model.dart

+ 70 - 109
.idea/workspace.xml

@@ -7,16 +7,12 @@
   </component>
   <component name="ChangeListManager">
     <list default="true" id="4ca5d027-8e00-4d4c-ab69-69171c049d48" name="Default" comment="">
-      <change beforePath="$PROJECT_DIR$/.idea/libraries/Dart_Packages.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/libraries/Dart_Packages.xml" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/.idea/libraries/Dart_SDK.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/libraries/Dart_SDK.xml" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/lib/datetime_picker_theme_data.dart" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/example/pubspec.lock" beforeDir="false" afterPath="$PROJECT_DIR$/example/pubspec.lock" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/lib/flutter_datetime_picker.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/flutter_datetime_picker.dart" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/lib/src/date_format.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/date_format.dart" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/lib/src/date_model.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/date_model.dart" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/lib/src/i18n_model.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/i18n_model.dart" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/pubspec.lock" beforeDir="false" afterPath="$PROJECT_DIR$/pubspec.lock" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/pubspec.yaml" beforeDir="false" afterPath="$PROJECT_DIR$/pubspec.yaml" afterDir="false" />
     </list>
     <ignored path="$PROJECT_DIR$/.dart_tool/" />
     <ignored path="$PROJECT_DIR$/.idea/" />
@@ -31,27 +27,6 @@
   </component>
   <component name="FileEditorManager">
     <leaf SIDE_TABS_SIZE_LIMIT_KEY="375">
-      <file leaf-file-name="date_model.dart" pinned="false" current-in-tab="true">
-        <entry file="file://$PROJECT_DIR$/lib/src/date_model.dart">
-          <provider selected="true" editor-type-id="text-editor">
-            <state relative-caret-position="66">
-              <caret line="3" lean-forward="true" selection-start-line="3" selection-end-line="3" />
-              <folding>
-                <element signature="e#0#64#0" expanded="true" />
-              </folding>
-            </state>
-          </provider>
-        </entry>
-      </file>
-      <file leaf-file-name="duration.dart" pinned="false" current-in-tab="false">
-        <entry file="file://C:/Java/flutter/bin/cache/pkg/sky_engine/lib/core/duration.dart">
-          <provider selected="true" editor-type-id="text-editor">
-            <state relative-caret-position="2002">
-              <caret line="93" column="8" selection-start-line="93" selection-start-column="8" selection-end-line="93" selection-end-column="8" />
-            </state>
-          </provider>
-        </entry>
-      </file>
       <file leaf-file-name="README.md" pinned="false" current-in-tab="false">
         <entry file="file://$PROJECT_DIR$/README.md">
           <provider selected="true" editor-type-id="split-provider[text-editor;MarkdownPreviewEditor]">
@@ -64,29 +39,29 @@
           </provider>
         </entry>
       </file>
-      <file leaf-file-name="flutter_datetime_picker_test.dart" pinned="false" current-in-tab="false">
-        <entry file="file://$PROJECT_DIR$/test/flutter_datetime_picker_test.dart">
+      <file leaf-file-name="date_model.dart" pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/lib/src/date_model.dart">
           <provider selected="true" editor-type-id="text-editor">
-            <state relative-caret-position="132">
-              <caret line="6" selection-start-line="6" selection-end-line="6" />
+            <state relative-caret-position="22">
+              <caret line="1" column="16" selection-start-line="1" selection-start-column="16" selection-end-line="1" selection-end-column="16" />
             </state>
           </provider>
         </entry>
       </file>
-      <file leaf-file-name="pubspec.yaml" pinned="false" current-in-tab="false">
-        <entry file="file://$PROJECT_DIR$/pubspec.yaml">
+      <file leaf-file-name="flutter_datetime_picker.dart" pinned="false" current-in-tab="true">
+        <entry file="file://$PROJECT_DIR$/lib/flutter_datetime_picker.dart">
           <provider selected="true" editor-type-id="text-editor">
-            <state relative-caret-position="264">
-              <caret line="12" column="30" selection-start-line="12" selection-start-column="30" selection-end-line="12" selection-end-column="30" />
+            <state relative-caret-position="220">
+              <caret line="10" column="59" selection-start-line="10" selection-start-column="59" selection-end-line="10" selection-end-column="59" />
             </state>
           </provider>
         </entry>
       </file>
-      <file leaf-file-name="i18n_model.dart" pinned="false" current-in-tab="false">
-        <entry file="file://$PROJECT_DIR$/lib/src/i18n_model.dart">
+      <file leaf-file-name="pubspec.yaml" pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/pubspec.yaml">
           <provider selected="true" editor-type-id="text-editor">
-            <state relative-caret-position="819">
-              <caret line="52" column="9" selection-start-line="52" selection-start-column="9" selection-end-line="52" selection-end-column="9" />
+            <state relative-caret-position="748">
+              <caret line="34" selection-start-line="34" selection-end-line="34" />
             </state>
           </provider>
         </entry>
@@ -100,24 +75,10 @@
           </provider>
         </entry>
       </file>
-      <file leaf-file-name="pubspec.lock" pinned="false" current-in-tab="false">
-        <entry file="file://$PROJECT_DIR$/pubspec.lock">
-          <provider selected="true" editor-type-id="text-editor">
-            <state />
-          </provider>
-        </entry>
-      </file>
-      <file leaf-file-name="pubspec.lock" pinned="false" current-in-tab="false">
-        <entry file="file://$PROJECT_DIR$/example/pubspec.lock">
-          <provider selected="true" editor-type-id="text-editor">
-            <state />
-          </provider>
-        </entry>
-      </file>
       <file leaf-file-name="pubspec.yaml" pinned="false" current-in-tab="false">
         <entry file="file://$PROJECT_DIR$/example/pubspec.yaml">
           <provider selected="true" editor-type-id="text-editor">
-            <state relative-caret-position="682">
+            <state relative-caret-position="22">
               <caret line="31" selection-start-line="31" selection-end-line="31" />
             </state>
           </provider>
@@ -152,6 +113,8 @@
       <find>daysOfTheMonth</find>
       <find>min</find>
       <find>DateUtil</find>
+      <find>done</find>
+      <find>intranet_app/plugins/</find>
     </findStrings>
     <replaceStrings>
       <replace>locale == LocaleType.nl</replace>
@@ -180,9 +143,9 @@
         <option value="$PROJECT_DIR$/README.md" />
         <option value="$PROJECT_DIR$/CHANGELOG.md" />
         <option value="$PROJECT_DIR$/.gitignore" />
-        <option value="$PROJECT_DIR$/lib/flutter_datetime_picker.dart" />
         <option value="$PROJECT_DIR$/pubspec.yaml" />
         <option value="$PROJECT_DIR$/lib/src/date_model.dart" />
+        <option value="$PROJECT_DIR$/lib/flutter_datetime_picker.dart" />
       </list>
     </option>
   </component>
@@ -207,11 +170,6 @@
               <item name="flutter_datetime_picker" type="b2602c69:ProjectViewProjectNode" />
               <item name="flutter_datetime_picker" type="462c0819:PsiDirectoryNode" />
             </path>
-            <path>
-              <item name="flutter_datetime_picker" type="b2602c69:ProjectViewProjectNode" />
-              <item name="flutter_datetime_picker" type="462c0819:PsiDirectoryNode" />
-              <item name="example" type="462c0819:PsiDirectoryNode" />
-            </path>
             <path>
               <item name="flutter_datetime_picker" type="b2602c69:ProjectViewProjectNode" />
               <item name="flutter_datetime_picker" type="462c0819:PsiDirectoryNode" />
@@ -223,11 +181,6 @@
               <item name="lib" type="462c0819:PsiDirectoryNode" />
               <item name="src" type="462c0819:PsiDirectoryNode" />
             </path>
-            <path>
-              <item name="flutter_datetime_picker" type="b2602c69:ProjectViewProjectNode" />
-              <item name="flutter_datetime_picker" type="462c0819:PsiDirectoryNode" />
-              <item name="test" type="462c0819:PsiDirectoryNode" />
-            </path>
           </expand>
           <select />
         </subPane>
@@ -246,15 +199,15 @@
     <property name="show.migrate.to.gradle.popup" value="false" />
   </component>
   <component name="RecentsManager">
-    <key name="CopyFile.RECENT_KEYS">
-      <recent name="D:\study\flutter\flutter_datetime_picker\lib" />
-    </key>
     <key name="MoveFile.RECENT_KEYS">
       <recent name="$PROJECT_DIR$" />
       <recent name="$PROJECT_DIR$/screenshot" />
       <recent name="$PROJECT_DIR$/lib/src" />
       <recent name="$PROJECT_DIR$/lib" />
     </key>
+    <key name="CopyFile.RECENT_KEYS">
+      <recent name="D:\study\flutter\flutter_datetime_picker\lib" />
+    </key>
   </component>
   <component name="RunDashboard">
     <option name="ruleStates">
@@ -462,7 +415,14 @@
       <option name="project" value="LOCAL" />
       <updated>1539570700389</updated>
     </task>
-    <option name="localTasksCounter" value="18" />
+    <task id="LOCAL-00018" summary="增加日期最大最小值的控制">
+      <created>1545615386807</created>
+      <option name="number" value="00018" />
+      <option name="presentableId" value="LOCAL-00018" />
+      <option name="project" value="LOCAL" />
+      <updated>1545615386807</updated>
+    </task>
+    <option name="localTasksCounter" value="19" />
     <servers />
   </component>
   <component name="ToolWindowManager">
@@ -511,7 +471,8 @@
     <MESSAGE value="1.0.2 change version" />
     <MESSAGE value="update i18n logic" />
     <MESSAGE value="add Dutch in demo" />
-    <option name="LAST_COMMIT_MESSAGE" value="add Dutch in demo" />
+    <MESSAGE value="增加日期最大最小值的控制" />
+    <option name="LAST_COMMIT_MESSAGE" value="增加日期最大最小值的控制" />
   </component>
   <component name="XDebuggerManager">
     <breakpoint-manager>
@@ -567,6 +528,9 @@
       <provider selected="true" editor-type-id="text-editor">
         <state relative-caret-position="4959">
           <caret line="262" selection-start-line="262" selection-end-line="276" selection-end-column="3" />
+          <folding>
+            <element signature="e#34#74#0" expanded="true" />
+          </folding>
         </state>
       </provider>
     </entry>
@@ -636,13 +600,6 @@
     </entry>
     <entry file="file://$USER_HOME$/flutter/packages/flutter/lib/src/cupertino/picker.dart" />
     <entry file="file://$USER_HOME$/flutter/packages/flutter/lib/src/widgets/list_wheel_scroll_view.dart" />
-    <entry file="file://$PROJECT_DIR$/lib/src/date_format.dart">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="6050">
-          <caret line="275" column="61" selection-start-line="275" selection-start-column="61" selection-end-line="275" selection-end-column="61" />
-        </state>
-      </provider>
-    </entry>
     <entry file="file://$USER_HOME$/flutter/bin/cache/pkg/sky_engine/lib/core/date_time.dart" />
     <entry file="file://$USER_HOME$/flutter/packages/flutter/lib/src/rendering/object.dart" />
     <entry file="file://$PROJECT_DIR$/CHANGELOG.md">
@@ -669,13 +626,6 @@
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/lib/flutter_datetime_picker.dart">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="154">
-          <caret line="7" column="61" selection-start-line="7" selection-start-column="61" selection-end-line="8" selection-end-column="61" />
-        </state>
-      </provider>
-    </entry>
     <entry file="file://$PROJECT_DIR$/test/flutter_datetime_picker_test.dart">
       <provider selected="true" editor-type-id="text-editor">
         <state relative-caret-position="132">
@@ -683,18 +633,6 @@
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/example/pubspec.lock">
-      <provider selected="true" editor-type-id="text-editor">
-        <state />
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/example/pubspec.yaml">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="682">
-          <caret line="31" selection-start-line="31" selection-end-line="31" />
-        </state>
-      </provider>
-    </entry>
     <entry file="file://C:/Java/flutter/bin/cache/pkg/sky_engine/lib/core/duration.dart">
       <provider selected="true" editor-type-id="text-editor">
         <state relative-caret-position="2002">
@@ -717,39 +655,62 @@
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/LICENSE">
+    <entry file="file://$PROJECT_DIR$/lib/src/date_format.dart">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="440">
-          <caret line="20" column="9" selection-start-line="20" selection-start-column="9" selection-end-line="20" selection-end-column="9" />
+        <state relative-caret-position="159">
+          <caret line="275" column="61" selection-start-line="275" selection-start-column="61" selection-end-line="275" selection-end-column="61" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lib/src/i18n_model.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="-567">
+          <caret line="52" column="9" selection-start-line="52" selection-start-column="9" selection-end-line="52" selection-end-column="9" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/example/pubspec.yaml">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="22">
+          <caret line="31" selection-start-line="31" selection-end-line="31" />
         </state>
       </provider>
     </entry>
+    <entry file="file://$PROJECT_DIR$/example/pubspec.lock">
+      <provider selected="true" editor-type-id="text-editor">
+        <state />
+      </provider>
+    </entry>
     <entry file="file://$PROJECT_DIR$/pubspec.lock">
       <provider selected="true" editor-type-id="text-editor">
         <state />
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/pubspec.yaml">
+    <entry file="file://$PROJECT_DIR$/LICENSE">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="264">
-          <caret line="12" column="30" selection-start-line="12" selection-start-column="30" selection-end-line="12" selection-end-column="30" />
+        <state relative-caret-position="440">
+          <caret line="20" column="9" selection-start-line="20" selection-start-column="9" selection-end-line="20" selection-end-column="9" />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/lib/src/i18n_model.dart">
+    <entry file="file://$PROJECT_DIR$/pubspec.yaml">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="819">
-          <caret line="52" column="9" selection-start-line="52" selection-start-column="9" selection-end-line="52" selection-end-column="9" />
+        <state relative-caret-position="748">
+          <caret line="34" selection-start-line="34" selection-end-line="34" />
         </state>
       </provider>
     </entry>
     <entry file="file://$PROJECT_DIR$/lib/src/date_model.dart">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="66">
-          <caret line="3" lean-forward="true" selection-start-line="3" selection-end-line="3" />
-          <folding>
-            <element signature="e#0#64#0" expanded="true" />
-          </folding>
+        <state relative-caret-position="22">
+          <caret line="1" column="16" selection-start-line="1" selection-start-column="16" selection-end-line="1" selection-end-column="16" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lib/flutter_datetime_picker.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="220">
+          <caret line="10" column="59" selection-start-line="10" selection-start-column="59" selection-end-line="10" selection-end-column="59" />
         </state>
       </provider>
     </entry>

+ 161 - 0
lib/datetime_picker_theme_data.dart

@@ -0,0 +1,161 @@
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+
+class DatePickerThemeData extends Diagnosticable {
+  /// Whether null values are replaced with their value in an ancestor text
+  /// style (e.g., in a [TextSpan] tree).
+  ///
+  /// If this is false, properties that don't have explicit values will revert
+  /// to the defaults: white in color, a font size of 10 pixels, in a sans-serif
+  /// font face.
+  final bool inherit;
+
+  final String cancelText;
+  final TextStyle cancelStyle;
+  final String doneText;
+  final TextStyle doneStyle;
+  final TextStyle itemStyle;
+  final Color backgroundColor;
+  final Color barrierColor;
+
+  const DatePickerThemeData(
+      {this.inherit, this.cancelText, this.cancelStyle, this.doneText, this.doneStyle, this.itemStyle, this.backgroundColor, this.barrierColor});
+
+  /// Creates a copy of this theme data but with the given fields replaced with
+  /// the new values.
+  DatePickerThemeData copyWith({
+    String cancelText,
+    TextStyle cancelStyle,
+    String doneText,
+    TextStyle doneStyle,
+    TextStyle itemStyle,
+    Color backgroundColor,
+    Color barrierColor,
+  }) {
+    return DatePickerThemeData(
+      inherit: inherit,
+      cancelText: cancelText ?? this.cancelText,
+      cancelStyle: cancelStyle != null ? cancelStyle.merge(this.cancelStyle) : this.cancelStyle,
+      doneText: doneText ?? this.doneText,
+      doneStyle: doneStyle != null ? doneStyle.merge(this.doneStyle) : this.doneStyle,
+      itemStyle: itemStyle != null ? itemStyle.merge(this.itemStyle) : this.itemStyle,
+      backgroundColor: backgroundColor ?? this.backgroundColor,
+      barrierColor: barrierColor ?? this.barrierColor,
+    );
+  }
+
+  /// Returns a new theme data that is a combination of this style and the given
+  /// [other] style.
+  ///
+  /// If the given [other] theme data has its [DatePickerThemeData.inherit] set to true,
+  /// its null properties are replaced with the non-null properties of this text
+  /// style. The [other] style _inherits_ the properties of this style. Another
+  /// way to think of it is that the "missing" properties of the [other] style
+  /// are _filled_ by the properties of this style.
+  ///
+  /// If the given [other] theme data has its [DatePickerThemeData.inherit] set to false,
+  /// returns the given [other] style unchanged. The [other] style does not
+  /// inherit properties of this style.
+  ///
+  /// If the given theme data is null, returns this theme data.
+  DatePickerThemeData merge(DatePickerThemeData other) {
+    if (other == null) return this;
+    if (!other.inherit) return other;
+
+    return copyWith(
+      cancelText: other.cancelText,
+      cancelStyle: other.cancelStyle,
+      doneText: other.doneText,
+      doneStyle: other.doneStyle,
+      itemStyle: other.itemStyle,
+      backgroundColor: other.backgroundColor,
+      barrierColor: other.barrierColor,
+    );
+  }
+}
+
+/// The theme data to apply to descendant [DatePicker] widgets without explicit style.
+class DefaultDatePickerThemeData extends InheritedWidget {
+  /// Creates a default theme data for the given subtree.
+  ///
+  /// Consider using [DefaultDatePickerThemeData.merge] to inherit styling information
+  /// from the current default theme data for a given [BuildContext].
+  ///
+  const DefaultDatePickerThemeData({
+    Key key,
+    @required this.theme,
+    @required Widget child,
+  })  : assert(theme != null),
+        assert(child != null),
+        super(key: key, child: child);
+
+  /// A const-constructible default theme data that provides fallback values.
+  ///
+  /// Returned from [of] when the given [BuildContext] doesn't have an enclosing default theme data.
+  ///
+  /// This constructor creates a [DefaultDatePickerThemeData] that lacks a [child], which
+  /// means the constructed value cannot be incorporated into the tree.
+  const DefaultDatePickerThemeData.fallback() : theme = const DatePickerThemeData();
+
+  /// Creates a default theme data that overrides the theme datas in scope at
+  /// this point in the widget tree.
+  ///
+  /// The given [style] is merged with the [style] from the default theme data
+  /// for the [BuildContext] where the widget is inserted, and any of the other
+  /// arguments that are not null replace the corresponding properties on that
+  /// same default theme data.
+  ///
+  /// This constructor cannot be used to override the [maxLines] property of the
+  /// ancestor with the value null, since null here is used to mean "defer to
+  /// ancestor". To replace a non-null [maxLines] from an ancestor with the null
+  /// value (to remove the restriction on number of lines), manually obtain the
+  /// ambient [DefaultDatePickerThemeData] using [DefaultDatePickerThemeData.of], then create a new
+  /// [DefaultDatePickerThemeData] using the [new DefaultDatePickerThemeData] constructor directly.
+  /// See the source below for an example of how to do this (since that's
+  /// essentially what this constructor does).
+  static Widget merge({
+    Key key,
+    DatePickerThemeData theme,
+    @required Widget child,
+  }) {
+    assert(child != null);
+    return Builder(
+      builder: (BuildContext context) {
+        final DefaultDatePickerThemeData parent = DefaultDatePickerThemeData.of(context);
+        return DefaultDatePickerThemeData(
+          key: key,
+          theme: parent.theme.merge(theme),
+          child: child,
+        );
+      },
+    );
+  }
+
+  /// The theme data to apply.
+  final DatePickerThemeData theme;
+
+  /// The closest instance of this class that encloses the given context.
+  ///
+  /// If no such instance exists, returns an instance created by
+  /// [DefaultDatePickerThemeData.fallback], which contains fallback values.
+  ///
+  /// Typical usage is as follows:
+  ///
+  /// ```dart
+  /// DefaultDatePickerThemeData style = DefaultDatePickerThemeData.of(context);
+  /// ```
+  static DefaultDatePickerThemeData of(BuildContext context) {
+    return context.inheritFromWidgetOfExactType(DefaultDatePickerThemeData) ?? const DefaultDatePickerThemeData.fallback();
+  }
+
+  @override
+  bool updateShouldNotify(DefaultDatePickerThemeData oldWidget) {
+    return theme != oldWidget.theme;
+  }
+
+  @override
+  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+    super.debugFillProperties(properties);
+    theme?.debugFillProperties(properties);
+  }
+}

+ 78 - 64
lib/flutter_datetime_picker.dart

@@ -2,9 +2,11 @@ library flutter_datetime_picker;
 
 import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter_datetime_picker/datetime_picker_theme_data.dart';
 import 'package:flutter_datetime_picker/src/date_model.dart';
 import 'package:flutter_datetime_picker/src/i18n_model.dart';
 
+export 'package:flutter_datetime_picker/datetime_picker_theme_data.dart';
 export 'package:flutter_datetime_picker/src/date_model.dart';
 export 'package:flutter_datetime_picker/src/i18n_model.dart';
 
@@ -14,20 +16,22 @@ typedef String StringAtIndexCallBack(int index);
 const double _kDatePickerHeight = 210.0;
 const double _kDatePickerTitleHeight = 44.0;
 const double _kDatePickerItemHeight = 36.0;
-const double _kDatePickerFontSize = 18.0;
 
 class DatePicker {
   ///
   /// Display date picker bottom sheet.
   ///
-  static void showDatePicker(BuildContext context,
-      {bool showTitleActions: true,
-      DateTime min,
-      DateTime max,
-      DateChangedCallback onChanged,
-      DateChangedCallback onConfirm,
-      locale: LocaleType.en,
-      DateTime currentTime}) {
+  static void showDatePicker(
+    BuildContext context, {
+    bool showTitleActions: true,
+    DateTime min,
+    DateTime max,
+    DateChangedCallback onChanged,
+    DateChangedCallback onConfirm,
+    locale: LocaleType.en,
+    DateTime currentTime,
+    DatePickerThemeData theme,
+  }) {
     Navigator.push(
         context,
         new _DatePickerRoute(
@@ -35,7 +39,7 @@ class DatePicker {
             onChanged: onChanged,
             onConfirm: onConfirm,
             locale: locale,
-            theme: Theme.of(context, shadowThemeOnly: true),
+            theme: theme,
             barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
             pickerModel: DatePickerModel(currentTime: currentTime, max: max, min: min, locale: locale)));
   }
@@ -43,14 +47,17 @@ class DatePicker {
   ///
   /// Display time picker bottom sheet.
   ///
-  static void showTimePicker(BuildContext context,
-      {bool showTitleActions: true,
-      DateTime min,
-      DateTime max,
-      DateChangedCallback onChanged,
-      DateChangedCallback onConfirm,
-      locale: LocaleType.en,
-      DateTime currentTime}) {
+  static void showTimePicker(
+    BuildContext context, {
+    bool showTitleActions: true,
+    DateTime min,
+    DateTime max,
+    DateChangedCallback onChanged,
+    DateChangedCallback onConfirm,
+    locale: LocaleType.en,
+    DateTime currentTime,
+    DatePickerThemeData theme,
+  }) {
     Navigator.push(
         context,
         new _DatePickerRoute(
@@ -58,7 +65,7 @@ class DatePicker {
             onChanged: onChanged,
             onConfirm: onConfirm,
             locale: locale,
-            theme: Theme.of(context, shadowThemeOnly: true),
+            theme: theme,
             barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
             pickerModel: TimePickerModel(currentTime: currentTime, max: max, min: min, locale: locale)));
   }
@@ -66,14 +73,17 @@ class DatePicker {
   ///
   /// Display date&time picker bottom sheet.
   ///
-  static void showDateTimePicker(BuildContext context,
-      {bool showTitleActions: true,
-      DateTime min,
-      DateTime max,
-      DateChangedCallback onChanged,
-      DateChangedCallback onConfirm,
-      locale: LocaleType.en,
-      DateTime currentTime}) {
+  static void showDateTimePicker(
+    BuildContext context, {
+    bool showTitleActions: true,
+    DateTime min,
+    DateTime max,
+    DateChangedCallback onChanged,
+    DateChangedCallback onConfirm,
+    locale: LocaleType.en,
+    DateTime currentTime,
+    DatePickerThemeData theme,
+  }) {
     Navigator.push(
         context,
         new _DatePickerRoute(
@@ -81,7 +91,7 @@ class DatePicker {
             onChanged: onChanged,
             onConfirm: onConfirm,
             locale: locale,
-            theme: Theme.of(context, shadowThemeOnly: true),
+            theme: theme,
             barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
             pickerModel: DateTimePickerModel(currentTime: currentTime, locale: locale)));
   }
@@ -89,8 +99,15 @@ class DatePicker {
   ///
   /// Display date picker bottom sheet witch custom picker model.
   ///
-  static void showPicker(BuildContext context,
-      {bool showTitleActions: true, DateChangedCallback onChanged, DateChangedCallback onConfirm, locale: LocaleType.en, BasePickerModel pickerModel}) {
+  static void showPicker(
+    BuildContext context, {
+    bool showTitleActions: true,
+    DateChangedCallback onChanged,
+    DateChangedCallback onConfirm,
+    locale: LocaleType.en,
+    BasePickerModel pickerModel,
+    DatePickerThemeData theme,
+  }) {
     Navigator.push(
         context,
         new _DatePickerRoute(
@@ -98,7 +115,7 @@ class DatePicker {
             onChanged: onChanged,
             onConfirm: onConfirm,
             locale: locale,
-            theme: Theme.of(context, shadowThemeOnly: true),
+            theme: theme,
             barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
             pickerModel: pickerModel));
   }
@@ -120,7 +137,7 @@ class _DatePickerRoute<T> extends PopupRoute<T> {
   final bool showTitleActions;
   final DateChangedCallback onChanged;
   final DateChangedCallback onConfirm;
-  final ThemeData theme;
+  final DatePickerThemeData theme;
   final LocaleType locale;
   final BasePickerModel pickerModel;
 
@@ -134,7 +151,7 @@ class _DatePickerRoute<T> extends PopupRoute<T> {
   final String barrierLabel;
 
   @override
-  Color get barrierColor => Colors.black54;
+  Color get barrierColor => theme?.barrierColor ?? Colors.black54;
 
   AnimationController _animationController;
 
@@ -158,7 +175,7 @@ class _DatePickerRoute<T> extends PopupRoute<T> {
       ),
     );
     if (theme != null) {
-      bottomSheet = new Theme(data: theme, child: bottomSheet);
+      bottomSheet = new Theme(data: Theme.of(context, shadowThemeOnly: true), child: bottomSheet);
     }
     return bottomSheet;
   }
@@ -198,6 +215,10 @@ class _DatePickerState extends State<_DatePickerComponent> {
 
   @override
   Widget build(BuildContext context) {
+    DatePickerThemeData theme = DefaultDatePickerThemeData.of(context).theme;
+    if (widget.route.theme != null) {
+      theme = widget.route.theme.merge(theme);
+    }
     return new GestureDetector(
       child: new AnimatedBuilder(
         animation: widget.route.animation,
@@ -208,7 +229,7 @@ class _DatePickerState extends State<_DatePickerComponent> {
               child: new GestureDetector(
                 child: Material(
                   color: Colors.transparent,
-                  child: _renderPickerView(),
+                  child: _renderPickerView(theme),
                 ),
               ),
             ),
@@ -224,12 +245,12 @@ class _DatePickerState extends State<_DatePickerComponent> {
     }
   }
 
-  Widget _renderPickerView() {
-    Widget itemView = _renderItemView();
+  Widget _renderPickerView(DatePickerThemeData theme) {
+    Widget itemView = _renderItemView(theme);
     if (widget.route.showTitleActions) {
       return Column(
         children: <Widget>[
-          _renderTitleActionsView(),
+          _renderTitleActionsView(theme),
           itemView,
         ],
       );
@@ -237,14 +258,14 @@ class _DatePickerState extends State<_DatePickerComponent> {
     return itemView;
   }
 
-  Widget _renderColumnView(ValueKey key, StringAtIndexCallBack stringAtIndexCB, ScrollController scrollController, int layoutProportion,
-      ValueChanged<int> selectedChangedWhenScrolling, ValueChanged<int> selectedChangedWhenScrollEnd) {
+  Widget _renderColumnView(ValueKey key, DatePickerThemeData theme, StringAtIndexCallBack stringAtIndexCB, ScrollController scrollController,
+      int layoutProportion, ValueChanged<int> selectedChangedWhenScrolling, ValueChanged<int> selectedChangedWhenScrollEnd) {
     return Expanded(
       flex: layoutProportion,
       child: Container(
           padding: EdgeInsets.all(8.0),
           height: _kDatePickerHeight,
-          decoration: BoxDecoration(color: Colors.white),
+          decoration: BoxDecoration(color: theme?.backgroundColor ?? Colors.white),
           child: NotificationListener(
               onNotification: (ScrollNotification notification) {
                 if (notification.depth == 0 &&
@@ -259,7 +280,7 @@ class _DatePickerState extends State<_DatePickerComponent> {
               },
               child: CupertinoPicker.builder(
                   key: key,
-                  backgroundColor: Colors.white,
+                  backgroundColor: theme?.backgroundColor ?? Colors.white,
                   scrollController: scrollController,
                   itemExtent: _kDatePickerItemHeight,
                   onSelectedItemChanged: (int index) {
@@ -276,7 +297,7 @@ class _DatePickerState extends State<_DatePickerComponent> {
                       alignment: Alignment.center,
                       child: Text(
                         content,
-                        style: TextStyle(color: Color(0xFF000046), fontSize: _kDatePickerFontSize),
+                        style: theme?.itemStyle,
                         textAlign: TextAlign.start,
                       ),
                     );
@@ -284,15 +305,14 @@ class _DatePickerState extends State<_DatePickerComponent> {
     );
   }
 
-  Widget _renderItemView() {
+  Widget _renderItemView(DatePickerThemeData theme) {
     return Container(
-      color: Colors.white,
+      color: theme?.backgroundColor ?? Colors.white,
       child: Row(
         mainAxisAlignment: MainAxisAlignment.spaceBetween,
         children: <Widget>[
-          _renderColumnView(
-              ValueKey(widget.pickerModel.currentLeftIndex()), widget.pickerModel.leftStringAtIndex, leftScrollCtrl, widget.pickerModel.layoutProportions()[0],
-              (index) {
+          _renderColumnView(ValueKey(widget.pickerModel.currentLeftIndex()), theme, widget.pickerModel.leftStringAtIndex, leftScrollCtrl,
+              widget.pickerModel.layoutProportions()[0], (index) {
             widget.pickerModel.setLeftIndex(index);
           }, (index) {
             setState(() {
@@ -302,9 +322,9 @@ class _DatePickerState extends State<_DatePickerComponent> {
           }),
           Text(
             widget.pickerModel.leftDivider(),
-            style: TextStyle(color: Color(0xFF000046), fontSize: _kDatePickerFontSize),
+            style: theme?.itemStyle,
           ),
-          _renderColumnView(ValueKey(widget.pickerModel.currentLeftIndex()), widget.pickerModel.middleStringAtIndex, middleScrollCtrl,
+          _renderColumnView(ValueKey(widget.pickerModel.currentLeftIndex()), theme, widget.pickerModel.middleStringAtIndex, middleScrollCtrl,
               widget.pickerModel.layoutProportions()[1], (index) {
             widget.pickerModel.setMiddleIndex(index);
           }, (index) {
@@ -315,9 +335,9 @@ class _DatePickerState extends State<_DatePickerComponent> {
           }),
           Text(
             widget.pickerModel.rightDivider(),
-            style: TextStyle(color: Color(0xFF000046), fontSize: _kDatePickerFontSize),
+            style: theme?.itemStyle,
           ),
-          _renderColumnView(ValueKey(widget.pickerModel.currentMiddleIndex()), widget.pickerModel.rightStringAtIndex, rightScrollCtrl,
+          _renderColumnView(ValueKey(widget.pickerModel.currentMiddleIndex()), theme, widget.pickerModel.rightStringAtIndex, rightScrollCtrl,
               widget.pickerModel.layoutProportions()[2], (index) {
             widget.pickerModel.setRightIndex(index);
             _notifyDateChanged();
@@ -328,13 +348,13 @@ class _DatePickerState extends State<_DatePickerComponent> {
   }
 
   // Title View
-  Widget _renderTitleActionsView() {
+  Widget _renderTitleActionsView(DatePickerThemeData theme) {
     String done = _localeDone();
     String cancel = _localeCancel();
 
     return Container(
       height: _kDatePickerTitleHeight,
-      decoration: BoxDecoration(color: Colors.white),
+      decoration: BoxDecoration(color: theme?.backgroundColor ?? Colors.white),
       child: Row(
         mainAxisAlignment: MainAxisAlignment.spaceBetween,
         children: <Widget>[
@@ -342,11 +362,8 @@ class _DatePickerState extends State<_DatePickerComponent> {
             height: _kDatePickerTitleHeight,
             child: FlatButton(
               child: Text(
-                '$cancel',
-                style: TextStyle(
-                  color: Theme.of(context).unselectedWidgetColor,
-                  fontSize: 16.0,
-                ),
+                theme?.cancelText ?? '$cancel',
+                style: theme?.cancelStyle,
               ),
               onPressed: () => Navigator.pop(context),
             ),
@@ -355,11 +372,8 @@ class _DatePickerState extends State<_DatePickerComponent> {
             height: _kDatePickerTitleHeight,
             child: FlatButton(
               child: Text(
-                '$done',
-                style: TextStyle(
-                  color: Theme.of(context).accentColor,
-                  fontSize: 16.0,
-                ),
+                theme?.doneText ?? '$done',
+                style: theme?.doneStyle,
               ),
               onPressed: () {
                 if (widget.route.onConfirm != null) {

+ 12 - 32
lib/src/date_model.dart

@@ -1,6 +1,6 @@
-import 'package:flutter_commons_lang/flutter_commons_lang.dart';
 import 'package:flutter_datetime_picker/src/date_format.dart';
 import 'package:flutter_datetime_picker/src/i18n_model.dart';
+import 'package:flutter_commons_lang/flutter_commons_lang.dart';
 
 abstract class BasePickerModel {
   String leftStringAtIndex(int index);
@@ -119,21 +119,6 @@ class DatePickerModel extends CommonPickerModel {
   int maxDay;
   int minDay;
 
-  List<int> _leapYearMonths = const <int>[1, 3, 5, 7, 8, 10, 12];
-
-  int _daysOfTheMonth(DateTime time) {
-    assert(time != null);
-    if (_leapYearMonths.contains(time.month)) {
-      return 31;
-    } else if (time.month == 2) {
-      if ((time.year % 4 == 0 && time.year % 100 != 0) || time.year % 400 == 0) {
-        return 29;
-      }
-      return 28;
-    }
-    return 30;
-  }
-
   DatePickerModel({DateTime currentTime, this.max, this.min, LocaleType locale}) : super(locale: locale) {
     if (currentTime != null) {
       if (max != null && currentTime.compareTo(max) > 0) {
@@ -143,39 +128,34 @@ class DatePickerModel extends CommonPickerModel {
       }
     }
     this.currentTime = currentTime ?? DateTime.now();
-
-    maxYear = max?.year ?? 2050;
-    minYear = min?.year ?? 1970;
-    minMonth = currentTime.year == min?.year ? min?.month : 1;
-    maxMonth = currentTime.year == max?.year ? max.month : 12;
-
-    _currentLeftIndex = this.currentTime.year - minYear;
-    _currentMiddleIndex = this.currentTime.month - minMonth;
-    _currentRightIndex = this.currentTime.day - 1;
-
     fillLeftLists();
     fillMiddleLists();
     fillRightLists();
+    _currentLeftIndex = this.currentTime.year - minYear;
+    _currentMiddleIndex = this.currentTime.month - minMonth;
+    _currentRightIndex = this.currentTime.day - minDay;
   }
 
   void fillLeftLists() {
+    maxYear = max?.year ?? 2050;
+    minYear = min?.year ?? 1970;
     this.leftList = List.generate(maxYear - minYear + 1, (int index) {
       return '${minYear + index}${_localeYear()}';
     });
   }
 
   void fillMiddleLists() {
-    minMonth = currentTime.year == min?.year ? min?.month : 1;
-    maxMonth = currentTime.year == max?.year ? max.month : 12;
+    minMonth = DateUtils.truncatedEquals(currentTime, min, DateUtils.YEAR) ? min?.month : 1;
+    maxMonth = DateUtils.truncatedEquals(currentTime, max, DateUtils.YEAR) ? max.month : 12;
     this.middleList = List.generate(maxMonth - minMonth + 1, (int index) {
       return '${minMonth + index}${_localeMonth()}';
     });
   }
 
   void fillRightLists() {
-    int dayCount = _daysOfTheMonth(currentTime);
-    minDay = currentTime.year == min?.year && currentTime.month == min?.month ? min.day : 1;
-    maxDay = currentTime.year == max?.year && currentTime.month == max?.month ? max.day : dayCount;
+    int dayCount = DateUtils.daysOfTheMonth(currentTime);
+    maxDay = DateUtils.truncatedEquals(currentTime, max, DateUtils.MONTH) ? max.day : dayCount;
+    minDay = DateUtils.truncatedEquals(currentTime, min, DateUtils.MONTH) ? min.day : 1;
     this.rightList = List.generate(maxDay - minDay + 1, (int index) {
       return '${minDay + index}${_localeDay()}';
     });
@@ -462,7 +442,7 @@ class DateTimePickerModel extends CommonPickerModel {
 
   @override
   List<int> layoutProportions() {
-    return [4, 1, 1];
+    return [3, 1, 1];
   }
 
   @override