浏览代码
Merge branch 'siyaoH/1.17/material' of github.com:Unity-Technologies/com.unity.uiwidgets into dev_1.17.5
/siyaoH-1.17-PlatformMessage
Merge branch 'siyaoH/1.17/material' of github.com:Unity-Technologies/com.unity.uiwidgets into dev_1.17.5
/siyaoH-1.17-PlatformMessage
xingweizhu
4 年前
当前提交
0c86e6e7
共有 72 个文件被更改,包括 11823 次插入 和 1386 次删除
-
3com.unity.uiwidgets/Runtime/animation/tween_sequence.cs
-
2com.unity.uiwidgets/Runtime/cupertino/action_Sheet.cs
-
36com.unity.uiwidgets/Runtime/material/bottom_sheet.cs
-
2com.unity.uiwidgets/Runtime/material/data_table.cs
-
2com.unity.uiwidgets/Runtime/material/drawer_header.cs
-
695com.unity.uiwidgets/Runtime/material/dropdown.cs
-
3com.unity.uiwidgets/Runtime/material/elevation_overlay.cs
-
44com.unity.uiwidgets/Runtime/material/expand_icon.cs
-
104com.unity.uiwidgets/Runtime/material/expansion_panel.cs
-
7com.unity.uiwidgets/Runtime/material/expansion_tile.cs
-
22com.unity.uiwidgets/Runtime/material/feedback.cs
-
69com.unity.uiwidgets/Runtime/material/flat_button.cs
-
215com.unity.uiwidgets/Runtime/material/flexible_space_bar.cs
-
179com.unity.uiwidgets/Runtime/material/floating_action_button.cs
-
35com.unity.uiwidgets/Runtime/material/floating_action_button_location.cs
-
114com.unity.uiwidgets/Runtime/material/floatting_action_button_theme.cs
-
74com.unity.uiwidgets/Runtime/material/icon_button.cs
-
61com.unity.uiwidgets/Runtime/material/input_border.cs
-
36com.unity.uiwidgets/Runtime/material/list_tile.cs
-
46com.unity.uiwidgets/Runtime/material/material.cs
-
2com.unity.uiwidgets/Runtime/material/material_state.cs
-
40com.unity.uiwidgets/Runtime/material/mergeable_material.cs
-
160com.unity.uiwidgets/Runtime/material/outline_button.cs
-
4com.unity.uiwidgets/Runtime/material/page.cs
-
224com.unity.uiwidgets/Runtime/material/page_transitions_theme.cs
-
385com.unity.uiwidgets/Runtime/material/popup_menu.cs
-
2com.unity.uiwidgets/Runtime/material/popup_menu_theme.cs
-
155com.unity.uiwidgets/Runtime/material/radio.cs
-
59com.unity.uiwidgets/Runtime/material/raised_button.cs
-
94com.unity.uiwidgets/Runtime/material/refresh_indicator.cs
-
34com.unity.uiwidgets/Runtime/material/reorderable_list.cs
-
710com.unity.uiwidgets/Runtime/material/scaffold.cs
-
110com.unity.uiwidgets/Runtime/material/scrollbar.cs
-
70com.unity.uiwidgets/Runtime/material/search.cs
-
203com.unity.uiwidgets/Runtime/material/slider.cs
-
846com.unity.uiwidgets/Runtime/material/slider_theme.cs
-
297com.unity.uiwidgets/Runtime/material/snack_bar.cs
-
20com.unity.uiwidgets/Runtime/material/snack_bar_theme.cs
-
24com.unity.uiwidgets/Runtime/material/stepper.cs
-
341com.unity.uiwidgets/Runtime/material/switch.cs
-
14com.unity.uiwidgets/Runtime/material/theme.cs
-
27com.unity.uiwidgets/Runtime/material/theme_data.cs
-
3com.unity.uiwidgets/Runtime/rendering/box.cs
-
360com.unity.uiwidgets/Runtime/widgets/draggable_scrollable_sheet.cs
-
24com.unity.uiwidgets/Runtime/widgets/editable_text.cs
-
2com.unity.uiwidgets/Runtime/widgets/media_query.cs
-
2com.unity.uiwidgets/Runtime/widgets/scroll_position_with_single_context.cs
-
1001com.unity.uiwidgets/Runtime/material/animated_icons/data/ellipsis_search.g.cs
-
3com.unity.uiwidgets/Runtime/material/animated_icons/data/ellipsis_search.g.cs.meta
-
1001com.unity.uiwidgets/Runtime/material/animated_icons/data/event_add.g.cs
-
3com.unity.uiwidgets/Runtime/material/animated_icons/data/event_add.g.cs.meta
-
118com.unity.uiwidgets/Runtime/material/grid_tile_bar.cs
-
3com.unity.uiwidgets/Runtime/material/grid_tile_bar.cs.meta
-
390com.unity.uiwidgets/Runtime/material/paginated_data_table.cs
-
1001com.unity.uiwidgets/Runtime/material/range_slider.cs
-
117com.unity.uiwidgets/Runtime/material/ratio_list_tile.cs
-
572com.unity.uiwidgets/Runtime/material/selectable_text.cs
-
187com.unity.uiwidgets/Runtime/material/swtich_list_tile.cs
-
1001com.unity.uiwidgets/Runtime/material/time_picker.cs
-
160com.unity.uiwidgets/Runtime/material/pickers/date_picker_head.cs
-
416com.unity.uiwidgets/Runtime/material/pickers/date_picker_dialog.cs
-
11com.unity.uiwidgets/Runtime/material/pickers/date_picker_dialog.cs.meta
-
238com.unity.uiwidgets/Runtime/material/pickers/input_date_picker.cs
-
11com.unity.uiwidgets/Runtime/material/pickers/input_date_picker.cs.meta
-
928com.unity.uiwidgets/Runtime/material/pickers/calendar_date_picker.cs
-
3com.unity.uiwidgets/Runtime/material/pickers/calendar_date_picker.cs.meta
-
3com.unity.uiwidgets/Runtime/material/pickers/date_picker_common.cs.meta
-
58com.unity.uiwidgets/Runtime/material/pickers/date_utils.cs
-
3com.unity.uiwidgets/Runtime/material/pickers/date_utils.cs.meta
-
20com.unity.uiwidgets/Runtime/material/pickers/date_picker_common.cs
-
0/com.unity.uiwidgets/Runtime/material/time.cs
710
com.unity.uiwidgets/Runtime/material/scaffold.cs
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
846
com.unity.uiwidgets/Runtime/material/slider_theme.cs
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
1001
com.unity.uiwidgets/Runtime/material/animated_icons/data/ellipsis_search.g.cs
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
fileFormatVersion: 2 |
|||
guid: 898296900a3f42f6a2c88a23b8cd3ab7 |
|||
timeCreated: 1611815012 |
1001
com.unity.uiwidgets/Runtime/material/animated_icons/data/event_add.g.cs
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
fileFormatVersion: 2 |
|||
guid: ccce6da1a5a3452f8b264aeefea4439d |
|||
timeCreated: 1611816735 |
|
|||
using System.Collections.Generic; |
|||
using uiwidgets; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.painting; |
|||
using Unity.UIWidgets.rendering; |
|||
using Unity.UIWidgets.ui; |
|||
using Unity.UIWidgets.widgets; |
|||
|
|||
namespace Unity.UIWidgets.material { |
|||
class GridTileBar : StatelessWidget { |
|||
public GridTileBar( |
|||
Key key = null, |
|||
Color backgroundColor = null, |
|||
Widget leading = null, |
|||
Widget title = null, |
|||
Widget subtitle = null, |
|||
Widget trailing = null |
|||
) : base(key: key) { |
|||
this.backgroundColor = backgroundColor; |
|||
this.leading = leading; |
|||
this.title = title; |
|||
this.subtitle = subtitle; |
|||
this.trailing = trailing; |
|||
} |
|||
|
|||
public readonly Color backgroundColor; |
|||
|
|||
public readonly Widget leading; |
|||
|
|||
public readonly Widget title; |
|||
|
|||
public readonly Widget subtitle; |
|||
|
|||
public readonly Widget trailing; |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
BoxDecoration decoration = null; |
|||
if (backgroundColor != null) |
|||
decoration = new BoxDecoration(color: backgroundColor); |
|||
|
|||
EdgeInsetsDirectional padding = EdgeInsetsDirectional.only( |
|||
start: leading != null ? 8.0f : 16.0f, |
|||
end: trailing != null ? 8.0f : 16.0f |
|||
); |
|||
|
|||
ThemeData theme = Theme.of(context); |
|||
ThemeData darkTheme = new ThemeData( |
|||
brightness: Brightness.dark, |
|||
accentColor: theme.accentColor, |
|||
accentColorBrightness: theme.accentColorBrightness |
|||
); |
|||
var expandChildren = new List<widgets.Widget>(); |
|||
|
|||
if (leading != null) { |
|||
//TODO: EdgeInsetsGeometry
|
|||
expandChildren.Add(new Padding( |
|||
padding: (EdgeInsets) (EdgeInsetsGeometry) EdgeInsetsDirectional.only(end: 8.0f), child: |
|||
leading)); |
|||
} |
|||
|
|||
if (title != null && subtitle != null) { |
|||
expandChildren.Add(new Expanded( |
|||
child: new Column( |
|||
mainAxisAlignment: MainAxisAlignment.center, |
|||
crossAxisAlignment: CrossAxisAlignment.start, |
|||
children: new List<Widget> { |
|||
new DefaultTextStyle( |
|||
style: darkTheme.textTheme.subtitle1, |
|||
softWrap: false, |
|||
overflow: TextOverflow.ellipsis, |
|||
child: title |
|||
), |
|||
new DefaultTextStyle( |
|||
style: darkTheme.textTheme.caption, |
|||
softWrap: false, |
|||
overflow: TextOverflow.ellipsis, |
|||
child: subtitle |
|||
) |
|||
} |
|||
) |
|||
)); |
|||
} |
|||
else if (title != null || subtitle != null) |
|||
expandChildren.Add(new Expanded( |
|||
child: new DefaultTextStyle( |
|||
style: darkTheme.textTheme.subtitle1, |
|||
softWrap: false, |
|||
overflow: TextOverflow.ellipsis, |
|||
child: title ?? subtitle |
|||
) |
|||
)); |
|||
|
|||
if (trailing != null) { |
|||
//TODO: EdgeInsetsGeometry
|
|||
expandChildren.Add(new Padding( |
|||
padding: (EdgeInsets) (EdgeInsetsGeometry) EdgeInsetsDirectional.only(start: 8.0f), |
|||
child: trailing)); |
|||
} |
|||
|
|||
return new Container( |
|||
//TODO: EdgeInsetsGeometry
|
|||
padding: (EdgeInsets) (EdgeInsetsGeometry) padding, |
|||
decoration: decoration, |
|||
height: (title != null && subtitle != null) ? 68.0f : 48.0f, |
|||
child: new Theme( |
|||
data: darkTheme, |
|||
child: IconTheme.merge( |
|||
data: new IconThemeData(color: Colors.white), |
|||
child: new Row( |
|||
crossAxisAlignment: CrossAxisAlignment.center, |
|||
children: expandChildren |
|||
) |
|||
) |
|||
) |
|||
); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: c7f1b23a6b52430d95ef28b3586542df |
|||
timeCreated: 1611735180 |
|
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.gestures; |
|||
using Unity.UIWidgets.material; |
|||
using Unity.UIWidgets.painting; |
|||
using Unity.UIWidgets.rendering; |
|||
using Unity.UIWidgets.ui; |
|||
using Unity.UIWidgets.widgets; |
|||
using UnityEngine; |
|||
using TextStyle = Unity.UIWidgets.painting.TextStyle; |
|||
|
|||
namespace Unity.UIWidgets.material { |
|||
public class PaginatedDataTable : StatefulWidget { |
|||
public PaginatedDataTable( |
|||
Key key = null, |
|||
Widget header = null, |
|||
List<Widget> actions = null, |
|||
List<DataColumn> columns = null, |
|||
int? sortColumnIndex = null, |
|||
bool sortAscending = false, |
|||
ValueSetter<bool> onSelectAll = null, |
|||
float dataRowHeight = material_.kMinInteractiveDimension, |
|||
float headingRowHeight = 56.0f, |
|||
float horizontalMargin = 24.0f, |
|||
float columnSpacing = 56.0f, |
|||
bool showCheckboxColumn = true, |
|||
int? initialFirstRowIndex = 0, |
|||
ValueChanged<int> onPageChanged = null, |
|||
int rowsPerPage = defaultRowsPerPage, |
|||
List<int> availableRowsPerPage = null, |
|||
ValueChanged<int> onRowsPerPageChanged = null, |
|||
DragStartBehavior dragStartBehavior = DragStartBehavior.start, |
|||
DataTableSource source = null |
|||
) : base(key: key) { |
|||
availableRowsPerPage = availableRowsPerPage ?? new List<int> { |
|||
defaultRowsPerPage, defaultRowsPerPage * 2, defaultRowsPerPage * 5, defaultRowsPerPage * 10 |
|||
}; |
|||
D.assert(columns.isNotEmpty); |
|||
D.assert(() => { |
|||
if (onRowsPerPageChanged != null) |
|||
D.assert(availableRowsPerPage != null && availableRowsPerPage.Contains(rowsPerPage)); |
|||
return true; |
|||
}); |
|||
D.assert(source != null); |
|||
this.header = header; |
|||
this.actions = actions; |
|||
this.columns = columns; |
|||
this.sortColumnIndex = sortColumnIndex; |
|||
this.sortAscending = sortAscending; |
|||
this.onSelectAll = onSelectAll; |
|||
this.dataRowHeight = dataRowHeight; |
|||
this.headingRowHeight = headingRowHeight; |
|||
this.horizontalMargin = horizontalMargin; |
|||
this.columnSpacing = columnSpacing; |
|||
this.showCheckboxColumn = showCheckboxColumn; |
|||
this.initialFirstRowIndex = initialFirstRowIndex; |
|||
this.onPageChanged = onPageChanged; |
|||
this.rowsPerPage = rowsPerPage; |
|||
this.availableRowsPerPage = availableRowsPerPage; |
|||
this.onRowsPerPageChanged = onRowsPerPageChanged; |
|||
this.dragStartBehavior = dragStartBehavior; |
|||
} |
|||
|
|||
|
|||
public readonly Widget header; |
|||
|
|||
public readonly List<Widget> actions; |
|||
|
|||
public readonly List<DataColumn> columns; |
|||
|
|||
public readonly int? sortColumnIndex; |
|||
|
|||
public readonly bool sortAscending; |
|||
|
|||
public readonly ValueSetter<bool> onSelectAll; |
|||
|
|||
public readonly float dataRowHeight; |
|||
|
|||
public readonly float headingRowHeight; |
|||
|
|||
public readonly float horizontalMargin; |
|||
|
|||
public readonly float columnSpacing; |
|||
|
|||
public readonly bool showCheckboxColumn; |
|||
|
|||
public readonly int? initialFirstRowIndex; |
|||
|
|||
public readonly ValueChanged<int> onPageChanged; |
|||
|
|||
public readonly int rowsPerPage; |
|||
|
|||
public const int defaultRowsPerPage = 10; |
|||
|
|||
public readonly List<int> availableRowsPerPage; |
|||
|
|||
public readonly ValueChanged<int> onRowsPerPageChanged; |
|||
|
|||
public readonly DataTableSource source; |
|||
|
|||
public readonly DragStartBehavior dragStartBehavior; |
|||
|
|||
public override State createState() => new PaginatedDataTableState(); |
|||
} |
|||
|
|||
class PaginatedDataTableState : State<PaginatedDataTable> { |
|||
int _firstRowIndex; |
|||
int _rowCount; |
|||
bool _rowCountApproximate; |
|||
int _selectedRowCount; |
|||
public readonly Dictionary<int, DataRow> _rows = new Dictionary<int, DataRow>(); |
|||
|
|||
public override void initState() { |
|||
base.initState(); |
|||
_firstRowIndex = (PageStorage.of(context)?.readState(context)) as int? ?? widget.initialFirstRowIndex ?? 0; |
|||
widget.source.addListener(_handleDataSourceChanged); |
|||
_handleDataSourceChanged(); |
|||
} |
|||
|
|||
public override void didUpdateWidget(StatefulWidget oldWidget) { |
|||
base.didUpdateWidget(oldWidget); |
|||
if (oldWidget is PaginatedDataTable painPaginatedDataTable) { |
|||
if (painPaginatedDataTable.source != widget.source) { |
|||
painPaginatedDataTable.source.removeListener(_handleDataSourceChanged); |
|||
widget.source.addListener(_handleDataSourceChanged); |
|||
_handleDataSourceChanged(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public override void dispose() { |
|||
widget.source.removeListener(_handleDataSourceChanged); |
|||
base.dispose(); |
|||
} |
|||
|
|||
void _handleDataSourceChanged() { |
|||
setState(() => { |
|||
_rowCount = widget.source.rowCount; |
|||
_rowCountApproximate = widget.source.isRowCountApproximate; |
|||
_selectedRowCount = widget.source.selectedRowCount; |
|||
_rows.Clear(); |
|||
}); |
|||
} |
|||
|
|||
void pageTo(int rowIndex) { |
|||
int oldFirstRowIndex = _firstRowIndex; |
|||
setState(() => { |
|||
int rowsPerPage = widget.rowsPerPage; |
|||
_firstRowIndex = ((int) (1.0f * rowIndex / rowsPerPage)) * rowsPerPage; |
|||
}); |
|||
if ((widget.onPageChanged != null) && |
|||
(oldFirstRowIndex != _firstRowIndex)) |
|||
widget.onPageChanged(_firstRowIndex); |
|||
} |
|||
|
|||
DataRow _getBlankRowFor(int index) { |
|||
return DataRow.byIndex( |
|||
index: index, |
|||
cells: widget.columns.Select((DataColumn column) => DataCell.empty).ToList() |
|||
); |
|||
} |
|||
|
|||
DataRow _getProgressIndicatorRowFor(int index) { |
|||
bool haveProgressIndicator = false; |
|||
List<DataCell> cells = widget.columns.Select((DataColumn column) => { |
|||
if (!column.numeric) { |
|||
haveProgressIndicator = true; |
|||
return new DataCell(new CircularProgressIndicator()); |
|||
} |
|||
|
|||
return DataCell.empty; |
|||
}).ToList(); |
|||
if (!haveProgressIndicator) { |
|||
haveProgressIndicator = true; |
|||
cells[0] = new DataCell(new CircularProgressIndicator()); |
|||
} |
|||
|
|||
return DataRow.byIndex( |
|||
index: index, |
|||
cells: cells |
|||
); |
|||
} |
|||
|
|||
List<DataRow> _getRows(int firstRowIndex, int rowsPerPage) { |
|||
List<DataRow> result = new List<DataRow>(); |
|||
int nextPageFirstRowIndex = firstRowIndex + rowsPerPage; |
|||
|
|||
bool haveProgressIndicator = false; |
|||
for (int index = firstRowIndex; |
|||
index < nextPageFirstRowIndex; |
|||
index += 1) { |
|||
DataRow row = null; |
|||
if (index < _rowCount || _rowCountApproximate) { |
|||
row = _rows.putIfAbsent(index, () => widget.source.getRow(index)); |
|||
if (row == null && !haveProgressIndicator) { |
|||
row = row ?? _getProgressIndicatorRowFor(index); |
|||
haveProgressIndicator = true; |
|||
} |
|||
} |
|||
|
|||
row = row ?? _getBlankRowFor(index); |
|||
result.Add(row); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
void _handlePrevious() { |
|||
pageTo(Mathf.Max(_firstRowIndex - widget.rowsPerPage, 0)); |
|||
} |
|||
|
|||
void _handleNext() { |
|||
pageTo(_firstRowIndex + widget.rowsPerPage); |
|||
} |
|||
|
|||
public readonly GlobalKey _tableKey = GlobalKey.key(); |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
D.assert(material_.debugCheckHasMaterialLocalizations(context)); |
|||
ThemeData themeData = Theme.of(context); |
|||
MaterialLocalizations localizations = MaterialLocalizations.of(context); |
|||
|
|||
List<Widget> headerWidgets = new List<Widget>(); |
|||
|
|||
float startPadding = 24.0f; |
|||
if (_selectedRowCount == 0) { |
|||
headerWidgets.Add(new Expanded(child: widget.header)); |
|||
if (widget.header is ButtonBar) { |
|||
startPadding = 12.0f; |
|||
} |
|||
} |
|||
else { |
|||
headerWidgets.Add(new Expanded( |
|||
child: new Text(localizations.selectedRowCountTitle(_selectedRowCount)) |
|||
)); |
|||
} |
|||
|
|||
if (widget.actions != null) { |
|||
headerWidgets.AddRange( |
|||
widget.actions.Select((Widget action) => { |
|||
return new Padding( |
|||
// TODO: up EdgeInsetsGeometry
|
|||
padding: (EdgeInsets) (EdgeInsetsGeometry) EdgeInsetsDirectional.only( |
|||
start: 24.0f - 8.0f * 2.0f), |
|||
child: action |
|||
); |
|||
}).ToList() |
|||
); |
|||
} |
|||
|
|||
TextStyle footerTextStyle = themeData.textTheme.caption; |
|||
List<Widget> footerWidgets = new List<Widget>(); |
|||
if (widget.onRowsPerPageChanged != null) { |
|||
List<Widget> availableRowsPerPage = widget.availableRowsPerPage |
|||
.Where((int value) => value <= _rowCount || value == widget.rowsPerPage) |
|||
.Select((int value) => { |
|||
return (Widget) new DropdownMenuItem<int>( |
|||
value: value, |
|||
child: new Text($"{value}") |
|||
); |
|||
}).ToList(); |
|||
footerWidgets.AddRange(new List<Widget>() { |
|||
new Container(width: 14.0f), // to match trailing padding in case we overflow and end up scrolling
|
|||
new Text(localizations.rowsPerPageTitle), |
|||
new ConstrainedBox( |
|||
constraints: new BoxConstraints(minWidth: 64.0f), // 40.0 for the text, 24.0 for the icon
|
|||
child: new Align( |
|||
alignment: AlignmentDirectional.centerEnd, |
|||
child: new DropdownButtonHideUnderline( |
|||
child: new DropdownButton<int>( |
|||
items: availableRowsPerPage.Cast<DropdownMenuItem<int>>().ToList(), |
|||
value: widget.rowsPerPage, |
|||
onChanged: widget.onRowsPerPageChanged, |
|||
style: footerTextStyle, |
|||
iconSize: 24.0f |
|||
) |
|||
) |
|||
) |
|||
), |
|||
}); |
|||
} |
|||
|
|||
footerWidgets.AddRange(new List<Widget>() { |
|||
new Container(width: 32.0f), |
|||
new Text( |
|||
localizations.pageRowsInfoTitle( |
|||
_firstRowIndex + 1, |
|||
_firstRowIndex + widget.rowsPerPage, |
|||
_rowCount, |
|||
_rowCountApproximate |
|||
) |
|||
), |
|||
new Container(width: 32.0f), |
|||
new IconButton( |
|||
icon: new Icon(Icons.chevron_left), |
|||
padding: EdgeInsets.zero, |
|||
tooltip: |
|||
localizations.previousPageTooltip, |
|||
onPressed: _firstRowIndex <= 0 ? (VoidCallback) null : _handlePrevious), |
|||
new Container(width: 24.0f), |
|||
new IconButton( |
|||
icon: new Icon(Icons.chevron_right), |
|||
padding: EdgeInsets.zero, |
|||
tooltip: |
|||
localizations.nextPageTooltip, |
|||
onPressed: (!_rowCountApproximate && (_firstRowIndex + widget.rowsPerPage >= _rowCount)) |
|||
? (VoidCallback) null |
|||
: _handleNext |
|||
), |
|||
|
|||
new Container(width: 14.0f), |
|||
}); |
|||
|
|||
return new LayoutBuilder( |
|||
builder: (BuildContext _context, BoxConstraints constraints) => { |
|||
return new Card( |
|||
child: new Column( |
|||
crossAxisAlignment: CrossAxisAlignment.stretch, |
|||
children: new List<Widget> { |
|||
new DefaultTextStyle( |
|||
style: _selectedRowCount > 0 |
|||
? themeData.textTheme.subtitle1.copyWith(color: themeData.accentColor) |
|||
: themeData.textTheme.headline6.copyWith(fontWeight: FontWeight.w400), |
|||
child: IconTheme.merge( |
|||
data: new IconThemeData( |
|||
opacity: 0.54f |
|||
), |
|||
child: new Ink( |
|||
height: 64.0f, |
|||
color: _selectedRowCount > 0 ? themeData.secondaryHeaderColor : null, |
|||
child: new Padding( |
|||
//TODO: update EdgeInsets
|
|||
padding: (EdgeInsets) (EdgeInsetsGeometry) EdgeInsetsDirectional.only( |
|||
start: startPadding, end: 14.0f), |
|||
child: new Row( |
|||
mainAxisAlignment: MainAxisAlignment.end, |
|||
children: headerWidgets |
|||
) |
|||
) |
|||
) |
|||
) |
|||
), |
|||
new SingleChildScrollView( |
|||
scrollDirection: Axis.horizontal, |
|||
dragStartBehavior: widget.dragStartBehavior, |
|||
child: new ConstrainedBox( |
|||
constraints: new BoxConstraints(minWidth: constraints.minWidth), |
|||
child: new DataTable( |
|||
key: _tableKey, |
|||
columns: widget.columns, |
|||
sortColumnIndex: widget.sortColumnIndex, |
|||
sortAscending: widget.sortAscending, |
|||
onSelectAll: widget.onSelectAll, |
|||
dataRowHeight: widget.dataRowHeight, |
|||
headingRowHeight: widget.headingRowHeight, |
|||
horizontalMargin: widget.horizontalMargin, |
|||
columnSpacing: widget.columnSpacing, |
|||
rows: _getRows(_firstRowIndex, widget.rowsPerPage) |
|||
) |
|||
) |
|||
), |
|||
new DefaultTextStyle( |
|||
style: footerTextStyle, |
|||
child: IconTheme.merge( |
|||
data: new IconThemeData( |
|||
opacity: 0.54f |
|||
), |
|||
child: |
|||
new Container( |
|||
height: 56.0f, |
|||
child: new SingleChildScrollView( |
|||
dragStartBehavior: widget.dragStartBehavior, |
|||
scrollDirection: Axis.horizontal, |
|||
reverse: true, |
|||
child: new Row( |
|||
children: footerWidgets |
|||
) |
|||
) |
|||
) |
|||
) |
|||
) |
|||
} |
|||
) |
|||
); |
|||
} |
|||
); |
|||
} |
|||
} |
|||
} |
1001
com.unity.uiwidgets/Runtime/material/range_slider.cs
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.gestures; |
|||
using Unity.UIWidgets.ui; |
|||
using Unity.UIWidgets.widgets; |
|||
|
|||
namespace Unity.UIWidgets.material { |
|||
public class RadioListTile<T> : StatelessWidget where T : class { |
|||
public RadioListTile( |
|||
Key key = null, |
|||
T value = default, |
|||
T groupValue = default, |
|||
ValueChanged<T> onChanged = null, |
|||
bool toggleable = false, |
|||
Color activeColor = null, |
|||
Widget title = null, |
|||
Widget subtitle = null, |
|||
bool isThreeLine = false, |
|||
bool? dense = null, |
|||
Widget secondary = null, |
|||
bool selected = false, |
|||
ListTileControlAffinity controlAffinity = ListTileControlAffinity.platform |
|||
) : base(key: key) { |
|||
D.assert(!isThreeLine || subtitle != null); |
|||
|
|||
this.value = value; |
|||
this.groupValue = groupValue; |
|||
this.onChanged = onChanged; |
|||
this.toggleable = toggleable; |
|||
this.activeColor = activeColor; |
|||
this.title = title; |
|||
this.subtitle = subtitle; |
|||
this.isThreeLine = isThreeLine; |
|||
this.dense = dense; |
|||
this.secondary = secondary; |
|||
this.selected = selected; |
|||
this.controlAffinity = controlAffinity; |
|||
} |
|||
|
|||
public readonly T value; |
|||
|
|||
public readonly T groupValue; |
|||
|
|||
public readonly ValueChanged<T> onChanged; |
|||
|
|||
public readonly bool toggleable; |
|||
|
|||
public readonly Color activeColor; |
|||
|
|||
public readonly Widget title; |
|||
|
|||
public readonly Widget subtitle; |
|||
|
|||
public readonly Widget secondary; |
|||
|
|||
public readonly bool isThreeLine; |
|||
|
|||
public readonly bool? dense; |
|||
|
|||
public readonly bool selected; |
|||
|
|||
public readonly ListTileControlAffinity controlAffinity; |
|||
|
|||
bool isChecked { |
|||
get { return value.Equals(groupValue); } |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
Widget control = new Radio<T>( |
|||
value: value, |
|||
groupValue: groupValue, |
|||
onChanged: onChanged, |
|||
toggleable: toggleable, |
|||
activeColor: activeColor, |
|||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap |
|||
); |
|||
Widget leading = null; |
|||
Widget trailing = null; |
|||
switch (controlAffinity) { |
|||
case ListTileControlAffinity.leading: |
|||
case ListTileControlAffinity.platform: |
|||
leading = control; |
|||
trailing = secondary; |
|||
break; |
|||
case ListTileControlAffinity.trailing: |
|||
leading = secondary; |
|||
trailing = control; |
|||
break; |
|||
} |
|||
|
|||
return ListTileTheme.merge( |
|||
selectedColor: activeColor ?? Theme.of(context).accentColor, |
|||
child: new ListTile( |
|||
leading: leading, |
|||
title: title, |
|||
subtitle: subtitle, |
|||
trailing: trailing, |
|||
isThreeLine: isThreeLine, |
|||
dense: dense, |
|||
enabled: onChanged != null, |
|||
onTap: onChanged != null |
|||
? () => { |
|||
if (toggleable && isChecked) { |
|||
onChanged(null); |
|||
return; |
|||
} |
|||
|
|||
if (!isChecked) { |
|||
onChanged(value); |
|||
} |
|||
} |
|||
: (GestureTapCallback) null, |
|||
selected: selected |
|||
) |
|||
); |
|||
} |
|||
} |
|||
} |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Unity.UIWidgets.animation; |
|||
using Unity.UIWidgets.async2; |
|||
using Unity.UIWidgets.cupertino; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.gestures; |
|||
using Unity.UIWidgets.painting; |
|||
using Unity.UIWidgets.rendering; |
|||
using Unity.UIWidgets.service; |
|||
using Unity.UIWidgets.ui; |
|||
using Unity.UIWidgets.widgets; |
|||
using UnityEngine; |
|||
using Color = Unity.UIWidgets.ui.Color; |
|||
using StrutStyle = Unity.UIWidgets.painting.StrutStyle; |
|||
using TextStyle = Unity.UIWidgets.painting.TextStyle; |
|||
|
|||
namespace Unity.UIWidgets.material { |
|||
|
|||
public static class SelectableTextUtils { |
|||
internal const int iOSHorizontalOffset = -2; |
|||
} |
|||
|
|||
class _TextSpanEditingController : TextEditingController { |
|||
public _TextSpanEditingController(TextSpan textSpan = null) : base(text: textSpan?.toPlainText()) { |
|||
D.assert(textSpan != null); |
|||
_textSpan = textSpan; |
|||
} |
|||
|
|||
public readonly TextSpan _textSpan; |
|||
|
|||
public override TextSpan buildTextSpan(TextStyle style = null ,bool withComposing = false) { |
|||
return new TextSpan( |
|||
style: style, |
|||
children: new List<InlineSpan> {_textSpan} |
|||
); |
|||
} |
|||
|
|||
public new string text { |
|||
get { return value.text; } |
|||
set { |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
class _SelectableTextSelectionGestureDetectorBuilder : TextSelectionGestureDetectorBuilder { |
|||
public _SelectableTextSelectionGestureDetectorBuilder( |
|||
_SelectableTextState state |
|||
) : base(_delegate: state) { |
|||
_state = state; |
|||
} |
|||
|
|||
public readonly _SelectableTextState _state; |
|||
|
|||
protected override void onForcePressStart(ForcePressDetails details) { |
|||
base.onForcePressStart(details); |
|||
if (_delegate.selectionEnabled && shouldShowSelectionToolbar) { |
|||
editableText.showToolbar(); |
|||
} |
|||
} |
|||
|
|||
protected override void onForcePressEnd(ForcePressDetails details) { |
|||
// Not required.
|
|||
} |
|||
|
|||
protected override void onSingleLongTapMoveUpdate(LongPressMoveUpdateDetails details) { |
|||
if (_delegate.selectionEnabled) { |
|||
switch (Theme.of(_state.context).platform) { |
|||
case RuntimePlatform.IPhonePlayer: |
|||
case RuntimePlatform.OSXEditor: |
|||
case RuntimePlatform.OSXPlayer: |
|||
renderEditable.selectPositionAt( |
|||
from: details.globalPosition, |
|||
cause: SelectionChangedCause.longPress |
|||
); |
|||
break; |
|||
default: |
|||
renderEditable.selectWordsInRange( |
|||
from: details.globalPosition - details.offsetFromOrigin, |
|||
to: details.globalPosition, |
|||
cause: SelectionChangedCause.longPress |
|||
); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
protected override void onSingleTapUp(TapUpDetails details) { |
|||
editableText.hideToolbar(); |
|||
if (_delegate.selectionEnabled) { |
|||
switch (Theme.of(_state.context).platform) { |
|||
case RuntimePlatform.IPhonePlayer: |
|||
case RuntimePlatform.OSXEditor: |
|||
case RuntimePlatform.OSXPlayer: |
|||
renderEditable.selectWordEdge(cause: SelectionChangedCause.tap); |
|||
break; |
|||
default: |
|||
renderEditable.selectPosition(cause: SelectionChangedCause.tap); |
|||
break; |
|||
} |
|||
} |
|||
if (_state.widget.onTap != null) |
|||
_state.widget.onTap(); |
|||
} |
|||
|
|||
protected override void onSingleLongTapStart(LongPressStartDetails details) { |
|||
if (_delegate.selectionEnabled) { |
|||
switch (Theme.of(_state.context).platform) { |
|||
case RuntimePlatform.IPhonePlayer: |
|||
case RuntimePlatform.OSXEditor: |
|||
case RuntimePlatform.OSXPlayer: |
|||
renderEditable.selectPositionAt( |
|||
from: details.globalPosition, |
|||
cause: SelectionChangedCause.longPress |
|||
); |
|||
break; |
|||
default: |
|||
renderEditable.selectWord(cause: SelectionChangedCause.longPress); |
|||
Feedback.forLongPress(_state.context); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
class SelectableText : StatefulWidget { |
|||
|
|||
public SelectableText( |
|||
string data, |
|||
Key key = null, |
|||
FocusNode focusNode = null, |
|||
TextStyle style = null, |
|||
StrutStyle strutStyle = null, |
|||
TextAlign? textAlign = null, |
|||
TextDirection? textDirection = null, |
|||
float? textScaleFactor = null, |
|||
bool showCursor = false, |
|||
bool autofocus = false, |
|||
ToolbarOptions toolbarOptions = null, |
|||
int? minLines = null, |
|||
int? maxLines = null, |
|||
float cursorWidth = 2.0f, |
|||
Radius cursorRadius = null, |
|||
Color cursorColor = null, |
|||
DragStartBehavior dragStartBehavior = DragStartBehavior.start, |
|||
bool enableInteractiveSelection = true, |
|||
GestureTapCallback onTap = null, |
|||
ScrollPhysics scrollPhysics = null, |
|||
TextWidthBasis? textWidthBasis = null |
|||
) : this( |
|||
data: data, |
|||
textSpan: null, |
|||
key: key, |
|||
focusNode: focusNode, |
|||
style: style, |
|||
strutStyle: strutStyle, |
|||
textAlign: textAlign, |
|||
textDirection: textDirection, |
|||
textScaleFactor: textScaleFactor, |
|||
showCursor: showCursor, |
|||
autofocus: autofocus, |
|||
toolbarOptions: toolbarOptions, |
|||
minLines: minLines, |
|||
maxLines: maxLines, |
|||
cursorWidth: cursorWidth, |
|||
cursorRadius: cursorRadius, |
|||
cursorColor: cursorColor, |
|||
dragStartBehavior: dragStartBehavior, |
|||
enableInteractiveSelection: enableInteractiveSelection, |
|||
onTap: onTap, |
|||
scrollPhysics: scrollPhysics, |
|||
textWidthBasis: textWidthBasis |
|||
) { |
|||
D.assert( |
|||
data != null, |
|||
() => "A non-null String must be provided to a SelectableText widget." |
|||
); |
|||
} |
|||
|
|||
SelectableText( |
|||
string data, |
|||
TextSpan textSpan, |
|||
Key key = null, |
|||
FocusNode focusNode = null, |
|||
TextStyle style = null, |
|||
StrutStyle strutStyle = null, |
|||
TextAlign? textAlign = null, |
|||
TextDirection? textDirection = null, |
|||
float? textScaleFactor = null, |
|||
bool showCursor = false, |
|||
bool autofocus = false, |
|||
ToolbarOptions toolbarOptions = null, |
|||
int? minLines = null, |
|||
int? maxLines = null, |
|||
float cursorWidth = 2.0f, |
|||
Radius cursorRadius = null, |
|||
Color cursorColor = null, |
|||
DragStartBehavior dragStartBehavior = DragStartBehavior.start, |
|||
bool enableInteractiveSelection = true, |
|||
GestureTapCallback onTap = null, |
|||
ScrollPhysics scrollPhysics = null, |
|||
TextWidthBasis? textWidthBasis = null |
|||
) : base(key: key) { |
|||
D.assert(maxLines == null || maxLines > 0); |
|||
D.assert(minLines == null || minLines > 0); |
|||
D.assert( |
|||
(maxLines == null) || (minLines == null) || (maxLines >= minLines), |
|||
() => "minLines can\'t be greater than maxLines" |
|||
); |
|||
toolbarOptions = toolbarOptions ?? |
|||
new ToolbarOptions( |
|||
selectAll: true, |
|||
copy: true |
|||
); |
|||
|
|||
this.data = data; |
|||
this.textSpan = textSpan; |
|||
this.focusNode = focusNode; |
|||
this.style = style; |
|||
this.strutStyle = strutStyle; |
|||
this.textAlign = textAlign; |
|||
this.textDirection = textDirection; |
|||
this.textScaleFactor = textScaleFactor; |
|||
this.showCursor = showCursor; |
|||
this.autofocus = autofocus; |
|||
this.toolbarOptions = toolbarOptions; |
|||
this.minLines = minLines; |
|||
this.maxLines = maxLines; |
|||
this.cursorWidth = cursorWidth; |
|||
this.cursorRadius = cursorRadius; |
|||
this.cursorColor = cursorColor; |
|||
this.dragStartBehavior = dragStartBehavior; |
|||
this.enableInteractiveSelection = enableInteractiveSelection; |
|||
this.onTap = onTap; |
|||
this.scrollPhysics = scrollPhysics; |
|||
this.textWidthBasis = textWidthBasis; |
|||
} |
|||
|
|||
public static SelectableText rich( |
|||
TextSpan textSpan, |
|||
Key key = null, |
|||
FocusNode focusNode = null, |
|||
TextStyle style = null, |
|||
StrutStyle strutStyle = null, |
|||
TextAlign? textAlign = null, |
|||
TextDirection? textDirection = null, |
|||
float? textScaleFactor = null, |
|||
bool showCursor = false, |
|||
bool autofocus = false, |
|||
ToolbarOptions toolbarOptions = null, |
|||
int? minLines = null, |
|||
int? maxLines = null, |
|||
float cursorWidth = 2.0f, |
|||
Radius cursorRadius = null, |
|||
Color cursorColor = null, |
|||
DragStartBehavior dragStartBehavior = DragStartBehavior.start, |
|||
bool enableInteractiveSelection = true, |
|||
GestureTapCallback onTap = null, |
|||
ScrollPhysics scrollPhysics = null, |
|||
TextWidthBasis? textWidthBasis = null |
|||
) { |
|||
D.assert( |
|||
textSpan != null, |
|||
() => "A non-null TextSpan must be provided to a SelectableText.rich widget." |
|||
); |
|||
|
|||
return new SelectableText( |
|||
data: null, |
|||
textSpan: textSpan, |
|||
key: key, |
|||
focusNode: focusNode, |
|||
style: style, |
|||
strutStyle: strutStyle, |
|||
textAlign: textAlign, |
|||
textDirection: textDirection, |
|||
textScaleFactor: textScaleFactor, |
|||
showCursor: showCursor, |
|||
autofocus: autofocus, |
|||
toolbarOptions: toolbarOptions, |
|||
minLines: minLines, |
|||
maxLines: maxLines, |
|||
cursorWidth: cursorWidth, |
|||
cursorRadius: cursorRadius, |
|||
cursorColor: cursorColor, |
|||
dragStartBehavior: dragStartBehavior, |
|||
enableInteractiveSelection: enableInteractiveSelection, |
|||
onTap: onTap, |
|||
scrollPhysics: scrollPhysics, |
|||
textWidthBasis: textWidthBasis |
|||
); |
|||
} |
|||
|
|||
public readonly string data; |
|||
|
|||
public readonly TextSpan textSpan; |
|||
|
|||
public readonly FocusNode focusNode; |
|||
|
|||
public readonly TextStyle style; |
|||
|
|||
public readonly StrutStyle strutStyle; |
|||
|
|||
public readonly TextAlign? textAlign; |
|||
|
|||
public readonly TextDirection? textDirection; |
|||
|
|||
public readonly float? textScaleFactor; |
|||
|
|||
|
|||
public readonly bool autofocus; |
|||
|
|||
|
|||
public readonly int? minLines; |
|||
|
|||
|
|||
public readonly int? maxLines; |
|||
|
|||
|
|||
public readonly bool showCursor; |
|||
|
|||
|
|||
public readonly float cursorWidth; |
|||
|
|||
|
|||
public readonly Radius cursorRadius; |
|||
|
|||
public readonly Color cursorColor; |
|||
|
|||
|
|||
public readonly bool enableInteractiveSelection; |
|||
|
|||
|
|||
public readonly DragStartBehavior dragStartBehavior; |
|||
|
|||
public readonly ToolbarOptions toolbarOptions; |
|||
|
|||
|
|||
internal bool selectionEnabled { |
|||
get { return enableInteractiveSelection; } |
|||
} |
|||
|
|||
public readonly GestureTapCallback onTap; |
|||
|
|||
|
|||
public readonly ScrollPhysics scrollPhysics; |
|||
|
|||
|
|||
public readonly TextWidthBasis? textWidthBasis; |
|||
|
|||
public override State createState() => new _SelectableTextState(); |
|||
|
|||
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
|||
base.debugFillProperties(properties); |
|||
properties.add( new DiagnosticsProperty<String>("data", data, defaultValue: null)); |
|||
properties.add( new DiagnosticsProperty<FocusNode>("focusNode", focusNode, defaultValue: null)); |
|||
properties.add( new DiagnosticsProperty<TextStyle>("style", style, defaultValue: null)); |
|||
properties.add( new DiagnosticsProperty<bool>("autofocus", autofocus, defaultValue: false)); |
|||
properties.add( new DiagnosticsProperty<bool>("showCursor", showCursor, defaultValue: false)); |
|||
properties.add( new IntProperty("minLines", minLines, defaultValue: null)); |
|||
properties.add( new IntProperty("maxLines", maxLines, defaultValue: null)); |
|||
properties.add( new EnumProperty<TextAlign?>("textAlign", textAlign, defaultValue: null)); |
|||
properties.add( new EnumProperty<TextDirection?>("textDirection", textDirection, defaultValue: null)); |
|||
properties.add( new FloatProperty("textScaleFactor", textScaleFactor, defaultValue: null)); |
|||
properties.add( new FloatProperty("cursorWidth", cursorWidth, defaultValue: 2.0f)); |
|||
properties.add( new DiagnosticsProperty<Radius>("cursorRadius", cursorRadius, defaultValue: null)); |
|||
properties.add( new DiagnosticsProperty<Color>("cursorColor", cursorColor, defaultValue: null)); |
|||
properties.add( new FlagProperty("selectionEnabled", value: selectionEnabled, defaultValue: true, ifFalse: "selection disabled")); |
|||
properties.add( new DiagnosticsProperty<ScrollPhysics>("scrollPhysics", scrollPhysics, defaultValue: null)); |
|||
} |
|||
} |
|||
|
|||
class _SelectableTextState : AutomaticKeepAliveClientMixin<SelectableText>, |
|||
TextSelectionGestureDetectorBuilderDelegate { |
|||
|
|||
EditableTextState _editableText => editableTextKey.currentState; |
|||
|
|||
_TextSpanEditingController _controller; |
|||
|
|||
FocusNode _focusNode; |
|||
FocusNode _effectiveFocusNode => widget.focusNode ?? (_focusNode = _focusNode ?? new FocusNode()); |
|||
|
|||
bool _showSelectionHandles = false; |
|||
|
|||
_SelectableTextSelectionGestureDetectorBuilder _selectionGestureDetectorBuilder; |
|||
|
|||
public bool forcePressEnabled { |
|||
get { return _forcePressEnabled; } |
|||
set { _forcePressEnabled = value; } |
|||
} |
|||
|
|||
bool _forcePressEnabled; |
|||
|
|||
public GlobalKey<EditableTextState> editableTextKey { |
|||
get { return _editableTextKey; } |
|||
} |
|||
|
|||
readonly GlobalKey<EditableTextState> _editableTextKey = GlobalKey<EditableTextState>.key(); |
|||
|
|||
public bool selectionEnabled => widget.selectionEnabled; |
|||
|
|||
public override void initState() { |
|||
base.initState(); |
|||
_selectionGestureDetectorBuilder = new _SelectableTextSelectionGestureDetectorBuilder(state: this); |
|||
_controller = new _TextSpanEditingController( |
|||
textSpan: widget.textSpan ?? new TextSpan(text: widget.data) |
|||
); |
|||
} |
|||
|
|||
public override void didUpdateWidget(StatefulWidget oldWidget) { |
|||
var _oldWidget = (SelectableText) oldWidget; |
|||
base.didUpdateWidget(oldWidget); |
|||
if (widget.data != _oldWidget.data || widget.textSpan != _oldWidget.textSpan) { |
|||
_controller = new _TextSpanEditingController( |
|||
textSpan: widget.textSpan ?? new TextSpan(text: widget.data) |
|||
); |
|||
} |
|||
if (_effectiveFocusNode.hasFocus && _controller.selection.isCollapsed) { |
|||
_showSelectionHandles = false; |
|||
} |
|||
} |
|||
|
|||
public override void dispose() { |
|||
_focusNode?.dispose(); |
|||
base.dispose(); |
|||
} |
|||
|
|||
void _handleSelectionChanged(TextSelection selection, SelectionChangedCause cause) { |
|||
bool willShowSelectionHandles = _shouldShowSelectionHandles(cause); |
|||
if (willShowSelectionHandles != _showSelectionHandles) { |
|||
setState(() => { |
|||
_showSelectionHandles = willShowSelectionHandles; |
|||
}); |
|||
} |
|||
|
|||
switch (Theme.of(context).platform) { |
|||
case RuntimePlatform.IPhonePlayer: |
|||
case RuntimePlatform.OSXEditor: |
|||
case RuntimePlatform.OSXPlayer: |
|||
if (cause == SelectionChangedCause.longPress) { |
|||
_editableText?.bringIntoView(selection.basePos); |
|||
} |
|||
return; |
|||
default: |
|||
// Do nothing.
|
|||
break; |
|||
} |
|||
} |
|||
|
|||
void _handleSelectionHandleTapped() { |
|||
if (_controller.selection.isCollapsed) { |
|||
_editableText.toggleToolbar(); |
|||
} |
|||
} |
|||
|
|||
bool _shouldShowSelectionHandles(SelectionChangedCause cause) { |
|||
if (!_selectionGestureDetectorBuilder.shouldShowSelectionToolbar) |
|||
return false; |
|||
|
|||
if (_controller.selection.isCollapsed) |
|||
return false; |
|||
|
|||
if (cause == SelectionChangedCause.keyboard) |
|||
return false; |
|||
|
|||
if (cause == SelectionChangedCause.longPress) |
|||
return true; |
|||
|
|||
if (_controller.text.isNotEmpty()) |
|||
return true; |
|||
|
|||
return false; |
|||
} |
|||
|
|||
protected override bool wantKeepAlive => true; |
|||
|
|||
Widget build(BuildContext context) { |
|||
base.build(context); |
|||
D.assert(() => { |
|||
return _controller._textSpan.visitChildren((InlineSpan span) => span is TextSpan); |
|||
}, () => "SelectableText only supports TextSpan; Other type of InlineSpan is not allowed"); |
|||
D.assert(WidgetsD.debugCheckHasMediaQuery(context)); |
|||
D.assert(WidgetsD.debugCheckHasDirectionality(context)); |
|||
D.assert( |
|||
!(widget.style != null && widget.style.inherit == false && |
|||
(widget.style.fontSize == null || widget.style.textBaseline == null)), |
|||
() => "inherit false style must supply fontSize and textBaseline" |
|||
); |
|||
|
|||
ThemeData themeData = Theme.of(context); |
|||
FocusNode focusNode = _effectiveFocusNode; |
|||
|
|||
TextSelectionControls textSelectionControls; |
|||
bool paintCursorAboveText; |
|||
bool cursorOpacityAnimates; |
|||
Offset cursorOffset = Offset.zero; |
|||
Color cursorColor = widget.cursorColor; |
|||
Radius cursorRadius = widget.cursorRadius; |
|||
|
|||
switch (themeData.platform) { |
|||
case RuntimePlatform.IPhonePlayer: |
|||
case RuntimePlatform.OSXEditor: |
|||
case RuntimePlatform.OSXPlayer: |
|||
forcePressEnabled = true; |
|||
textSelectionControls = CupertinoTextSelectionUtils.cupertinoTextSelectionControls; |
|||
paintCursorAboveText = true; |
|||
cursorOpacityAnimates = true; |
|||
cursorColor = cursorColor ?? CupertinoTheme.of(context).primaryColor; |
|||
cursorRadius = cursorRadius?? Radius.circular(2.0f); |
|||
cursorOffset = new Offset(SelectableTextUtils.iOSHorizontalOffset / MediaQuery.of(context).devicePixelRatio, 0); |
|||
break; |
|||
|
|||
default: |
|||
forcePressEnabled = false; |
|||
textSelectionControls = _MaterialTextSelectionControls.materialTextSelectionControls; |
|||
paintCursorAboveText = false; |
|||
cursorOpacityAnimates = false; |
|||
cursorColor = cursorColor ?? themeData.cursorColor; |
|||
break; |
|||
} |
|||
|
|||
DefaultTextStyle defaultTextStyle = DefaultTextStyle.of(context); |
|||
TextStyle effectiveTextStyle = widget.style; |
|||
if (widget.style == null || widget.style.inherit) |
|||
effectiveTextStyle = defaultTextStyle.style.merge(widget.style); |
|||
if (MediaQuery.boldTextOverride(context)) |
|||
effectiveTextStyle = effectiveTextStyle.merge(new TextStyle(fontWeight: FontWeight.bold)); |
|||
|
|||
Widget child = new RepaintBoundary( |
|||
child: new EditableText( |
|||
key: editableTextKey, |
|||
style: effectiveTextStyle, |
|||
readOnly: true, |
|||
textWidthBasis: widget.textWidthBasis ?? defaultTextStyle.textWidthBasis, |
|||
showSelectionHandles: _showSelectionHandles, |
|||
showCursor: widget.showCursor, |
|||
controller: _controller, |
|||
focusNode: focusNode, |
|||
strutStyle: widget.strutStyle ?? new StrutStyle(), |
|||
textAlign: widget.textAlign ?? defaultTextStyle.textAlign ?? TextAlign.start, |
|||
textDirection: widget.textDirection, |
|||
textScaleFactor: widget.textScaleFactor, |
|||
autofocus: widget.autofocus, |
|||
forceLine: false, |
|||
toolbarOptions: widget.toolbarOptions, |
|||
minLines: widget.minLines, |
|||
maxLines: widget.maxLines ?? defaultTextStyle.maxLines, |
|||
selectionColor: themeData.textSelectionColor, |
|||
selectionControls: widget.selectionEnabled ? textSelectionControls : null, |
|||
onSelectionChanged: _handleSelectionChanged, |
|||
onSelectionHandleTapped: _handleSelectionHandleTapped, |
|||
rendererIgnoresPointer: true, |
|||
cursorWidth: widget.cursorWidth, |
|||
cursorRadius: cursorRadius, |
|||
cursorColor: cursorColor, |
|||
cursorOpacityAnimates: cursorOpacityAnimates, |
|||
cursorOffset: cursorOffset, |
|||
paintCursorAboveText: paintCursorAboveText, |
|||
backgroundCursorColor: CupertinoColors.inactiveGray, |
|||
enableInteractiveSelection: widget.enableInteractiveSelection, |
|||
dragStartBehavior: widget.dragStartBehavior, |
|||
scrollPhysics: widget.scrollPhysics |
|||
) |
|||
); |
|||
|
|||
return _selectionGestureDetectorBuilder.buildGestureDetector( |
|||
behavior: HitTestBehavior.translucent, |
|||
child: child |
|||
); |
|||
} |
|||
} |
|||
} |
|
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.gestures; |
|||
using Unity.UIWidgets.painting; |
|||
using Unity.UIWidgets.ui; |
|||
using Unity.UIWidgets.widgets; |
|||
|
|||
namespace Unity.UIWidgets.material { |
|||
public enum _SwitchListTileType { |
|||
material, |
|||
adaptive |
|||
} |
|||
|
|||
public class SwitchListTile : StatelessWidget { |
|||
public SwitchListTile( |
|||
Key key = null, |
|||
bool? value = null, |
|||
ValueChanged<bool?> onChanged = null, |
|||
Color activeColor = null, |
|||
Color activeTrackColor = null, |
|||
Color inactiveThumbColor = null, |
|||
Color inactiveTrackColor = null, |
|||
ImageProvider activeThumbImage = null, |
|||
ImageProvider inactiveThumbImage = null, |
|||
Widget title = null, |
|||
Widget subtitle = null, |
|||
bool isThreeLine = false, |
|||
bool? dense = null, |
|||
EdgeInsets contentPadding = null, |
|||
Widget secondary = null, |
|||
bool selected = false, |
|||
_SwitchListTileType _switchListTileType = _SwitchListTileType.material |
|||
) : base(key: key) { |
|||
D.assert(value != null); |
|||
D.assert(!isThreeLine || subtitle != null); |
|||
this.value = value.Value; |
|||
this.onChanged = onChanged; |
|||
this.activeColor = activeColor; |
|||
this.activeTrackColor = activeTrackColor; |
|||
this.inactiveThumbColor = inactiveThumbColor; |
|||
this.inactiveTrackColor = inactiveTrackColor; |
|||
this.activeThumbImage = activeThumbImage; |
|||
this.inactiveThumbImage = inactiveThumbImage; |
|||
this.title = title; |
|||
this.subtitle = subtitle; |
|||
this.isThreeLine = isThreeLine; |
|||
this.dense = dense; |
|||
this.contentPadding = contentPadding; |
|||
this.secondary = secondary; |
|||
this.selected = selected; |
|||
this._switchListTileType = _switchListTileType; |
|||
} |
|||
|
|||
public static SwitchListTile adaptive( |
|||
Key key = null, |
|||
bool? value = null, |
|||
ValueChanged<bool?> onChanged = null, |
|||
Color activeColor = null, |
|||
Color activeTrackColor = null, |
|||
Color inactiveThumbColor = null, |
|||
Color inactiveTrackColor = null, |
|||
ImageProvider activeThumbImage = null, |
|||
ImageProvider inactiveThumbImage = null, |
|||
Widget title = null, |
|||
Widget subtitle = null, |
|||
bool isThreeLine = false, |
|||
bool? dense = null, |
|||
EdgeInsets contentPadding = null, |
|||
Widget secondary = null, |
|||
bool selected = false) { |
|||
return new SwitchListTile( |
|||
key: key, |
|||
value: value, |
|||
onChanged: onChanged, |
|||
activeColor: activeColor, |
|||
activeTrackColor: activeTrackColor, |
|||
inactiveThumbColor: inactiveThumbColor, |
|||
inactiveTrackColor: inactiveTrackColor, |
|||
activeThumbImage: activeThumbImage, |
|||
inactiveThumbImage: inactiveThumbImage, |
|||
title: title, |
|||
subtitle: subtitle, |
|||
isThreeLine: isThreeLine, |
|||
dense: dense, |
|||
contentPadding: contentPadding, |
|||
secondary: secondary, |
|||
selected: selected, |
|||
_switchListTileType: _SwitchListTileType.adaptive |
|||
); |
|||
} |
|||
|
|||
|
|||
public readonly bool value; |
|||
|
|||
public readonly ValueChanged<bool?> onChanged; |
|||
|
|||
|
|||
public readonly Color activeColor; |
|||
|
|||
|
|||
public readonly Color activeTrackColor; |
|||
|
|||
|
|||
public readonly Color inactiveThumbColor; |
|||
|
|||
|
|||
public readonly Color inactiveTrackColor; |
|||
|
|||
|
|||
public readonly ImageProvider activeThumbImage; |
|||
|
|||
|
|||
public readonly ImageProvider inactiveThumbImage; |
|||
|
|||
|
|||
public readonly Widget title; |
|||
|
|||
|
|||
public readonly Widget subtitle; |
|||
|
|||
|
|||
public readonly Widget secondary; |
|||
|
|||
|
|||
public readonly bool isThreeLine; |
|||
|
|||
|
|||
public readonly bool? dense; |
|||
|
|||
|
|||
public readonly EdgeInsets contentPadding; |
|||
|
|||
|
|||
public readonly bool selected; |
|||
|
|||
|
|||
public readonly _SwitchListTileType _switchListTileType; |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
Widget control = null; |
|||
switch (_switchListTileType) { |
|||
case _SwitchListTileType.adaptive: |
|||
control = Switch.adaptive( |
|||
value: value, |
|||
onChanged: onChanged, |
|||
activeColor: activeColor, |
|||
activeThumbImage: activeThumbImage, |
|||
inactiveThumbImage: inactiveThumbImage, |
|||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, |
|||
activeTrackColor: activeTrackColor, |
|||
inactiveTrackColor: inactiveTrackColor, |
|||
inactiveThumbColor: inactiveThumbColor |
|||
); |
|||
break; |
|||
|
|||
case _SwitchListTileType.material: |
|||
control = new Switch( |
|||
value: value, |
|||
onChanged: onChanged, |
|||
activeColor: activeColor, |
|||
activeThumbImage: activeThumbImage, |
|||
inactiveThumbImage: inactiveThumbImage, |
|||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, |
|||
activeTrackColor: activeTrackColor, |
|||
inactiveTrackColor: inactiveTrackColor, |
|||
inactiveThumbColor: inactiveThumbColor |
|||
); |
|||
break; |
|||
} |
|||
|
|||
return ListTileTheme.merge( |
|||
selectedColor: activeColor ?? Theme.of(context).accentColor, |
|||
child: new ListTile( |
|||
leading: secondary, |
|||
title: title, |
|||
subtitle: subtitle, |
|||
trailing: control, |
|||
isThreeLine: isThreeLine, |
|||
dense: dense, |
|||
contentPadding: contentPadding, |
|||
enabled: onChanged != null, |
|||
onTap: onChanged != null ? () => { onChanged(!value); } : (GestureTapCallback) null, |
|||
selected: selected |
|||
) |
|||
); |
|||
} |
|||
} |
|||
} |
1001
com.unity.uiwidgets/Runtime/material/time_picker.cs
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
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 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 partial class material_ { |
|||
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, |
|||
material_.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, |
|||
material_.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.Value); |
|||
firstDate = utils.dateOnly(firstDate.Value); |
|||
lastDate = utils.dateOnly(lastDate.Value); |
|||
|
|||
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.Value), |
|||
() => $"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 material_.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 material_._calendarPortraitDialogSize; |
|||
case Orientation.landscape: |
|||
return material_._calendarLandscapeDialogSize; |
|||
} |
|||
|
|||
break; |
|||
case DatePickerEntryMode.input: |
|||
switch (orientation) { |
|||
case Orientation.portrait: |
|||
return material_._inputPortraitDialogSize; |
|||
case Orientation.landscape: |
|||
return material_._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: material_._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 |
|||
); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 906efcdd9b5d70846bd214c325e5387f |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
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, |
|||
material_.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.Value) : (DateTime?)null; |
|||
firstDate = utils.dateOnly(firstDate.Value); |
|||
lastDate = utils.dateOnly(lastDate.Value); |
|||
|
|||
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.Value), |
|||
() => $"Provided initialDate {initialDate} must satisfy provided selectableDayPredicate." |
|||
); |
|||
|
|||
this.initialDate = initialDate; |
|||
this.firstDate = firstDate.Value; |
|||
this.lastDate = lastDate.Value; |
|||
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 material_.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) |
|||
); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 0581869944bd34247af2ee4ada2f3a1f |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using com.unity.uiwidgets.Runtime.rendering; |
|||
using Unity.UIWidgets.animation; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.material; |
|||
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 partial class material_ { |
|||
public static readonly TimeSpan _monthScrollDuration = new TimeSpan(0, 0, 0, 0, 200); |
|||
|
|||
public const float _dayPickerRowHeight = 42.0f; |
|||
|
|||
public const int _maxDayPickerRowCount = 6; // A 31 day month that starts on Saturday.
|
|||
|
|||
public const float _maxDayPickerHeight = _dayPickerRowHeight * (_maxDayPickerRowCount + 1); |
|||
public const float _monthPickerHorizontalPadding = 8.0f; |
|||
|
|||
public const int _yearPickerColumnCount = 3; |
|||
public const float _yearPickerPadding = 16.0f; |
|||
public const float _yearPickerRowHeight = 52.0f; |
|||
public const float _yearPickerRowSpacing = 8.0f; |
|||
|
|||
public const float _subHeaderHeight = 52.0f; |
|||
public const float _monthNavButtonsWidth = 108.0f; |
|||
} |
|||
|
|||
public class CalendarDatePicker : StatefulWidget { |
|||
public CalendarDatePicker( |
|||
Key key = null, |
|||
DateTime? initialDate = null, |
|||
DateTime? firstDate = null, |
|||
DateTime? lastDate = null, |
|||
ValueChanged<DateTime> onDateChanged = null, |
|||
ValueChanged<DateTime> onDisplayedMonthChanged = null, |
|||
DatePickerMode initialCalendarMode = DatePickerMode.day, |
|||
material_.SelectableDayPredicate selectableDayPredicate = null |
|||
) : base(key: key) { |
|||
D.assert(initialDate != null); |
|||
D.assert(firstDate != null); |
|||
D.assert(lastDate != null); |
|||
initialDate = utils.dateOnly(initialDate.Value); |
|||
firstDate = utils.dateOnly(firstDate.Value); |
|||
lastDate = utils.dateOnly(lastDate.Value); |
|||
D.assert(onDateChanged != null); |
|||
D.assert(initialCalendarMode != null); |
|||
|
|||
this.onDateChanged = onDateChanged; |
|||
this.onDisplayedMonthChanged = onDisplayedMonthChanged; |
|||
this.initialCalendarMode = initialCalendarMode; |
|||
this.selectableDayPredicate = selectableDayPredicate; |
|||
D.assert( |
|||
!(this.lastDate < this.firstDate), |
|||
() => $"lastDate {lastDate} must be on or after firstDate {firstDate}." |
|||
); |
|||
D.assert( |
|||
!(this.initialDate < this.firstDate), |
|||
() => $"initialDate {initialDate} must be on or after firstDate {firstDate}." |
|||
); |
|||
D.assert( |
|||
!(this.initialDate > this.lastDate), |
|||
() => $"initialDate {initialDate} must be on or before lastDate {lastDate}." |
|||
); |
|||
D.assert( |
|||
selectableDayPredicate == null || selectableDayPredicate(this.initialDate), |
|||
() => $"Provided initialDate {initialDate} must satisfy provided selectableDayPredicate." |
|||
); |
|||
} |
|||
|
|||
public readonly DateTime initialDate; |
|||
|
|||
public readonly DateTime firstDate; |
|||
|
|||
public readonly DateTime lastDate; |
|||
|
|||
public readonly ValueChanged<DateTime> onDateChanged; |
|||
|
|||
public readonly ValueChanged<DateTime> onDisplayedMonthChanged; |
|||
|
|||
public readonly DatePickerMode initialCalendarMode; |
|||
|
|||
public readonly material_.SelectableDayPredicate selectableDayPredicate; |
|||
|
|||
public override State createState() => new UIWidgets.material._CalendarDatePickerState(); |
|||
} |
|||
|
|||
internal class _CalendarDatePickerState : State<CalendarDatePicker> { |
|||
bool _announcedInitialDate = false; |
|||
DatePickerMode _mode; |
|||
DateTime _currentDisplayedMonthDate; |
|||
DateTime _selectedDate; |
|||
public readonly GlobalKey _monthPickerKey = GlobalKey.key(); |
|||
public readonly GlobalKey _yearPickerKey = GlobalKey.key(); |
|||
MaterialLocalizations _localizations; |
|||
TextDirection _textDirection; |
|||
|
|||
public override void initState() { |
|||
base.initState(); |
|||
_mode = widget.initialCalendarMode; |
|||
_currentDisplayedMonthDate = new DateTime(widget.initialDate.Year, widget.initialDate.Month, 1); |
|||
_selectedDate = widget.initialDate; |
|||
} |
|||
|
|||
public override void didChangeDependencies() { |
|||
base.didChangeDependencies(); |
|||
_localizations = MaterialLocalizations.of(context); |
|||
_textDirection = Directionality.of(context); |
|||
if (!_announcedInitialDate) { |
|||
_announcedInitialDate = true; |
|||
// SemanticsService.announce(
|
|||
// _localizations.formatFullDate(_selectedDate),
|
|||
// _textDirection,
|
|||
// );
|
|||
} |
|||
} |
|||
|
|||
void _vibrate() { |
|||
// switch (Theme.of(context).platform) {
|
|||
// case TargetPlatform.android:
|
|||
// case TargetPlatform.fuchsia:
|
|||
// case TargetPlatform.linux:
|
|||
// case TargetPlatform.windows:
|
|||
// // HapticFeedback.vibrate();
|
|||
// break;
|
|||
// case TargetPlatform.iOS:
|
|||
// case TargetPlatform.macOS:
|
|||
// break;
|
|||
// }
|
|||
} |
|||
|
|||
void _handleModeChanged(DatePickerMode mode) { |
|||
_vibrate(); |
|||
setState(() => { |
|||
_mode = mode; |
|||
// if (_mode == DatePickerMode.day) {
|
|||
// SemanticsService.announce(
|
|||
// _localizations.formatMonthYear(_selectedDate),
|
|||
// _textDirection,
|
|||
// );
|
|||
// } else {
|
|||
// SemanticsService.announce(
|
|||
// _localizations.formatYear(_selectedDate),
|
|||
// _textDirection,
|
|||
// );
|
|||
// }
|
|||
}); |
|||
} |
|||
|
|||
void _handleMonthChanged(DateTime date) { |
|||
setState(() => { |
|||
if (_currentDisplayedMonthDate.Year != date.Year || _currentDisplayedMonthDate.Month != date.Month) { |
|||
_currentDisplayedMonthDate = new DateTime(date.Year, date.Month, 1); |
|||
widget.onDisplayedMonthChanged?.Invoke(_currentDisplayedMonthDate); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
void _handleYearChanged(DateTime value) { |
|||
_vibrate(); |
|||
|
|||
if (value < widget.firstDate) { |
|||
value = widget.firstDate; |
|||
} |
|||
else if (value > widget.lastDate) { |
|||
value = widget.lastDate; |
|||
} |
|||
|
|||
setState(() => { |
|||
_mode = DatePickerMode.day; |
|||
_handleMonthChanged(value); |
|||
}); |
|||
} |
|||
|
|||
void _handleDayChanged(DateTime value) { |
|||
_vibrate(); |
|||
setState(() => { |
|||
_selectedDate = value; |
|||
widget.onDateChanged?.Invoke(_selectedDate); |
|||
}); |
|||
} |
|||
|
|||
Widget _buildPicker() { |
|||
D.assert(_mode != null); |
|||
switch (_mode) { |
|||
case DatePickerMode.day: |
|||
return new _MonthPicker( |
|||
key: _monthPickerKey, |
|||
initialMonth: _currentDisplayedMonthDate, |
|||
currentDate: DateTime.Now, |
|||
firstDate: widget.firstDate, |
|||
lastDate: widget.lastDate, |
|||
selectedDate: _selectedDate, |
|||
onChanged: _handleDayChanged, |
|||
onDisplayedMonthChanged: _handleMonthChanged, |
|||
selectableDayPredicate: widget.selectableDayPredicate |
|||
); |
|||
case DatePickerMode.year: |
|||
return new Padding( |
|||
padding: EdgeInsets.only(top: material_._subHeaderHeight), |
|||
child: new _YearPicker( |
|||
key: _yearPickerKey, |
|||
currentDate: DateTime.Now, |
|||
firstDate: widget.firstDate, |
|||
lastDate: widget.lastDate, |
|||
initialDate: _currentDisplayedMonthDate, |
|||
selectedDate: _selectedDate, |
|||
onChanged: _handleYearChanged |
|||
) |
|||
); |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
return new Stack( |
|||
children: new List<Widget> { |
|||
new SingleChildScrollView( |
|||
child: new SizedBox( |
|||
height: material_._maxDayPickerHeight, |
|||
child: _buildPicker() |
|||
) |
|||
), |
|||
// Put the mode toggle button on top so that it won"t be covered up by the _MonthPicker
|
|||
new _DatePickerModeToggleButton( |
|||
mode: _mode, |
|||
title: _localizations.formatMonthYear(_currentDisplayedMonthDate), |
|||
onTitlePressed: () => { |
|||
// Toggle the day/year mode.
|
|||
_handleModeChanged(_mode == DatePickerMode.day ? DatePickerMode.year : DatePickerMode.day); |
|||
} |
|||
) |
|||
} |
|||
); |
|||
} |
|||
} |
|||
|
|||
class _DatePickerModeToggleButton : StatefulWidget { |
|||
internal _DatePickerModeToggleButton( |
|||
DatePickerMode mode, |
|||
string title, |
|||
VoidCallback onTitlePressed |
|||
) { |
|||
this.mode = mode; |
|||
this.title = title; |
|||
this.onTitlePressed = onTitlePressed; |
|||
} |
|||
|
|||
public readonly DatePickerMode mode; |
|||
|
|||
public readonly string title; |
|||
|
|||
public readonly VoidCallback onTitlePressed; |
|||
|
|||
public override State createState() => new _DatePickerModeToggleButtonState(); |
|||
} |
|||
|
|||
class _DatePickerModeToggleButtonState : SingleTickerProviderStateMixin<_DatePickerModeToggleButton> { |
|||
AnimationController _controller; |
|||
|
|||
public override void initState() { |
|||
base.initState(); |
|||
_controller = new AnimationController( |
|||
value: widget.mode == DatePickerMode.year ? 0.5f : 0, |
|||
upperBound: 0.5f, |
|||
duration: new TimeSpan(0, 0, 0, 0, 200), |
|||
vsync: this |
|||
); |
|||
} |
|||
|
|||
public override void didUpdateWidget(StatefulWidget statefulWidget) { |
|||
base.didUpdateWidget(statefulWidget); |
|||
if (statefulWidget is _DatePickerModeToggleButton oldWidget) { |
|||
if (oldWidget.mode == widget.mode) { |
|||
return; |
|||
} |
|||
|
|||
if (widget.mode == DatePickerMode.year) { |
|||
_controller.forward(); |
|||
} |
|||
else { |
|||
_controller.reverse(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
ColorScheme colorScheme = Theme.of(context).colorScheme; |
|||
TextTheme textTheme = Theme.of(context).textTheme; |
|||
Color controlColor = colorScheme.onSurface.withOpacity(0.60f); |
|||
|
|||
var rowChildren = new List<Widget> { |
|||
new Flexible( |
|||
child: new Container( |
|||
height: material_._subHeaderHeight, |
|||
child: new InkWell( |
|||
onTap: () => widget.onTitlePressed(), |
|||
child: new Padding( |
|||
padding: EdgeInsets.symmetric(horizontal: 8), |
|||
child: new Row( |
|||
children: new List<Widget> { |
|||
new Flexible( |
|||
child: new Text( |
|||
widget.title, |
|||
overflow: TextOverflow.ellipsis, |
|||
style: textTheme.subtitle2?.copyWith( |
|||
color: controlColor |
|||
) |
|||
) |
|||
), |
|||
new RotationTransition( |
|||
turns: _controller, |
|||
child: new Icon( |
|||
Icons.arrow_drop_down, |
|||
color: controlColor |
|||
) |
|||
), |
|||
} |
|||
) |
|||
) |
|||
) |
|||
) |
|||
) |
|||
}; |
|||
if (widget.mode == DatePickerMode.day) { |
|||
// Give space for the prev/next month buttons that are underneath this row
|
|||
rowChildren.Add(new SizedBox(width: material_._monthNavButtonsWidth)); |
|||
} |
|||
|
|||
return new Container( |
|||
//TODO: update to EdgeInsetsGeometry
|
|||
padding: (EdgeInsets) (EdgeInsetsGeometry) EdgeInsetsDirectional.only(start: 16, end: 4), |
|||
height: material_._subHeaderHeight, |
|||
child: new Row( |
|||
children: rowChildren |
|||
) |
|||
); |
|||
} |
|||
|
|||
public override void dispose() { |
|||
_controller.dispose(); |
|||
base.dispose(); |
|||
} |
|||
} |
|||
|
|||
class _MonthPicker : StatefulWidget { |
|||
internal _MonthPicker( |
|||
Key key = null, |
|||
DateTime? initialMonth = null, |
|||
DateTime? currentDate = null, |
|||
DateTime? firstDate = null, |
|||
DateTime? lastDate = null, |
|||
DateTime? selectedDate = null, |
|||
ValueChanged<DateTime> onChanged = null, |
|||
ValueChanged<DateTime> onDisplayedMonthChanged = null, |
|||
material_.SelectableDayPredicate selectableDayPredicate = null |
|||
) : base(key: key) { |
|||
D.assert(selectedDate != null); |
|||
D.assert(currentDate != null); |
|||
D.assert(onChanged != null); |
|||
D.assert(firstDate != null); |
|||
D.assert(lastDate != null); |
|||
D.assert(!(firstDate > lastDate)); |
|||
D.assert(!(selectedDate < firstDate)); |
|||
D.assert(!(selectedDate > lastDate)); |
|||
this.initialMonth = initialMonth.Value; |
|||
this.currentDate = currentDate.Value; |
|||
this.firstDate = firstDate.Value; |
|||
this.lastDate = lastDate.Value; |
|||
this.selectedDate = selectedDate.Value; |
|||
this.onChanged = onChanged; |
|||
this.onDisplayedMonthChanged = onDisplayedMonthChanged; |
|||
this.selectableDayPredicate = selectableDayPredicate; |
|||
} |
|||
|
|||
public readonly DateTime initialMonth; |
|||
|
|||
public readonly DateTime currentDate; |
|||
|
|||
public readonly DateTime firstDate; |
|||
|
|||
public readonly DateTime lastDate; |
|||
|
|||
public readonly DateTime selectedDate; |
|||
|
|||
public readonly ValueChanged<DateTime> onChanged; |
|||
|
|||
public readonly ValueChanged<DateTime> onDisplayedMonthChanged; |
|||
|
|||
public readonly material_.SelectableDayPredicate selectableDayPredicate; |
|||
|
|||
public override State createState() => new _MonthPickerState(); |
|||
} |
|||
|
|||
class _MonthPickerState : State<_MonthPicker> { |
|||
DateTime _currentMonth; |
|||
DateTime _nextMonthDate; |
|||
DateTime _previousMonthDate; |
|||
PageController _pageController; |
|||
MaterialLocalizations _localizations; |
|||
TextDirection _textDirection; |
|||
|
|||
public override void initState() { |
|||
base.initState(); |
|||
_currentMonth = widget.initialMonth; |
|||
_previousMonthDate = utils.addMonthsToMonthDate(_currentMonth, -1); |
|||
_nextMonthDate = utils.addMonthsToMonthDate(_currentMonth, 1); |
|||
_pageController = new PageController(initialPage: utils.monthDelta(widget.firstDate, _currentMonth)); |
|||
} |
|||
|
|||
public override void didChangeDependencies() { |
|||
base.didChangeDependencies(); |
|||
_localizations = MaterialLocalizations.of(context); |
|||
_textDirection = Directionality.of(context); |
|||
} |
|||
|
|||
public override void dispose() { |
|||
_pageController?.dispose(); |
|||
base.dispose(); |
|||
} |
|||
|
|||
void _handleMonthPageChanged(int monthPage) { |
|||
DateTime monthDate = utils.addMonthsToMonthDate(widget.firstDate, monthPage); |
|||
if (_currentMonth.Year != monthDate.Year || _currentMonth.Month != monthDate.Month) { |
|||
_currentMonth = new DateTime(monthDate.Year, monthDate.Month, 1); |
|||
_previousMonthDate = utils.addMonthsToMonthDate(_currentMonth, -1); |
|||
_nextMonthDate = utils.addMonthsToMonthDate(_currentMonth, 1); |
|||
widget.onDisplayedMonthChanged?.Invoke(_currentMonth); |
|||
} |
|||
} |
|||
|
|||
void _handleNextMonth() { |
|||
if (!_isDisplayingLastMonth) { |
|||
// SemanticsService.announce(
|
|||
// _localizations.formatMonthYear(_nextMonthDate),
|
|||
// _textDirection,
|
|||
// );
|
|||
_pageController.nextPage( |
|||
duration: material_._monthScrollDuration, |
|||
curve: Curves.ease |
|||
); |
|||
} |
|||
} |
|||
|
|||
void _handlePreviousMonth() { |
|||
if (!_isDisplayingFirstMonth) { |
|||
// SemanticsService.announce(
|
|||
// _localizations.formatMonthYear(_previousMonthDate),
|
|||
// _textDirection,
|
|||
// );
|
|||
_pageController.previousPage( |
|||
duration: material_._monthScrollDuration, |
|||
curve: Curves.ease |
|||
); |
|||
} |
|||
} |
|||
|
|||
bool _isDisplayingFirstMonth { |
|||
get { return !(_currentMonth > new DateTime(widget.firstDate.Year, widget.firstDate.Month, 1)); } |
|||
} |
|||
|
|||
bool _isDisplayingLastMonth { |
|||
get { |
|||
return !(_currentMonth < new DateTime(widget.lastDate.Year, widget.lastDate.Month, 1) |
|||
); |
|||
} |
|||
} |
|||
|
|||
Widget _buildItems(BuildContext context, int index) { |
|||
DateTime month = utils.addMonthsToMonthDate(widget.firstDate, index); |
|||
return new _DayPicker( |
|||
key: new ValueKey<DateTime>(month), |
|||
selectedDate: widget.selectedDate, |
|||
currentDate: widget.currentDate, |
|||
onChanged: widget.onChanged, |
|||
firstDate: widget.firstDate, |
|||
lastDate: widget.lastDate, |
|||
displayedMonth: month, |
|||
selectableDayPredicate: widget.selectableDayPredicate |
|||
); |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
string previousTooltipText = |
|||
$"{_localizations.previousMonthTooltip} {_localizations.formatMonthYear(_previousMonthDate)}"; |
|||
string nextTooltipText = |
|||
$"{_localizations.nextMonthTooltip} {_localizations.formatMonthYear(_nextMonthDate)}"; |
|||
Color controlColor = Theme.of(context).colorScheme.onSurface.withOpacity(0.60f); |
|||
|
|||
return new Column( |
|||
children: new List<Widget> { |
|||
new Container( |
|||
//TODO: update EdgeInsetsGeometry
|
|||
padding: (EdgeInsets) (EdgeInsetsGeometry) EdgeInsetsDirectional.only(start: 16, end: 4), |
|||
height: material_._subHeaderHeight, |
|||
child: |
|||
new Row( |
|||
children: new List<Widget> { |
|||
new Spacer(), |
|||
new IconButton( |
|||
icon: new Icon(Icons.chevron_left), |
|||
color: controlColor, |
|||
tooltip: _isDisplayingFirstMonth ? null : previousTooltipText, |
|||
onPressed: _isDisplayingFirstMonth ? (VoidCallback) null : _handlePreviousMonth |
|||
), |
|||
new IconButton(icon: new Icon(Icons.chevron_right), |
|||
color: controlColor, |
|||
tooltip: _isDisplayingLastMonth ? null : nextTooltipText, |
|||
onPressed: _isDisplayingLastMonth ? (VoidCallback) null : _handleNextMonth |
|||
) |
|||
} |
|||
) |
|||
), |
|||
new _DayHeaders(), |
|||
new Expanded( |
|||
child: PageView.builder( |
|||
controller: _pageController, |
|||
itemBuilder: _buildItems, |
|||
itemCount: utils.monthDelta(widget.firstDate, widget.lastDate) + 1, |
|||
scrollDirection: Axis.horizontal, |
|||
onPageChanged: _handleMonthPageChanged |
|||
) |
|||
) |
|||
} |
|||
); |
|||
} |
|||
} |
|||
|
|||
class _DayPicker : StatelessWidget { |
|||
internal _DayPicker( |
|||
Key key = null, |
|||
DateTime? currentDate = null, |
|||
DateTime? displayedMonth = null, |
|||
DateTime? firstDate = null, |
|||
DateTime? lastDate = null, |
|||
DateTime? selectedDate = null, |
|||
ValueChanged<DateTime> onChanged = null, |
|||
material_.SelectableDayPredicate selectableDayPredicate = null |
|||
) : base(key: key) { |
|||
D.assert(currentDate != null); |
|||
D.assert(displayedMonth != null); |
|||
D.assert(firstDate != null); |
|||
D.assert(lastDate != null); |
|||
D.assert(selectedDate != null); |
|||
D.assert(onChanged != null); |
|||
D.assert(!(firstDate > lastDate)); |
|||
D.assert(!(selectedDate < firstDate)); |
|||
D.assert(!(selectedDate > lastDate)); |
|||
this.currentDate = currentDate.Value; |
|||
this.displayedMonth = displayedMonth.Value; |
|||
this.firstDate = firstDate.Value; |
|||
this.lastDate = lastDate.Value; |
|||
this.selectedDate = selectedDate.Value; |
|||
this.onChanged = onChanged; |
|||
this.selectableDayPredicate = selectableDayPredicate; |
|||
} |
|||
|
|||
public readonly DateTime selectedDate; |
|||
|
|||
public readonly DateTime currentDate; |
|||
|
|||
public readonly ValueChanged<DateTime> onChanged; |
|||
|
|||
public readonly DateTime firstDate; |
|||
|
|||
public readonly DateTime lastDate; |
|||
|
|||
public readonly DateTime displayedMonth; |
|||
|
|||
public readonly material_.SelectableDayPredicate selectableDayPredicate; |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
ColorScheme colorScheme = Theme.of(context).colorScheme; |
|||
MaterialLocalizations localizations = MaterialLocalizations.of(context); |
|||
TextTheme textTheme = Theme.of(context).textTheme; |
|||
TextStyle dayStyle = textTheme.caption; |
|||
Color enabledDayColor = colorScheme.onSurface.withOpacity(0.87f); |
|||
Color disabledDayColor = colorScheme.onSurface.withOpacity(0.38f); |
|||
Color selectedDayColor = colorScheme.onPrimary; |
|||
Color selectedDayBackground = colorScheme.primary; |
|||
Color todayColor = colorScheme.primary; |
|||
|
|||
int year = displayedMonth.Year; |
|||
int month = displayedMonth.Month; |
|||
|
|||
int daysInMonth = utils.getDaysInMonth(year, month); |
|||
int dayOffset = utils.firstDayOffset(year, month, localizations); |
|||
|
|||
List<Widget> dayItems = new List<Widget>(); |
|||
// 1-based day of month, e.g. 1-31 for January, and 1-29 for February on
|
|||
// a leap year.
|
|||
int day = -dayOffset; |
|||
while (day < daysInMonth) { |
|||
day++; |
|||
if (day < 1) { |
|||
dayItems.Add(new Container()); |
|||
} |
|||
else { |
|||
DateTime dayToBuild = new DateTime(year, month, day); |
|||
bool isDisabled = dayToBuild > lastDate || |
|||
dayToBuild < firstDate || |
|||
(selectableDayPredicate != null && !selectableDayPredicate(dayToBuild)); |
|||
|
|||
BoxDecoration decoration = null; |
|||
Color dayColor = enabledDayColor; |
|||
bool isSelectedDay = utils.isSameDay(selectedDate, dayToBuild); |
|||
if (isSelectedDay) { |
|||
// The selected day gets a circle background highlight, and a
|
|||
// contrasting text color.
|
|||
dayColor = selectedDayColor; |
|||
decoration = new BoxDecoration( |
|||
color: selectedDayBackground, |
|||
shape: BoxShape.circle |
|||
); |
|||
} |
|||
else if (isDisabled) { |
|||
dayColor = disabledDayColor; |
|||
} |
|||
else if (utils.isSameDay(currentDate, dayToBuild)) { |
|||
// The current day gets a different text color and a circle stroke
|
|||
// border.
|
|||
dayColor = todayColor; |
|||
decoration = new BoxDecoration( |
|||
border: Border.all(color: todayColor, width: 1), |
|||
shape: BoxShape.circle |
|||
); |
|||
} |
|||
|
|||
Widget dayWidget = new Container( |
|||
decoration: decoration, |
|||
child: new Center( |
|||
child: new Text(localizations.formatDecimal(day), style: dayStyle.apply(color: dayColor)) |
|||
) |
|||
); |
|||
|
|||
if (isDisabled) { |
|||
dayWidget = dayWidget; |
|||
} |
|||
else { |
|||
dayWidget = new GestureDetector( |
|||
behavior: HitTestBehavior.opaque, |
|||
onTap: () => onChanged(dayToBuild), |
|||
child: dayWidget |
|||
); |
|||
} |
|||
|
|||
dayItems.Add(dayWidget); |
|||
} |
|||
} |
|||
|
|||
return new Padding( |
|||
padding: EdgeInsets.symmetric( |
|||
horizontal: material_._monthPickerHorizontalPadding |
|||
), |
|||
child: GridView.custom( |
|||
physics: new ClampingScrollPhysics(), |
|||
gridDelegate: material_._dayPickerGridDelegate, |
|||
childrenDelegate: new SliverChildListDelegate( |
|||
dayItems, |
|||
addRepaintBoundaries: false |
|||
) |
|||
) |
|||
); |
|||
} |
|||
} |
|||
|
|||
class _DayPickerGridDelegate : SliverGridDelegate { |
|||
internal _DayPickerGridDelegate() { |
|||
} |
|||
|
|||
const int daysPerWeek = 7; |
|||
|
|||
public override SliverGridLayout getLayout(SliverConstraints constraints) { |
|||
const int columnCount = daysPerWeek; |
|||
float tileWidth = constraints.crossAxisExtent / columnCount; |
|||
float tileHeight = Mathf.Min(material_._dayPickerRowHeight, |
|||
constraints.viewportMainAxisExtent / material_._maxDayPickerRowCount); |
|||
return new SliverGridRegularTileLayout( |
|||
childCrossAxisExtent: tileWidth, |
|||
childMainAxisExtent: tileHeight, |
|||
crossAxisCount: columnCount, |
|||
crossAxisStride: tileWidth, |
|||
mainAxisStride: tileHeight, |
|||
reverseCrossAxis: AxisUtils.axisDirectionIsReversed(constraints.crossAxisDirection) |
|||
); |
|||
} |
|||
|
|||
public override bool shouldRelayout(SliverGridDelegate sliverGridDelegate) => false; |
|||
} |
|||
|
|||
public partial class material_ { |
|||
internal static readonly _DayPickerGridDelegate _dayPickerGridDelegate = new _DayPickerGridDelegate(); |
|||
} |
|||
|
|||
class _DayHeaders : StatelessWidget { |
|||
List<Widget> _getDayHeaders(TextStyle headerStyle, MaterialLocalizations localizations) { |
|||
List<Widget> result = new List<Widget>(); |
|||
for (int i = localizations.firstDayOfWeekIndex; true; i = (i + 1) % 7) { |
|||
string weekday = localizations.narrowWeekdays[i]; |
|||
result.Add(new Center(child: new Text(weekday, style: headerStyle))); |
|||
if (i == (localizations.firstDayOfWeekIndex - 1) % 7) |
|||
break; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
ThemeData theme = Theme.of(context); |
|||
ColorScheme colorScheme = theme.colorScheme; |
|||
TextStyle dayHeaderStyle = theme.textTheme.caption?.apply( |
|||
color: colorScheme.onSurface.withOpacity(0.60f) |
|||
); |
|||
MaterialLocalizations localizations = MaterialLocalizations.of(context); |
|||
List<Widget> |
|||
labels = _getDayHeaders(dayHeaderStyle, localizations); |
|||
|
|||
return new Padding( |
|||
padding: EdgeInsets.symmetric(horizontal: material_._monthPickerHorizontalPadding), |
|||
child: |
|||
GridView.custom( |
|||
shrinkWrap: true, |
|||
gridDelegate: material_._dayPickerGridDelegate, |
|||
childrenDelegate: new SliverChildListDelegate( |
|||
labels, |
|||
addRepaintBoundaries: false |
|||
) |
|||
) |
|||
); |
|||
} |
|||
} |
|||
|
|||
class _YearPicker : StatefulWidget { |
|||
internal _YearPicker( |
|||
Key key = null, |
|||
DateTime? currentDate = null, |
|||
DateTime? firstDate = null, |
|||
DateTime? lastDate = null, |
|||
DateTime? initialDate = null, |
|||
DateTime? selectedDate = null, |
|||
ValueChanged<DateTime> onChanged = null |
|||
) : base(key: key) { |
|||
D.assert(currentDate != null); |
|||
D.assert(firstDate != null); |
|||
D.assert(lastDate != null); |
|||
D.assert(initialDate != null); |
|||
D.assert(selectedDate != null); |
|||
D.assert(onChanged != null); |
|||
D.assert(!(firstDate > lastDate)); |
|||
this.currentDate = currentDate.Value; |
|||
this.firstDate = firstDate.Value; |
|||
this.lastDate = lastDate.Value; |
|||
this.initialDate = initialDate.Value; |
|||
this.selectedDate = selectedDate.Value; |
|||
this.onChanged = onChanged; |
|||
} |
|||
|
|||
public readonly DateTime currentDate; |
|||
|
|||
public readonly DateTime firstDate; |
|||
|
|||
public readonly DateTime lastDate; |
|||
|
|||
public readonly DateTime initialDate; |
|||
|
|||
public readonly DateTime selectedDate; |
|||
|
|||
public readonly ValueChanged<DateTime> onChanged; |
|||
|
|||
public override |
|||
State createState() => new _YearPickerState(); |
|||
} |
|||
|
|||
class _YearPickerState : State<_YearPicker> { |
|||
ScrollController scrollController; |
|||
|
|||
// The approximate number of years necessary to fill the available space.
|
|||
public const int minYears = 18; |
|||
|
|||
public override void initState() { |
|||
base.initState(); |
|||
|
|||
// Set the scroll position to approximately center the initial year.
|
|||
int initialYearIndex = widget.selectedDate.Year - widget.firstDate.Year; |
|||
int initialYearRow = (int) (1.0f * initialYearIndex / material_._yearPickerColumnCount); |
|||
// Move the offset down by 2 rows to approximately center it.
|
|||
int centeredYearRow = initialYearRow - 2; |
|||
float scrollOffset = _itemCount < minYears ? 0 : centeredYearRow * material_._yearPickerRowHeight; |
|||
scrollController = new ScrollController(initialScrollOffset: scrollOffset); |
|||
} |
|||
|
|||
Widget _buildYearItem(BuildContext context, int index) { |
|||
ColorScheme colorScheme = Theme.of(context).colorScheme; |
|||
TextTheme textTheme = Theme.of(context).textTheme; |
|||
|
|||
// Backfill the _YearPicker with disabled years if necessary.
|
|||
int offset = _itemCount < minYears ? (int) (1.0f * (minYears - _itemCount) / 2) : 0; |
|||
int year = widget.firstDate.Year + index - offset; |
|||
bool isSelected = year == widget.selectedDate.Year; |
|||
bool isCurrentYear = year == widget.currentDate.Year; |
|||
bool isDisabled = year < widget.firstDate.Year || year > widget.lastDate.Year; |
|||
const float decorationHeight = 36.0f; |
|||
const float decorationWidth = 72.0f; |
|||
|
|||
Color textColor; |
|||
if (isSelected) { |
|||
textColor = colorScheme.onPrimary; |
|||
} |
|||
else if (isDisabled) { |
|||
textColor = colorScheme.onSurface.withOpacity(0.38f); |
|||
} |
|||
else if (isCurrentYear) { |
|||
textColor = colorScheme.primary; |
|||
} |
|||
else { |
|||
textColor = colorScheme.onSurface.withOpacity(0.87f); |
|||
} |
|||
|
|||
TextStyle itemStyle = textTheme.bodyText1?.apply(color: textColor); |
|||
|
|||
BoxDecoration decoration = null; |
|||
if (isSelected) { |
|||
decoration = new BoxDecoration( |
|||
color: colorScheme.primary, |
|||
borderRadius: BorderRadius.circular(decorationHeight / 2), |
|||
shape: BoxShape.rectangle |
|||
); |
|||
} |
|||
else if (isCurrentYear && !isDisabled) { |
|||
decoration = new BoxDecoration( |
|||
border: Border.all( |
|||
color: colorScheme.primary, |
|||
width: 1 |
|||
), |
|||
borderRadius: BorderRadius.circular(decorationHeight / 2), |
|||
shape: BoxShape.rectangle |
|||
); |
|||
} |
|||
|
|||
Widget yearItem = new Center( |
|||
child: new Container( |
|||
decoration: decoration, |
|||
height: decorationHeight, |
|||
width: decorationWidth, |
|||
child: new Center( |
|||
child: new Text(year.ToString(), style: itemStyle) |
|||
) |
|||
) |
|||
); |
|||
|
|||
if (isDisabled) { |
|||
yearItem = yearItem; |
|||
} |
|||
else { |
|||
yearItem = new InkWell( |
|||
key: new ValueKey<int>(year), |
|||
onTap: () => { |
|||
widget.onChanged( |
|||
new DateTime( |
|||
year, |
|||
widget.initialDate.Month, |
|||
widget.initialDate.Day |
|||
) |
|||
); |
|||
}, |
|||
child: yearItem |
|||
); |
|||
} |
|||
|
|||
return yearItem; |
|||
} |
|||
|
|||
int _itemCount { |
|||
get { return widget.lastDate.Year - widget.firstDate.Year + 1; } |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
return new Column( |
|||
children: new List<Widget> { |
|||
new Divider(), |
|||
new Expanded( |
|||
child: GridView.builder( |
|||
controller: scrollController, |
|||
gridDelegate: material_._yearPickerGridDelegate, |
|||
itemBuilder: _buildYearItem, |
|||
itemCount: Math.Max(_itemCount, minYears), |
|||
padding: EdgeInsets.symmetric(horizontal: material_._yearPickerPadding) |
|||
) |
|||
), |
|||
new Divider(), |
|||
} |
|||
); |
|||
} |
|||
} |
|||
|
|||
class _YearPickerGridDelegate : SliverGridDelegate { |
|||
internal _YearPickerGridDelegate() { |
|||
} |
|||
|
|||
public override SliverGridLayout getLayout(SliverConstraints constraints) { |
|||
float tileWidth = |
|||
(constraints.crossAxisExtent - |
|||
(material_._yearPickerColumnCount - 1) * material_._yearPickerRowSpacing) / |
|||
material_._yearPickerColumnCount; |
|||
return new SliverGridRegularTileLayout( |
|||
childCrossAxisExtent: tileWidth, |
|||
childMainAxisExtent: material_._yearPickerRowHeight, |
|||
crossAxisCount: material_._yearPickerColumnCount, |
|||
crossAxisStride: tileWidth + material_._yearPickerRowSpacing, |
|||
mainAxisStride: material_._yearPickerRowHeight, |
|||
reverseCrossAxis: AxisUtils.axisDirectionIsReversed(constraints.crossAxisDirection) |
|||
); |
|||
} |
|||
|
|||
public override bool shouldRelayout(SliverGridDelegate sliverGridDelegate) => false; |
|||
} |
|||
|
|||
public partial class material_ { |
|||
internal static readonly _YearPickerGridDelegate _yearPickerGridDelegate = new _YearPickerGridDelegate(); |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: b29a37617b764cbe8c7174802e4792f7 |
|||
timeCreated: 1611818184 |
|
|||
fileFormatVersion: 2 |
|||
guid: c80395d6c7ce4450a7c24ab750bbed67 |
|||
timeCreated: 1611820996 |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Unity.UIWidgets.material { |
|||
public static partial class utils { |
|||
public static DateTime dateOnly(DateTime date) { |
|||
return new DateTime(date.Year, date.Month, date.Day); |
|||
} |
|||
|
|||
public static bool isSameDay(DateTime dateA, DateTime dateB) { |
|||
return |
|||
dateA.Year == dateB.Year && |
|||
dateA.Month == dateB.Month && |
|||
dateA.Day == dateB.Day; |
|||
} |
|||
|
|||
public static int monthDelta(DateTime startDate, DateTime endDate) { |
|||
return (endDate.Year - startDate.Year) * 12 + endDate.Month - startDate.Month; |
|||
} |
|||
|
|||
public static DateTime addMonthsToMonthDate(DateTime monthDate, int monthsToAdd) { |
|||
return new DateTime(monthDate.Year, monthDate.Month + monthsToAdd, 1); |
|||
} |
|||
|
|||
public static int firstDayOffset(int year, int month, MaterialLocalizations localizations) { |
|||
// 0-based day of week for the month and year, with 0 representing Monday.
|
|||
int weekdayFromMonday = (int) ((new DateTime(year, month, 1)).DayOfWeek) - 1; |
|||
|
|||
// 0-based start of week depending on the locale, with 0 representing Sunday.
|
|||
int firstDayOfWeekIndex = localizations.firstDayOfWeekIndex; |
|||
|
|||
// firstDayOfWeekIndex recomputed to be Monday-based, in order to compare with
|
|||
// weekdayFromMonday.
|
|||
firstDayOfWeekIndex = (firstDayOfWeekIndex - 1) % 7; |
|||
|
|||
// Number of days between the first day of week appearing on the calendar,
|
|||
// and the day corresponding to the first of the month.
|
|||
return (weekdayFromMonday - firstDayOfWeekIndex) % 7; |
|||
} |
|||
|
|||
public const int february = 2; |
|||
|
|||
public static int getDaysInMonth(int year, int month) { |
|||
if (month == february) { |
|||
bool isLeapYear = (year % 4 == 0) && (year % 100 != 0) || |
|||
(year % 400 == 0); |
|||
if (isLeapYear) |
|||
return 29; |
|||
return 28; |
|||
} |
|||
|
|||
List<int> daysInMonth = new List<int> { |
|||
31, -1, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 |
|||
}; |
|||
return daysInMonth[month - 1]; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 5a5006599804476d959581f0fa09860f |
|||
timeCreated: 1611819837 |
|
|||
using System; |
|||
|
|||
namespace Unity.UIWidgets.material { |
|||
public enum DatePickerEntryMode { |
|||
calendar, |
|||
|
|||
input, |
|||
} |
|||
|
|||
public enum DatePickerMode { |
|||
day, |
|||
|
|||
year, |
|||
} |
|||
|
|||
public partial class material_ { |
|||
public delegate bool SelectableDayPredicate(DateTime? day); |
|||
} |
|||
|
|||
} |
撰写
预览
正在加载...
取消
保存
Reference in new issue