widgets_components.dart 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  1. // ignore_for_file: use_build_context_synchronously, prefer_if_null_operators, prefer_conditional_assignment
  2. import 'dart:io';
  3. import 'dart:typed_data';
  4. import 'package:camera/camera.dart';
  5. import 'package:flutter/material.dart';
  6. //import 'package:yoshi_papas_app/viewmodels/media_view_model.dart';
  7. import 'package:intl/intl.dart';
  8. import 'package:omni_datetime_picker/omni_datetime_picker.dart';
  9. import 'package:flutter/material.dart' as d;
  10. import 'package:datetime_picker_formfield/datetime_picker_formfield.dart' as d2;
  11. import 'package:provider/provider.dart';
  12. import 'package:url_launcher/url_launcher.dart';
  13. import 'package:flutter/foundation.dart' show kIsWeb;
  14. import 'package:path_provider/path_provider.dart';
  15. import '../data/session/session_storage.dart';
  16. import '../models/media_model.dart';
  17. import '../themes/themes.dart';
  18. import 'package:timezone/timezone.dart' as tz;
  19. import 'package:http/http.dart' as http;
  20. import "package:universal_html/html.dart" as html;
  21. //import '../viewmodels/ordenes_view_model.dart';
  22. //import 'dart:html';
  23. //import 'dart:html' as html;
  24. encabezado(
  25. {double elevacion = 1,
  26. List<Widget>? acciones,
  27. PreferredSize? bottom,
  28. Color? backgroundColor,
  29. String? nombre,
  30. Widget? leading = null,
  31. String? titulo,
  32. bool centerTitle = true}) {
  33. TextStyle estilo = const TextStyle(fontWeight: FontWeight.bold);
  34. if (acciones == null) {
  35. acciones = [];
  36. }
  37. return AppBar(
  38. leading: leading,
  39. elevation: elevacion,
  40. backgroundColor: backgroundColor,
  41. centerTitle: centerTitle,
  42. title: titulo == null
  43. ? Image.asset("assets/JoshiLogoHorizontal.png", width: 100)
  44. : Text(titulo.toString(), style: estilo),
  45. actions: [
  46. ...acciones,
  47. Image.asset("assets/JoshiLogoHorizontal.png", width: 100),
  48. ],
  49. bottom: bottom);
  50. }
  51. tarjeta(Widget item, {Color color = Colors.white, double padding = 0}) {
  52. if (padding > 0) {
  53. return Padding(
  54. padding: const EdgeInsets.all(0),
  55. child: Card(
  56. color: color,
  57. shape:
  58. RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
  59. margin: const EdgeInsets.only(bottom: 16),
  60. elevation: 0,
  61. borderOnForeground: true,
  62. child: Padding(padding: EdgeInsets.all(padding), child: item)));
  63. }
  64. return Card(
  65. color: color,
  66. shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
  67. margin: const EdgeInsets.only(bottom: 8),
  68. elevation: 0,
  69. borderOnForeground: true,
  70. child: Padding(
  71. padding: const EdgeInsets.all(8),
  72. child: item,
  73. ));
  74. }
  75. class Cargando extends StatelessWidget {
  76. const Cargando({super.key});
  77. @override
  78. Widget build(BuildContext context) {
  79. return Scaffold(
  80. backgroundColor: const Color(0xFFE0E0E0),
  81. body: Center(
  82. child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
  83. Padding(
  84. padding: const EdgeInsets.fromLTRB(16, 0, 16, 10),
  85. child: Image.asset("assets/JoshiLogo.png", height: 200)),
  86. const CircularProgressIndicator(backgroundColor: Colors.grey),
  87. Container(margin: const EdgeInsets.only(bottom: 8.0)),
  88. const Text("Cargando contenido...",
  89. style: TextStyle(
  90. fontWeight: FontWeight.bold,
  91. fontSize: 16,
  92. color: Colors.black)),
  93. const Text("Por favor espere.",
  94. style: TextStyle(
  95. fontWeight: FontWeight.bold,
  96. fontSize: 16,
  97. color: Colors.black),
  98. textAlign: TextAlign.center)
  99. ]),
  100. ));
  101. }
  102. }
  103. boton(String etiqueta, Function()? accion,
  104. {double height = 75, double width = 380}) {
  105. return Align(
  106. alignment: Alignment.topLeft,
  107. child: SizedBox(
  108. height: height,
  109. width: 380,
  110. child: ElevatedButton(
  111. style: ButtonStyle(
  112. shape: MaterialStatePropertyAll(
  113. RoundedRectangleBorder(
  114. borderRadius: BorderRadius.circular(10),
  115. ),
  116. ),
  117. backgroundColor: MaterialStatePropertyAll(AppTheme.tertiary),
  118. ),
  119. onPressed: accion,
  120. child: Row(
  121. mainAxisAlignment: MainAxisAlignment.center,
  122. children: [
  123. Text(etiqueta,
  124. style: TextStyle(fontSize: 18, color: AppTheme.quaternary)),
  125. ],
  126. ),
  127. ),
  128. ),
  129. );
  130. }
  131. Future<DateTime?> showHora(BuildContext context, DateTime? fecha) async {
  132. if (fecha == null) {
  133. return fecha;
  134. }
  135. final tiempo = await showTimePicker(
  136. useRootNavigator: false,
  137. helpText: "CAPTURAR HORA",
  138. confirmText: "ACEPTAR",
  139. cancelText: "CANCELAR",
  140. hourLabelText: "HORA",
  141. minuteLabelText: "MINUTO",
  142. initialEntryMode: TimePickerEntryMode.input,
  143. context: context,
  144. initialTime: TimeOfDay.fromDateTime(fecha),
  145. builder: (context, childs) {
  146. return MediaQuery(
  147. data: MediaQuery.of(context).copyWith(
  148. alwaysUse24HourFormat: true,
  149. padding: const EdgeInsets.all(1),
  150. size: const Size.square(1),
  151. textScaler: const TextScaler.linear(.8)),
  152. child: childs!);
  153. },
  154. );
  155. return d2.DateTimeField.combine(fecha!, tiempo);
  156. }
  157. Future<DateTime?> showDatetimePicker(BuildContext context, DateTime? fecha,
  158. {DateTime? inicia,
  159. DateTime? termina,
  160. OmniDateTimePickerType tipo = OmniDateTimePickerType.dateAndTime,
  161. bool solofecha = false}) async {
  162. if (termina == null) {
  163. termina = DateTime(2030);
  164. }
  165. final f = await showDatePicker(
  166. context: context,
  167. initialDate: fecha ?? DateTime.now(),
  168. firstDate: inicia ?? DateTime(2023),
  169. lastDate: termina,
  170. cancelText: "CANCELAR",
  171. confirmText: "ACEPTAR",
  172. builder: (context, child) {
  173. return Theme(
  174. data: Theme.of(context).copyWith(
  175. colorScheme: ColorScheme.light(
  176. primary: AppTheme.primary,
  177. onSurface: AppTheme.secondary,
  178. ),
  179. textButtonTheme: TextButtonThemeData(
  180. style: TextButton.styleFrom(
  181. primary: AppTheme.secondary,
  182. ),
  183. ),
  184. ),
  185. child: child!,
  186. );
  187. },
  188. );
  189. if (f == null || solofecha) {
  190. return f;
  191. }
  192. final tiempo = await showTimePicker(
  193. context: context,
  194. initialTime: TimeOfDay.fromDateTime(f),
  195. helpText: "CAPTURAR HORA",
  196. confirmText: "ACEPTAR",
  197. cancelText: "CANCELAR",
  198. hourLabelText: "HORA",
  199. minuteLabelText: "MINUTO",
  200. initialEntryMode: TimePickerEntryMode.input,
  201. builder: (context, child) {
  202. return Theme(
  203. data: Theme.of(context).copyWith(
  204. colorScheme: ColorScheme.light(
  205. primary: AppTheme.primary,
  206. onSurface: AppTheme.secondary,
  207. ),
  208. textButtonTheme: TextButtonThemeData(
  209. style: TextButton.styleFrom(
  210. primary: AppTheme.secondary,
  211. ),
  212. ),
  213. ),
  214. child: child!,
  215. );
  216. },
  217. );
  218. return d2.DateTimeField.combine(f, tiempo);
  219. }
  220. Widget circulo(Widget item, {Function()? accion, Color? color}) {
  221. color ??= Colors.black;
  222. return InkWell(
  223. onTap: accion,
  224. child: Container(
  225. decoration: BoxDecoration(
  226. border: Border.all(
  227. color: color, // <--- border color
  228. width: 2.0,
  229. ),
  230. borderRadius: const BorderRadius.all(
  231. Radius.circular(50) // <--- border radius here
  232. ),
  233. ),
  234. child: item));
  235. }
  236. alerta(BuildContext context, {String? etiqueta = "Capturar búsqueda"}) {
  237. return showDialog(
  238. context: context,
  239. builder: (context) {
  240. return AlertDialog(
  241. title: Text(etiqueta.toString()),
  242. actions: [
  243. Row(children: [
  244. Expanded(
  245. child: TextButton(
  246. onPressed: () async {
  247. Navigator.pop(context);
  248. },
  249. child: const Text('Continuar'),
  250. ))
  251. ])
  252. ],
  253. );
  254. });
  255. }
  256. botonElevated(
  257. {Function()? accion,
  258. String? titulo = "Buscar",
  259. Color color = Colors.black}) {
  260. return ElevatedButton(
  261. style: ElevatedButton.styleFrom(
  262. backgroundColor: color,
  263. shape: RoundedRectangleBorder(
  264. borderRadius: BorderRadius.circular(12), // <-- Radius
  265. )),
  266. onPressed: accion,
  267. child: Text(
  268. titulo.toString(),
  269. style: const TextStyle(color: Colors.white),
  270. ),
  271. );
  272. }
  273. Widget xMedia(XFile m) {
  274. bool archivo = false;
  275. Widget icono = const Icon(Icons.file_copy, size: 50);
  276. int lastIndex = m.name.lastIndexOf('.');
  277. String extension = "";
  278. if (lastIndex != -1 && lastIndex < m.name.length - 1) {
  279. extension = m.name.substring(lastIndex + 1);
  280. }
  281. switch (extension) {
  282. case "mp4":
  283. archivo = true;
  284. icono = const Icon(Icons.video_file, size: 50);
  285. break;
  286. case "wav":
  287. archivo = true;
  288. icono = Image.asset("assets/audio-file.png");
  289. archivo = true;
  290. break;
  291. case "jpeg":
  292. case "jpg":
  293. case "png":
  294. break;
  295. case "pdf":
  296. icono = Image.asset("assets/pdf-file.png");
  297. archivo = true;
  298. break;
  299. case "csv":
  300. case "docx":
  301. icono = Image.asset("assets/word.png");
  302. archivo = true;
  303. break;
  304. case "xlsx":
  305. icono = Image.asset("assets/excel.png");
  306. archivo = true;
  307. break;
  308. }
  309. String nombre = m.name.toString();
  310. if (nombre.length > 25) {
  311. nombre = "${m.name.toString().substring(0, 24)}...";
  312. }
  313. return InkWell(
  314. onTap: () async {
  315. String url = m.path.toString();
  316. await canLaunch(url)
  317. ? await launch(url, forceSafariVC: false, forceWebView: false)
  318. : print('Could not launch '); //aqui
  319. },
  320. child: Stack(alignment: Alignment.center, children: [
  321. Container(
  322. height: 100.0,
  323. width: 100.0,
  324. clipBehavior: Clip.antiAlias,
  325. decoration: BoxDecoration(
  326. border: Border.all(color: Colors.black, width: 1.0),
  327. borderRadius: const BorderRadius.all(Radius.circular(15.0)),
  328. ),
  329. child: archivo
  330. ? icono
  331. : Image.network(m.path.toString(),
  332. scale:
  333. 1) // _firma == null?const Icon(Icons.pages, size: 30, color: Colors.black): contenedorFirma,
  334. ),
  335. Positioned(
  336. bottom: 0,
  337. child: Container(
  338. width: 200,
  339. height: 40,
  340. color: Colors.grey.shade800,
  341. child: Column(
  342. mainAxisAlignment: MainAxisAlignment.center,
  343. children: [
  344. Text(
  345. nombre,
  346. textAlign: TextAlign.center,
  347. textScaler: const TextScaler.linear(.8),
  348. style: const TextStyle(color: Colors.white),
  349. ),
  350. ]),
  351. )),
  352. ]));
  353. }
  354. Widget wMedia(Media m, {Function()? eliminar}) {
  355. bool archivo = false;
  356. Widget icono = const Icon(Icons.file_copy, size: 50);
  357. switch (m.extension) {
  358. case "mp4":
  359. archivo = true;
  360. icono = const Icon(Icons.video_file, size: 50);
  361. break;
  362. case "wav":
  363. archivo = true;
  364. icono = Image.asset("assets/audio-file.png");
  365. archivo = true;
  366. break;
  367. case "jpeg":
  368. case "jpg":
  369. case "png":
  370. break;
  371. case "pdf":
  372. icono = Image.asset("assets/pdf-file.png");
  373. archivo = true;
  374. break;
  375. case "csv":
  376. case "docx":
  377. icono = Image.asset("assets/word.png");
  378. archivo = true;
  379. break;
  380. case "xlsx":
  381. icono = Image.asset("assets/excel.png");
  382. archivo = true;
  383. break;
  384. }
  385. String fecha = "";
  386. if (m.creado != null) {
  387. String timeZone = 'America/Hermosillo';
  388. fecha = formatFechaConZonaHoraria(m.creado!, timeZone);
  389. }
  390. String nombre = m.nombre.toString();
  391. if (nombre.length > 25) {
  392. nombre = "${m.nombre.toString().substring(0, 24)}...";
  393. }
  394. return InkWell(
  395. onTap: () async {
  396. String url = m.ruta.toString();
  397. await canLaunch(url)
  398. ? await launch(url, forceSafariVC: false, forceWebView: false)
  399. : print('Could not launch '); //aqui
  400. },
  401. child: Stack(alignment: Alignment.center, children: [
  402. Container(
  403. height: 160.0,
  404. width: 160.0,
  405. clipBehavior: Clip.antiAlias,
  406. decoration: BoxDecoration(
  407. border: Border.all(color: Colors.black, width: 1.0),
  408. borderRadius: const BorderRadius.all(Radius.circular(15.0)),
  409. ),
  410. child: archivo
  411. ? icono
  412. : Image.network(m.ruta.toString(),
  413. scale:
  414. 1) // _firma == null?const Icon(Icons.pages, size: 30, color: Colors.black): contenedorFirma,
  415. ),
  416. Positioned(
  417. bottom: 0,
  418. child: Container(
  419. width: 500,
  420. height: 40,
  421. color: Colors.grey.shade800,
  422. child: Column(
  423. mainAxisAlignment: MainAxisAlignment.center,
  424. children: [
  425. Text(
  426. nombre,
  427. textAlign: TextAlign.center,
  428. textScaler: const TextScaler.linear(.8),
  429. style: const TextStyle(color: Colors.white),
  430. ),
  431. Text(
  432. fecha,
  433. textAlign: TextAlign.center,
  434. textScaler: const TextScaler.linear(.8),
  435. style: const TextStyle(color: Colors.white),
  436. ),
  437. ]),
  438. )),
  439. Positioned(
  440. left: 0,
  441. top: 0,
  442. child: ElevatedButton(
  443. onPressed: eliminar,
  444. style: ElevatedButton.styleFrom(
  445. shape: CircleBorder(),
  446. padding: EdgeInsets.all(16),
  447. ),
  448. child: Icon(
  449. Icons.delete,
  450. color: Colors.red.shade800,
  451. ),
  452. ),
  453. ),
  454. ]));
  455. }
  456. Widget agregarMedia(
  457. {Color color = Colors.green,
  458. Function()? accion,
  459. String titulo = "Agregar Media",
  460. Icon? icono,
  461. double tamano = 160}) {
  462. if (icono == null) {
  463. icono = Icon(
  464. Icons.add,
  465. size: tamano < 100 ? 60 : 100,
  466. color: Colors.white,
  467. );
  468. }
  469. return InkWell(
  470. onTap: accion,
  471. child: Stack(alignment: Alignment.center, children: [
  472. // Contenedor con un color de fondo y dimensiones específicas
  473. Container(
  474. width: tamano,
  475. height: tamano,
  476. color: color,
  477. ),
  478. // Texto flotante
  479. Positioned(
  480. child:
  481. Column(mainAxisAlignment: MainAxisAlignment.center, children: [
  482. icono,
  483. titulo.isEmpty
  484. ? Container()
  485. : Text(
  486. titulo,
  487. style: const TextStyle(
  488. color: Colors.white,
  489. fontSize: 16,
  490. ),
  491. ),
  492. ])),
  493. ]));
  494. }
  495. String formatFechaConZonaHoraria(DateTime dateTime, String timeZone) {
  496. // Obtiene la ubicación de la zona horaria
  497. tz.Location location = tz.getLocation(timeZone);
  498. // Convierte la fecha a la zona horaria deseada
  499. tz.TZDateTime fechaConZona = tz.TZDateTime.from(dateTime, location);
  500. // Formatea la fecha con la zona horaria
  501. //String formattedFecha = DateFormat("dd/MM/yyyy HH:mm '($timeZone)'").format(fechaConZona);
  502. String formattedFecha = DateFormat("dd/MM/yyyy HH:mm").format(fechaConZona);
  503. return formattedFecha;
  504. }
  505. html.Location getLocation(String timeZone) {
  506. // Obtiene la ubicación de la zona horaria
  507. return getLocation(timeZone);
  508. }
  509. int obtenerAxiscount(Size s) {
  510. int axiscount = 8;
  511. if (s.width > 1300) {
  512. axiscount = 8;
  513. }
  514. if (s.width > 900 && s.width < 1300) {
  515. axiscount = 5;
  516. }
  517. if (s.width > 700 && s.width < 900) {
  518. axiscount = 4;
  519. }
  520. if (s.width > 600 && s.width < 700) {
  521. axiscount = 3;
  522. }
  523. if (s.width < 600) {
  524. axiscount = 2;
  525. }
  526. return axiscount;
  527. }
  528. imprimirExcel(String url, String nombre) async {
  529. var t = await SessionStorage().getToken();
  530. Map<String, String> headers = {
  531. 'Authorization': 'Bearer $t',
  532. 'Content-Type': 'application/json', // Puedes ajustar según sea necesario
  533. };
  534. http.Response response = await http.get(
  535. Uri.parse(url),
  536. headers: headers,
  537. );
  538. if (response.statusCode == 200) {
  539. // Obtener el contenido del archivo como Uint8List
  540. Uint8List fileBytes = response.bodyBytes;
  541. if (!kIsWeb) {
  542. // Lógica para dispositivos móviles y escritorio
  543. final directory = await getApplicationDocumentsDirectory();
  544. final path = '${directory.path}/$nombre.xlsx';
  545. final file = File(path);
  546. await file.writeAsBytes(fileBytes);
  547. final Uri fileUri = Uri.file(path);
  548. if (await canLaunchUrl(fileUri)) {
  549. await launchUrl(fileUri);
  550. } else {
  551. throw 'No se pudo abrir el archivo';
  552. }
  553. return; // Retorna después de manejar la lógica de dispositivos móviles
  554. }
  555. // Lógica específica para la web sigue aquí
  556. // Asegúrate de que este código solo se ejecute en la web
  557. if (kIsWeb) {
  558. // Crea y descarga el archivo para la web
  559. final blob = html.Blob([fileBytes]);
  560. final url = html.Url.createObjectUrlFromBlob(blob);
  561. final anchor = html.AnchorElement(href: url)
  562. ..setAttribute('download', '$nombre.xlsx')
  563. ..click();
  564. html.Url.revokeObjectUrl(url);
  565. }
  566. } else {
  567. print(
  568. 'Error al descargar el archivo. Código de estado: ${response.statusCode}');
  569. }
  570. }
  571. imprimirPdf(String url, String nombre) async {
  572. var t = await SessionStorage().getToken();
  573. Map<String, String> headers = {
  574. 'Authorization': 'Bearer $t',
  575. 'Content-Type': 'application/json', // Puedes ajustar según sea necesario
  576. };
  577. http.Response response = await http.get(
  578. Uri.parse(url),
  579. headers: headers,
  580. );
  581. if (response.statusCode == 200) {
  582. // Obtener el contenido del archivo como Uint8List
  583. Uint8List fileBytes = response.bodyBytes;
  584. // Crear un blob con los bytes del archivo
  585. final blob = html.Blob([fileBytes]);
  586. // Crear un objeto URL para el blob
  587. final url = html.Url.createObjectUrlFromBlob(blob);
  588. // Crear un enlace de descarga y hacer clic en él para descargar el archivo
  589. final anchor = html.AnchorElement(href: url)
  590. ..target = 'blank'
  591. ..download =
  592. '$nombre.pdf'; // Ajustar el nombre y la extensión del archivo
  593. anchor.click();
  594. // Liberar el objeto URL
  595. html.Url.revokeObjectUrl(url);
  596. } else {
  597. print(
  598. 'Error al descargar el archivo. Código de estado: ${response.statusCode}');
  599. }
  600. }
  601. Widget usuarioHeader(String nombre, String correo) {
  602. return Row(
  603. mainAxisAlignment: MainAxisAlignment.center,
  604. crossAxisAlignment: CrossAxisAlignment.center,
  605. children: [
  606. Column(
  607. mainAxisAlignment: MainAxisAlignment.center,
  608. crossAxisAlignment: CrossAxisAlignment.end,
  609. children: [
  610. Text(nombre,
  611. style: TextStyle(
  612. color: Colors.black, fontWeight: FontWeight.bold),
  613. textAlign: TextAlign.right),
  614. Text(correo,
  615. style: TextStyle(color: Colors.grey.shade900),
  616. textAlign: TextAlign.right),
  617. ]),
  618. Text(" "),
  619. CircleAvatar(
  620. child: Icon(Icons.verified_user, color: Colors.white),
  621. backgroundColor: Colors.black),
  622. Text(" "),
  623. ]);
  624. }
  625. Future eliminarMedia(
  626. BuildContext context, Media m, Function()? confirmacion) async {
  627. return await showDialog(
  628. context: context,
  629. builder: (context) {
  630. return AlertDialog(
  631. surfaceTintColor: AppTheme.secondary,
  632. title: const Text('¿Desea eliminar esta archivo?'),
  633. actions: [
  634. TextButton(
  635. onPressed: () {
  636. Navigator.pop(context);
  637. },
  638. child: const Text('Cancelar'),
  639. ),
  640. TextButton(
  641. onPressed: confirmacion,
  642. child: const Text(
  643. 'Sí, estoy seguro',
  644. style: TextStyle(color: Colors.red),
  645. ),
  646. ),
  647. ],
  648. );
  649. },
  650. );
  651. }