您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 
 

1147 行
52 KiB

using System;
using System.Collections.Generic;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.scheduler;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using TextStyle = Unity.UIWidgets.painting.TextStyle;
namespace Unity.UIWidgets.cupertino {
class CupertinoDatePickerUtils {
public const float _kItemExtent = 32.0f;
public const float _kPickerWidth = 330.0f;
public const bool _kUseMagnifier = true;
public const float _kMagnification = 1.05f;
public const float _kDatePickerPadSize = 12.0f;
public static Color _kBackgroundColor = CupertinoColors.white;
public static TextStyle _kDefaultPickerTextStyle = new TextStyle(
letterSpacing: -0.83f
);
public delegate Widget listGenerateDelegate(int index);
public static List<Widget> listGenerate(int count, listGenerateDelegate func) {
var list = new List<Widget>();
for (int i = 0; i < count; i++) {
list.Add(func(i));
}
return list;
}
}
public class _DatePickerLayoutDelegate : MultiChildLayoutDelegate {
public _DatePickerLayoutDelegate(
List<float> columnWidths,
int textDirectionFactor
) {
D.assert(columnWidths != null);
this.columnWidths = columnWidths;
this.textDirectionFactor = textDirectionFactor;
}
public readonly List<float> columnWidths;
public readonly int textDirectionFactor;
public override void performLayout(Size size) {
float remainingWidth = size.width;
for (int i = 0; i < this.columnWidths.Count; i++) {
remainingWidth -= this.columnWidths[i] + CupertinoDatePickerUtils._kDatePickerPadSize * 2;
}
float currentHorizontalOffset = 0.0f;
for (int i = 0; i < this.columnWidths.Count; i++) {
int index = this.textDirectionFactor == 1 ? i : this.columnWidths.Count - i - 1;
float childWidth = this.columnWidths[index] + CupertinoDatePickerUtils._kDatePickerPadSize * 2;
if (index == 0 || index == this.columnWidths.Count - 1) {
childWidth += remainingWidth / 2;
}
this.layoutChild(index, BoxConstraints.tight(new Size(childWidth, size.height)));
this.positionChild(index, new Offset(currentHorizontalOffset, 0.0f));
currentHorizontalOffset += childWidth;
}
}
public override bool shouldRelayout(MultiChildLayoutDelegate oldDelegate) {
return this.columnWidths != ((_DatePickerLayoutDelegate) oldDelegate).columnWidths
|| this.textDirectionFactor != ((_DatePickerLayoutDelegate) oldDelegate).textDirectionFactor;
}
}
public enum CupertinoDatePickerMode {
time,
date,
dateAndTime,
}
enum _PickerColumnType {
dayOfMonth,
month,
year,
date,
hour,
minute,
dayPeriod,
}
public class CupertinoDatePicker : StatefulWidget {
public CupertinoDatePicker(
ValueChanged<DateTime> onDateTimeChanged,
CupertinoDatePickerMode mode = CupertinoDatePickerMode.dateAndTime,
DateTime? initialDateTime = null,
DateTime? minimumDate = null,
DateTime? maximumDate = null,
int minimumYear = 1,
int? maximumYear = null,
int minuteInterval = 1,
bool use24hFormat = false
) {
this.initialDateTime = initialDateTime ?? DateTime.Now;
D.assert(onDateTimeChanged != null);
D.assert(
minuteInterval > 0 && 60 % minuteInterval == 0,
() => "minute interval is not a positive integer factor of 60"
);
D.assert(this.initialDateTime != null);
D.assert(
mode != CupertinoDatePickerMode.dateAndTime || minimumDate == null ||
!(this.initialDateTime < minimumDate),
() => "initial date is before minimum date"
);
D.assert(
mode != CupertinoDatePickerMode.dateAndTime || maximumDate == null ||
!(this.initialDateTime > maximumDate),
() => "initial date is after maximum date"
);
D.assert(
mode != CupertinoDatePickerMode.date || (minimumYear >= 1 && this.initialDateTime.Year >= minimumYear),
() => "initial year is not greater than minimum year, or mininum year is not positive"
);
D.assert(
mode != CupertinoDatePickerMode.date || maximumYear == null || this.initialDateTime.Year <= maximumYear,
() => "initial year is not smaller than maximum year"
);
D.assert(
this.initialDateTime.Minute % minuteInterval == 0,
() => "initial minute is not divisible by minute interval"
);
this.onDateTimeChanged = onDateTimeChanged;
this.mode = mode;
this.minimumDate = minimumDate;
this.maximumDate = maximumDate;
this.minimumYear = minimumYear;
this.maximumYear = maximumYear;
this.minuteInterval = minuteInterval;
this.use24hFormat = use24hFormat;
}
public readonly CupertinoDatePickerMode mode;
public readonly DateTime initialDateTime;
public readonly DateTime? minimumDate;
public readonly DateTime? maximumDate;
public readonly int minimumYear;
public readonly int? maximumYear;
public readonly int minuteInterval;
public readonly bool use24hFormat;
public readonly ValueChanged<DateTime> onDateTimeChanged;
public override State createState() {
if (this.mode == CupertinoDatePickerMode.time || this.mode == CupertinoDatePickerMode.dateAndTime) {
return new _CupertinoDatePickerDateTimeState();
}
else {
return new _CupertinoDatePickerDateState();
}
}
internal static float _getColumnWidth(
_PickerColumnType columnType,
CupertinoLocalizations localizations,
BuildContext context
) {
string longestText = "";
switch (columnType) {
case _PickerColumnType.date:
for (int i = 1; i <= 12; i++) {
string date =
localizations.datePickerMediumDate(new DateTime(2018, i, 25));
if (longestText.Length < date.Length) {
longestText = date;
}
}
break;
case _PickerColumnType.hour:
for (int i = 0; i < 24; i++) {
string hour = localizations.datePickerHour(i);
if (longestText.Length < hour.Length) {
longestText = hour;
}
}
break;
case _PickerColumnType.minute:
for (int i = 0; i < 60; i++) {
string minute = localizations.datePickerMinute(i);
if (longestText.Length < minute.Length) {
longestText = minute;
}
}
break;
case _PickerColumnType.dayPeriod:
longestText =
localizations.anteMeridiemAbbreviation.Length > localizations.postMeridiemAbbreviation.Length
? localizations.anteMeridiemAbbreviation
: localizations.postMeridiemAbbreviation;
break;
case _PickerColumnType.dayOfMonth:
for (int i = 1; i <= 31; i++) {
string dayOfMonth = localizations.datePickerDayOfMonth(i);
if (longestText.Length < dayOfMonth.Length) {
longestText = dayOfMonth;
}
}
break;
case _PickerColumnType.month:
for (int i = 1; i <= 12; i++) {
string month = localizations.datePickerMonth(i);
if (longestText.Length < month.Length) {
longestText = month;
}
}
break;
case _PickerColumnType.year:
longestText = localizations.datePickerYear(2018);
break;
}
D.assert(longestText != "", () => "column type is not appropriate");
TextPainter painter = new TextPainter(
text: new TextSpan(
style: DefaultTextStyle.of(context).style,
text: longestText
),
textDirection: Directionality.of(context)
);
painter.layout();
return painter.maxIntrinsicWidth;
}
}
delegate Widget _ColumnBuilder(float offAxisFraction, TransitionBuilder itemPositioningBuilder);
class _CupertinoDatePickerDateTimeState : State<CupertinoDatePicker> {
public int textDirectionFactor;
public CupertinoLocalizations localizations;
public Alignment alignCenterLeft;
public Alignment alignCenterRight;
public DateTime initialDateTime;
public int selectedDayFromInitial;
public int selectedHour;
public int previousHourIndex;
public int selectedMinute;
public int selectedAmPm;
public FixedExtentScrollController amPmController;
public static Dictionary<int, float> estimatedColumnWidths = new Dictionary<int, float>();
public override void initState() {
base.initState();
this.initialDateTime = this.widget.initialDateTime;
this.selectedDayFromInitial = 0;
this.selectedHour = this.widget.initialDateTime.Hour;
this.selectedMinute = this.widget.initialDateTime.Minute;
this.selectedAmPm = 0;
if (!this.widget.use24hFormat) {
this.selectedAmPm = this.selectedHour / 12;
this.selectedHour = this.selectedHour % 12;
if (this.selectedHour == 0) {
this.selectedHour = 12;
}
this.amPmController = new FixedExtentScrollController(initialItem: this.selectedAmPm);
}
this.previousHourIndex = this.selectedHour;
}
public override void didUpdateWidget(StatefulWidget oldWidget) {
base.didUpdateWidget(oldWidget);
D.assert(
((CupertinoDatePicker) oldWidget).mode == this.widget.mode,
() => "The CupertinoDatePicker's mode cannot change once it's built"
);
}
public override void didChangeDependencies() {
base.didChangeDependencies();
this.textDirectionFactor = Directionality.of(this.context) == TextDirection.ltr ? 1 : -1;
this.localizations = CupertinoLocalizations.of(this.context);
this.alignCenterLeft = this.textDirectionFactor == 1 ? Alignment.centerLeft : Alignment.centerRight;
this.alignCenterRight = this.textDirectionFactor == 1 ? Alignment.centerRight : Alignment.centerLeft;
estimatedColumnWidths.Clear();
}
float _getEstimatedColumnWidth(_PickerColumnType columnType) {
if (!estimatedColumnWidths.TryGetValue((int) columnType, out float _)) {
estimatedColumnWidths[(int) columnType] =
CupertinoDatePicker._getColumnWidth(columnType, this.localizations, this.context);
}
return estimatedColumnWidths[(int) columnType];
}
DateTime _getDateTime() {
DateTime date = new DateTime(this.initialDateTime.Year, this.initialDateTime.Month, this.initialDateTime.Day
).Add(new TimeSpan(this.selectedDayFromInitial, 0, 0, 0));
return new DateTime(
date.Year,
date.Month,
date.Day,
this.widget.use24hFormat ? this.selectedHour : this.selectedHour % 12 + this.selectedAmPm * 12,
this.selectedMinute,
0
);
}
Widget _buildMediumDatePicker(float offAxisFraction, TransitionBuilder itemPositioningBuilder) {
return CupertinoPicker.builder(
scrollController: new FixedExtentScrollController(initialItem: this.selectedDayFromInitial),
offAxisFraction: offAxisFraction,
itemExtent: CupertinoDatePickerUtils._kItemExtent,
useMagnifier: CupertinoDatePickerUtils._kUseMagnifier,
magnification: CupertinoDatePickerUtils._kMagnification,
backgroundColor: CupertinoDatePickerUtils._kBackgroundColor,
onSelectedItemChanged: (int index) => {
this.selectedDayFromInitial = index;
this.widget.onDateTimeChanged(this._getDateTime());
},
itemBuilder: (BuildContext context, int index) => {
DateTime dateTime = new DateTime(this.initialDateTime.Year, this.initialDateTime.Month,
this.initialDateTime.Day
).Add(new TimeSpan(index, 0, 0, 0));
if (this.widget.minimumDate != null && dateTime < this.widget.minimumDate) {
return null;
}
if (this.widget.maximumDate != null && dateTime > this.widget.maximumDate) {
return null;
}
return itemPositioningBuilder(
context,
new Text(this.localizations.datePickerMediumDate(dateTime))
);
}
);
}
Widget _buildHourPicker(float offAxisFraction, TransitionBuilder itemPositioningBuilder) {
return new CupertinoPicker(
scrollController: new FixedExtentScrollController(initialItem: this.selectedHour),
offAxisFraction: offAxisFraction,
itemExtent: CupertinoDatePickerUtils._kItemExtent,
useMagnifier: CupertinoDatePickerUtils._kUseMagnifier,
magnification: CupertinoDatePickerUtils._kMagnification,
backgroundColor: CupertinoDatePickerUtils._kBackgroundColor,
onSelectedItemChanged: (int index) => {
if (this.widget.use24hFormat) {
this.selectedHour = index;
this.widget.onDateTimeChanged(this._getDateTime());
}
else {
this.selectedHour = index % 12;
bool wasAm = this.previousHourIndex >= 0 && this.previousHourIndex <= 11;
bool isAm = index >= 0 && index <= 11;
if (wasAm != isAm) {
this.amPmController.animateToItem(
1 - this.amPmController.selectedItem,
duration: new TimeSpan(0, 0, 0, 0, 300),
curve:
Curves.easeOut
);
}
else {
this.widget.onDateTimeChanged(this._getDateTime());
}
}
this.previousHourIndex = index;
},
children: CupertinoDatePickerUtils.listGenerate(24, (index) => {
int hour = index;
if (!this.widget.use24hFormat) {
hour = hour % 12 == 0 ? 12 : hour % 12;
}
return itemPositioningBuilder(this.context,
new Text(this.localizations.datePickerHour(hour)
)
);
}),
looping: true
);
}
Widget _buildMinutePicker(float offAxisFraction, TransitionBuilder itemPositioningBuilder) {
return new CupertinoPicker(
scrollController: new FixedExtentScrollController(
initialItem: this.selectedMinute / this.widget.minuteInterval),
offAxisFraction: offAxisFraction,
itemExtent: CupertinoDatePickerUtils._kItemExtent,
useMagnifier: CupertinoDatePickerUtils._kUseMagnifier,
magnification: CupertinoDatePickerUtils._kMagnification,
backgroundColor: CupertinoDatePickerUtils._kBackgroundColor,
onSelectedItemChanged: (int index) => {
this.selectedMinute = index * this.widget.minuteInterval;
this.widget.onDateTimeChanged(this._getDateTime());
},
children: CupertinoDatePickerUtils.listGenerate(60 / this.widget.minuteInterval, (int index) => {
int minute = index * this.widget.minuteInterval;
return itemPositioningBuilder(this.context,
new Text(this.localizations.datePickerMinute(minute)
)
);
}),
looping: true
);
}
Widget _buildAmPmPicker(float offAxisFraction, TransitionBuilder itemPositioningBuilder) {
return new CupertinoPicker(
scrollController: this.amPmController,
offAxisFraction: offAxisFraction,
itemExtent: CupertinoDatePickerUtils._kItemExtent,
useMagnifier: CupertinoDatePickerUtils._kUseMagnifier,
magnification: CupertinoDatePickerUtils._kMagnification,
backgroundColor: CupertinoDatePickerUtils._kBackgroundColor,
onSelectedItemChanged: (int index) => {
this.selectedAmPm = index;
this.widget.onDateTimeChanged(this._getDateTime());
},
children: CupertinoDatePickerUtils.listGenerate(2, (int index) => {
return itemPositioningBuilder(this.context,
new Text(
index == 0
? this.localizations.anteMeridiemAbbreviation
: this.localizations.postMeridiemAbbreviation
)
);
})
);
}
public override Widget build(BuildContext context) {
List<float> columnWidths = new List<float>() {
this._getEstimatedColumnWidth(_PickerColumnType.hour),
this._getEstimatedColumnWidth(_PickerColumnType.minute),
};
List<_ColumnBuilder> pickerBuilders = new List<_ColumnBuilder> {
this._buildHourPicker, this._buildMinutePicker,
};
if (!this.widget.use24hFormat) {
if (this.localizations.datePickerDateTimeOrder == DatePickerDateTimeOrder.date_time_dayPeriod
|| this.localizations.datePickerDateTimeOrder == DatePickerDateTimeOrder.time_dayPeriod_date) {
pickerBuilders.Add(this._buildAmPmPicker);
columnWidths.Add(this._getEstimatedColumnWidth(_PickerColumnType.dayPeriod));
}
else {
pickerBuilders.Insert(0, this._buildAmPmPicker);
columnWidths.Insert(0, this._getEstimatedColumnWidth(_PickerColumnType.dayPeriod));
}
}
if (this.widget.mode == CupertinoDatePickerMode.dateAndTime) {
if (this.localizations.datePickerDateTimeOrder == DatePickerDateTimeOrder.time_dayPeriod_date
|| this.localizations.datePickerDateTimeOrder == DatePickerDateTimeOrder.dayPeriod_time_date) {
pickerBuilders.Add(this._buildMediumDatePicker);
columnWidths.Add(this._getEstimatedColumnWidth(_PickerColumnType.date));
}
else {
pickerBuilders.Insert(0, this._buildMediumDatePicker);
columnWidths.Insert(0, this._getEstimatedColumnWidth(_PickerColumnType.date));
}
}
List<Widget> pickers = new List<Widget>();
for (int i = 0; i < columnWidths.Count; i++) {
var _i = i;
float offAxisFraction = 0.0f;
if (_i == 0) {
offAxisFraction = -0.5f * this.textDirectionFactor;
}
else if (_i >= 2 || columnWidths.Count == 2) {
offAxisFraction = 0.5f * this.textDirectionFactor;
}
EdgeInsets padding = EdgeInsets.only(right: CupertinoDatePickerUtils._kDatePickerPadSize);
if (_i == columnWidths.Count - 1) {
padding = padding.flipped;
}
if (this.textDirectionFactor == -1) {
padding = padding.flipped;
}
pickers.Add(new LayoutId(
id: _i,
child: pickerBuilders[_i](
offAxisFraction,
(BuildContext _context, Widget child) => {
return new Container(
alignment: _i == columnWidths.Count - 1
? this.alignCenterLeft
: this.alignCenterRight,
padding: padding,
child: new Container(
alignment: _i == columnWidths.Count - 1
? this.alignCenterLeft
: this.alignCenterRight,
width: _i == 0 || _i == columnWidths.Count - 1
? (float?) null
: columnWidths[_i] + CupertinoDatePickerUtils._kDatePickerPadSize,
child: child
)
);
}
)
));
}
return new MediaQuery(
data: new MediaQueryData(textScaleFactor: 1.0f),
child: DefaultTextStyle.merge(
style: CupertinoDatePickerUtils._kDefaultPickerTextStyle,
child: new CustomMultiChildLayout(
layoutDelegate: new _DatePickerLayoutDelegate(
columnWidths: columnWidths,
textDirectionFactor: this.textDirectionFactor
),
children: pickers
)
)
);
}
}
class _CupertinoDatePickerDateState : State<CupertinoDatePicker> {
int textDirectionFactor;
CupertinoLocalizations localizations;
Alignment alignCenterLeft;
Alignment alignCenterRight;
int selectedDay;
int selectedMonth;
int selectedYear;
FixedExtentScrollController dayController;
Dictionary<int, float> estimatedColumnWidths = new Dictionary<int, float>();
public override void initState() {
base.initState();
this.selectedDay = this.widget.initialDateTime.Day;
this.selectedMonth = this.widget.initialDateTime.Month;
this.selectedYear = this.widget.initialDateTime.Year;
this.dayController = new FixedExtentScrollController(initialItem: this.selectedDay - 1);
}
public override void didChangeDependencies() {
base.didChangeDependencies();
this.textDirectionFactor = Directionality.of(this.context) == TextDirection.ltr ? 1 : -1;
this.localizations = CupertinoLocalizations.of(this.context);
this.alignCenterLeft = this.textDirectionFactor == 1 ? Alignment.centerLeft : Alignment.centerRight;
this.alignCenterRight = this.textDirectionFactor == 1 ? Alignment.centerRight : Alignment.centerLeft;
this.estimatedColumnWidths[(int) _PickerColumnType.dayOfMonth] = CupertinoDatePicker._getColumnWidth(
_PickerColumnType.dayOfMonth, this.localizations, this.context);
this.estimatedColumnWidths[(int) _PickerColumnType.month] = CupertinoDatePicker._getColumnWidth(
_PickerColumnType.month, this.localizations, this.context);
this.estimatedColumnWidths[(int) _PickerColumnType.year] = CupertinoDatePicker._getColumnWidth(
_PickerColumnType.year, this.localizations, this.context);
}
Widget _buildDayPicker(float offAxisFraction, TransitionBuilder itemPositioningBuilder) {
int daysInCurrentMonth = DateTime.DaysInMonth(this.selectedYear, this.selectedMonth);
return new CupertinoPicker(
scrollController: this.dayController,
offAxisFraction: offAxisFraction,
itemExtent: CupertinoDatePickerUtils._kItemExtent,
useMagnifier: CupertinoDatePickerUtils._kUseMagnifier,
magnification: CupertinoDatePickerUtils._kMagnification,
backgroundColor: CupertinoDatePickerUtils._kBackgroundColor,
onSelectedItemChanged: (int index) => {
this.selectedDay = index + 1;
var validDay = this.selectedDay;
if (DateTime.DaysInMonth(this.selectedYear, this.selectedMonth) < this.selectedDay) {
validDay -= DateTime.DaysInMonth(this.selectedYear, this.selectedMonth);
}
if (this.selectedDay == validDay) {
this.widget.onDateTimeChanged(new DateTime(this.selectedYear, this.selectedMonth,
this.selectedDay));
}
},
children: CupertinoDatePickerUtils.listGenerate(31, (int index) => {
TextStyle disableTextStyle = null;
if (index >= daysInCurrentMonth) {
disableTextStyle = new TextStyle(color: CupertinoColors.inactiveGray);
}
return itemPositioningBuilder(this.context,
new Text(this.localizations.datePickerDayOfMonth(index + 1),
style: disableTextStyle
)
);
}),
looping: true
);
}
Widget _buildMonthPicker(float offAxisFraction, TransitionBuilder itemPositioningBuilder) {
return new CupertinoPicker(
scrollController: new FixedExtentScrollController(initialItem: this.selectedMonth - 1),
offAxisFraction: offAxisFraction,
itemExtent: CupertinoDatePickerUtils._kItemExtent,
useMagnifier: CupertinoDatePickerUtils._kUseMagnifier,
magnification: CupertinoDatePickerUtils._kMagnification,
backgroundColor: CupertinoDatePickerUtils._kBackgroundColor,
onSelectedItemChanged: (int index) => {
this.selectedMonth = index + 1;
var validDay = this.selectedDay;
if (DateTime.DaysInMonth(this.selectedYear, this.selectedMonth) < this.selectedDay) {
validDay -= DateTime.DaysInMonth(this.selectedYear, this.selectedMonth);
}
if (this.selectedDay == validDay) {
this.widget.onDateTimeChanged(new DateTime(this.selectedYear, this.selectedMonth,
this.selectedDay));
}
},
children: CupertinoDatePickerUtils.listGenerate(12, (int index) => {
return itemPositioningBuilder(this.context,
new Text(this.localizations.datePickerMonth(index + 1))
);
}),
looping: true
);
}
Widget _buildYearPicker(float offAxisFraction, TransitionBuilder itemPositioningBuilder) {
return CupertinoPicker.builder(
scrollController: new FixedExtentScrollController(initialItem: this.selectedYear),
itemExtent: CupertinoDatePickerUtils._kItemExtent,
offAxisFraction: offAxisFraction,
useMagnifier: CupertinoDatePickerUtils._kUseMagnifier,
magnification: CupertinoDatePickerUtils._kMagnification,
backgroundColor: CupertinoDatePickerUtils._kBackgroundColor,
onSelectedItemChanged: (int index) => {
this.selectedYear = index;
if (new DateTime(this.selectedYear, this.selectedMonth, this.selectedDay).Day == this.selectedDay) {
this.widget.onDateTimeChanged(new DateTime(this.selectedYear, this.selectedMonth,
this.selectedDay));
}
},
itemBuilder: (BuildContext context, int index) => {
if (index < this.widget.minimumYear) {
return null;
}
if (this.widget.maximumYear != null && index > this.widget.maximumYear) {
return null;
}
return itemPositioningBuilder(
context,
new Text(this.localizations.datePickerYear(index))
);
}
);
}
bool _keepInValidRange(ScrollEndNotification notification) {
var desiredDay = this.selectedDay;
if (DateTime.DaysInMonth(this.selectedYear, this.selectedMonth) < this.selectedDay) {
desiredDay -= DateTime.DaysInMonth(this.selectedYear, this.selectedMonth);
}
if (desiredDay != this.selectedDay) {
SchedulerBinding.instance.addPostFrameCallback((TimeSpan timestamp) => {
this.dayController.animateToItem(this.dayController.selectedItem - desiredDay,
duration: new TimeSpan(0, 0, 0, 0, 200),
curve:
Curves.easeOut
);
});
}
this.setState(() => { });
return false;
}
public override Widget build(BuildContext context) {
List<_ColumnBuilder> pickerBuilders = new List<_ColumnBuilder>();
List<float> columnWidths = new List<float>();
switch (this.localizations.datePickerDateOrder) {
case DatePickerDateOrder.mdy:
pickerBuilders = new List<_ColumnBuilder>()
{this._buildMonthPicker, this._buildDayPicker, this._buildYearPicker};
columnWidths = new List<float>() {
this.estimatedColumnWidths[(int) _PickerColumnType.month],
this.estimatedColumnWidths[(int) _PickerColumnType.dayOfMonth],
this.estimatedColumnWidths[(int) _PickerColumnType.year]
};
break;
case DatePickerDateOrder.dmy:
pickerBuilders = new List<_ColumnBuilder>
{this._buildDayPicker, this._buildMonthPicker, this._buildYearPicker};
columnWidths = new List<float> {
this.estimatedColumnWidths[(int) _PickerColumnType.dayOfMonth],
this.estimatedColumnWidths[(int) _PickerColumnType.month],
this.estimatedColumnWidths[(int) _PickerColumnType.year]
};
break;
case DatePickerDateOrder.ymd:
pickerBuilders = new List<_ColumnBuilder>
{this._buildYearPicker, this._buildMonthPicker, this._buildDayPicker};
columnWidths = new List<float>() {
this.estimatedColumnWidths[(int) _PickerColumnType.year],
this.estimatedColumnWidths[(int) _PickerColumnType.month],
this.estimatedColumnWidths[(int) _PickerColumnType.dayOfMonth]
};
break;
case DatePickerDateOrder.ydm:
pickerBuilders = new List<_ColumnBuilder>
{this._buildYearPicker, this._buildDayPicker, this._buildMonthPicker};
columnWidths = new List<float> {
this.estimatedColumnWidths[(int) _PickerColumnType.year],
this.estimatedColumnWidths[(int) _PickerColumnType.dayOfMonth],
this.estimatedColumnWidths[(int) _PickerColumnType.month]
};
break;
default:
D.assert(false, () => "date order is not specified");
break;
}
List<Widget> pickers = new List<Widget>();
for (int i = 0; i < columnWidths.Count; i++) {
var _i = i;
float offAxisFraction = (_i - 1) * 0.3f * this.textDirectionFactor;
EdgeInsets padding = EdgeInsets.only(right: CupertinoDatePickerUtils._kDatePickerPadSize);
if (this.textDirectionFactor == -1) {
padding = EdgeInsets.only(left: CupertinoDatePickerUtils._kDatePickerPadSize);
}
pickers.Add(new LayoutId(
id: _i,
child: pickerBuilders[_i](
offAxisFraction,
(BuildContext _context, Widget child) => {
return new Container(
alignment: _i == columnWidths.Count - 1
? this.alignCenterLeft
: this.alignCenterRight,
padding: _i == 0 ? null : padding,
child: new Container(
alignment: _i == 0 ? this.alignCenterLeft : this.alignCenterRight,
width: columnWidths[_i] + CupertinoDatePickerUtils._kDatePickerPadSize,
child: child
)
);
}
)
));
}
return new MediaQuery(
data: new MediaQueryData(textScaleFactor: 1.0f),
child: new NotificationListener<ScrollEndNotification>(
onNotification: this._keepInValidRange,
child: DefaultTextStyle.merge(
style: CupertinoDatePickerUtils._kDefaultPickerTextStyle,
child: new CustomMultiChildLayout(
layoutDelegate: new _DatePickerLayoutDelegate(
columnWidths: columnWidths,
textDirectionFactor: this.textDirectionFactor
),
children:
pickers
)
)
)
);
}
}
public enum CupertinoTimerPickerMode {
hm,
ms,
hms,
}
public class CupertinoTimerPicker : StatefulWidget {
public CupertinoTimerPicker(
ValueChanged<TimeSpan> onTimerDurationChanged,
CupertinoTimerPickerMode mode = CupertinoTimerPickerMode.hms,
TimeSpan initialTimerDuration = new TimeSpan(),
int minuteInterval = 1,
int secondInterval = 1
) {
D.assert(onTimerDurationChanged != null);
D.assert(initialTimerDuration >= TimeSpan.Zero);
D.assert(initialTimerDuration < new TimeSpan(1, 0, 0, 0));
D.assert(minuteInterval > 0 && 60 % minuteInterval == 0);
D.assert(secondInterval > 0 && 60 % secondInterval == 0);
D.assert((int) initialTimerDuration.TotalMinutes % minuteInterval == 0);
D.assert((int) initialTimerDuration.TotalSeconds % secondInterval == 0);
this.onTimerDurationChanged = onTimerDurationChanged;
this.mode = mode;
this.initialTimerDuration = initialTimerDuration;
this.minuteInterval = minuteInterval;
this.secondInterval = secondInterval;
}
public readonly CupertinoTimerPickerMode mode;
public readonly TimeSpan initialTimerDuration;
public readonly int minuteInterval;
public readonly int secondInterval;
public readonly ValueChanged<TimeSpan> onTimerDurationChanged;
public override State createState() {
return new _CupertinoTimerPickerState();
}
}
class _CupertinoTimerPickerState : State<CupertinoTimerPicker> {
int textDirectionFactor;
CupertinoLocalizations localizations;
Alignment alignCenterLeft;
Alignment alignCenterRight;
int selectedHour;
int selectedMinute;
int selectedSecond;
public override void initState() {
base.initState();
this.selectedMinute = (int) this.widget.initialTimerDuration.TotalMinutes % 60;
if (this.widget.mode != CupertinoTimerPickerMode.ms) {
this.selectedHour = (int) this.widget.initialTimerDuration.TotalHours;
}
if (this.widget.mode != CupertinoTimerPickerMode.hm) {
this.selectedSecond = (int) this.widget.initialTimerDuration.TotalSeconds % 60;
}
}
Widget _buildLabel(string text) {
return new Text(
text,
textScaleFactor: 0.8f,
style: new TextStyle(fontWeight: FontWeight.w600)
);
}
public override void didChangeDependencies() {
base.didChangeDependencies();
this.textDirectionFactor = Directionality.of(this.context) == TextDirection.ltr ? 1 : -1;
this.localizations = CupertinoLocalizations.of(this.context);
this.alignCenterLeft = this.textDirectionFactor == 1 ? Alignment.centerLeft : Alignment.centerRight;
this.alignCenterRight = this.textDirectionFactor == 1 ? Alignment.centerRight : Alignment.centerLeft;
}
Widget _buildHourPicker() {
return new CupertinoPicker(
scrollController: new FixedExtentScrollController(initialItem: this.selectedHour),
offAxisFraction: -0.5f * this.textDirectionFactor,
itemExtent: CupertinoDatePickerUtils._kItemExtent,
backgroundColor: CupertinoDatePickerUtils._kBackgroundColor,
onSelectedItemChanged: (int index) => {
this.setState(() => {
this.selectedHour = index;
this.widget.onTimerDurationChanged(
new TimeSpan(
hours: this.selectedHour,
minutes: this.selectedMinute,
seconds: this.selectedSecond));
});
},
children: CupertinoDatePickerUtils.listGenerate(24, (int index) => {
float hourLabelWidth = this.widget.mode == CupertinoTimerPickerMode.hm
? CupertinoDatePickerUtils._kPickerWidth / 4
: CupertinoDatePickerUtils._kPickerWidth / 6;
string semanticsLabel = this.textDirectionFactor == 1
? this.localizations.timerPickerHour(index) + this.localizations.timerPickerHourLabel(index)
: this.localizations.timerPickerHourLabel(index) + this.localizations.timerPickerHour(index);
return new Container(
alignment: this.alignCenterRight,
padding: this.textDirectionFactor == 1
? EdgeInsets.only(right: hourLabelWidth)
: EdgeInsets.only(left: hourLabelWidth),
child: new Container(
alignment: this.alignCenterRight,
padding: EdgeInsets.symmetric(horizontal: 2.0f),
child: new Text(this.localizations.timerPickerHour(index))
)
);
})
);
}
Widget _buildHourColumn() {
Widget hourLabel = new IgnorePointer(
child: new Container(
alignment: this.alignCenterRight,
child: new Container(
alignment: this.alignCenterLeft,
padding: EdgeInsets.symmetric(horizontal: 2.0f),
width: this.widget.mode == CupertinoTimerPickerMode.hm
? CupertinoDatePickerUtils._kPickerWidth / 4
: CupertinoDatePickerUtils._kPickerWidth / 6,
child: this._buildLabel(this.localizations.timerPickerHourLabel(this.selectedHour))
)
)
);
return new Stack(
children: new List<Widget> {
this._buildHourPicker(),
hourLabel
}
);
}
Widget _buildMinutePicker() {
float offAxisFraction;
if (this.widget.mode == CupertinoTimerPickerMode.hm) {
offAxisFraction = 0.5f * this.textDirectionFactor;
}
else if (this.widget.mode == CupertinoTimerPickerMode.hms) {
offAxisFraction = 0.0f;
}
else {
offAxisFraction = -0.5f * this.textDirectionFactor;
}
return new CupertinoPicker(
scrollController: new FixedExtentScrollController(
initialItem: this.selectedMinute / this.widget.minuteInterval
),
offAxisFraction: offAxisFraction,
itemExtent: CupertinoDatePickerUtils._kItemExtent,
backgroundColor: CupertinoDatePickerUtils._kBackgroundColor,
onSelectedItemChanged: (int index) => {
this.setState(() => {
this.selectedMinute = index * this.widget.minuteInterval;
this.widget.onTimerDurationChanged(
new TimeSpan(
hours: this.selectedHour,
minutes: this.selectedMinute,
seconds: this.selectedSecond));
});
},
children: CupertinoDatePickerUtils.listGenerate(60 / this.widget.minuteInterval, (int index) => {
int minute = index * this.widget.minuteInterval;
string semanticsLabel = this.textDirectionFactor == 1
? this.localizations.timerPickerMinute(minute) +
this.localizations.timerPickerMinuteLabel(minute)
: this.localizations.timerPickerMinuteLabel(minute) +
this.localizations.timerPickerMinute(minute);
if (this.widget.mode == CupertinoTimerPickerMode.ms) {
return new Container(
alignment: this.alignCenterRight,
padding: this.textDirectionFactor == 1
? EdgeInsets.only(right: CupertinoDatePickerUtils._kPickerWidth / 4)
: EdgeInsets.only(left: CupertinoDatePickerUtils._kPickerWidth / 4),
child: new Container(
alignment: this.alignCenterRight,
padding: EdgeInsets.symmetric(horizontal: 2.0f),
child: new Text(this.localizations.timerPickerMinute(minute))
)
);
}
else {
return new Container(
alignment: this.alignCenterLeft,
child: new Container(
alignment: this.alignCenterRight,
width: this.widget.mode == CupertinoTimerPickerMode.hm
? CupertinoDatePickerUtils._kPickerWidth / 10
: CupertinoDatePickerUtils._kPickerWidth / 6,
padding: EdgeInsets.symmetric(horizontal: 2.0f),
child:
new Text(this.localizations.timerPickerMinute(minute))
)
);
}
})
);
}
Widget _buildMinuteColumn() {
Widget minuteLabel;
if (this.widget.mode == CupertinoTimerPickerMode.hm) {
minuteLabel = new IgnorePointer(
child: new Container(
alignment: this.alignCenterLeft,
padding: this.textDirectionFactor == 1
? EdgeInsets.only(left: CupertinoDatePickerUtils._kPickerWidth / 10)
: EdgeInsets.only(right: CupertinoDatePickerUtils._kPickerWidth / 10),
child: new Container(
alignment: this.alignCenterLeft,
padding: EdgeInsets.symmetric(horizontal: 2.0f),
child: this._buildLabel(this.localizations.timerPickerMinuteLabel(this.selectedMinute))
)
)
);
}
else {
minuteLabel = new IgnorePointer(
child: new Container(
alignment: this.alignCenterRight,
child: new Container(
alignment: this.alignCenterLeft,
width: this.widget.mode == CupertinoTimerPickerMode.ms
? CupertinoDatePickerUtils._kPickerWidth / 4
: CupertinoDatePickerUtils._kPickerWidth / 6,
padding: EdgeInsets.symmetric(horizontal: 2.0f),
child: this._buildLabel(this.localizations.timerPickerMinuteLabel(this.selectedMinute))
)
)
);
}
return new Stack(
children: new List<Widget> {
this._buildMinutePicker(),
minuteLabel
}
);
}
Widget _buildSecondPicker() {
float offAxisFraction = 0.5f * this.textDirectionFactor;
float secondPickerWidth = this.widget.mode == CupertinoTimerPickerMode.ms
? CupertinoDatePickerUtils._kPickerWidth / 10
: CupertinoDatePickerUtils._kPickerWidth / 6;
return new CupertinoPicker(
scrollController: new FixedExtentScrollController(
initialItem: this.selectedSecond / this.widget.secondInterval
),
offAxisFraction: offAxisFraction,
itemExtent: CupertinoDatePickerUtils._kItemExtent,
backgroundColor: CupertinoDatePickerUtils._kBackgroundColor,
onSelectedItemChanged: (int index) => {
this.setState(() => {
this.selectedSecond = index * this.widget.secondInterval;
this.widget.onTimerDurationChanged(
new TimeSpan(
hours: this.selectedHour,
minutes: this.selectedMinute,
seconds: this.selectedSecond));
});
},
children: CupertinoDatePickerUtils.listGenerate(60 / this.widget.secondInterval, (int index) => {
int second = index * this.widget.secondInterval;
string semanticsLabel = this.textDirectionFactor == 1
? this.localizations.timerPickerSecond(second) +
this.localizations.timerPickerSecondLabel(second)
: this.localizations.timerPickerSecondLabel(second) +
this.localizations.timerPickerSecond(second);
return new Container(
alignment: this.alignCenterLeft,
child: new Container(
alignment: this.alignCenterRight,
padding: EdgeInsets.symmetric(horizontal: 2.0f),
width: secondPickerWidth,
child: new Text(this.localizations.timerPickerSecond(second))
)
);
})
);
}
Widget _buildSecondColumn() {
float secondPickerWidth = this.widget.mode == CupertinoTimerPickerMode.ms
? CupertinoDatePickerUtils._kPickerWidth / 10
: CupertinoDatePickerUtils._kPickerWidth / 6;
Widget secondLabel = new IgnorePointer(
child: new Container(
alignment: this.alignCenterLeft,
padding: this.textDirectionFactor == 1
? EdgeInsets.only(left: secondPickerWidth)
: EdgeInsets.only(right: secondPickerWidth),
child: new Container(
alignment: this.alignCenterLeft,
padding: EdgeInsets.symmetric(horizontal: 2.0f),
child: this._buildLabel(this.localizations.timerPickerSecondLabel(this.selectedSecond))
)
)
);
return new Stack(
children: new List<Widget> {
this._buildSecondPicker(),
secondLabel
}
);
}
public override Widget build(BuildContext context) {
Widget picker;
if (this.widget.mode == CupertinoTimerPickerMode.hm) {
picker = new Row(
children: new List<Widget> {
new Expanded(child: this._buildHourColumn()),
new Expanded(child: this._buildMinuteColumn()),
}
);
}
else if (this.widget.mode == CupertinoTimerPickerMode.ms) {
picker = new Row(
children: new List<Widget> {
new Expanded(child: this._buildMinuteColumn()),
new Expanded(child: this._buildSecondColumn()),
}
);
}
else {
picker = new Row(
children: new List<Widget> {
new Expanded(child: this._buildHourColumn()),
new Container(
width: CupertinoDatePickerUtils._kPickerWidth / 3,
child: this._buildMinuteColumn()
),
new Expanded(child: this._buildSecondColumn()),
}
);
}
return new MediaQuery(
data: new MediaQueryData(
textScaleFactor: 1.0f
),
child: picker
);
}
}
}