widgets_components.dart 23 KB

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