导入包flutter_datetime_picker: 1.5.0
封装
import 'package:atui/jade/utils/JadeColors.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_datetime_picker/flutter_datetime_picker.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class ATuiDateTimePickerDialog {
static Future<DateTime> showDatePicker(
BuildContext context, {
bool showTitleActions: true,
bool isShowDay: true,
DateTime minTime,
DateTime maxTime,
DateChangedCallback onChanged,
DateChangedCallback onConfirm,
Function onCustomDateConfirm,
DateCancelledCallback onCancel,
locale: LocaleType.en,
DateTime currentTime,
DatePickerTheme theme,
}) async {
return await Navigator.push(
context,
_DatePickerRoute(
showTitleActions: showTitleActions,
onChanged: onChanged,
onConfirm: onConfirm,
onCustomDateConfirm: onCustomDateConfirm,
onCancel: onCancel,
locale: locale,
theme: theme,
isShowDay: isShowDay,
barrierLabel:
MaterialLocalizations.of(context).modalBarrierDismissLabel,
pickerModel: DatePickerModel(
currentTime: currentTime,
maxTime: maxTime,
minTime: minTime,
locale: locale,
),
),
);
}
}
class _DatePickerRoute<T> extends PopupRoute<T> {
_DatePickerRoute({
this.showTitleActions,
this.onChanged,
this.onConfirm,
this.onCustomDateConfirm,
this.onCancel,
theme,
this.barrierLabel,
this.locale,
this.isShowDay,
RouteSettings settings,
pickerModel,
}) : this.pickerModel = pickerModel ?? DatePickerModel(),
this.theme = theme ?? DatePickerTheme(),
super(settings: settings);
final bool showTitleActions;
final DateChangedCallback onChanged;
final DateChangedCallback onConfirm;
final Function onCustomDateConfirm;
final DateCancelledCallback onCancel;
final DatePickerTheme theme;
final LocaleType locale;
final BasePickerModel pickerModel;
final bool isShowDay;
Duration get transitionDuration => const Duration(milliseconds: 200);
bool get barrierDismissible => true;
final String barrierLabel;
Color get barrierColor => Colors.black54;
AnimationController _animationController;
AnimationController createAnimationController() {
assert(_animationController == null);
_animationController =
BottomSheet.createAnimationController(navigator.overlay);
return _animationController;
}
Widget buildPage(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation) {
Widget bottomSheet = MediaQuery.removePadding(
context: context,
removeTop: true,
child: _DatePickerComponent(
onChanged: onChanged,
locale: this.locale,
route: this,
pickerModel: pickerModel,
isShowDay: isShowDay,
),
);
return InheritedTheme.captureAll(context, bottomSheet);
}
}
class _DatePickerComponent extends StatefulWidget {
_DatePickerComponent(
{Key key,
this.route,
this.onChanged,
this.locale,
this.pickerModel,
this.isShowDay})
: super(key: key);
final DateChangedCallback onChanged;
final _DatePickerRoute route;
final LocaleType locale;
final BasePickerModel pickerModel;
bool isShowDay;
State<StatefulWidget> createState() {
return _DatePickerState();
}
}
class _DatePickerState extends State<_DatePickerComponent> {
FixedExtentScrollController leftScrollCtrl, middleScrollCtrl, rightScrollCtrl;
List<String> _btnTitleList = ['按日','按月','自定义'];
int _selectPosition = 0;
bool _isCustomTime = false;
String _startDate;
String _endDate = '结束时间';
int _customDateType = 1; //1:开始时间 2:结束时间
void initState() {
super.initState();
refreshScrollOffset();
_startDate = '${widget.pickerModel.finalTime().year}/${widget.pickerModel.finalTime().month}/${widget.pickerModel.finalTime().day}';
}
void refreshScrollOffset() {
// print('refreshScrollOffset ${widget.pickerModel.currentRightIndex()}');
leftScrollCtrl = FixedExtentScrollController(
initialItem: widget.pickerModel.currentLeftIndex());
middleScrollCtrl = FixedExtentScrollController(
initialItem: widget.pickerModel.currentMiddleIndex());
rightScrollCtrl = FixedExtentScrollController(
initialItem: widget.pickerModel.currentRightIndex());
}
Widget build(BuildContext context) {
DatePickerTheme theme = widget.route.theme;
return GestureDetector(
child: AnimatedBuilder(
animation: widget.route.animation,
builder: (BuildContext context, Widget child) {
final double bottomPadding = MediaQuery.of(context).padding.bottom;
return ClipRect(
child: CustomSingleChildLayout(
delegate: _BottomPickerLayout(
widget.route.animation.value,
theme,
showTitleActions: widget.route.showTitleActions,
bottomPadding: bottomPadding,
),
child: GestureDetector(
child: Material(
color: theme.backgroundColor ?? Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(topLeft: Radius.circular(15.0),topRight: Radius.circular(10.0)), // 设置圆角半径
),
child: _renderPickerView(theme)
),
),
),
);
},
),
);
}
void _notifyDateChanged() {
if (widget.onChanged != null) {
widget.onChanged(widget.pickerModel.finalTime());
}
String _date = '${widget.pickerModel.finalTime().year}/${widget.pickerModel.finalTime().month}/${widget.pickerModel.finalTime().day}';
if(_customDateType == 1){
_startDate = _date;
}else if(_customDateType == 2){
_endDate = _date;
}
}
Widget _renderPickerView(DatePickerTheme theme) {
Widget itemView = _renderItemView(theme);
return Column(
children: <Widget>[
if (widget.route.showTitleActions)
_renderTitleActionsView(theme),
_switchBtnView(),
itemView,
],
);
}
Widget _renderColumnView(
ValueKey key,
DatePickerTheme theme,
StringAtIndexCallBack stringAtIndexCB,
ScrollController scrollController,
int layoutProportion,
ValueChanged<int> selectedChangedWhenScrolling,
ValueChanged<int> selectedChangedWhenScrollEnd,
) {
return Expanded(
flex: layoutProportion,
child: Container(
padding: EdgeInsets.all(8.0),
height: theme.containerHeight,
decoration: BoxDecoration(color: theme.backgroundColor ?? Colors.white),
child: NotificationListener(
onNotification: (ScrollNotification notification) {
if (notification.depth == 0 &&
selectedChangedWhenScrollEnd != null &&
notification is ScrollEndNotification &&
notification.metrics is FixedExtentMetrics) {
final FixedExtentMetrics metrics = notification.metrics;
final int currentItemIndex = metrics.itemIndex;
selectedChangedWhenScrollEnd(currentItemIndex);
}
return false;
},
child: CupertinoPicker.builder(
key: key,
backgroundColor: theme.backgroundColor ?? Colors.white,
scrollController: scrollController,
itemExtent: theme.itemHeight,
onSelectedItemChanged: (int index) {
selectedChangedWhenScrolling(index);
},
useMagnifier: true,
itemBuilder: (BuildContext context, int index) {
final content = stringAtIndexCB(index);
if (content == null) {
return null;
}
return Container(
height: theme.itemHeight,
alignment: Alignment.center,
child: Text(
content,
style: theme.itemStyle,
textAlign: TextAlign.start,
),
);
},
),
),
),
);
}
Widget _renderItemView(DatePickerTheme theme) {
return Container(
color: theme.backgroundColor ?? Colors.white,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(
child: widget.pickerModel.layoutProportions()[0] > 0
? _renderColumnView(
ValueKey(widget.pickerModel.currentLeftIndex()),
theme,
widget.pickerModel.leftStringAtIndex,
leftScrollCtrl,
widget.pickerModel.layoutProportions()[0], (index) {
widget.pickerModel.setLeftIndex(index);
}, (index) {
setState(() {
refreshScrollOffset();
_notifyDateChanged();
});
})
: null,
),
Text(
widget.pickerModel.leftDivider(),
style: theme.itemStyle,
),
Container(
child: widget.pickerModel.layoutProportions()[1] > 0
? _renderColumnView(
ValueKey(widget.pickerModel.currentLeftIndex()),
theme,
widget.pickerModel.middleStringAtIndex,
middleScrollCtrl,
widget.pickerModel.layoutProportions()[1], (index) {
widget.pickerModel.setMiddleIndex(index);
}, (index) {
setState(() {
refreshScrollOffset();
_notifyDateChanged();
});
})
: null,
),
Text(
widget.pickerModel.rightDivider(),
style: theme.itemStyle,
),
widget.isShowDay ?? true
? Container(
child: widget.pickerModel.layoutProportions()[2] > 0
? _renderColumnView(
ValueKey(
widget.pickerModel.currentMiddleIndex() * 100 +
widget.pickerModel.currentLeftIndex()),
theme,
widget.pickerModel.rightStringAtIndex,
rightScrollCtrl,
widget.pickerModel.layoutProportions()[2], (index) {
widget.pickerModel.setRightIndex(index);
}, (index) {
setState(() {
refreshScrollOffset();
_notifyDateChanged();
});
})
: null,
)
: Container(),
],
),
);
}
// Title View
Widget _renderTitleActionsView(DatePickerTheme theme) {
final done = _localeDone();
final cancel = _localeCancel();
return Container(
height: theme.titleHeight,
decoration: BoxDecoration(
color: theme.headerColor ?? theme.backgroundColor ?? Colors.white,
borderRadius: BorderRadius.only(topLeft: Radius.circular(15.0),topRight: Radius.circular(15.0))
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(
height: theme.titleHeight,
child: CupertinoButton(
pressedOpacity: 0.3,
padding: EdgeInsets.only(left: 16, top: 0),
child: Text(
'$cancel',
style: theme.cancelStyle,
),
onPressed: () {
Navigator.pop(context);
if (widget.route.onCancel != null) {
widget.route.onCancel();
}
},
),
),
Container(
height: theme.titleHeight,
child: CupertinoButton(
pressedOpacity: 0.3,
padding: EdgeInsets.only(right: 16, top: 0),
child: Text(
'$done',
style: theme.doneStyle,
),
onPressed: () {
Navigator.pop(context, widget.pickerModel.finalTime());
if (widget.route.onConfirm != null) {
if(!_isCustomTime){
widget.route.onConfirm(widget.pickerModel.finalTime());
}else{
widget.route.onCustomDateConfirm(_startDate,_endDate);
}
}
},
),
),
],
),
);
}
String _localeDone() {
return i18nObjInLocale(widget.locale)['done'];
}
String _localeCancel() {
return i18nObjInLocale(widget.locale)['cancel'];
}
//切换按钮
Widget _switchBtnView(){
return Container(
height: 120.w,
padding: EdgeInsets.symmetric(horizontal: 30.w),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Wrap(
alignment: WrapAlignment.start,
direction: Axis.horizontal,
spacing: 30.w,
textDirection: TextDirection.ltr,
children: List.generate(_btnTitleList.length, (index) {
return GestureDetector(
child: Container(
padding: EdgeInsets.only(left: 24.w,right: 24.w,top: 10.w,bottom: 12.w),
decoration: BoxDecoration(
color: _selectPosition == index ? JadeColors.yellow : JadeColors.lightGrey,
borderRadius: BorderRadius.all(Radius.circular(20)),
),
child: Text(_btnTitleList[index],style: TextStyle(fontSize: 28.sp,color: _selectPosition == index ? JadeColors.grey_2 : JadeColors.grey),),
),
onTap: (){
setState(() {
_selectPosition = index;
if(_selectPosition == 0){
widget.isShowDay = true;
_isCustomTime = false;
}else if(_selectPosition == 1){
widget.isShowDay = false;
_isCustomTime = false;
}else if(_selectPosition == 2){
widget.isShowDay = true;
_isCustomTime = true;
}
});
},
);
})
),
GestureDetector(
child: Container(
padding: EdgeInsets.only(left: 20.w,right: 20.w,top: 10.w,bottom: 10.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
border: Border.all(width: 1.w,color: JadeColors.grey_2)
),
child: Text('清除筛选',style: TextStyle(fontSize: 28.sp,color: JadeColors.grey_2),),
),
)
],
),
_customDataSelectView()
],
),
);
}
_customDataSelectView(){
return _isCustomTime?Container(
margin: EdgeInsets.only(top: 15.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GestureDetector(
child: Column(children: [
IntrinsicWidth(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"$_startDate",
style: TextStyle(overflow: TextOverflow.fade, color: _customDateType == 1?JadeColors.red_6 : JadeColors.grey_7, fontSize: 30.sp),
),
Container(
margin: EdgeInsets.only(top: 1.h),
height: 1,
color: _customDateType == 1?JadeColors.red_6 : JadeColors.grey_7,
)
],
),
),
]),
onTap: () {
setState(() {
_customDateType = 1;
});
}),
Container(width: 30.w, height: 1, color: JadeColors.grey_7, margin: EdgeInsets.symmetric(horizontal: 40.w)),
GestureDetector(
child: Column(children: [
IntrinsicWidth(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"$_endDate",
style: TextStyle(overflow: TextOverflow.fade, color: _customDateType == 2?JadeColors.red_6 : JadeColors.grey_7, fontSize: 30.sp),
),
Container(
margin: EdgeInsets.only(top: 1.h),
height: 1,
color: _customDateType == 2?JadeColors.red_6 : JadeColors.grey_7,
)
],
),
),
]),
onTap: () {
setState(() {
_customDateType = 2;
});
})
],
),
):Container();
}
}
class _BottomPickerLayout extends SingleChildLayoutDelegate {
_BottomPickerLayout(
this.progress,
this.theme, {
this.itemCount,
this.showTitleActions,
this.bottomPadding = 0,
});
final double progress;
final int itemCount;
final bool showTitleActions;
final DatePickerTheme theme;
final double bottomPadding;
BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
double maxHeight = theme.containerHeight;
if (showTitleActions) {
maxHeight += theme.titleHeight + 120.w;//增加actionTitle布局(取消、确定)时和切换按钮的布局时要在当前的布局高度上再加上去
}
return BoxConstraints(
minWidth: constraints.maxWidth,
maxWidth: constraints.maxWidth,
minHeight: 0.0,
maxHeight: maxHeight + bottomPadding,
);
}
Offset getPositionForChild(Size size, Size childSize) {
final height = size.height - childSize.height * progress;
return Offset(0.0, height);
}
bool shouldRelayout(_BottomPickerLayout oldDelegate) {
return progress != oldDelegate.progress;
}
}
引用
_dateFilter() async {
LocaleType localeType;
await ATuiSharedPreferences.getStorage("localType")
.then((value) {
if (value == "zh") {
localeType = LocaleType.zh;
} else if (value == "en") {
localeType = LocaleType.en;
}
});
ATuiDateTimePickerDialog.showDatePicker(
context,
//isShowDay: false,
locale: localeType ?? LocaleType.zh,
theme: DatePickerTheme(
cancelStyle: TextStyle(
fontSize: 30.sp,
color: JadeColors.grey_2),
doneStyle: TextStyle(
fontSize: 30.sp,
color: JadeColors.grey_2,
fontWeight: FontWeight.w600)),
onConfirm: (datetime) async {
String newDate = '${datetime.year}-${datetime.month}-${datetime.day}';
newDate += datetime.month < 10
? '0${datetime.month}'
: datetime.month.toString();
print('非自定义时点击确定按钮= $newDate');
},
onCustomDateConfirm: (startDatetime,endDatetime) async {
print('自定义时点击确定按钮');
print('startDatetime= $startDatetime');
print('endDatetime= $endDatetime');
},
onChanged: (datetime) {
print('onChanged dateTime: ${datetime}');
},
);
}