Created
May 5, 2019 08:06
-
-
Save abhishekwebcode/10e4ea7443c342946c0f00ff46a3b11f to your computer and use it in GitHub Desktop.
picker modified to give headers for children in myjobs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/cupertino.dart'; | |
import 'package:flutter/material.dart'; | |
import 'package:flutter/src/material/dialog.dart' as Dialog; | |
const bool __printDebug = false; | |
/// Picker selected callback. | |
typedef PickerSelectedCallback = void Function( | |
Picker picker, int index, List<int> selecteds); | |
/// Picker confirm callback. | |
typedef PickerConfirmCallback = void Function( | |
Picker picker, List<int> selecteds); | |
/// Picker value format callback. | |
typedef PickerValueFormat<T> = String Function(T value); | |
/// localizations | |
class PickerLocalizations { | |
static const Map<String, Map<String, Object>> localizedValues = { | |
'en': { | |
'cancelText': 'Cancel', | |
'confirmText': 'Confirm', | |
'ampm': ['AM', 'PM'], | |
}, | |
'zh': { | |
'cancelText': '取消', | |
'confirmText': '确定', | |
'ampm': ['上午', '下午'], | |
}, | |
'ko': { | |
'cancelText': '취소', | |
'confirmText': '확인', | |
'ampm': ['오전', '오후'], | |
}, | |
'it': { | |
'cancelText': 'Annulla', | |
'confirmText': 'Conferma', | |
'ampm': ['AM', 'PM'], | |
}, | |
'ar':{ | |
'cancelText': 'إلغاء الأمر', | |
'confirmText': 'تأكيد', | |
'ampm': ['صباحاً', 'مساءً'], | |
}, | |
}; | |
static PickerLocalizations _static = const PickerLocalizations(null); | |
final Locale locale; | |
const PickerLocalizations(this.locale); | |
static PickerLocalizations of(BuildContext context) { | |
return Localizations.of<PickerLocalizations>(context, PickerLocalizations) ?? _static; | |
} | |
Object getItem(String key) { | |
Map localData; | |
if (locale != null) localData = localizedValues[locale.languageCode]; | |
if (localData == null) return localizedValues['en'][key]; | |
return localData[key]; | |
} | |
String get cancelText => getItem("cancelText"); | |
String get confirmText => getItem("confirmText"); | |
List get ampm => getItem("ampm"); | |
} | |
/// Picker | |
class Picker { | |
static const double DefaultTextSize = 20.0; | |
List<int> selecteds; | |
final PickerAdapter adapter; | |
final List<PickerDelimiter> delimiter; | |
final VoidCallback onCancel; | |
final PickerSelectedCallback onSelect; | |
final PickerConfirmCallback onConfirm; | |
final changeToFirst; | |
final List<int> columnFlex; | |
final Widget title; | |
final Widget cancel; | |
final Widget confirm; | |
final String cancelText; | |
final String confirmText; | |
final double height, itemExtent; | |
final TextStyle textStyle, cancelTextStyle, confirmTextStyle; | |
final TextAlign textAlign; | |
final EdgeInsetsGeometry columnPadding; | |
final Color backgroundColor, headercolor, containerColor; | |
final bool hideHeader; | |
final bool looping; | |
final Decoration headerDecoration; | |
Widget _widget; | |
PickerWidgetState _state; | |
Picker( | |
{this.adapter, | |
this.delimiter, | |
this.selecteds, | |
this.height = 150.0, | |
this.itemExtent = 28.0, | |
this.columnPadding, | |
this.textStyle, | |
this.cancelTextStyle, | |
this.confirmTextStyle, | |
this.textAlign = TextAlign.start, | |
this.title, | |
this.cancel, | |
this.confirm, | |
this.cancelText, | |
this.confirmText, | |
this.backgroundColor = Colors.white, | |
this.containerColor, | |
this.headercolor, | |
this.changeToFirst = false, | |
this.hideHeader = false, | |
this.looping = false, | |
this.headerDecoration, | |
this.columnFlex, | |
this.onCancel, | |
this.onSelect, | |
this.onConfirm}) | |
: assert(adapter != null); | |
Widget get widget => _widget; | |
PickerWidgetState get state => _state; | |
int _maxLevel = 1; | |
/// 生成picker控件 | |
Widget makePicker([ThemeData themeData, bool isModal = false]) { | |
_maxLevel = adapter.maxLevel; | |
adapter.picker = this; | |
adapter.initSelects(); | |
_widget = _PickerWidget(picker: this, themeData: themeData, isModal: isModal); | |
return _widget; | |
} | |
/// 显示 picker | |
void show(ScaffoldState state, [ThemeData themeData]) { | |
state.showBottomSheet((BuildContext context) { | |
return makePicker(themeData); | |
}); | |
} | |
/// 显示模态 picker | |
void showModal(BuildContext context, [ThemeData themeData]) { | |
showModalBottomSheet( | |
context: context, //state.context, | |
builder: (BuildContext context) { | |
return makePicker(themeData, true); | |
}); | |
} | |
/// show dialog picker | |
void showDialog(BuildContext context) { | |
Dialog.showDialog( | |
context: context, | |
builder: (BuildContext context) { | |
List<Widget> actions = []; | |
if (cancel == null) { | |
String _cancelText = cancelText ?? PickerLocalizations.of(context).cancelText; | |
if (_cancelText != null && _cancelText != "") { | |
actions.add(FlatButton( | |
onPressed: () { | |
Navigator.pop(context); | |
if (onCancel != null) onCancel(); | |
}, | |
child: Text(_cancelText))); | |
} | |
} else { | |
actions.add(cancel); | |
} | |
if (confirm == null) { | |
String _confirmText = confirmText ?? PickerLocalizations.of(context).confirmText; | |
if (_confirmText != null && _confirmText != "") { | |
actions.add(FlatButton( | |
onPressed: () { | |
Navigator.pop(context); | |
if (onConfirm != null) onConfirm(this, selecteds); | |
}, | |
child: Text(_confirmText))); | |
} | |
} else { | |
actions.add(confirm); | |
} | |
return AlertDialog( | |
title: title, | |
actions: actions, | |
content: makePicker(), | |
); | |
}); | |
} | |
/// 获取当前选择的值 | |
List getSelectedValues() { | |
return adapter.getSelectedValues(); | |
} | |
/// 取消 | |
void doCancel(BuildContext context) { | |
if (onCancel != null) onCancel(); | |
Navigator.of(context).pop(); | |
_widget = null; | |
} | |
/// 确定 | |
void doConfirm(BuildContext context) { | |
if (onConfirm != null) | |
onConfirm(this, selecteds); | |
Navigator.of(context).pop(); | |
_widget = null; | |
} | |
} | |
/// 分隔符 | |
class PickerDelimiter { | |
final Widget child; | |
final int column; | |
PickerDelimiter({this.child, this.column = 1}) : assert(child != null); | |
} | |
/// picker data list item | |
class PickerItem<T> { | |
/// 显示内容 | |
final Widget text; | |
/// 数据值 | |
final T value; | |
/// 子项 | |
final List<PickerItem<T>> children; | |
PickerItem({this.text, this.value, this.children}); | |
} | |
class _PickerWidget<T> extends StatefulWidget { | |
final Picker picker; | |
final ThemeData themeData; | |
final bool isModal; | |
_PickerWidget({Key key, @required this.picker, @required this.themeData, this.isModal}) | |
: super(key: key); | |
@override | |
PickerWidgetState createState() => | |
PickerWidgetState<T>(picker: this.picker, themeData: this.themeData); | |
} | |
class PickerWidgetState<T> extends State<_PickerWidget> { | |
final Picker picker; | |
final ThemeData themeData; | |
PickerWidgetState({Key key, @required this.picker, @required this.themeData}); | |
ThemeData theme; | |
final List<FixedExtentScrollController> scrollController = []; | |
@override | |
void initState() { | |
super.initState(); | |
theme = themeData; | |
picker._state = this; | |
picker.adapter.doShow(); | |
if (scrollController.length == 0) { | |
for (int i = 0; i < picker._maxLevel; i++) | |
scrollController.add( | |
FixedExtentScrollController(initialItem: picker.selecteds[i])); | |
} | |
} | |
@override | |
Widget build(BuildContext context) { | |
var v = Column( | |
mainAxisSize: MainAxisSize.min, | |
children: <Widget>[ | |
(picker.hideHeader) | |
? SizedBox() | |
: Container( | |
child: Row( | |
children: _buildHeaderViews(), | |
), | |
decoration: picker.headerDecoration ?? BoxDecoration( | |
border: Border( | |
top: BorderSide(color: theme.dividerColor, width: 0.5)), | |
color: picker.headercolor == null | |
? theme.bottomAppBarColor | |
: picker.headercolor, | |
), | |
), | |
(false)?Row( | |
children: <Widget>[ | |
Expanded( | |
flex: 2, // 20% | |
child: getText("Location")), | |
Expanded( | |
flex: 2, // 60% | |
child: getText("Job Scope")), | |
Expanded( | |
flex: 2, // 20% | |
child: getText("Expected Salary")) | |
], | |
):Container(), | |
Row( | |
mainAxisAlignment: MainAxisAlignment.spaceBetween, | |
children: _buildViews(), | |
), | |
], | |
); | |
if (widget.isModal == null || widget.isModal == false) | |
return v; | |
return GestureDetector( | |
onTap: () {}, | |
child: v, | |
); | |
} | |
Padding getText(String text) { | |
return Padding( | |
padding: EdgeInsets.all(5), | |
child: Text( | |
text, | |
textAlign: TextAlign.center, | |
style: TextStyle(color: Colors.black, fontSize: 15), | |
), | |
); | |
} | |
List<Widget> _buildHeaderViews() { | |
if (theme == null) theme = Theme.of(context); | |
List<Widget> items = []; | |
if (picker.cancel != null) { | |
items.add(DefaultTextStyle(style: picker.cancelTextStyle ?? TextStyle( | |
color: theme.accentColor, fontSize: Picker.DefaultTextSize | |
), child: picker.cancel)); | |
} else { | |
String _cancelText = picker.cancelText ?? PickerLocalizations.of(context).cancelText; | |
if (_cancelText != null || _cancelText != "") { | |
items.add(FlatButton( | |
onPressed: () { | |
picker.doCancel(context); | |
}, | |
child: Text(_cancelText, overflow: TextOverflow.ellipsis, | |
style: picker.cancelTextStyle ?? TextStyle( | |
color: theme.accentColor, fontSize: Picker.DefaultTextSize | |
) | |
) | |
)); | |
} | |
} | |
items.add(Expanded( | |
child: Container( | |
alignment: Alignment.center, | |
child: picker.title == null | |
? picker.title | |
: DefaultTextStyle( | |
style: TextStyle( | |
fontSize: Picker.DefaultTextSize, | |
color: theme.textTheme.title.color), | |
child: picker.title), | |
))); | |
if (picker.confirm != null) { | |
items.add(DefaultTextStyle(style: picker.confirmTextStyle ?? TextStyle( | |
color: theme.accentColor, fontSize: Picker.DefaultTextSize | |
), child: picker.confirm)); | |
} else { | |
String _confirmText = picker.confirmText ?? PickerLocalizations.of(context).confirmText; | |
if (_confirmText != null || _confirmText != "") { | |
items.add(FlatButton( | |
onPressed: () { | |
picker.doConfirm(context); | |
}, | |
child: Text(_confirmText, | |
overflow: TextOverflow.ellipsis, | |
style: picker.confirmTextStyle ?? | |
TextStyle( | |
color: theme.accentColor, | |
fontSize: Picker.DefaultTextSize)))); | |
} | |
} | |
return items; | |
} | |
bool _changeing = false; | |
final Map<int, int> lastData = {}; | |
List<Widget> _buildViews() { | |
if (__printDebug) print("_buildViews"); | |
if (theme == null) theme = Theme.of(context); | |
List<Widget> items = []; | |
PickerAdapter adapter = picker.adapter; | |
if (adapter != null) adapter.setColumn(-1); | |
if (adapter != null && adapter.length > 0) { | |
for (int i = 0; i < picker._maxLevel; i++) { | |
final int _length = adapter.length; | |
Widget view = new Expanded( | |
flex: adapter.getColumnFlex(i), | |
child: Container( | |
padding: picker.columnPadding, | |
height: picker.height, | |
decoration: BoxDecoration( | |
border: picker.hideHeader | |
? null | |
: new Border( | |
top: BorderSide(color: theme.dividerColor, width: 0.5)), | |
color: picker.containerColor == null | |
? theme.dialogBackgroundColor | |
: picker.containerColor, | |
), | |
child: CupertinoPicker( | |
backgroundColor: picker.backgroundColor, | |
scrollController: scrollController[i], | |
itemExtent: picker.itemExtent, | |
looping: picker.looping, | |
onSelectedItemChanged: (int index) { | |
if (__printDebug) print("onSelectedItemChanged"); | |
setState(() { | |
picker.selecteds[i] = index; | |
updateScrollController(i); | |
adapter.doSelect(i, index); | |
if (picker.changeToFirst) { | |
for (int j = i + 1; j < picker.selecteds.length; j++) { | |
picker.selecteds[j] = 0; | |
scrollController[j].jumpTo(0.0); | |
} | |
} | |
if (picker.onSelect != null) | |
picker.onSelect(picker, i, picker.selecteds); | |
}); | |
}, | |
children: List<Widget>.generate(_length, (int index) { | |
return adapter.buildItem(context, index); | |
}), | |
), | |
), | |
); | |
items.add(view); | |
adapter.setColumn(i); | |
} | |
} | |
if (picker.delimiter != null) { | |
for (int i = 0; i < picker.delimiter.length; i++) { | |
var o = picker.delimiter[i]; | |
if (o.child == null) continue; | |
var item = Container(child: o.child, height: picker.height); | |
if (o.column < 0) | |
items.insert(0, item); | |
else if (o.column >= items.length) | |
items.add(item); | |
else | |
items.insert(o.column, item); | |
} | |
} | |
return items; | |
} | |
void updateScrollController(int i) { | |
if (_changeing || !picker.adapter.isLinkage) return; | |
_changeing = true; | |
for (int j = 0; j < picker.selecteds.length; j++) { | |
if (j != i) { | |
if (scrollController[j].position.maxScrollExtent == null) | |
continue; | |
scrollController[j].position.notifyListeners(); | |
} | |
} | |
_changeing = false; | |
} | |
} | |
/// 选择器数据适配器 | |
abstract class PickerAdapter<T> { | |
Picker picker; | |
int getLength(); | |
int getMaxLevel(); | |
void setColumn(int index); | |
void initSelects(); | |
Widget buildItem(BuildContext context, int index); | |
Widget makeText(Widget child, String text) { | |
return new Container( | |
alignment: Alignment.center, | |
child: DefaultTextStyle( | |
overflow: TextOverflow.ellipsis, | |
maxLines: 1, | |
textAlign: picker.textAlign, | |
style: picker.textStyle ?? | |
new TextStyle( | |
color: Colors.black87, fontSize: Picker.DefaultTextSize), | |
child: child ?? new Text(text) | |
) | |
); | |
} | |
Widget makeTextEx(Widget child, String text, Widget postfix, Widget suffix) { | |
List<Widget> items = []; | |
if (postfix != null) | |
items.add(postfix); | |
items.add(child ?? new Text(text)); | |
if (suffix != null) | |
items.add(suffix); | |
return new Container( | |
alignment: Alignment.center, | |
child: DefaultTextStyle( | |
overflow: TextOverflow.ellipsis, | |
maxLines: 1, | |
textAlign: picker.textAlign, | |
style: picker.textStyle ?? | |
new TextStyle( | |
color: Colors.black87, fontSize: Picker.DefaultTextSize), | |
child: Wrap( | |
children: items, | |
) | |
) | |
); | |
} | |
String getText() { | |
return getSelectedValues().toString(); | |
} | |
List<T> getSelectedValues() { | |
return []; | |
} | |
void doShow() {} | |
void doSelect(int column, int index) {} | |
int getColumnFlex(int column) { | |
if (picker.columnFlex != null && column < picker.columnFlex.length) | |
return picker.columnFlex[column]; | |
return 1; | |
} | |
int get maxLevel => getMaxLevel(); | |
int get length => getLength(); | |
String get text => getText(); | |
// 是否联动,即后面的列受前面列数据影响 | |
bool get isLinkage => getIsLinkage(); | |
@override | |
String toString() { | |
return getText(); | |
} | |
bool getIsLinkage() { | |
return true; | |
} | |
/// 通知适配器数据改变 | |
void notifyDataChanged() { | |
if (picker != null && picker.state != null) { | |
picker.adapter.doShow(); | |
picker.adapter.initSelects(); | |
for (int j = 0; j < picker.selecteds.length; j++) | |
picker.state.scrollController[j].jumpToItem(picker.selecteds[j]); | |
} | |
} | |
} | |
/// 数据适配器 | |
class PickerDataAdapter<T> extends PickerAdapter<T> { | |
List<PickerItem<T>> data; | |
List<PickerItem<dynamic>> _datas; | |
int _maxLevel = -1; | |
final bool isArray; | |
PickerDataAdapter({List pickerdata, this.data, this.isArray = false}) { | |
_parseData(pickerdata); | |
} | |
@override | |
bool getIsLinkage() { | |
return !isArray; | |
} | |
void _parseData(final List pickerData) { | |
if (pickerData != null && | |
pickerData.length > 0 && | |
(data == null || data.length == 0)) { | |
if (data == null) data = new List<PickerItem<T>>(); | |
if (isArray) { | |
_parseArrayPickerDataItem(pickerData, data); | |
} else { | |
_parsePickerDataItem(pickerData, data); | |
} | |
} | |
} | |
_parseArrayPickerDataItem(List pickerData, List<PickerItem> data) { | |
if (pickerData == null) return; | |
for (int i = 0; i < pickerData.length; i++) { | |
var v = pickerData[i]; | |
if (!(v is List)) continue; | |
List lv = v; | |
if (lv.length == 0) continue; | |
PickerItem item = new PickerItem<T>(children: List<PickerItem<T>>()); | |
data.add(item); | |
for (int j = 0; j < lv.length; j++) { | |
var o = lv[j]; | |
if (o is T) { | |
item.children.add(new PickerItem<T>(value: o)); | |
} else if (T == String) { | |
String _v = o.toString(); | |
item.children.add(new PickerItem<T>(value: _v as T)); | |
} | |
} | |
} | |
if (__printDebug) print("data.length: ${data.length}"); | |
} | |
_parsePickerDataItem(List pickerData, List<PickerItem> data) { | |
if (pickerData == null) return; | |
for (int i = 0; i < pickerData.length; i++) { | |
var item = pickerData[i]; | |
if (item is T) { | |
data.add(new PickerItem<T>(value: item)); | |
} else if (item is Map) { | |
final Map map = item; | |
if (map.length == 0) continue; | |
List<T> _mapList = map.keys.toList(); | |
for (int j = 0; j < _mapList.length; j++) { | |
var _o = map[_mapList[j]]; | |
if (_o is List && _o.length > 0) { | |
List<PickerItem> _children = new List<PickerItem<T>>(); | |
//print('add: ${data.runtimeType.toString()}'); | |
data.add(new PickerItem<T>(value: _mapList[j], children: _children)); | |
_parsePickerDataItem(_o, _children); | |
} | |
} | |
} else if (T == String && !(item is List)) { | |
String _v = item.toString(); | |
//print('add: $_v'); | |
data.add(new PickerItem<T>(value: _v as T)); | |
} | |
} | |
} | |
void setColumn(int index) { | |
if (isArray) { | |
if (__printDebug) print("index: $index"); | |
if (index + 1 < data.length) | |
_datas = data[index + 1].children; | |
else | |
_datas = null; | |
return; | |
} | |
if (index < 0) | |
_datas = data; | |
else { | |
int select = picker.selecteds[index]; | |
if (_datas != null && _datas.length > select) | |
_datas = _datas[select].children; | |
else | |
_datas = null; | |
} | |
} | |
@override | |
int getLength() { | |
return _datas == null ? 0 : _datas.length; | |
} | |
@override | |
getMaxLevel() { | |
if (_maxLevel == -1) _checkPickerDataLevel(data, 1); | |
return _maxLevel; | |
} | |
@override | |
Widget buildItem(BuildContext context, int index) { | |
final PickerItem item = _datas[index]; | |
if (item.text != null) { | |
return item.text; | |
} | |
return makeText(item.text, item.text != null ? null : item.value.toString()); | |
} | |
@override | |
void initSelects() { | |
if (picker.selecteds == null || picker.selecteds.length == 0) { | |
if (picker.selecteds == null) picker.selecteds = new List<int>(); | |
for (int i = 0; i < _maxLevel; i++) picker.selecteds.add(0); | |
} | |
} | |
@override | |
List<T> getSelectedValues() { | |
List<T> _items = []; | |
if (picker.selecteds != null) { | |
if (isArray) { | |
for (int i = 0; i < picker.selecteds.length; i++) { | |
int j = picker.selecteds[i]; | |
if (j < 0 || data[i].children == null || j >= data[i].children.length) | |
break; | |
_items.add(data[i].children[j].value); | |
} | |
} else { | |
List<PickerItem<dynamic>> datas = data; | |
for (int i = 0; i < picker.selecteds.length; i++) { | |
int j = picker.selecteds[i]; | |
if (j < 0 || j >= datas.length) break; | |
_items.add(datas[j].value); | |
datas = datas[j].children; | |
if (datas == null || datas.length == 0) break; | |
} | |
} | |
} | |
return _items; | |
} | |
_checkPickerDataLevel(List<PickerItem> data, int level) { | |
if (data == null) return; | |
if (isArray) { | |
_maxLevel = data.length; | |
return; | |
} | |
for (int i = 0; i < data.length; i++) { | |
if (data[i].children != null && data[i].children.length > 0) | |
_checkPickerDataLevel(data[i].children, level + 1); | |
} | |
if (_maxLevel < level) _maxLevel = level; | |
} | |
} | |
class NumberPickerColumn { | |
final List<int> items; | |
final int begin; | |
final int end; | |
final int initValue; | |
final int columnFlex; | |
final int jump; | |
final Widget postfix, suffix; | |
final PickerValueFormat<int> onFormatValue; | |
const NumberPickerColumn({ | |
this.begin = 0, | |
this.end = 9, | |
this.items, | |
this.initValue, | |
this.jump = 1, | |
this.columnFlex = 1, | |
this.postfix, | |
this.suffix, | |
this.onFormatValue, | |
}) : assert(jump != null); | |
int indexOf(int value) { | |
if (value == null) return -1; | |
if (items != null) return items.indexOf(value); | |
if (value < begin || value > end) return -1; | |
return (value - begin) ~/ (this.jump == 0 ? 1 : this.jump); | |
} | |
int valueOf(int index) { | |
if (items != null) { | |
return items[index]; | |
} | |
return begin + index * (this.jump == 0 ? 1 : this.jump); | |
} | |
String getValueText(int index) { | |
return onFormatValue == null ? "${valueOf(index)}" : onFormatValue(valueOf(index)); | |
} | |
int count() { | |
var v = (end - begin) ~/ (this.jump == 0 ? 1 : this.jump) + 1; | |
if (v < 1) return 0; | |
return v; | |
} | |
} | |
class NumberPickerAdapter extends PickerAdapter<int> { | |
NumberPickerAdapter({this.data}) : assert(data != null); | |
final List<NumberPickerColumn> data; | |
NumberPickerColumn cur; | |
@override | |
int getLength() { | |
if (cur == null) return 0; | |
if (cur.items != null) return cur.items.length; | |
return cur.count(); | |
} | |
@override | |
int getMaxLevel() { | |
return data == null ? 0 : data.length; | |
} | |
@override | |
bool getIsLinkage() { | |
return false; | |
} | |
@override | |
void setColumn(int index) { | |
if (index + 1 >= data.length) | |
cur = null; | |
else | |
cur = data[index + 1]; | |
} | |
@override | |
void initSelects() { | |
int _maxLevel = getMaxLevel(); | |
if (picker.selecteds == null || picker.selecteds.length == 0) { | |
if (picker.selecteds == null) picker.selecteds = new List<int>(); | |
for (int i = 0; i < _maxLevel; i++) { | |
int v = data[i].indexOf(data[i].initValue); | |
if (v < 0) v = 0; | |
picker.selecteds.add(v); | |
} | |
} | |
} | |
@override | |
Widget buildItem(BuildContext context, int index) { | |
if (cur.postfix == null && cur.suffix == null) | |
return makeText(null, cur.getValueText(index)); | |
else | |
return makeTextEx(null, cur.getValueText(index), cur.postfix, cur.suffix); | |
} | |
@override | |
int getColumnFlex(int column) { | |
return data[column].columnFlex; | |
} | |
@override | |
List<int> getSelectedValues() { | |
List<int> _items = []; | |
if (picker.selecteds != null) { | |
for (int i = 0; i < picker.selecteds.length; i++) { | |
int j = picker.selecteds[i]; | |
int v = data[i].valueOf(j); | |
_items.add(v); | |
} | |
} | |
return _items; | |
} | |
} | |
/// Picker DateTime Adapter Type | |
class PickerDateTimeType { | |
static const int kMDY = 0; // m, d, y | |
static const int kHM = 1; // hh, mm | |
static const int kHMS = 2; // hh, mm, ss | |
static const int kHM_AP = 3; // hh, mm, ap(AM/PM) | |
static const int kMDYHM = 4; // m, d, y, hh, mm | |
static const int kMDYHM_AP = 5; // m, d, y, hh, mm, AM/PM | |
static const int kMDYHMS = 6; // m, d, y, hh, mm, ss | |
static const int kYMD = 7; // y, m, d | |
static const int kYMDHM = 8; // y, m, d, hh, mm | |
static const int kYMDHMS = 9; // y, m, d, hh, mm, ss | |
static const int kYMD_AP_HM = 10; // y, m, d, ap, hh, mm | |
static const int kYM = 11; // y, m | |
static const int kDMY = 12; // d, m, y | |
} | |
class DateTimePickerAdapter extends PickerAdapter<DateTime> { | |
final int type; | |
final bool isNumberMonth; | |
final List<String> months; | |
final List<String> strAMPM; | |
final int yearBegin, yearEnd; | |
final DateTime minValue, maxValue; | |
final String yearSuffix, monthSuffix, daySuffix; | |
/// year 0, month 1, day 2, hour 3, minute 4, sec 5, am/pm 6, hour-ap: 7 | |
final List<int> customColumnType; | |
static const List<String> MonthsList_EN = const [ | |
"Jan", | |
"Feb", | |
"Mar", | |
"Apr", | |
"May", | |
"Jun", | |
"Jul", | |
"Aug", | |
"Sep", | |
"Oct", | |
"Nov", | |
"Dec" | |
]; | |
static const List<String> MonthsList_EN_L = const [ | |
"January", | |
"February", | |
"March", | |
"April", | |
"May", | |
"June", | |
"July", | |
"August", | |
"September", | |
"October", | |
"November", | |
"December" | |
]; | |
DateTimePickerAdapter({ | |
Picker picker, | |
this.type = 0, | |
this.isNumberMonth = false, | |
this.months = MonthsList_EN, | |
this.strAMPM, | |
this.yearBegin = 1900, | |
this.yearEnd = 2100, | |
this.value, | |
this.minValue, | |
this.maxValue, | |
this.yearSuffix, | |
this.monthSuffix, | |
this.daySuffix, | |
this.customColumnType, | |
}) { | |
super.picker = picker; | |
} | |
int _col = 0; | |
int _colAP = -1; | |
DateTime value; | |
// but it can improve the performance, so keep it. | |
static const List<List<int>> lengths = const [ | |
[12, 31, 0], | |
[24, 60], | |
[24, 60, 60], | |
[12, 60, 2], | |
[12, 31, 0, 24, 60], | |
[12, 31, 0, 12, 60, 2], | |
[12, 31, 0, 24, 60, 60], | |
[0, 12, 31], | |
[0, 12, 31, 24, 60], | |
[0, 12, 31, 24, 60, 60], | |
[0, 12, 31, 2, 12, 60], | |
[0, 12], | |
[31, 12, 0], | |
]; | |
static const Map<int, int> columnTypeLength = { | |
0: 0, | |
1: 12, | |
2: 31, | |
3: 24, | |
4: 60, | |
5: 60, | |
6: 2, | |
7: 12 | |
}; | |
/// year 0, month 1, day 2, hour 3, minute 4, sec 5, am/pm 6, hour-ap: 7 | |
static const List<List<int>> columnType = const [ | |
[1, 2, 0], | |
[3, 4], | |
[3, 4, 5], | |
[7, 4, 6], | |
[1, 2, 0, 3, 4], | |
[1, 2, 0, 7, 4, 6], | |
[1, 2, 0, 3, 4, 5], | |
[0, 1, 2], | |
[0, 1, 2, 3, 4], | |
[0, 1, 2, 3, 4, 5], | |
[0, 1, 2, 6, 7, 4], | |
[0, 1], | |
[2, 1, 0], | |
]; | |
static const List<int> leapYearMonths = const <int>[1, 3, 5, 7, 8, 10, 12]; | |
// 获取当前列的类型 | |
int getColumnType(int index) { | |
if (customColumnType != null) | |
return customColumnType[index]; | |
List<int> items = columnType[type]; | |
if (index >= items.length) return -1; | |
return items[index]; | |
} | |
@override | |
int getLength() { | |
int v = customColumnType == null ? lengths[type][_col] : columnTypeLength[customColumnType[_col]]; | |
if (v == 0) { | |
int ye = yearEnd; | |
int yb = yearBegin; | |
if (minValue != null) | |
ye = minValue.year; | |
if (maxValue != null) | |
yb = maxValue.year; | |
return ye - yb; | |
} | |
if (v == 31) return _calcDateCount(value.year, value.month); | |
return v; | |
} | |
@override | |
int getMaxLevel() { | |
return customColumnType == null ? lengths[type].length : customColumnType.length; | |
} | |
@override | |
void setColumn(int index) { | |
//print("setColumn index: $index"); | |
_col = index + 1; | |
if (_col < 0) _col = 0; | |
} | |
@override | |
void initSelects() { | |
if (value == null) value = DateTime.now(); | |
_colAP = _getAPColIndex(); | |
int _maxLevel = getMaxLevel(); | |
if (picker.selecteds == null || picker.selecteds.length == 0) { | |
if (picker.selecteds == null) picker.selecteds = new List<int>(); | |
for (int i = 0; i < _maxLevel; i++) picker.selecteds.add(0); | |
} | |
} | |
@override | |
Widget buildItem(BuildContext context, int index) { | |
String _text = ""; | |
int colType = getColumnType(_col); | |
switch (colType) { | |
case 0: | |
_text = "${yearBegin + index}${_checkStr(yearSuffix)}"; | |
break; | |
case 1: | |
if (isNumberMonth) { | |
_text = "${index + 1}${_checkStr(monthSuffix)}"; | |
} else { | |
_text = "${months[index]}"; | |
} | |
break; | |
case 2: | |
_text = "${index + 1}${_checkStr(daySuffix)}"; | |
break; | |
case 3: | |
case 4: | |
case 5: | |
_text = "${intToStr(index)}"; | |
break; | |
case 6: | |
List _ampm = strAMPM ?? PickerLocalizations.of(context).ampm; | |
if (_ampm == null) _ampm = const ['AM', 'PM']; | |
_text = "${_ampm[index]}"; | |
break; | |
case 7: | |
_text = "${intToStr(index+1)}"; | |
break; | |
} | |
return makeText(null, _text); | |
} | |
@override | |
String getText() { | |
return value.toString(); | |
} | |
@override | |
int getColumnFlex(int column) { | |
if (getColumnType(column) == 0) { | |
return 3; | |
} | |
return 2; | |
} | |
@override | |
void doShow() { | |
for (int i = 0; i < getMaxLevel(); i++) { | |
int colType = getColumnType(i); | |
switch (colType) { | |
case 0: | |
picker.selecteds[i] = value.year - yearBegin; | |
break; | |
case 1: | |
picker.selecteds[i] = value.month - 1; | |
break; | |
case 2: | |
picker.selecteds[i] = value.day - 1; | |
break; | |
case 3: | |
picker.selecteds[i] = value.hour; | |
break; | |
case 4: | |
picker.selecteds[i] = value.minute; | |
break; | |
case 5: | |
picker.selecteds[i] = value.second; | |
break; | |
case 6: | |
picker.selecteds[i] = (value.hour > 12 || value.hour == 0) ? 1 : 0; | |
break; | |
case 7: | |
picker.selecteds[i] = value.hour == 0 ? 11 : | |
(value.hour > 12) ? value.hour - 12 - 1 : value.hour - 1; | |
break; | |
} | |
} | |
} | |
@override | |
void doSelect(int column, int index) { | |
int year, month, day, h, m, s; | |
year = value.year; | |
month = value.month; | |
day = value.day; | |
h = value.hour; | |
m = value.minute; | |
s = value.second; | |
if (type != 2 && type != 6) s = 0; | |
int colType = getColumnType(column); | |
switch (colType) { | |
case 0: | |
year = yearBegin + index; | |
break; | |
case 1: | |
month = index + 1; | |
break; | |
case 2: | |
day = index + 1; | |
break; | |
case 3: | |
h = index; | |
break; | |
case 4: | |
m = index; | |
break; | |
case 5: | |
s = index; | |
break; | |
case 6: | |
if (picker.selecteds[_colAP] == 0) { | |
if (h == 0) h = 12; | |
if (h > 12) h = h - 12; | |
} else { | |
if (h < 12) h = h + 12; | |
if (h == 12) h = 0; | |
} | |
break; | |
case 7: | |
h = index + 1; | |
if (_colAP >= 0 && picker.selecteds[_colAP] == 1) h = h + 12; | |
if (h > 23) h = 0; | |
break; | |
} | |
int cday = _calcDateCount(year, month); | |
if (day > cday) day = cday; | |
value = new DateTime(year, month, day, h, m, s); | |
if (minValue != null && (value.millisecondsSinceEpoch < minValue.millisecondsSinceEpoch)) { | |
value = minValue; | |
notifyDataChanged(); | |
} else if (maxValue != null && value.millisecondsSinceEpoch > maxValue.millisecondsSinceEpoch) { | |
value = maxValue; | |
notifyDataChanged(); | |
} | |
} | |
int _getAPColIndex() { | |
List<int> items = customColumnType ?? columnType[type]; | |
for (int i = 0; i < items.length; i++) { | |
if (items[i] == 6) return i; | |
} | |
return -1; | |
} | |
int _calcDateCount(int year, int month) { | |
if (leapYearMonths.contains(month)) { | |
return 31; | |
} else if (month == 2) { | |
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) { | |
return 29; | |
} | |
return 28; | |
} | |
return 30; | |
} | |
String intToStr(int v) { | |
if (v < 10) return "0$v"; | |
return "$v"; | |
} | |
String _checkStr(String v) { | |
return v == null ? "" : v; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment