YSTools.dart 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. import 'dart:io';
  2. import 'package:device_info_plus/device_info_plus.dart';
  3. import 'package:flutter/foundation.dart';
  4. import 'package:flutter/material.dart';
  5. import 'package:flutter/services.dart';
  6. import 'package:flutter_screenutil/flutter_screenutil.dart';
  7. import 'package:intl/intl.dart';
  8. import 'package:permission_handler/permission_handler.dart';
  9. import 'dart:math' as math;
  10. enum FillInType{
  11. choose,
  12. input,
  13. none,
  14. file,
  15. }
  16. double ysHeight(BuildContext context) => MediaQuery.of(context).size.height;
  17. double ysWidth(BuildContext context) => MediaQuery.of(context).size.width;
  18. double ysTOP(BuildContext context) => MediaQuery.of(context).padding.top;
  19. double ysBottom(BuildContext context) => MediaQuery.of(context).padding.bottom;
  20. Color ysBgColor = const Color(0xFF23262B);
  21. Color ysTitleColor = const Color(0xFFACB5C5);
  22. Color ysValueColor = Colors.white;
  23. String accessToken = "sk.eyJ1IjoiYmxvY2tjYXJib24iLCJhIjoiY2xrcm81enRyMmx6dTNra2VhZnMwcmNneCJ9.inGPH4-QFL6UX1NsDHKeiA";
  24. hsp(int number){
  25. return ScreenUtil().setHeight(number);
  26. }
  27. zsp(int number){
  28. return ScreenUtil().setSp(number);
  29. }
  30. ///手机号验证
  31. bool isChinaPhoneLegal(String str) {
  32. return RegExp(r"^1([38][0-9]|4[579]|5[0-3,5-9]|6[6]|7[0135678]|9[89])\d{8}$").hasMatch(str);
  33. }
  34. ///邮箱验证
  35. bool isEmail(String str) {
  36. return RegExp(r"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$").hasMatch(str);
  37. }
  38. ///验证URL
  39. bool isUrl(String value) {
  40. return RegExp(r"^((https|http|ftp|rtsp|mms)?:\/\/)[^\s]+").hasMatch(value);
  41. }
  42. ///验证车牌
  43. bool isCarNumber(String value) {
  44. ////1、这条正则可以校验 普通汽车的车牌、新能源客(货)车车牌
  45. // const carNoReg = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}(([A-Z0-9]{4}[A-Z0-9挂学警港澳]{1}$)|([0-9]{5}[DF]$)|([DF][A-HJ-NP-Z0-9][0-9]{4}$))/; //普通车牌 | 新能源货车 | 新能源小客车 车牌校验规则
  46. // //2、普通车车牌校验
  47. // const carCardP = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳]{1}$/;
  48. // //3、新能源车牌校验
  49. // const carCardD = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}(([0-9]{5}[DF]$)|([DF][A-HJ-NP-Z0-9][0-9]{4}$))/;
  50. // //4、农用车辆及拖拉机车牌号验证:
  51. // const carCardD = /^(([\u4e00-\u9fa5][a-zA-Z]|[\u4e00-\u9fa5]{2}\d{2}|[\u4e00-\u9fa5]{2}[a-zA-Z])[-]?|([wW][Jj][\u4e00-\u9fa5]{1}[-]?)|([a-zA-Z]{2}))([A-Za-z0-9]{5}|[DdFf][A-HJ-NP-Za-hj-np-z0-9][0-9]{4}|[0-9]{5}[DdFf])$/
  52. return RegExp(r"^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}(([A-Z0-9]{4}[A-Z0-9挂学警港澳]{1}$)|([0-9]{5}[DF]$)|([DF][A-HJ-NP-Z0-9][0-9]{4}$))").hasMatch(value);
  53. }
  54. ///验证中文
  55. bool isChinese(String value) {
  56. return RegExp(r"[\u4e00-\u9fa5]").hasMatch(value);
  57. }
  58. ///验证身份证
  59. bool isIdCard(String value) {
  60. return RegExp(r"\d{17}[\d|x]|\d{15}").hasMatch(value);
  61. }
  62. class LogUtil {
  63. static const _separator = "=";
  64. static const _split =
  65. "$_separator$_separator$_separator$_separator$_separator$_separator$_separator$_separator$_separator";
  66. static const _title = "Yl-Log";
  67. static const _isDebug = true;
  68. static const int _limitLength = 800;
  69. static const String _startLine = "$_split$_title$_split";
  70. static const String _endLine = "$_split$_separator$_separator$_separator$_split";
  71. //仅Debug模式可见
  72. static void d(dynamic obj) {
  73. if (_isDebug) {
  74. _log(obj.toString());
  75. }
  76. }
  77. static void v(dynamic obj) {
  78. _log(obj.toString());
  79. }
  80. static void _log(String msg) {
  81. if (kDebugMode) {
  82. print(_startLine);
  83. }
  84. _logEmpyLine();
  85. if(msg.length<_limitLength){
  86. if (kDebugMode) {
  87. print(msg);
  88. }
  89. }else{
  90. segmentationLog(msg);
  91. }
  92. _logEmpyLine();
  93. if (kDebugMode) {
  94. print(_endLine);
  95. }
  96. }
  97. static void segmentationLog(String msg) {
  98. var outStr = StringBuffer();
  99. for (var index = 0; index < msg.length; index++) {
  100. outStr.write(msg[index]);
  101. if (index % _limitLength == 0 && index!=0) {
  102. if (kDebugMode) {
  103. print(outStr);
  104. }
  105. outStr.clear();
  106. var lastIndex = index+1;
  107. if(msg.length-lastIndex<_limitLength){
  108. var remainderStr = msg.substring(lastIndex,msg.length);
  109. if (kDebugMode) {
  110. print(remainderStr);
  111. }
  112. break;
  113. }
  114. }
  115. }
  116. }
  117. static void _logEmpyLine(){
  118. if (kDebugMode) {
  119. print("");
  120. }
  121. }
  122. }
  123. ysShowBottomAlertView(BuildContext context,Widget widget,{bool isBarr = false}) {
  124. showModalBottomSheet(
  125. context: context,
  126. isScrollControlled: true,
  127. backgroundColor: Colors.transparent,
  128. barrierColor: !isBarr?Colors.transparent:Colors.black54,
  129. builder: (context){
  130. return widget;
  131. }
  132. );
  133. }
  134. ysShowCenterAlertView(BuildContext context,Widget widget,{bool isTrans = false,bool isBarr = false}) {
  135. showGeneralDialog(
  136. context: context,
  137. barrierDismissible: isBarr,
  138. barrierLabel: '',
  139. barrierColor: isTrans==true?Colors.transparent:Colors.black54,
  140. pageBuilder: (context,animation,scAnimation){
  141. return widget;
  142. }
  143. );
  144. }
  145. class YSData {
  146. List urgentArray = [];
  147. String modelId = '';
  148. double bili = 1.0;
  149. String detailsId = '0';
  150. Image image = Image.asset('images/tzh_clear.png',color: Colors.transparent,);
  151. YSData._internal();
  152. //保存单例
  153. static final YSData _singleton = YSData._internal();
  154. //工厂构造函数
  155. factory YSData()=> _singleton;
  156. }
  157. ///单例类
  158. class YSMapUntil {
  159. // 工厂方法构造函数 - 通过UserModel()获取对象1
  160. factory YSMapUntil() => _getInstance();
  161. // instance的getter方法 - 通过UserModel.instance获取对象2
  162. static YSMapUntil get instance => _getInstance();
  163. // 静态变量_instance,存储唯一对象
  164. static YSMapUntil? _instance;
  165. // 获取唯一对象
  166. static YSMapUntil _getInstance() {
  167. _instance ??= YSMapUntil._internal();
  168. return _instance!;
  169. }
  170. YSMapUntil._internal();
  171. }
  172. extension HexColor on Color {
  173. /// String is in the format "aabbcc" or "ffaabbcc" with an optional leading "#".
  174. static Color fromHex(String hexString) {
  175. final buffer = StringBuffer();
  176. if (hexString.length == 6 || hexString.length == 7) buffer.write('ff');
  177. buffer.write(hexString.replaceFirst('#', ''));
  178. return Color(int.parse(buffer.toString(), radix: 16));
  179. }
  180. /// Prefixes a hash sign if [leadingHashSign] is set to `true` (default is `true`).
  181. String toHex({bool leadingHashSign = true}) => '${leadingHashSign ? '#' : ''}'
  182. '${alpha.toRadixString(16).padLeft(2, '0')}'
  183. '${red.toRadixString(16).padLeft(2, '0')}'
  184. '${green.toRadixString(16).padLeft(2, '0')}'
  185. '${blue.toRadixString(16).padLeft(2, '0')}';
  186. }
  187. Future<bool> permissionHandler(String serviceStr) async{
  188. if(serviceStr=='location'){
  189. if (await Permission.location.request().isGranted||await Permission.locationWhenInUse.request().isGranted) {
  190. return true;
  191. }else if (await Permission.location.request().isPermanentlyDenied&&await Permission.locationWhenInUse.request().isPermanentlyDenied) {
  192. return false;
  193. }else{
  194. Map<Permission, PermissionStatus> statuses = await [Permission.location,Permission.locationWhenInUse].request();
  195. if(statuses[Permission.location]!.isGranted || statuses[Permission.locationWhenInUse]!.isGranted){
  196. return true;
  197. }
  198. }
  199. }else if(serviceStr=='file'){
  200. // LogUtil.d(await Permission.photos.request().isGranted&&await Permission.videos.request().isGranted);
  201. DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
  202. AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
  203. // int version = int.parse(androidInfo.version.release);
  204. // LogUtil.d(androidInfo.version.sdkInt);
  205. // return false;
  206. if(androidInfo.version.sdkInt > 32){
  207. if (await Permission.photos.request().isGranted&&await Permission.videos.request().isGranted) {
  208. return true;
  209. }else if (await Permission.photos.request().isPermanentlyDenied||await Permission.videos.request().isPermanentlyDenied) {
  210. return false;
  211. }else{
  212. Map<Permission, PermissionStatus> statuses = await [Permission.photos,Permission.videos].request();
  213. if(statuses[Permission.photos]!.isGranted&&statuses[Permission.videos]!.isGranted){
  214. return true;
  215. }
  216. }
  217. }else{
  218. if (await Permission.storage.request().isGranted) {
  219. return true;
  220. }else if (await Permission.storage.request().isPermanentlyDenied) {
  221. return false;
  222. }else{
  223. Map<Permission, PermissionStatus> statuses = await [Permission.storage].request();
  224. if(statuses[Permission.storage]!.isGranted){
  225. return true;
  226. }
  227. }
  228. }
  229. }
  230. return false;
  231. }
  232. timeFormat(int time,{String format = 'yyyy-MM-dd HH:mm:ss'}) {
  233. // LogUtil.d('timeFormat1=====>$time');
  234. DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(time);
  235. // LogUtil.d('timeFormat2=====>$dateTime');
  236. String dateString = DateFormat(format).format(dateTime).toString();
  237. // LogUtil.d('timeFormat3=====>$dateString');
  238. return dateString;
  239. }
  240. class CustomerNotification extends Notification {
  241. CustomerNotification();
  242. }
  243. class CustomerValueNotification extends Notification {
  244. final Map value;
  245. CustomerValueNotification(this.value);
  246. }
  247. class YSNumberView2 extends StatefulWidget {
  248. final ValueSetter numberSetter;
  249. final int number;
  250. const YSNumberView2({Key? key, required this.numberSetter, this.number = 1}) : super(key: key);
  251. @override
  252. YSNumberView2State createState() => YSNumberView2State();
  253. }
  254. class YSNumberView2State extends State<YSNumberView2> {
  255. int _number = 1;
  256. @override
  257. void initState() {
  258. _number = widget.number;
  259. super.initState();
  260. }
  261. @override
  262. Widget build(BuildContext context) {
  263. return SizedBox(
  264. width: hsp(100),
  265. height: hsp(30),
  266. child: Row(
  267. children: [
  268. GestureDetector(
  269. onTap: (){
  270. // if(_number<=1)return;
  271. _number--;
  272. setState(() {});
  273. widget.numberSetter(_number);
  274. },
  275. behavior: HitTestBehavior.opaque,
  276. child: Container(
  277. height: hsp(30),
  278. width: hsp(30),
  279. alignment: Alignment.center,
  280. color: const Color(0xFFF2F3F5),
  281. child: Icon(Icons.remove,size: hsp(15),color: const Color(0xFF333333),),
  282. ),
  283. ),
  284. Container(
  285. margin: EdgeInsets.only(left: hsp(2),right: hsp(2)),
  286. color: const Color(0xFFF2F3F5),
  287. width: hsp(36),
  288. height: hsp(30),
  289. alignment: Alignment.center,
  290. child: SingleChildScrollView(
  291. scrollDirection: Axis.horizontal,
  292. child: Text('$_number',style: TextStyle(fontSize: zsp(14),color: const Color(0xFF333333)),),
  293. ),
  294. ),
  295. GestureDetector(
  296. onTap: (){
  297. _number++;
  298. setState(() {});
  299. widget.numberSetter(_number);
  300. },
  301. behavior: HitTestBehavior.opaque,
  302. child: Container(
  303. height: hsp(30),
  304. width: hsp(30),
  305. alignment: Alignment.center,
  306. color: const Color(0xFFF2F3F5),
  307. child: Icon(Icons.add,size: hsp(15),color: const Color(0xFF333333),),
  308. ),
  309. ),
  310. ],
  311. ),
  312. );
  313. }
  314. }
  315. class YSNumberView extends StatefulWidget {
  316. final ValueSetter numberSetter;
  317. final int number;
  318. const YSNumberView({Key? key, required this.numberSetter, this.number = 1}) : super(key: key);
  319. @override
  320. YSNumberViewState createState() => YSNumberViewState();
  321. }
  322. class YSNumberViewState extends State<YSNumberView> {
  323. int _number = 1;
  324. @override
  325. void initState() {
  326. _number = widget.number;
  327. super.initState();
  328. }
  329. @override
  330. Widget build(BuildContext context) {
  331. return SizedBox(
  332. width: hsp(100),
  333. height: hsp(30),
  334. child: Row(
  335. children: [
  336. GestureDetector(
  337. onTap: (){
  338. // if(_number<=1)return;
  339. _number--;
  340. setState(() {});
  341. widget.numberSetter(_number);
  342. },
  343. behavior: HitTestBehavior.opaque,
  344. child: Container(
  345. height: hsp(30),
  346. width: hsp(30),
  347. alignment: Alignment.center,
  348. color: const Color(0xFFF2F3F5),
  349. child: Icon(Icons.remove,size: hsp(15),color: const Color(0xFF333333),),
  350. ),
  351. ),
  352. Container(
  353. margin: EdgeInsets.only(left: hsp(2),right: hsp(2)),
  354. color: const Color(0xFFF2F3F5),
  355. width: hsp(36),
  356. height: hsp(30),
  357. alignment: Alignment.center,
  358. child: SingleChildScrollView(
  359. scrollDirection: Axis.horizontal,
  360. child: Text('$_number',style: TextStyle(fontSize: zsp(14),color: const Color(0xFF333333)),),
  361. ),
  362. ),
  363. GestureDetector(
  364. onTap: (){
  365. _number++;
  366. setState(() {});
  367. widget.numberSetter(_number);
  368. },
  369. behavior: HitTestBehavior.opaque,
  370. child: Container(
  371. height: hsp(30),
  372. width: hsp(30),
  373. alignment: Alignment.center,
  374. color: const Color(0xFFF2F3F5),
  375. child: Icon(Icons.add,size: hsp(15),color: const Color(0xFF333333),),
  376. ),
  377. ),
  378. ],
  379. ),
  380. );
  381. }
  382. }
  383. networkDelay(VoidCallback callback) {
  384. Future.delayed(const Duration(seconds: 0)).then((value) {
  385. callback();
  386. });
  387. }
  388. String statusString(int status) {
  389. String statusStr = '';
  390. switch(status) {
  391. case 0: statusStr = '待确认';
  392. break;
  393. case 1: statusStr = '待采样';
  394. break;
  395. case 2: statusStr = '进行中';
  396. break;
  397. case 3: statusStr = '待检测';
  398. break;
  399. case 4: statusStr = '已完成';
  400. break;
  401. case 5: statusStr = '已拒绝';
  402. break;
  403. }
  404. return statusStr;
  405. }
  406. class NumberFormat extends TextInputFormatter {
  407. NumberFormat({this.decimalRange = 3})
  408. : assert(decimalRange > 0);
  409. final int decimalRange;
  410. @override
  411. TextEditingValue formatEditUpdate(TextEditingValue oldValue,
  412. TextEditingValue newValue) {
  413. // 拿到录入后的字符
  414. String nValue = newValue.text;
  415. //当前所选择的文字区域
  416. TextSelection nSelection = newValue.selection;
  417. // 先来一波过滤,过滤出数字及小数点
  418. // 匹配包含数字和小数点的字符
  419. Pattern p = RegExp(r'(\d+\.?)|(\.?\d+)|(\.?)');
  420. nValue = p.allMatches(nValue)
  421. .map<String>((Match match) => match.group(0)!)
  422. .join();
  423. // 用匹配完的字符判断
  424. if (nValue.startsWith('.')) { //如果小数点开头,我们给他补个0
  425. nValue = '0.';
  426. } else if (nValue.contains('.')) {
  427. //来验证小数点位置
  428. if (nValue.substring(nValue.indexOf('.') + 1).length > decimalRange) {
  429. nValue = oldValue.text;
  430. } else {
  431. if (nValue.split('.').length > 2) { //多个小数点,去掉后面的
  432. List<String> split = nValue.split('.');
  433. nValue = '${split[0]}.${split[1]}';
  434. }
  435. }
  436. }
  437. //使光标定位到最后一个字符后面
  438. nSelection = newValue.selection.copyWith(
  439. baseOffset: math.min(nValue.length, nValue.length + 1),
  440. extentOffset: math.min(nValue.length, nValue.length + 1),
  441. );
  442. return TextEditingValue(
  443. text: nValue,
  444. selection: nSelection,
  445. composing: TextRange.empty
  446. );
  447. }
  448. }