template.dart 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855
  1. import 'dart:async';
  2. import 'dart:convert';
  3. import 'dart:io';
  4. import 'dart:ui' as ui;
  5. import 'package:another_flushbar/flushbar.dart';
  6. import 'package:auto_route/auto_route.dart';
  7. import 'package:cached_network_image/cached_network_image.dart';
  8. import 'package:dotted_line/dotted_line.dart';
  9. import 'package:easy_localization/easy_localization.dart';
  10. import 'package:flutter/foundation.dart';
  11. import 'package:flutter/material.dart';
  12. import 'package:flutter/services.dart';
  13. import 'package:flutter_cache_manager/flutter_cache_manager.dart';
  14. import 'package:fluttertoast/fluttertoast.dart';
  15. import 'package:loading_indicator/loading_indicator.dart';
  16. import 'package:lottie/lottie.dart';
  17. import 'package:page_transition/page_transition.dart';
  18. import 'package:photo_view/photo_view.dart';
  19. import 'package:photo_view/photo_view_gallery.dart';
  20. import 'package:telnow_mobile_new/src/api/jwt_token.dart';
  21. import 'package:telnow_mobile_new/src/injector/injector.dart';
  22. import 'package:telnow_mobile_new/src/layouts/auth/qr.dart';
  23. import 'package:telnow_mobile_new/src/storage/sharedpreferences/shared_preferences_manager.dart';
  24. import 'package:telnow_mobile_new/src/utils/U.dart';
  25. import 'package:telnow_mobile_new/src/utils/cache_manager.dart';
  26. import 'package:uuid/uuid.dart';
  27. PreferredSizeWidget appBarTemplate({required BuildContext context, required String title, String icon = 'arrow-left', List<Widget>? action, exc = false}){
  28. return AppBar(
  29. elevation: 0,
  30. bottomOpacity: 0,
  31. backgroundColor: backgroundColor,
  32. leading: GestureDetector(
  33. child: U.iconsax(icon, color: textColor),
  34. onTap: ()=>Navigator.of(context).pop(exc),
  35. ),
  36. titleSpacing: 0,
  37. title: Text(title, style: TextStyle(color: textColor, fontSize: 17, fontWeight: FontWeight.w500), overflow: TextOverflow.ellipsis),
  38. actions: action,
  39. );
  40. }
  41. Widget buttonTemplate({double width = double.infinity, double height = 51, required String text, required Function() action, backgroundColor = primaryColor, textColor = Colors.white, borderColor = primaryColor}){
  42. return SizedBox(
  43. width: width,
  44. height: height,
  45. child: ElevatedButton(
  46. style: ButtonStyle(
  47. elevation: WidgetStateProperty.all<double>(0),
  48. backgroundColor: WidgetStateProperty.all<Color>(backgroundColor),
  49. shape: WidgetStateProperty.all<RoundedRectangleBorder>(RoundedRectangleBorder(borderRadius: BorderRadius.circular(12))),
  50. side: WidgetStateProperty.all<BorderSide>(BorderSide(color: borderColor))
  51. ),
  52. onPressed: ()=>action(),
  53. child: Text(text, style: TextStyle(fontSize: 16, color: textColor, fontWeight: FontWeight.w500)),
  54. ),
  55. );
  56. }
  57. Widget textVertical(String title, String subtitle){
  58. return Column(
  59. crossAxisAlignment: CrossAxisAlignment.start,
  60. children: [
  61. Text(title, style: TextStyle(color: textColor)),
  62. SizedBox(height: 5),
  63. Text(subtitle, style: TextStyle(color: textColor, fontSize: 12, fontWeight: FontWeight.w300)),
  64. ],
  65. );
  66. }
  67. Widget textHorizontal(String title, String subtitle, {double size = 14, double opacity = 1, bool copy = false}){
  68. return Row(
  69. crossAxisAlignment: CrossAxisAlignment.start,
  70. children: [
  71. Text(title, style: TextStyle(color: textColor.withValues(alpha: opacity), fontSize: size)),
  72. Expanded(
  73. child: copy?Row(
  74. mainAxisAlignment: MainAxisAlignment.end,
  75. children: [
  76. Expanded(child: Text(subtitle, style: TextStyle(color: textColor, fontSize: size), maxLines: 3, overflow: TextOverflow.ellipsis, textAlign: TextAlign.end)),
  77. SizedBox(width: 5),
  78. InkWell(
  79. child: Icon(Icons.copy, color: textColor, size: size+2),
  80. onTap: ()async{
  81. await Clipboard.setData(ClipboardData(text: subtitle));
  82. Fluttertoast.showToast(msg: 'link_copied'.tr());
  83. },
  84. ),
  85. ],
  86. ):Text(subtitle, style: TextStyle(color: textColor, fontSize: size), maxLines: 3, overflow: TextOverflow.ellipsis, textAlign: TextAlign.end,),
  87. ),
  88. ],
  89. );
  90. }
  91. Widget divider({opacity = 0.15, EdgeInsetsGeometry padding = const EdgeInsets.only(top: 0)}){
  92. return Padding(
  93. padding: padding,
  94. child: Divider(height: 1, thickness: 1, color: textColor.withValues(alpha: opacity)),
  95. );
  96. }
  97. Widget dashed({double top = 8, double bottom = 8, double left = 0, double right = 0, Color color = textColor, double width = double.infinity}){
  98. return Padding(padding: EdgeInsets.only(top: top, bottom: bottom, left: left, right: right), child: DottedLine(dashColor: color, lineLength: width, dashLength: 5, dashGapLength: 5, lineThickness: 0.15, dashRadius: 1));
  99. }
  100. Widget separator(){
  101. return Container(
  102. color: Color(0xffF3F3F3),
  103. width: double.infinity, height: 8,
  104. );
  105. }
  106. //------------------------------------------------------------------------------
  107. Widget categoryContainer({required BuildContext context, required String iconUrl}){
  108. double size = U.bodyWidth(context)/6;
  109. // String ext = iconUrl.split(".").last;
  110. return Container(
  111. padding: EdgeInsets.all(5),
  112. decoration: BoxDecoration(
  113. color: Colors.white,
  114. borderRadius: BorderRadius.all(Radius.circular(20)),
  115. border: Border.all(width: 0.2, color: Colors.black12),
  116. boxShadow: [BoxShadow(color: Colors.grey.withValues(alpha: 0.3), blurRadius: 2, offset: Offset(0, 2))]
  117. ),
  118. child: Image(image: CachedNetworkImageProvider('$iconUrl?bridge-cache=true', cacheManager: CacheManager(CacheMan.config('$iconUrl?bridge-cache=true'))), width: size, height: size),
  119. );
  120. }
  121. Widget shimmerTopMenu(BuildContext context){
  122. double size = (U.bodyWidth(context)/6)+10;
  123. return Row(
  124. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  125. children: List.generate(4, (index){
  126. return Container(
  127. width: size, height: size,
  128. decoration: BoxDecoration(
  129. color: Colors.black26,
  130. borderRadius: BorderRadius.all(Radius.circular(20)),
  131. ),
  132. );
  133. // return Shimmer.fromColors(
  134. // baseColor: Colors.grey[400]!,
  135. // highlightColor: Colors.white,
  136. // child: Container(
  137. // width: size, height: size,
  138. // decoration: BoxDecoration(
  139. // color: Colors.white,
  140. // borderRadius: BorderRadius.all(Radius.circular(20)),
  141. // ),
  142. // ),
  143. // );
  144. }),
  145. );
  146. }
  147. //------------------------------------------------------------------------------
  148. Widget requestTiles({required String image, required String title, required String subtitle, bool border = false, double vertical = 16}){
  149. return Container(
  150. margin: EdgeInsets.symmetric(vertical: vertical),
  151. padding: EdgeInsetsDirectional.only(end: border?16:0),
  152. decoration: BoxDecoration(
  153. color: Colors.white,
  154. border: border?Border.all(color: textColor.withValues(alpha: 0.15)):null,
  155. borderRadius: border?BorderRadius.all(Radius.circular(12)):null
  156. ),
  157. child: Row(
  158. children: [
  159. imageTiles(imageUrl: image),
  160. SizedBox(width: 16),
  161. Expanded(
  162. child: Column(
  163. crossAxisAlignment: CrossAxisAlignment.start,
  164. children: [
  165. Text(title, style: TextStyle(color: textColor, fontWeight: FontWeight.w500), overflow: TextOverflow.ellipsis),
  166. dashed(),
  167. Text(subtitle, style: TextStyle(color: textColor, fontSize: 13, fontWeight: FontWeight.w300), maxLines: 2, overflow: TextOverflow.ellipsis)
  168. ],
  169. ),
  170. )
  171. ],
  172. ),
  173. );
  174. }
  175. Widget imageTiles({required String imageUrl, double width = 100, double height = 80, double radius = 12}){
  176. return imageUrl != "null" ? Container(
  177. padding: EdgeInsets.fromLTRB(1, 0, 1, 0),
  178. decoration: BoxDecoration(
  179. color: textColor.withValues(alpha: 0.1),
  180. borderRadius: BorderRadius.all(Radius.circular(radius)),
  181. ),
  182. child: Container(
  183. width: width, height: height,
  184. decoration: BoxDecoration(
  185. color: textColor.withValues(alpha: 0.1),
  186. borderRadius: BorderRadius.all(Radius.circular(radius)),
  187. image: DecorationImage(
  188. image: CachedNetworkImageProvider('$imageUrl?bridge-cache=true', cacheManager: CacheManager(CacheMan.config('$imageUrl?bridge-cache=true'))),
  189. fit: BoxFit.cover,
  190. ),
  191. ),
  192. ),
  193. ): Container(
  194. height: height,
  195. width: width,
  196. decoration: BoxDecoration(
  197. color: Colors.black12,
  198. borderRadius: BorderRadius.all(Radius.circular(12.0))
  199. ),
  200. child: Center(child: Text("noImage".tr(), style: TextStyle(fontStyle: FontStyle.italic, fontSize: width<100?10:14, color: Colors.black38), maxLines: 2, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center,)),
  201. );
  202. }
  203. typedef FieldCallback = void Function(String value);
  204. Widget loginFieldTemplate(
  205. {
  206. required String value,
  207. placeholder = '',
  208. required FieldCallback action,
  209. required FieldCallback submit,
  210. isPassword = false
  211. }
  212. ){
  213. TextEditingController controller = TextEditingController()..text = value;
  214. bool hidePass = true;
  215. return SizedBox(
  216. width: double.infinity,
  217. child: StatefulBuilder(
  218. builder: (context, fieldState) => TextFormField(
  219. controller: controller,
  220. style: TextStyle(fontSize: 16, color: Colors.white),
  221. keyboardType: TextInputType.text,
  222. obscureText: isPassword?hidePass:false,
  223. cursorColor: Colors.white,
  224. decoration: InputDecoration(
  225. hintText: placeholder,
  226. hintStyle: TextStyle(fontSize: 16, color: Colors.white.withValues(alpha: 0.6)),
  227. filled: true,
  228. fillColor: Colors.white.withValues(alpha: 0.25),
  229. hoverColor: Colors.white.withValues(alpha: 0.26),
  230. contentPadding: EdgeInsets.all(16),
  231. border: InputBorder.none,
  232. suffixIconConstraints: BoxConstraints(maxHeight: 40, maxWidth: 40, minHeight: 40, minWidth: 40),
  233. suffixIcon: isPassword?GestureDetector(child: U.iconsax(hidePass?'eye':'eye-slash', color: Colors.white.withValues(alpha: 0.6)), onTap: ()=>fieldState(()=>hidePass=!hidePass)):null,
  234. enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.white)),
  235. focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.white)),
  236. isDense: true
  237. ),
  238. onChanged: (val)=>action(val.toString()),
  239. onFieldSubmitted: (val)=>submit(val.toString()),
  240. ),
  241. ),
  242. );
  243. }
  244. Widget loadingTemplate(VoidCallback setState){
  245. Future.delayed(Duration(seconds: 10),() {
  246. setState();
  247. });
  248. return Center(
  249. child: SizedBox(
  250. height: 36,
  251. child: LoadingIndicator(
  252. indicatorType: Indicator.ballPulseRise,
  253. colors: U.defaultRainbowColors(),
  254. strokeWidth: 2,
  255. backgroundColor: Colors.black.withValues(alpha: 0),
  256. pathBackgroundColor: Colors.black,
  257. ),
  258. ),
  259. );
  260. }
  261. Widget loadingTemplateNoVoid(){
  262. return Center(
  263. child: SizedBox(
  264. height: 36,
  265. child: LoadingIndicator(
  266. indicatorType: Indicator.ballPulseRise,
  267. colors: U.defaultRainbowColors(),
  268. strokeWidth: 2,
  269. backgroundColor: Colors.black.withValues(alpha: 0),
  270. pathBackgroundColor: Colors.black,
  271. ),
  272. ),
  273. );
  274. }
  275. Widget showButton(BuildContext context){
  276. var pid = U.getPidFromUrl(context.router.currentUrl);
  277. return Center(
  278. child: GestureDetector(
  279. onTap: (){
  280. context.router.removeLast();
  281. context.navigateToPath("/app/$pid/login");
  282. },
  283. child: Container(
  284. decoration: BoxDecoration(color: Colors.amber.withAlpha(0)),
  285. height: MediaQuery.of(context).size.height,
  286. width: MediaQuery.of(context).size.width,
  287. child: Center(
  288. child: Column(
  289. mainAxisAlignment: MainAxisAlignment.center,
  290. children: [
  291. Text("failedLoad".tr()),
  292. SizedBox(height: 12,),
  293. Container(
  294. decoration: BoxDecoration(
  295. border: Border.all(color: Color(0xFFA5A5A5)),
  296. borderRadius: BorderRadius.all(Radius.circular(4))
  297. ),
  298. child: Padding(
  299. padding: const EdgeInsets.all(8.0),
  300. child: Text("tap2retry".tr()),
  301. ),
  302. )
  303. ],
  304. )
  305. )
  306. ),
  307. ),
  308. );
  309. }
  310. Widget infoContainer(String text){
  311. return Container(
  312. width: double.infinity,
  313. padding: EdgeInsets.all(12),
  314. decoration: BoxDecoration(
  315. color: secondaryColor.withValues(alpha: 0.1),
  316. border: Border.all(color: secondaryColor),
  317. borderRadius: BorderRadius.all(Radius.circular(12))
  318. ),
  319. child: Text(text, style: TextStyle(color: textColor), textAlign: TextAlign.center),
  320. );
  321. }
  322. class PhotoPreview extends StatelessWidget {
  323. final String title;
  324. final imageUrl;
  325. final bool isUrl;
  326. final bool isNetwork;
  327. const PhotoPreview(this.title, this.imageUrl, this.isUrl, {super.key, this.isNetwork = false});
  328. @override
  329. Widget build(BuildContext context) {
  330. return Scaffold(
  331. backgroundColor: Colors.black,
  332. appBar: AppBar(
  333. elevation: 0,
  334. bottomOpacity: 0,
  335. titleSpacing: 0,
  336. title: Text(title, style: TextStyle(color: Colors.white, fontSize: 17, fontWeight: FontWeight.w500), overflow: TextOverflow.ellipsis),
  337. backgroundColor: Colors.black.withValues(alpha: 0.4),
  338. leading: GestureDetector(
  339. child: U.iconsax('arrow-left', color: Colors.white),
  340. onTap: () => Navigator.of(context).pop(),
  341. ),
  342. ),
  343. body: SizedBox(
  344. width: MediaQuery.of(context).size.width,
  345. height: MediaQuery.of(context).size.height,
  346. child: PhotoView(
  347. maxScale: 5.0,
  348. minScale: 0.3,
  349. imageProvider: (isUrl ? NetworkImage(imageUrl) : isNetwork ? MemoryImage(imageUrl) : FileImage(imageUrl)) as ImageProvider<Object>?,
  350. )),
  351. );
  352. }
  353. }
  354. class PhotoPreviewGallery extends StatelessWidget {
  355. final String title;
  356. final List imageList;
  357. final int startIndex;
  358. final bool isUrl;
  359. final String column;
  360. const PhotoPreviewGallery({required this.title, required this.imageList, required this.startIndex, this.isUrl = true, this.column = 'image', super.key});
  361. @override
  362. Widget build(BuildContext context) {
  363. return Scaffold(
  364. backgroundColor: Colors.black,
  365. appBar: AppBar(
  366. title: Text(title, style: TextStyle(fontSize: 18)),
  367. backgroundColor: Colors.black.withValues(alpha: 0.4),
  368. titleSpacing: 0,
  369. leading: IconButton(
  370. icon: Icon(Icons.arrow_back_rounded),
  371. onPressed: () => Navigator.of(context).pop(),
  372. ),
  373. ),
  374. body: SizedBox(
  375. width: MediaQuery.of(context).size.width,
  376. height: MediaQuery.of(context).size.height,
  377. child: PhotoViewGallery.builder(
  378. itemCount: imageList.length,
  379. pageController: PageController(initialPage: startIndex),
  380. builder: (context, index) {
  381. var uuid = Uuid().v1().replaceAll('-', '');
  382. return PhotoViewGalleryPageOptions(
  383. imageProvider: (isUrl ? NetworkImage('${imageList[index][column]}?uuid=$uuid') : imageList[index] is File ? FileImage(imageList[index]) : MemoryImage(imageList[index])) as ImageProvider<Object>?,
  384. maxScale: 5.0, minScale: 0.3,
  385. );
  386. },
  387. )
  388. ),
  389. );
  390. }
  391. }
  392. //------------------------------------------------------------------------------
  393. class MyClipper extends CustomClipper<Path> {
  394. final bool isNip;
  395. MyClipper(this.isNip);
  396. @override
  397. Path getClip(Size size) {
  398. var path = Path();
  399. double factor = 6.0;
  400. path.lineTo(0, size.height - factor);
  401. path.quadraticBezierTo(0, size.height, factor, size.height);
  402. if (isNip) {
  403. path.lineTo(size.width - (factor * 2), size.height);
  404. path.quadraticBezierTo(size.width - factor, size.height, size.width - factor, size.height - factor);
  405. path.lineTo(size.width - factor, factor);
  406. } else {
  407. path.lineTo(size.width - factor, size.height);
  408. path.quadraticBezierTo(size.width, size.height, size.width, size.height - factor);
  409. path.lineTo(size.width, factor);
  410. path.quadraticBezierTo(size.width, 0, size.width - factor, 0);
  411. }
  412. path.lineTo(size.width, 0);
  413. path.lineTo(factor, 0);
  414. path.quadraticBezierTo(0, 0, 0, factor);
  415. return path;
  416. }
  417. @override
  418. bool shouldReclip(CustomClipper oldClipper) => true;
  419. }
  420. class YourClipper extends CustomClipper<Path> {
  421. final bool isNip;
  422. YourClipper(this.isNip);
  423. @override
  424. Path getClip(Size size) {
  425. var path = Path();
  426. double factor = 6.0;
  427. if (isNip) {
  428. path.lineTo(factor, factor);
  429. path.lineTo(factor, size.height - factor);
  430. path.quadraticBezierTo(factor, size.height, factor * 2, size.height);
  431. } else {
  432. path.lineTo(0, size.height - factor);
  433. path.quadraticBezierTo(0, size.height, factor, size.height);
  434. }
  435. path.lineTo(size.width - factor, size.height);
  436. path.quadraticBezierTo(size.width, size.height, size.width, size.height - factor);
  437. path.lineTo(size.width, factor);
  438. path.quadraticBezierTo(size.width, 0, size.width - factor, 0);
  439. if (!isNip) {
  440. path.lineTo(factor, 0);
  441. path.quadraticBezierTo(0, 0, 0, factor);
  442. }
  443. return path;
  444. }
  445. @override
  446. bool shouldReclip(CustomClipper oldClipper) => true;
  447. }
  448. isBase64(value) {
  449. try {
  450. base64Decode(value);
  451. return true;
  452. } catch (e) {
  453. return false;
  454. }
  455. }
  456. //------------------------------------------------------------------------------
  457. class Debouncer {
  458. final int? milliseconds;
  459. VoidCallback? action;
  460. Timer? _timer;
  461. Debouncer({this.milliseconds});
  462. run(VoidCallback action) {
  463. if (null != _timer) {
  464. _timer?.cancel();
  465. }
  466. _timer = Timer(Duration(milliseconds: milliseconds!), action);
  467. }
  468. }
  469. navigateTo(BuildContext context, child){
  470. return Navigator.push(context, PageTransition(type: PageTransitionType.rightToLeft, child: child));
  471. }
  472. navigateToThen(BuildContext context, child, act){
  473. Navigator.push(context, PageTransition(type: PageTransitionType.rightToLeft, child: child)).then((v) => act());
  474. }
  475. navigateBack(BuildContext context, {exc = false}){
  476. return Navigator.of(context).pop(exc);
  477. }
  478. void showError(BuildContext context, message) {
  479. Flushbar(
  480. message: message,
  481. icon: Icon(
  482. Icons.info_outline,
  483. size: 28.0,
  484. color: Colors.red,
  485. ),
  486. duration: Duration(seconds: 5),
  487. flushbarPosition: FlushbarPosition.BOTTOM,
  488. margin: EdgeInsets.all(8),
  489. borderRadius: BorderRadius.all(Radius.circular(8)),
  490. ).show(context);
  491. }
  492. void showSuccess(context, message) {
  493. Flushbar(
  494. message: message,
  495. icon: Icon(
  496. Icons.info_outline,
  497. size: 28.0,
  498. color: Colors.green,
  499. ),
  500. duration: Duration(seconds: 5),
  501. flushbarPosition: FlushbarPosition.BOTTOM,
  502. margin: EdgeInsets.all(8),
  503. borderRadius: BorderRadius.all(Radius.circular(8)),
  504. ).show(context);
  505. }
  506. String convertDate(date, String locale) {
  507. if (date == "-" || date.isEmpty) {
  508. return date;
  509. }
  510. final dateToCheck = DateTime.parse(date);
  511. final now = DateTime.now();
  512. final today = DateTime(now.year, now.month, now.day);
  513. final yesterday = DateTime(now.year, now.month, now.day - 1);
  514. final aDate = DateTime(dateToCheck.year, dateToCheck.month, dateToCheck.day);
  515. if (aDate == today) {
  516. return '${'today'.tr()} ${DateFormat('HH:mm', locale).format(DateTime.parse(date))}';
  517. } else if (aDate == yesterday) {
  518. return '${'yesterday'.tr()} ${DateFormat('HH:mm', locale).format(DateTime.parse(date))}';
  519. } else {
  520. return DateFormat('dd MMM HH:mm', locale).format(DateTime.parse(date));
  521. }
  522. }
  523. //------------------------------------------------------------------------------
  524. class RefreshPage extends StatelessWidget {
  525. final VoidCallback onRefresh;
  526. const RefreshPage(this.onRefresh, {super.key});
  527. @override
  528. Widget build(BuildContext context) {
  529. return Container(
  530. padding: EdgeInsets.only(top: MediaQuery.of(context).size.height / 2.5),
  531. height: MediaQuery.of(context).size.height,
  532. child: Center(
  533. child: GestureDetector(
  534. child: Column(
  535. children: [
  536. Icon(Icons.refresh_rounded, size: 50, color: Colors.grey),
  537. Text('refresh'.tr(), style: TextStyle(color: Colors.grey))
  538. ],
  539. ),
  540. onTap: () => onRefresh(),
  541. ),
  542. ),
  543. );
  544. }
  545. }
  546. //------------------------------------------------------------------------------
  547. final JwtToken token = JwtToken();
  548. final SharedPreferencesManager _sharedPreferencesManager = locator<SharedPreferencesManager>();
  549. class NoDataPage extends StatelessWidget {
  550. const NoDataPage({super.key});
  551. @override
  552. Widget build(BuildContext context) {
  553. return Center(
  554. child: Column(
  555. mainAxisSize: MainAxisSize.min,
  556. children: <Widget>[
  557. kIsWeb && !isCanvasKit ? Container(
  558. width: 150, margin: EdgeInsets.only(top: 20, bottom: 10), padding: EdgeInsets.all(10),
  559. child: Image(image: AssetImage('assets/image/error/EmptyData.png'))
  560. ) : Lottie.asset('assets/image/lottie/Nodata.json', width: 250, height: 250, fit: BoxFit.fill),
  561. Padding(
  562. padding: const EdgeInsets.fromLTRB(15, 0, 15, 10),
  563. child: Text('notFound'.tr(),
  564. style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
  565. textAlign: TextAlign.center),
  566. ),
  567. Padding(
  568. padding: const EdgeInsets.fromLTRB(15, 0, 15, 10),
  569. child: Text('notFound2'.tr(),
  570. style: TextStyle(fontSize: 14), textAlign: TextAlign.center),
  571. )
  572. ],
  573. ),
  574. );
  575. }
  576. }
  577. handlingError(context, type) {
  578. var data = [
  579. {
  580. 'image': 'NoInternet.png',
  581. 'title': 'noInternetTitle'.tr(),
  582. 'description': 'noInternetDesc'.tr()
  583. },
  584. {
  585. 'image': 'ErrorServer.png',
  586. 'title': 'errorServerTitle'.tr(),
  587. 'description': 'errorServerDesc'.tr()
  588. },
  589. {
  590. 'image': 'ErrorConnection.png',
  591. 'title': 'errorConnectTitle'.tr(),
  592. 'description': 'errorConnectDesc'.tr()
  593. },
  594. {
  595. 'image': 'ErrorAuth.png',
  596. 'title': 'invalidAccountTitle'.tr(),
  597. 'description': 'invalidAccountDesc'.tr()
  598. },
  599. {
  600. 'image': 'ErrorAuth.png',
  601. 'title': 'expAccountTitle'.tr(),
  602. 'description': 'expAccountDesc'.tr()
  603. }
  604. ];
  605. showModalBottomSheet<void>(
  606. context: context,
  607. backgroundColor: Colors.white,
  608. isDismissible: false,
  609. enableDrag: false,
  610. builder: (BuildContext context) {
  611. return SingleChildScrollView(
  612. child: Column(
  613. mainAxisSize: MainAxisSize.min,
  614. children: <Widget>[
  615. Container(
  616. padding: const EdgeInsets.fromLTRB(15, 10, 15, 0),
  617. alignment: Alignment.centerRight,
  618. child: GestureDetector(
  619. child: Icon(Icons.clear),
  620. onTap: () => Navigator.of(context).pop(),
  621. ),
  622. ),
  623. Container(
  624. width: 200,
  625. padding: const EdgeInsets.fromLTRB(15, 0, 15, 10),
  626. child: Image(
  627. image: AssetImage('assets/image/error/${data[type]['image']!}'))),
  628. Padding(
  629. padding: const EdgeInsets.fromLTRB(15, 0, 15, 10),
  630. child: Text(data[type]['title']!,
  631. style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
  632. textAlign: TextAlign.center),
  633. ),
  634. Padding(
  635. padding: const EdgeInsets.fromLTRB(15, 0, 15, 10),
  636. child: Text(data[type]['description']!,
  637. style: TextStyle(fontSize: 14),
  638. textAlign: TextAlign.center),
  639. ),
  640. type > 2
  641. ? Padding(
  642. padding: const EdgeInsets.fromLTRB(15, 0, 15, 10),
  643. child: SizedBox(
  644. width: double.infinity,
  645. height: 45,
  646. child: TextButton(
  647. style: ButtonStyle(
  648. backgroundColor:
  649. WidgetStateProperty.all<Color>(primaryColor),
  650. shape: WidgetStateProperty.all<
  651. RoundedRectangleBorder>(
  652. RoundedRectangleBorder(
  653. borderRadius: BorderRadius.circular(5),
  654. ))),
  655. child: Text('logout'.tr().toUpperCase(),
  656. style: TextStyle(color: Colors.white),
  657. textAlign: TextAlign.center),
  658. onPressed: () {
  659. if (type == 4) {
  660. _sharedPreferencesManager.clearKey(
  661. SharedPreferencesManager.keyAccessCode);
  662. _sharedPreferencesManager.clearKey(
  663. SharedPreferencesManager.keySerialCode);
  664. _sharedPreferencesManager.clearKey(
  665. SharedPreferencesManager.keyAccessToken);
  666. _sharedPreferencesManager.clearKey(
  667. SharedPreferencesManager.keyRefreshToken);
  668. _sharedPreferencesManager.clearKey(
  669. SharedPreferencesManager.keyUsername);
  670. _sharedPreferencesManager.clearKey(
  671. SharedPreferencesManager.keyIsLogin);
  672. _sharedPreferencesManager.clearKey(
  673. SharedPreferencesManager.keyScoope);
  674. _sharedPreferencesManager.clearKey(
  675. SharedPreferencesManager.debugString);
  676. _sharedPreferencesManager.clearKey(SharedPreferencesManager.keyHistoryMark);
  677. Navigator.pushAndRemoveUntil(
  678. context,
  679. MaterialPageRoute(
  680. builder: (context) => NewQrPage()),
  681. ModalRoute.withName("/home"));
  682. } else {
  683. var pid = U.getPidFromUrl(context.router.currentUrl);
  684. token.logout();
  685. context.router.removeLast();
  686. context.navigateToPath("/app/$pid/login");
  687. // print(pid);
  688. // context.router.removeLast();
  689. // context.navigateToPath('/app/$pid/login');
  690. }
  691. },
  692. )),
  693. )
  694. : SizedBox(height: 1, width: 1)
  695. ],
  696. ),
  697. );
  698. });
  699. }
  700. // ignore: must_be_immutable
  701. class NoImageFound extends StatelessWidget {
  702. bool show = true;
  703. NoImageFound(this.show, {super.key});
  704. @override
  705. Widget build(BuildContext context) {
  706. return SizedBox(
  707. width: MediaQuery.of(context).size.width - 150,
  708. height: MediaQuery.of(context).size.height / 4,
  709. child: Stack(
  710. fit: StackFit.expand,
  711. children: [
  712. Image.asset('assets/image/general/QrCode.jpg', fit: BoxFit.cover),
  713. ClipRRect(
  714. // Clip it cleanly.
  715. child: BackdropFilter(
  716. filter: ui.ImageFilter.blur(sigmaX: 10, sigmaY: 10),
  717. child: Container(
  718. color: Colors.grey.withValues(alpha: 0.5),
  719. alignment: Alignment.center,
  720. child: show
  721. ? Container(
  722. padding: const EdgeInsets.all(10),
  723. decoration: BoxDecoration(
  724. color: Colors.white24,
  725. borderRadius:
  726. BorderRadius.all(Radius.circular(50))),
  727. child: Text('notFoundImg'.tr(),
  728. style: TextStyle(fontSize: 12)),
  729. )
  730. : Center(
  731. child: CircularProgressIndicator(),
  732. ),
  733. ),
  734. ),
  735. ),
  736. ],
  737. ),
  738. );
  739. }
  740. }
  741. dialogConfirm({required BuildContext context, required String title, required String text, required Function actionYes}){
  742. showDialog(
  743. context: context,
  744. builder: (BuildContext context) {
  745. return AlertDialog(
  746. title: Text(title),
  747. content: Text(text),
  748. actions: <Widget>[
  749. TextButton(
  750. child: Text("buttonNo".tr()),
  751. onPressed: () {
  752. Navigator.of(context).pop();
  753. },
  754. ),
  755. TextButton(
  756. onPressed: (){
  757. Navigator.of(context).pop();
  758. actionYes();
  759. },
  760. child: Text("buttonYes".tr())),
  761. ],
  762. );
  763. },
  764. );
  765. }
  766. showLoading(BuildContext context, {String? text, String? lottie}){
  767. showDialog(
  768. context: context,
  769. barrierDismissible: false,
  770. barrierColor: lottie!=null?Colors.white:null,
  771. builder: (BuildContext context) {
  772. return WillPopScope(
  773. onWillPop: ()=>Future.value(false),
  774. child: Center(
  775. child: Column(
  776. mainAxisSize: MainAxisSize.min,
  777. children: [
  778. lottie != null ? Lottie.asset('assets/image/lottie/$lottie', width: 250, height: 250, fit: BoxFit.fill) : CircularProgressIndicator(color: primaryColor),
  779. SizedBox(height: 5),
  780. Text(text??'inProcess'.tr(), style: TextStyle(color: lottie!=null?Colors.black:Colors.white))
  781. ],
  782. ),
  783. ),
  784. );
  785. },
  786. );
  787. }
  788. closeLoading(BuildContext context){
  789. Navigator.of(context, rootNavigator: true).pop();
  790. }