/* * @Author: XianKaiQun * @Date: 2020-08-25 16:49:31 * @LastEditors : WuWei * @LastEditTime : 2023-11-27 15:28:53 * @Description: */ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:wisdom_cli/new/utils/month_picker/season_picker.dart'; import 'package:wisdom_cli/wisdom_cli.dart'; import 'month_picker/date_period.dart'; import 'month_picker/month_picker.dart'; import 'month_picker/styles/date_picker_styles.dart'; import 'month_picker/week_picker.dart'; import 'month_picker/year_picker.dart'; class WPickerUtil { WPickerUtil._(); ///单选 static Future single( BuildContext context, { Widget? title, required WSinglePickerGetData getData, T? initialValue, // List selectValue, bool? isSearch, }) { List _value = []; bool _isSearch = isSearch ?? false; if (initialValue != null) { _value = List.of([initialValue]); } // if (selectValue != null) { // _value = selectValue; // } void onChanged(List? value, bool isTap) { _value = value!; if (isTap) Navigator.pop(context, _value[0]); } return WBottomModalUtil.show( context, title: title, plain: true, body: WPicker( multiple: false, initialValue: _value, getData: getData, onChanged: onChanged, isSearch: _isSearch, ), bodyConstraints: _isSearch ? BoxConstraints( maxHeight: 450, minHeight: 450, ) : BoxConstraints( maxHeight: 350, minHeight: 250, ), ); } ///多选 static Future?> multiple( BuildContext context, { Widget? title, required WSinglePickerGetData? getData, List? initialValue = const [], bool? isSearch = false, }) { List _value = List.of(initialValue ?? []); void onCancel(context) => Navigator.pop(context); void onConfirm(context) { Navigator.pop(context, _value); } bool _isSearch = isSearch ?? false; return WBottomModalUtil.show?>( context, title: title, plain: false, onCancel: onCancel, onConfirm: onConfirm, body: WPicker( multiple: true, initialValue: _value, // enableValue: enableValue, getData: getData, isSearch: _isSearch, onChanged: (value, _) { _value = value!; }, ), bodyConstraints: _isSearch ? BoxConstraints( maxHeight: 450, minHeight: 450, ) : BoxConstraints( maxHeight: 350, minHeight: 250, ), ); } ///无限单选(级联选择) static Future?> infinity( BuildContext context, { Widget? title, required Future> Function(List item) getData, ///判断是否已选择到了最后一项,跳出选择 required Future Function(List)? isLast, List initialValue = const [], }) { List? _value = List.of(initialValue); int index = (_value.length - 1 < 0) ? 0 : initialValue.length - 1; bool showFutrue = _value.length == 0; T? currentValue() { if (index >= _value.length) { return null; } else { return _value[index]; } } void onCancel(context) => Navigator.pop(context); void onConfirm(context) { if (_value.length != 0) Navigator.pop(context, _value); } return WBottomModalUtil.show>( context, title: title, plain: true, onCancel: onCancel, onConfirm: onConfirm, body: StatefulBuilder( builder: (BuildContext context, setState) { add(List items, isTap) async { if (isTap) { showFutrue = true; if (index <= _value.length - 1) { _value.removeRange(index, _value.length); } index++; _value.add(items[0]); if (isLast != null && await isLast(_value) == true) { onConfirm(context); } else { setState(() {}); } } } behind(int i) { index = i; setState(() {}); } final primaryColor = Theme.of(context).primaryColor; Widget tab(i) { String text = (_value.length > i) ? '${_value[i].selectName}' : '请选择'; return Wisdom.column( onTap: () => behind(i), maxWidth: 100.pt, mainAxisAlignment: MainAxisAlignment.center, padding: EdgeInsets.symmetric(horizontal: 12.5.pt), children: [ Spacer(), Text( text, overflow: TextOverflow.ellipsis, style: TextStyle( fontSize: 15.pt, fontWeight: i == index ? null : FontWeight.bold, color: i == index ? primaryColor : null, ), ), Spacer(), SizedBox( height: 4.pt, child: (i == index) ? WisActiveness( margin: EdgeInsets.only(top: 0), color: primaryColor, ) : null, ), ], ); } Widget tabs() { return Container( height: 44.pt, margin: EdgeInsets.symmetric(horizontal: 2.5.pt), child: ListView( scrollDirection: Axis.horizontal, children: [ if (showFutrue) for (var i = 0; i < _value.length + 1; i++) tab(i) else for (var i = 0; i < _value.length; i++) tab(i), ], ), ); } return Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ tabs(), Divider(height: 1), WPicker( key: UniqueKey(), multiple: false, initialValue: currentValue() == null ? [] : [currentValue()!], getData: () => getData([for (var i = 0; i < index; i++) _value[i]]), onChanged: (selects, isTap) => add(selects!, isTap), ).asExpanded(), ], ); }, ), ); } /// 级联选择------------------------------------- static Future?> cascade( BuildContext context, { Widget? title, bool? isSearch, /// 是否多选 bool isMulti = false, required List data, required int maxLength, String? label, String? value, List? initialValue = const [], }) { List names = initialValue != null && initialValue.length > 0 ? initialValue.last.name!.split(',') : []; List uniques = initialValue != null && initialValue.length > 0 ? initialValue.last.unique.toString().split(',') : []; _getValue() { List tempList = []; if (initialValue!.length > 0) { for (int i = 0; i < names.length; i++) { tempList.add( SinglePickerEntity( name: names[i], unique: uniques[i], child: null, ), ); } } List _list = List.of(initialValue); _list.last = tempList; return _list; } List _value = isMulti == true ? initialValue!.length > 0 ? _getValue() : [] : List.of(initialValue ?? []); List? curlist = data; String? _searchValue; int index = (_value.length - 1 < 0) ? 0 : initialValue!.length - 1; bool showFutrue = _value.length == 0; bool _isSearch = isSearch ?? false; List? currentValue() { if (index >= _value.length) { return null; } else { return _value[index] is List ? List.from((_value[index] ?? []).map( (item) => SinglePickerEntity( name: item.name, unique: item.unique, child: item.child ?? null, ), )) : [_value[index]]; } } void onCancel(context) => Navigator.pop(context); void onConfirm(context) { if (_value.length != 0 && _value.length == maxLength) { if (isMulti == true) { List tempList = _value; String name = tempList.last.map((item) => item.name).join(','); String value = tempList.last.map((item) => item.unique).join(','); tempList.last = SinglePickerEntity(name: name, unique: value); tempList = List.from((tempList).map( (item) => SinglePickerEntity( name: item.name, unique: item.unique, child: item.child ?? null, ), )); Navigator.pop(context, tempList); } else { List tempList = _value; tempList = List.from((tempList).map( (item) => SinglePickerEntity( name: item.name, unique: item.unique, child: item.child ?? null, ), )); Navigator.pop(context, tempList); } } } Future> getList() async { if (index == 0 || index < 0) { curlist = data; } else { List? tempList = data; for (var i = 0; i < (index); i++) { tempList!.forEach((listitem) { if (listitem.unique == _value[i].unique) { tempList = List.from((listitem.child ?? []).map( (item) => SinglePickerEntity( name: item[label], unique: item[value], child: item['child'] ?? null, ), )); } }); } curlist = tempList; } return curlist!; } /// 获取当前标签列表值 _getName() { List names = []; _value.last.forEach((element) { names.add(element.name); }); return names.join(','); } // List tempList = []; // listLoop(List data, v, count) { // while (count <= maxLength) { // data.forEach((item) => { // if (count == maxLength) // { // if (item.name.indexOf(v) != -1) // { // tempList.add(item), // } // } // else if (item.child != null && item.child.length > 0) // { // listLoop( // List.from((item.child ?? []).map( // (item) => SinglePickerEntity( // name: item['name'], // unique: item['unique'], // child: item['child'] ?? null, // ), // )), // v, // count + 1), // } // }); // } // } return WBottomModalUtil.show>( context, title: title, plain: isMulti == true ? false : true, onCancel: onCancel, onConfirm: onConfirm, body: StatefulBuilder( builder: (BuildContext context, setState) { add(List items, isTap) async { if (isTap) { showFutrue = true; if (index <= _value.length - 1) { _value.removeRange(index, _value.length); } if (index == (maxLength - 1)) { if (isMulti == true) { if (_value.length >= maxLength) { _value.last = items; } else { _value.add(items); } } else { _value.add(items[0]); onConfirm(context); } } else { index++; _value.add(items[0]); setState(() {}); } } } behind(int i) { index = i; setState(() {}); } final primaryColor = Theme.of(context).primaryColor; Widget tab(i) { String text = (_value.length > i) ? '${_value[i] is List ? _getName() : _value[i].selectName}' : '请选择'; return Wisdom.column( onTap: () => behind(i), maxWidth: 100.pt, mainAxisAlignment: MainAxisAlignment.center, padding: EdgeInsets.symmetric(horizontal: 12.5.pt), children: [ Spacer(), Text( text, overflow: TextOverflow.ellipsis, style: TextStyle( fontSize: 15.pt, fontWeight: i == index ? null : FontWeight.bold, color: i == index ? primaryColor : null, ), ), Spacer(), SizedBox( height: 4.pt, child: (i == index) ? WisActiveness( margin: EdgeInsets.only(top: 0), color: primaryColor, ) : null, ), ], ); } Widget tabs() { return Container( height: 44.pt, margin: EdgeInsets.symmetric(horizontal: 2.5.pt), child: ListView( scrollDirection: Axis.horizontal, children: [ if (showFutrue) ...[ for (var i = 0; i < _value.length + 1; i++) if (i < maxLength) ...[tab(i)], ] else ...[ for (var i = 0; i < _value.length; i++) if (i < maxLength) ...[tab(i)], ], ], ), ); } Future?> getSearchList() async { if (_searchValue == null || _searchValue == '') { return curlist; } else { List temp = []; data.forEach((element1) { if (element1.child != null && element1.child!.length > 0) { element1.child!.forEach((element2) { if (element2['child'] != null && element2['child'].length > 0) { element2['child'].forEach((element3) { if (element3['name'].indexOf(_searchValue) != -1) { temp.add(SinglePickerEntity( name: element3['name'], unique: element3['unique'], cascade: [ SinglePickerEntity( name: element1.name, unique: element1.unique, cascade: [], child: element1.child, ), SinglePickerEntity( name: element2['name'], unique: element2['unique'], cascade: [], child: element2['child'], ) ], child: null, )); } }); } }); } }); return temp; } } return Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ WSearch( hintText: '输入关键词搜索', onSubmitted: (v) => { _searchValue = v, setState(() {}), }, moonlight: false, padding: EdgeInsets.symmetric(vertical: 10.pt), isDark: true, backColor: Colors.white, ), if (_searchValue == null || _searchValue == '') ...[ tabs(), ], Divider(height: 1), WPicker( key: UniqueKey(), multiple: isMulti == true, isSearch: false, initialValue: currentValue() ?? [], getData: () => getList(), onChanged: (selects, isTap) => add(selects!, isTap), ).asExpanded(), ], ); }, ), bodyConstraints: _isSearch ? BoxConstraints( maxHeight: 450, minHeight: 450, ) : BoxConstraints( maxHeight: 350, minHeight: 250, ), ); } /// 树------------------------------------- /// 级联 static Future?> tree( BuildContext context, { Widget? title, required List data, required String label, required String value, required String child, required String firstName, required String firstValue, bool? isSearch, /// 是否多选 bool? isMulti, List? initialValue = const [], }) { List _value = List.of(initialValue ?? []); List _selectValue = []; bool _isSearch = isSearch ?? false; void onCancel(context) => Navigator.pop(context); void onConfirm(context) { Navigator.pop(context, _selectValue); } void onChanged(List values) { _value = values; _selectValue = []; _value.forEach((item) { _selectValue .add(SinglePickerEntity(name: item[label], unique: item[value])); }); if (isMulti != null && isMulti == false) { Navigator.pop( context, _selectValue.length == 0 ? null : _selectValue, ); } } return WBottomModalUtil.show>( context, title: title, plain: false, onCancel: onCancel, onConfirm: onConfirm, body: WTreePicker( key: UniqueKey(), labelname: label, valuename: value, childname: child, firstName: firstName, firstValue: firstValue, multiple: isMulti ?? false, isSearch: isSearch ?? false, initialValue: initialValue, data: data, onChanged: (value, _) => onChanged(value), ), bodyConstraints: _isSearch ? BoxConstraints( maxHeight: 400, minHeight: 400, ) : BoxConstraints( maxHeight: 350, minHeight: 250, ), ); } /// 周选择 static Future week( BuildContext context, { Widget? title, DateTime? firstDate, DateTime? lastDate, DatePeriod? initialDate, }) { final _firstDate = firstDate ?? DateTime(2015); final _lastDate = firstDate ?? DateTime(2100); final now = DateTime.now().toLocal(); DatePeriod _value = initialDate ?? DatePeriod( DateTime(now.year, now.month, now.day), DateTime(now.year, now.month, now.day + 1), ); return WBottomModalUtil.show( context, title: title, plain: false, onCancel: (_) => Navigator.pop(context), onConfirm: (_) => Navigator.pop(context, _value), body: StatefulBuilder( builder: (BuildContext context, setState) { return UtilWeekPicker( firstDate: _firstDate, lastDate: _lastDate, selectedDate: _value.start, onChanged: (DatePeriod value) { _value = value; setState(() {}); }, datePickerStyles: DatePickerRangeStyles(firstDayOfWeekIndexs: 1), ); }, ), ); } ///月份选择 /// static Future month( BuildContext context, { Widget? title, DateTime? firstDate, DateTime? lastDate, DateTime? initialDate, DatePickerStyles? datePickerStyles, }) { final _firstDate = firstDate ?? DateTime(2015); final now = DateTime.now().toLocal(); DateTime _value = initialDate ?? DateTime(now.year, now.month); DateTime _lastDate = lastDate ?? DateTime(2100); return WBottomModalUtil.show( context, title: title, plain: false, onCancel: (_) => Navigator.pop(context), onConfirm: (_) => Navigator.pop(context, _value), body: StatefulBuilder( builder: (BuildContext context, setState) { return WMonthPicker.single( datePickerStyles: datePickerStyles, firstDate: _firstDate, lastDate: _lastDate, selectedDate: _value, onChanged: (value) => { _value = value, setState(() {}), }); }, ), ); } static Future monthRange( BuildContext context, { Widget? title, DateTime? firstDate, DateTime? lastDate, List? initialDate, }) { final _firstDate = firstDate ?? DateTime(2015); // final now = DateTime.now().toLocal(); List _value = initialDate ?? [DateTime.now(), DateTime.now()]; DateTime _lastDate = lastDate ?? DateTime(2100); return WBottomModalUtil.show( context, title: title, plain: false, onCancel: (_) => Navigator.pop(context), onConfirm: (_) => Navigator.pop(context, _value), body: StatefulBuilder( builder: (BuildContext context, setState) { return WMonthPicker.multi( firstDate: _firstDate, lastDate: _lastDate, selectedDates: _value, onChanged: (value) => { _value = value, setState(() {}), }); }, ), ); } //年份选择 /// static Future year( BuildContext context, { Widget? title, DateTime? firstDate, DateTime? lastDate, DateTime? initialDate, }) { final _firstDate = firstDate ?? DateTime(2015); final now = DateTime.now().toLocal(); DateTime _value = initialDate ?? DateTime(now.year, now.month); DateTime _lastDate = lastDate ?? DateTime(2100); return WBottomModalUtil.show( context, title: title, plain: false, onCancel: (_) => Navigator.pop(context), onConfirm: (_) => Navigator.pop(context, _value), body: StatefulBuilder( builder: (BuildContext context, setState) { return UtilYearPicker.single( firstDate: _firstDate, lastDate: _lastDate, selectedDate: _value, onChanged: (DateTime value) { _value = value; setState(() {}); }, ); }, ), ); } /// 季度选择 static Future season( BuildContext context, { Widget? title, DateTime? firstDate, DateTime? lastDate, DateTime? initialDate, }) { final _firstDate = firstDate ?? DateTime(2015); final now = DateTime.now().toLocal(); DateTime _value = initialDate ?? DateTime(now.year, now.month); DateTime _lastDate = lastDate ?? DateTime(2100); return WBottomModalUtil.show( context, title: title, plain: false, onCancel: (_) => Navigator.pop(context), onConfirm: (_) => Navigator.pop(context, _value), body: StatefulBuilder( builder: (BuildContext context, setState) { return UtilSeasonPicker.single( firstDate: _firstDate, lastDate: _lastDate, selectedDate: _value, onChanged: (DateTime value) { _value = value; setState(() {}); }, ); }, ), ); } ///日期选择 /// static Future date( BuildContext context, { Widget? title, DateTime? firstDate, DateTime? lastDate, DateTime? initialDate, }) { final _firstDate = firstDate ?? DateTime(2015); final now = DateTime.now().toLocal(); DateTime _value = initialDate ?? DateTime(now.year, now.month, now.day); if (firstDate != null && lastDate == null) { _value = initialDate ?? firstDate.add(Duration(seconds: 1)); } if (lastDate != null && firstDate == null) { _value = initialDate ?? lastDate.add(Duration(seconds: -1)); } if (lastDate != null && firstDate != null) { _value = (now.difference(_firstDate).inMilliseconds > 0) && (now.difference(lastDate).inMilliseconds < 0) ? now : (now.difference(_firstDate).inMilliseconds.abs() < now.difference(lastDate).inMilliseconds.abs()) ? initialDate ?? firstDate : initialDate ?? lastDate; } DateTime _lastDate = lastDate ?? DateTime(now.year + 50, now.month, now.day); return WBottomModalUtil.show( context, title: title, plain: true, onCancel: (_) => Navigator.pop(context), onConfirm: (_) => Navigator.pop(context, _value), body: CalendarDatePicker( initialDate: _value, firstDate: _firstDate, lastDate: _lastDate, onDateChanged: (value) { _value = value.toLocal(); Navigator.pop(context, _value); }, ), ); } ///时间日期选择 /// static Future dateAndTime( BuildContext context, { Widget? title, DateTime? firstDate, DateTime? lastDate, DateTime? initialDate, ///分钟间隔, /// ///如若设置此参数之后, ///`firstDate`、`lastDate`、`initialDate`的minute(分钟)必须都要可整除此参数 int minuteInterval = 1, }) { final _firstDate = firstDate ?? DateTime(2015); final now = DateTime.now().toLocal(); final now2 = DateTime( now.year, now.month, now.day, now.hour, now.minute % minuteInterval < minuteInterval ? (now.minute - (now.minute % minuteInterval)) + minuteInterval : now.minute); //转换一下,去掉秒、微秒等 DateTime _value = initialDate ?? now2; DateTime _lastDate = lastDate ?? DateTime( now.year + 50, now.month, now.day, now.hour, minuteInterval == 5 && now.minute % 5 < 5 ? now.minute - (now.minute % 5) : now.minute); //转换一下,去掉秒、微秒等; ///设置日期 void setDate(DateTime dateTime) { _value = DateTime( dateTime.year, dateTime.month, dateTime.day, _value.hour, _value.minute, ); } ///设置时间 void setTime(DateTime dateTime) { _value = DateTime( _value.year, _value.month, _value.day, dateTime.hour, dateTime.minute, ); } int indexedStack = 0; StateSetter? setstate1; StateSetter? setstate2; return WBottomModalUtil.show( context, title: StatefulBuilder(builder: (_, s) { final primaryColor = Theme.of(context).primaryColor; setstate1 = s; return Row( children: [ Wisdom( onTap: () { indexedStack = 0; setstate1!(() {}); setstate2!(() {}); }, padding: EdgeInsets.all(5.pt), child: Text( _value.toFormat(WisDateTimeFormat.date), style: TextStyle( color: indexedStack == 0 ? primaryColor : null, ), ), ), SizedBox(width: 5.pt), Wisdom( onTap: () { indexedStack = 1; setstate1!(() {}); setstate2!(() {}); }, padding: EdgeInsets.all(5.pt), child: Text( _value.toFormat('{hh}:{mm}'), style: TextStyle( color: indexedStack == 1 ? primaryColor : null, ), ), ), ], ); }), plain: false, onCancel: null, onConfirm: (_) => Navigator.pop(context, _value), body: StatefulBuilder( builder: (_, s) { setstate2 = s; return IndexedStack( index: indexedStack, children: [ CalendarDatePicker( initialDate: _value, firstDate: _firstDate, lastDate: _lastDate, onDateChanged: (datetime) { setDate(datetime); indexedStack = 1; setstate1!(() {}); setstate2!(() {}); }, ), CupertinoDatePicker( mode: CupertinoDatePickerMode.time, use24hFormat: true, minimumDate: '${_value.year}${_value.month}${_value.day}' == '${_firstDate.year}${_firstDate.month}${_firstDate.day}' ? _firstDate : null, minuteInterval: minuteInterval, initialDateTime: _value, onDateTimeChanged: (datetime) { setTime(datetime); setstate1!(() {}); setstate2!(() {}); }, ) ], ); }, ), ); } /// 选择时间 static Future time( BuildContext context, { Widget? title, DateTime? initialDate, ///分钟间隔, /// ///如若设置此参数之后, ///`firstDate`、`lastDate`、`initialDate`的minute(分钟)必须都要可整除此参数 int minuteInterval = 1, }) { final now = DateTime.now().toLocal(); final now2 = DateTime( now.year, now.month, now.day, now.hour, minuteInterval == 5 && now.minute % 5 < 5 ? now.minute - (now.minute % 5) : now.minute); //转换一下,去掉秒、微秒等 DateTime _value = initialDate ?? now2; ///设置时间 void setTime(DateTime dateTime) { _value = DateTime( _value.year, _value.month, _value.day, dateTime.hour, dateTime.minute, ); } StateSetter? setstate1; return WBottomModalUtil.show( context, title: StatefulBuilder(builder: (_, s) { final primaryColor = Theme.of(context).primaryColor; setstate1 = s; return Text( _value.toFormat('{hh}:{mm}'), style: TextStyle( color: primaryColor, ), ); }), plain: false, onCancel: null, onConfirm: (_) => Navigator.pop(context, _value), body: StatefulBuilder( builder: (_, s) { return CupertinoDatePicker( mode: CupertinoDatePickerMode.time, use24hFormat: true, minuteInterval: minuteInterval, initialDateTime: _value, onDateTimeChanged: (datetime) { setTime(datetime); setstate1!(() {}); }, ); }, ), ); } }