widgets_components.dart 23 KB

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