xingweizhu
4 年前
当前提交
927fd3d3
共有 4 个文件被更改,包括 814 次插入 和 0 次删除
-
416com.unity.uiwidgets/Runtime/material/pickers/date_picker_dialog.cs
-
160com.unity.uiwidgets/Runtime/material/pickers/date_picker_head.cs
-
238com.unity.uiwidgets/Runtime/material/pickers/input_date_picker.cs
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Unity.UIWidgets.animation; |
|||
using Unity.UIWidgets.async2; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.painting; |
|||
using Unity.UIWidgets.rendering; |
|||
using Unity.UIWidgets.ui; |
|||
using Unity.UIWidgets.widgets; |
|||
using UnityEngine; |
|||
using Color = Unity.UIWidgets.ui.Color; |
|||
using TextStyle = Unity.UIWidgets.painting.TextStyle; |
|||
|
|||
namespace Unity.UIWidgets.material { |
|||
public static class DatePickerDialogUtils { |
|||
public static readonly Size _calendarPortraitDialogSize = new Size(330.0f, 518.0f); |
|||
public static readonly Size _calendarLandscapeDialogSize = new Size(496.0f, 346.0f); |
|||
public static readonly Size _inputPortraitDialogSize = new Size(330.0f, 270.0f); |
|||
public static readonly Size _inputLandscapeDialogSize = new Size(496f, 160.0f); |
|||
public static readonly TimeSpan _dialogSizeAnimationDuration = new TimeSpan(0, 0, 0, 0, 200); |
|||
|
|||
public Future<DateTime> showDatePicker( |
|||
BuildContext context, |
|||
DateTime initialDate, |
|||
DateTime firstDate, |
|||
DateTime lastDate, |
|||
DatePickerEntryMode initialEntryMode = DatePickerEntryMode.calendar, |
|||
SelectableDayPredicate selectableDayPredicate = null, |
|||
string helpText = null, |
|||
string cancelText = null, |
|||
string confirmText = null, |
|||
Locale locale = null, |
|||
bool useRootNavigator = true, |
|||
RouteSettings routeSettings = null, |
|||
TextDirection? textDirection = null, |
|||
TransitionBuilder builder = null, |
|||
DatePickerMode initialDatePickerMode = DatePickerMode.day, |
|||
string errorFormatText = null, |
|||
string errorInvalidText = null, |
|||
string fieldHintText = null, |
|||
string fieldLabelText = null |
|||
) { |
|||
D.assert(context != null); |
|||
initialDate = utils.dateOnly(initialDate); |
|||
firstDate = utils.dateOnly(firstDate); |
|||
lastDate = utils.dateOnly(lastDate); |
|||
|
|||
D.assert( |
|||
!lastDate.isBefore(firstDate), |
|||
() => $"lastDate {lastDate} must be on or after firstDate {firstDate}." |
|||
); |
|||
D.assert( |
|||
!initialDate.isBefore(firstDate), |
|||
() => $"initialDate {initialDate} must be on or after firstDate {firstDate}." |
|||
); |
|||
D.assert( |
|||
!initialDate.isAfter(lastDate), |
|||
() => $"initialDate {initialDate} must be on or before lastDate {lastDate}." |
|||
); |
|||
D.assert( |
|||
selectableDayPredicate == null || selectableDayPredicate(initialDate), |
|||
() => $"Provided initialDate {initialDate} must satisfy provided selectableDayPredicate." |
|||
); |
|||
|
|||
D.assert(initialEntryMode != null); |
|||
D.assert(initialDatePickerMode != null); |
|||
D.assert(material_.debugCheckHasMaterialLocalizations(context)); |
|||
|
|||
Widget dialog = new _DatePickerDialog( |
|||
initialDate: initialDate, |
|||
firstDate: firstDate, |
|||
lastDate: lastDate, |
|||
initialEntryMode: initialEntryMode, |
|||
selectableDayPredicate: selectableDayPredicate, |
|||
helpText: helpText, |
|||
cancelText: cancelText, |
|||
confirmText: confirmText, |
|||
initialCalendarMode: initialDatePickerMode, |
|||
errorFormatText: errorFormatText, |
|||
errorInvalidText: errorInvalidText, |
|||
fieldHintText: fieldHintText, |
|||
fieldLabelText: fieldLabelText |
|||
); |
|||
|
|||
if (textDirection != null) { |
|||
dialog = new Directionality( |
|||
textDirection: textDirection.Value, |
|||
child: dialog |
|||
); |
|||
} |
|||
|
|||
if (locale != null) { |
|||
dialog = Localizations.overrides( |
|||
context: context, |
|||
locale: locale, |
|||
child: dialog |
|||
); |
|||
} |
|||
|
|||
return material_.showDialog<DateTime>( |
|||
context: context, |
|||
useRootNavigator: useRootNavigator, |
|||
routeSettings: routeSettings, |
|||
builder: (BuildContext subContext) => { return builder == null ? dialog : builder(subContext, dialog); } |
|||
); |
|||
} |
|||
} |
|||
|
|||
class _DatePickerDialog : StatefulWidget { |
|||
public _DatePickerDialog( |
|||
Key key = null, |
|||
DateTime? initialDate = null, |
|||
DateTime? firstDate = null, |
|||
DateTime? lastDate = null, |
|||
DatePickerEntryMode initialEntryMode = DatePickerEntryMode.calendar, |
|||
SelectableDayPredicate selectableDayPredicate = null, |
|||
string cancelText = null, |
|||
string confirmText = null, |
|||
string helpText = null, |
|||
DatePickerMode initialCalendarMode = DatePickerMode.day, |
|||
string errorFormatText = null, |
|||
string errorInvalidText = null, |
|||
string fieldHintText = null, |
|||
string fieldLabelText = null |
|||
) : base(key: key) { |
|||
D.assert(initialDate != null); |
|||
D.assert(firstDate != null); |
|||
D.assert(lastDate != null); |
|||
|
|||
initialDate = utils.dateOnly(initialDate); |
|||
firstDate = utils.dateOnly(firstDate); |
|||
lastDate = utils.dateOnly(lastDate); |
|||
|
|||
D.assert( |
|||
!lastDate.Value.isBefore(firstDate.Value), |
|||
() => $"lastDate {lastDate} must be on or after firstDate {firstDate}." |
|||
); |
|||
D.assert( |
|||
initialDate == null || !initialDate.Value.isBefore(firstDate.Value), |
|||
() => $"initialDate {initialDate} must be on or after firstDate {firstDate}." |
|||
); |
|||
D.assert( |
|||
initialDate == null || !initialDate.Value.isAfter(lastDate.Value), |
|||
() => $"initialDate {initialDate} must be on or before lastDate {lastDate}." |
|||
); |
|||
D.assert( |
|||
selectableDayPredicate == null || initialDate == null || selectableDayPredicate(initialDate), |
|||
() => $"Provided initialDate {initialDate} must satisfy provided selectableDayPredicate." |
|||
); |
|||
|
|||
this.initialDate = initialDate.Value; |
|||
this.firstDate = firstDate.Value; |
|||
this.lastDate = lastDate.Value; |
|||
this.initialEntryMode = initialEntryMode; |
|||
this.selectableDayPredicate = selectableDayPredicate; |
|||
this.cancelText = cancelText; |
|||
this.confirmText = confirmText; |
|||
this.helpText = helpText; |
|||
this.initialCalendarMode = initialCalendarMode; |
|||
this.errorFormatText = errorFormatText; |
|||
this.errorInvalidText = errorInvalidText; |
|||
this.fieldHintText = fieldHintText; |
|||
this.fieldLabelText = fieldLabelText; |
|||
} |
|||
|
|||
public readonly DateTime initialDate; |
|||
|
|||
public readonly DateTime firstDate; |
|||
|
|||
public readonly DateTime lastDate; |
|||
|
|||
public readonly DatePickerEntryMode initialEntryMode; |
|||
|
|||
public readonly SelectableDayPredicate selectableDayPredicate; |
|||
|
|||
public readonly string cancelText; |
|||
|
|||
public readonly string confirmText; |
|||
|
|||
public readonly string helpText; |
|||
|
|||
public readonly DatePickerMode initialCalendarMode; |
|||
|
|||
public readonly string errorFormatText; |
|||
|
|||
public readonly string errorInvalidText; |
|||
|
|||
public readonly string fieldHintText; |
|||
|
|||
public readonly string fieldLabelText; |
|||
|
|||
public override State createState() { |
|||
return new _DatePickerDialogState(); |
|||
} |
|||
} |
|||
|
|||
class _DatePickerDialogState : State<_DatePickerDialog> { |
|||
DatePickerEntryMode _entryMode; |
|||
DateTime _selectedDate; |
|||
bool _autoValidate; |
|||
public readonly GlobalKey _calendarPickerKey = GlobalKey.key(); |
|||
public readonly GlobalKey<FormState> _formKey = GlobalKey<FormState>.key(); |
|||
|
|||
public override void initState() { |
|||
base.initState(); |
|||
_entryMode = widget.initialEntryMode; |
|||
_selectedDate = widget.initialDate; |
|||
_autoValidate = false; |
|||
} |
|||
|
|||
void _handleOk() { |
|||
if (_entryMode == DatePickerEntryMode.input) { |
|||
FormState form = _formKey.currentState; |
|||
if (!form.validate()) { |
|||
setState(() => _autoValidate = true); |
|||
return; |
|||
} |
|||
|
|||
form.save(); |
|||
} |
|||
|
|||
Navigator.pop(context, _selectedDate); |
|||
} |
|||
|
|||
void _handleCancel() { |
|||
Navigator.pop<object>(context); |
|||
} |
|||
|
|||
void _handelEntryModeToggle() { |
|||
setState(() => { |
|||
switch (_entryMode) { |
|||
case DatePickerEntryMode.calendar: |
|||
_autoValidate = false; |
|||
_entryMode = DatePickerEntryMode.input; |
|||
break; |
|||
case DatePickerEntryMode.input: |
|||
_formKey.currentState.save(); |
|||
_entryMode = DatePickerEntryMode.calendar; |
|||
break; |
|||
} |
|||
}); |
|||
} |
|||
|
|||
void _handleDateChanged(DateTime date) { |
|||
setState(() => _selectedDate = date); |
|||
} |
|||
|
|||
Size _dialogSize(BuildContext context) { |
|||
Orientation? orientation = MediaQuery.of(context).orientation; |
|||
switch (_entryMode) { |
|||
case DatePickerEntryMode.calendar: |
|||
switch (orientation) { |
|||
case Orientation.portrait: |
|||
return DatePickerDialogUtils._calendarPortraitDialogSize; |
|||
case Orientation.landscape: |
|||
return DatePickerDialogUtils._calendarLandscapeDialogSize; |
|||
} |
|||
|
|||
break; |
|||
case DatePickerEntryMode.input: |
|||
switch (orientation) { |
|||
case Orientation.portrait: |
|||
return DatePickerDialogUtils._inputPortraitDialogSize; |
|||
case Orientation.landscape: |
|||
return DatePickerDialogUtils._inputLandscapeDialogSize; |
|||
} |
|||
|
|||
break; |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
ThemeData theme = Theme.of(context); |
|||
ColorScheme colorScheme = theme.colorScheme; |
|||
MaterialLocalizations localizations = MaterialLocalizations.of(context); |
|||
Orientation? orientation = MediaQuery.of(context).orientation; |
|||
TextTheme textTheme = theme.textTheme; |
|||
|
|||
float textScaleFactor = Mathf.Min(MediaQuery.of(context).textScaleFactor, 1.3f); |
|||
|
|||
string dateText = _selectedDate != null |
|||
? localizations.formatMediumDate(_selectedDate) |
|||
: "Date"; |
|||
Color dateColor = colorScheme.brightness == Brightness.light |
|||
? colorScheme.onPrimary |
|||
: colorScheme.onSurface; |
|||
TextStyle dateStyle = orientation == Orientation.landscape |
|||
? textTheme.headline5?.copyWith(color: dateColor) |
|||
: textTheme.headline4?.copyWith(color: dateColor); |
|||
|
|||
Widget actions = new ButtonBar( |
|||
buttonTextTheme: ButtonTextTheme.primary, |
|||
layoutBehavior: ButtonBarLayoutBehavior.constrained, |
|||
children: new List<Widget> { |
|||
new FlatButton( |
|||
child: new Text(widget.cancelText ?? localizations.cancelButtonLabel), |
|||
onPressed: _handleCancel |
|||
), |
|||
new FlatButton( |
|||
child: new Text(widget.confirmText ?? localizations.okButtonLabel), |
|||
onPressed: _handleOk |
|||
), |
|||
} |
|||
); |
|||
|
|||
Widget picker = null; |
|||
IconData entryModeIcon = null; |
|||
string entryModeTooltip = null; |
|||
switch (_entryMode) { |
|||
case DatePickerEntryMode.calendar: |
|||
picker = new CalendarDatePicker( |
|||
key: _calendarPickerKey, |
|||
initialDate: _selectedDate, |
|||
firstDate: widget.firstDate, |
|||
lastDate: widget.lastDate, |
|||
onDateChanged: _handleDateChanged, |
|||
selectableDayPredicate: widget.selectableDayPredicate, |
|||
initialCalendarMode: widget.initialCalendarMode |
|||
); |
|||
entryModeIcon = Icons.edit; |
|||
entryModeTooltip = "Switch to input"; |
|||
break; |
|||
|
|||
case DatePickerEntryMode.input: |
|||
picker = new Form( |
|||
key: _formKey, |
|||
autovalidate: _autoValidate, |
|||
child: new InputDatePickerFormField( |
|||
initialDate: _selectedDate, |
|||
firstDate: widget.firstDate, |
|||
lastDate: widget.lastDate, |
|||
onDateSubmitted: _handleDateChanged, |
|||
onDateSaved: _handleDateChanged, |
|||
selectableDayPredicate: widget.selectableDayPredicate, |
|||
errorFormatText: widget.errorFormatText, |
|||
errorInvalidText: widget.errorInvalidText, |
|||
fieldHintText: widget.fieldHintText, |
|||
fieldLabelText: widget.fieldLabelText, |
|||
autofocus: true |
|||
) |
|||
); |
|||
entryModeIcon = Icons.calendar_today; |
|||
entryModeTooltip = "Switch to calendar"; |
|||
break; |
|||
} |
|||
|
|||
Widget header = new DatePickerHeader( |
|||
helpText: widget.helpText ?? "SELECT DATE", |
|||
titleText: dateText, |
|||
titleStyle: dateStyle, |
|||
orientation: orientation, |
|||
isShort: orientation == Orientation.landscape, |
|||
icon: entryModeIcon, |
|||
iconTooltip: entryModeTooltip, |
|||
onIconPressed: _handelEntryModeToggle |
|||
); |
|||
|
|||
Size dialogSize = _dialogSize(context) * textScaleFactor; |
|||
DialogTheme dialogTheme = Theme.of(context).dialogTheme; |
|||
return new Dialog( |
|||
child: new AnimatedContainer( |
|||
width: dialogSize.width, |
|||
height: dialogSize.height, |
|||
duration: DatePickerDialogUtils._dialogSizeAnimationDuration, |
|||
curve: Curves.easeIn, |
|||
child: new MediaQuery( |
|||
data: MediaQuery.of(context).copyWith( |
|||
textScaleFactor: textScaleFactor |
|||
), |
|||
child: new Builder(builder: (BuildContext subContext) => { |
|||
switch (orientation) { |
|||
case Orientation.portrait: |
|||
return new Column( |
|||
mainAxisSize: MainAxisSize.min, |
|||
crossAxisAlignment: CrossAxisAlignment.stretch, |
|||
children: new List<Widget> { |
|||
header, |
|||
new Expanded(child: picker), |
|||
actions, |
|||
} |
|||
); |
|||
case Orientation.landscape: |
|||
return new Row( |
|||
mainAxisSize: MainAxisSize.min, |
|||
crossAxisAlignment: CrossAxisAlignment.stretch, |
|||
children: new List<Widget> { |
|||
header, |
|||
new Flexible( |
|||
child: new Column( |
|||
mainAxisSize: MainAxisSize.min, |
|||
crossAxisAlignment: CrossAxisAlignment.stretch, |
|||
children: new List<Widget> { |
|||
new Expanded(child: picker), |
|||
actions, |
|||
} |
|||
) |
|||
), |
|||
} |
|||
); |
|||
} |
|||
|
|||
return null; |
|||
}) |
|||
) |
|||
), |
|||
insetPadding: EdgeInsets.symmetric(horizontal: 16.0f, vertical: 24.0f), |
|||
shape: dialogTheme.shape ?? new RoundedRectangleBorder( |
|||
borderRadius: BorderRadius.all(Radius.circular(4.0f)) |
|||
), |
|||
clipBehavior: Clip.antiAlias |
|||
); |
|||
} |
|||
} |
|||
} |
|
|||
using System.Collections.Generic; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.painting; |
|||
using Unity.UIWidgets.rendering; |
|||
using Unity.UIWidgets.ui; |
|||
using Unity.UIWidgets.widgets; |
|||
using TextStyle = Unity.UIWidgets.painting.TextStyle; |
|||
|
|||
namespace Unity.UIWidgets.material { |
|||
public static class DatePickerHeaderUtils { |
|||
public const float _datePickerHeaderLandscapeWidth = 152.0f; |
|||
public const float _datePickerHeaderPortraitHeight = 120.0f; |
|||
public const float _headerPaddingLandscape = 16.0f; |
|||
} |
|||
|
|||
class DatePickerHeader : StatelessWidget { |
|||
public DatePickerHeader( |
|||
Key key = null, |
|||
string helpText = null, |
|||
string titleText = null, |
|||
TextStyle titleStyle = null, |
|||
Orientation? orientation = null, |
|||
bool isShort = false, |
|||
IconData icon = null, |
|||
string iconTooltip = null, |
|||
VoidCallback onIconPressed = null |
|||
) : base(key: key) { |
|||
D.assert(helpText != null); |
|||
D.assert(orientation != null); |
|||
this.helpText = helpText; |
|||
this.titleText = titleText; |
|||
this.titleStyle = titleStyle; |
|||
this.orientation = orientation.Value; |
|||
this.isShort = isShort; |
|||
this.icon = icon; |
|||
this.iconTooltip = iconTooltip; |
|||
this.onIconPressed = onIconPressed; |
|||
} |
|||
|
|||
public readonly string helpText; |
|||
|
|||
public readonly string titleText; |
|||
|
|||
public readonly TextStyle titleStyle; |
|||
|
|||
public readonly Orientation orientation; |
|||
|
|||
public readonly bool isShort; |
|||
|
|||
public readonly IconData icon; |
|||
|
|||
public readonly string iconTooltip; |
|||
|
|||
public readonly VoidCallback onIconPressed; |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
ThemeData theme = Theme.of(context); |
|||
ColorScheme colorScheme = theme.colorScheme; |
|||
TextTheme textTheme = theme.textTheme; |
|||
|
|||
bool isDark = colorScheme.brightness == Brightness.dark; |
|||
Color primarySurfaceColor = isDark ? colorScheme.surface : colorScheme.primary; |
|||
Color onPrimarySurfaceColor = isDark ? colorScheme.onSurface : colorScheme.onPrimary; |
|||
|
|||
TextStyle helpStyle = textTheme.overline?.copyWith( |
|||
color: onPrimarySurfaceColor |
|||
); |
|||
|
|||
Text help = new Text( |
|||
helpText, |
|||
style: helpStyle, |
|||
maxLines: 1, |
|||
overflow: TextOverflow.ellipsis |
|||
); |
|||
|
|||
Text title = new Text( |
|||
titleText, |
|||
style: titleStyle, |
|||
maxLines: (isShort || orientation == Orientation.portrait) ? 1 : 2, |
|||
overflow: TextOverflow.ellipsis |
|||
); |
|||
IconButton icon = new IconButton( |
|||
icon: new Icon(this.icon), |
|||
color: onPrimarySurfaceColor, |
|||
tooltip: iconTooltip, |
|||
onPressed: onIconPressed |
|||
); |
|||
|
|||
switch (orientation) { |
|||
case Orientation.portrait: |
|||
return new Column( |
|||
crossAxisAlignment: CrossAxisAlignment.start, |
|||
children: new List<Widget> { |
|||
new Container( |
|||
height: DatePickerHeaderUtils._datePickerHeaderPortraitHeight, |
|||
color: primarySurfaceColor, |
|||
//FixMe: uncomment this after EdgeInsetsGeometry is ready for use
|
|||
/*padding: EdgeInsetsDirectional.only( |
|||
start: 24f, |
|||
end: 12f |
|||
),*/ |
|||
child: new Column( |
|||
crossAxisAlignment: CrossAxisAlignment.start, |
|||
children: new List<Widget> { |
|||
new SizedBox(height: 16f), |
|||
new Flexible(child: help), |
|||
new SizedBox(height: 38), |
|||
new Row( |
|||
children: new List<Widget> { |
|||
new Expanded(child: title), |
|||
icon, |
|||
} |
|||
), |
|||
} |
|||
) |
|||
) |
|||
} |
|||
); |
|||
case Orientation.landscape: |
|||
return new Row( |
|||
crossAxisAlignment: CrossAxisAlignment.start, |
|||
children: new List<Widget> { |
|||
new Container( |
|||
width: DatePickerHeaderUtils._datePickerHeaderLandscapeWidth, |
|||
color: primarySurfaceColor, |
|||
child: new Column( |
|||
crossAxisAlignment: CrossAxisAlignment.start, |
|||
children: new List<Widget> { |
|||
new SizedBox(height: 16), |
|||
new Padding( |
|||
padding: EdgeInsets.symmetric( |
|||
horizontal: DatePickerHeaderUtils._headerPaddingLandscape |
|||
), |
|||
child: help |
|||
), |
|||
new SizedBox(height: isShort ? 16 : 56), |
|||
new Padding( |
|||
padding: EdgeInsets.symmetric( |
|||
horizontal: DatePickerHeaderUtils._headerPaddingLandscape |
|||
), |
|||
child: title |
|||
), |
|||
new Spacer(), |
|||
new Padding( |
|||
padding: EdgeInsets.symmetric( |
|||
horizontal: 4 |
|||
), |
|||
child: icon |
|||
), |
|||
} |
|||
) |
|||
), |
|||
} |
|||
); |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
} |
|||
} |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text.RegularExpressions; |
|||
using UIWidgets.Runtime.material; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.painting; |
|||
using Unity.UIWidgets.service; |
|||
using Unity.UIWidgets.widgets; |
|||
|
|||
namespace Unity.UIWidgets.material { |
|||
public static class InputDatePickerUtils { |
|||
public const float _inputPortraitHeight = 98.0f; |
|||
public const float _inputLandscapeHeight = 108.0f; |
|||
|
|||
public static bool isBefore(this DateTime t1, DateTime t2) { |
|||
return t1.CompareTo(t2) < 0; |
|||
} |
|||
|
|||
public static bool isAfter(this DateTime t1, DateTime t2) { |
|||
return t1.CompareTo(t2) > 0; |
|||
} |
|||
} |
|||
|
|||
class InputDatePickerFormField : StatefulWidget { |
|||
public InputDatePickerFormField( |
|||
Key key = null, |
|||
DateTime? initialDate = null, |
|||
DateTime? firstDate = null, |
|||
DateTime? lastDate = null, |
|||
ValueChanged<DateTime> onDateSubmitted = null, |
|||
ValueChanged<DateTime> onDateSaved = null, |
|||
SelectableDayPredicate selectableDayPredicate = null, |
|||
string errorFormatText = null, |
|||
string errorInvalidText = null, |
|||
string fieldHintText = null, |
|||
string fieldLabelText = null, |
|||
bool autofocus = false |
|||
) : base(key: key) { |
|||
D.assert(firstDate != null); |
|||
D.assert(lastDate != null); |
|||
|
|||
initialDate = initialDate != null ? utils.dateOnly(initialDate) : null; |
|||
firstDate = utils.dateOnly(firstDate); |
|||
lastDate = utils.dateOnly(lastDate); |
|||
|
|||
D.assert( |
|||
!lastDate.Value.isBefore(firstDate.Value), |
|||
() => $"lastDate {lastDate} must be on or after firstDate {firstDate}." |
|||
); |
|||
D.assert( |
|||
initialDate == null || !initialDate.Value.isBefore(firstDate.Value), |
|||
() => $"initialDate {initialDate} must be on or after firstDate {firstDate}." |
|||
); |
|||
D.assert( |
|||
initialDate == null || !initialDate.Value.isAfter(lastDate.Value), |
|||
() => $"initialDate {initialDate} must be on or before lastDate {lastDate}." |
|||
); |
|||
D.assert( |
|||
selectableDayPredicate == null || initialDate == null || selectableDayPredicate(initialDate), |
|||
() => $"Provided initialDate {initialDate} must satisfy provided selectableDayPredicate." |
|||
); |
|||
|
|||
this.initialDate = initialDate; |
|||
this.firstDate = firstDate; |
|||
this.lastDate = lastDate; |
|||
this.onDateSubmitted = onDateSubmitted; |
|||
this.onDateSaved = onDateSaved; |
|||
this.selectableDayPredicate = selectableDayPredicate; |
|||
this.errorFormatText = errorFormatText; |
|||
this.errorInvalidText = errorInvalidText; |
|||
this.fieldHintText = fieldHintText; |
|||
this.fieldLabelText = fieldLabelText; |
|||
this.autofocus = autofocus; |
|||
} |
|||
|
|||
public readonly DateTime? initialDate; |
|||
|
|||
public readonly DateTime firstDate; |
|||
|
|||
public readonly DateTime lastDate; |
|||
|
|||
public readonly ValueChanged<DateTime> onDateSubmitted; |
|||
|
|||
public readonly ValueChanged<DateTime> onDateSaved; |
|||
|
|||
public readonly SelectableDayPredicate selectableDayPredicate; |
|||
|
|||
public readonly string errorFormatText; |
|||
|
|||
public readonly string errorInvalidText; |
|||
|
|||
public readonly string fieldHintText; |
|||
|
|||
public readonly string fieldLabelText; |
|||
|
|||
public readonly bool autofocus; |
|||
|
|||
public override State createState() { |
|||
return new _InputDatePickerFormFieldState(); |
|||
} |
|||
} |
|||
|
|||
class _InputDatePickerFormFieldState : State<InputDatePickerFormField> { |
|||
public readonly TextEditingController _controller = new TextEditingController(); |
|||
DateTime? _selectedDate; |
|||
string _inputText; |
|||
bool _autoSelected = false; |
|||
|
|||
public override void initState() { |
|||
base.initState(); |
|||
_selectedDate = widget.initialDate; |
|||
} |
|||
|
|||
public override void dispose() { |
|||
_controller.dispose(); |
|||
base.dispose(); |
|||
} |
|||
|
|||
public override void didChangeDependencies() { |
|||
base.didChangeDependencies(); |
|||
if (_selectedDate != null) { |
|||
MaterialLocalizations localizations = MaterialLocalizations.of(context); |
|||
_inputText = localizations.formatCompactDate(_selectedDate.Value); |
|||
TextEditingValue textEditingValue = _controller.value.copyWith(text: _inputText); |
|||
|
|||
if (widget.autofocus && !_autoSelected) { |
|||
textEditingValue = textEditingValue.copyWith(selection: new TextSelection( |
|||
baseOffset: 0, |
|||
extentOffset: _inputText.Length |
|||
)); |
|||
_autoSelected = true; |
|||
} |
|||
|
|||
_controller.value = textEditingValue; |
|||
} |
|||
} |
|||
|
|||
DateTime? _parseDate(string text) { |
|||
MaterialLocalizations localizations = MaterialLocalizations.of(context); |
|||
return localizations.parseCompactDate(text); |
|||
} |
|||
|
|||
bool _isValidAcceptableDate(DateTime? date) { |
|||
return |
|||
date != null && |
|||
!date.Value.isBefore(widget.firstDate) && |
|||
!date.Value.isAfter(widget.lastDate) && |
|||
(widget.selectableDayPredicate == null || widget.selectableDayPredicate(date)); |
|||
} |
|||
|
|||
string _validateDate(string text) { |
|||
DateTime? date = _parseDate(text); |
|||
if (date == null) { |
|||
return widget.errorFormatText ?? "Invalid format."; |
|||
} |
|||
else if (!_isValidAcceptableDate(date)) { |
|||
return widget.errorInvalidText ?? "Out of range."; |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
void _handleSaved(string text) { |
|||
if (widget.onDateSaved != null) { |
|||
DateTime? date = _parseDate(text); |
|||
if (_isValidAcceptableDate(date)) { |
|||
_selectedDate = date; |
|||
_inputText = text; |
|||
widget.onDateSaved(date.Value); |
|||
} |
|||
} |
|||
} |
|||
|
|||
void _handleSubmitted(string text) { |
|||
if (widget.onDateSubmitted != null) { |
|||
DateTime? date = _parseDate(text); |
|||
if (_isValidAcceptableDate(date)) { |
|||
_selectedDate = date; |
|||
_inputText = text; |
|||
widget.onDateSubmitted(date.Value); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
return new OrientationBuilder(builder: (BuildContext subContext, Orientation orientation) => { |
|||
return new Container( |
|||
padding: EdgeInsets.symmetric(horizontal: 24f), |
|||
height: orientation == Orientation.portrait |
|||
? InputDatePickerUtils._inputPortraitHeight |
|||
: InputDatePickerUtils._inputLandscapeHeight, |
|||
child: new Column( |
|||
children: new List<Widget> { |
|||
new Spacer(), |
|||
new TextFormField( |
|||
decoration: new InputDecoration( |
|||
border: new UnderlineInputBorder(), |
|||
filled: true, |
|||
hintText: widget.fieldHintText ?? "mm/dd/yyyy", |
|||
labelText: widget.fieldLabelText ?? "Enter Date" |
|||
), |
|||
validator: _validateDate, |
|||
inputFormatters: new List<TextInputFormatter> { |
|||
new _DateTextInputFormatter("/") |
|||
}, |
|||
keyboardType: TextInputType.datetime, |
|||
onSaved: _handleSaved, |
|||
onFieldSubmitted: _handleSubmitted, |
|||
autofocus: widget.autofocus, |
|||
controller: _controller |
|||
), |
|||
new Spacer(), |
|||
} |
|||
) |
|||
); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
class _DateTextInputFormatter : TextInputFormatter { |
|||
public _DateTextInputFormatter(string separator) { |
|||
this.separator = separator; |
|||
} |
|||
|
|||
public readonly string separator; |
|||
|
|||
public readonly WhitelistingTextInputFormatter _filterFormatter = |
|||
// Only allow digits and separators (slash, dot, comma, hyphen, space).
|
|||
new WhitelistingTextInputFormatter(new Regex(@"[\d\/\.,-\s]+")); |
|||
|
|||
public override TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) { |
|||
TextEditingValue filteredValue = _filterFormatter.formatEditUpdate(oldValue, newValue); |
|||
return filteredValue.copyWith( |
|||
text: Regex.Replace(filteredValue.text, @"[\D]", separator) |
|||
); |
|||
} |
|||
} |
|||
} |
撰写
预览
正在加载...
取消
保存
Reference in new issue