mesa_screen.dart 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. import '/models/mesa_model.dart';
  2. import '/themes/themes.dart';
  3. import '/viewmodels/mesa_view_model.dart';
  4. import 'mesa_form.dart';
  5. import '/widgets/widgets.dart';
  6. import 'package:flutter/material.dart';
  7. import 'package:provider/provider.dart';
  8. import '../../widgets/widgets_components.dart' as clase;
  9. class MesasScreen extends StatefulWidget {
  10. @override
  11. _MesasScreenState createState() => _MesasScreenState();
  12. }
  13. class _MesasScreenState extends State<MesasScreen> {
  14. final _busqueda = TextEditingController(text: '');
  15. ScrollController horizontalScrollController = ScrollController();
  16. @override
  17. void initState() {
  18. super.initState();
  19. Provider.of<MesaViewModel>(context, listen: false).fetchLocalAll();
  20. }
  21. void go(Mesa variable) {
  22. Navigator.push(
  23. context,
  24. MaterialPageRoute(
  25. builder: (context) => MesaForm(mesa: variable),
  26. ),
  27. ).then((_) =>
  28. Provider.of<MesaViewModel>(context, listen: false).fetchLocalAll());
  29. }
  30. void clearSearchAndReset() {
  31. setState(() {
  32. _busqueda.clear();
  33. Provider.of<MesaViewModel>(context, listen: false).fetchLocalAll();
  34. });
  35. }
  36. @override
  37. Widget build(BuildContext context) {
  38. final model = Provider.of<MesaViewModel>(context);
  39. double screenWidth = MediaQuery.of(context).size.width;
  40. final isMobile = screenWidth < 1250;
  41. final double? columnSpacing = isMobile ? null : 0;
  42. TextStyle estilo = const TextStyle(fontWeight: FontWeight.bold);
  43. List<DataRow> registros = [];
  44. for (Mesa item in model.mesas) {
  45. registros.add(DataRow(cells: [
  46. DataCell(
  47. Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
  48. PopupMenuButton(
  49. itemBuilder: (context) => [
  50. PopupMenuItem(
  51. child: const Text('Editar'),
  52. onTap: () => go(item),
  53. ),
  54. PopupMenuItem(
  55. child: const Text('Eliminar'),
  56. onTap: () async {
  57. await Future.delayed(Duration(milliseconds: 100));
  58. bool confirmado = await showDialog<bool>(
  59. context: context,
  60. builder: (context) {
  61. return AlertDialog(
  62. title: const Text("Eliminar",
  63. style: TextStyle(
  64. fontWeight: FontWeight.w500, fontSize: 22)),
  65. content: const Text(
  66. '¿Estás seguro de que deseas eliminar esta variable?',
  67. style: TextStyle(fontSize: 18)),
  68. actions: [
  69. Row(
  70. mainAxisAlignment:
  71. MainAxisAlignment.spaceBetween,
  72. children: [
  73. TextButton(
  74. onPressed: () =>
  75. Navigator.of(context).pop(false),
  76. child: const Text('No',
  77. style: TextStyle(fontSize: 18)),
  78. style: ButtonStyle(
  79. padding: MaterialStatePropertyAll(
  80. EdgeInsets.fromLTRB(
  81. 20, 10, 20, 10)),
  82. backgroundColor:
  83. MaterialStatePropertyAll(
  84. AppTheme.primary),
  85. foregroundColor:
  86. MaterialStatePropertyAll(
  87. AppTheme.secondary)),
  88. ),
  89. TextButton(
  90. onPressed: () =>
  91. Navigator.of(context).pop(true),
  92. child: const Text('Sí',
  93. style: TextStyle(fontSize: 18)),
  94. style: ButtonStyle(
  95. padding: MaterialStatePropertyAll(
  96. EdgeInsets.fromLTRB(
  97. 20, 10, 20, 10)),
  98. backgroundColor:
  99. MaterialStatePropertyAll(
  100. AppTheme.tertiary),
  101. foregroundColor:
  102. MaterialStatePropertyAll(
  103. AppTheme.quaternary)),
  104. ),
  105. ],
  106. )
  107. ],
  108. );
  109. },
  110. ) ??
  111. false;
  112. if (confirmado) {
  113. await model.deleteMesa(item.id!);
  114. model.fetchLocalAll();
  115. }
  116. },
  117. )
  118. ],
  119. icon: const Icon(Icons.more_vert),
  120. ),
  121. ])),
  122. DataCell(
  123. Text(item.id.toString()),
  124. onTap: () {
  125. Provider.of<MesaViewModel>(context, listen: false).selectMesa(item);
  126. go(item);
  127. },
  128. ),
  129. DataCell(
  130. Text(item.nombre.toString()),
  131. onTap: () {
  132. Provider.of<MesaViewModel>(context, listen: false).selectMesa(item);
  133. go(item);
  134. },
  135. ),
  136. ]));
  137. }
  138. return Scaffold(
  139. appBar: AppBar(
  140. title: Text(
  141. 'Mesas',
  142. style: TextStyle(color: AppTheme.secondary),
  143. ),
  144. iconTheme: IconThemeData(color: AppTheme.secondary),
  145. ),
  146. body: Column(
  147. children: [
  148. Expanded(
  149. child: ListView(
  150. padding: const EdgeInsets.fromLTRB(8, 0, 8, 0),
  151. children: [
  152. const SizedBox(height: 8),
  153. clase.tarjeta(
  154. Padding(
  155. padding: const EdgeInsets.all(8.0),
  156. child: LayoutBuilder(
  157. builder: (context, constraints) {
  158. if (screenWidth > 1000) {
  159. return Row(
  160. children: [
  161. Expanded(
  162. flex: 7,
  163. child: busquedaTextField(),
  164. ),
  165. SizedBox(width: 5),
  166. botonBuscar()
  167. ],
  168. );
  169. } else {
  170. return Column(
  171. children: [
  172. Row(
  173. children: [busquedaTextField()],
  174. ),
  175. SizedBox(height: 15),
  176. Row(
  177. children: [botonBuscar()],
  178. ),
  179. ],
  180. );
  181. }
  182. },
  183. ),
  184. ),
  185. ),
  186. const SizedBox(height: 8),
  187. model.isLoading
  188. ? const Center(child: CircularProgressIndicator())
  189. : Container(),
  190. clase.tarjeta(
  191. Column(
  192. children: [
  193. LayoutBuilder(builder: (context, constraints) {
  194. return SingleChildScrollView(
  195. scrollDirection: Axis.vertical,
  196. child: Scrollbar(
  197. controller: horizontalScrollController,
  198. interactive: true,
  199. thumbVisibility: true,
  200. thickness: 10.0,
  201. child: SingleChildScrollView(
  202. controller: horizontalScrollController,
  203. scrollDirection: Axis.horizontal,
  204. child: ConstrainedBox(
  205. constraints: BoxConstraints(
  206. minWidth: isMobile
  207. ? constraints.maxWidth
  208. : screenWidth),
  209. child: DataTable(
  210. columnSpacing: columnSpacing,
  211. sortAscending: true,
  212. sortColumnIndex: 1,
  213. columns: [
  214. DataColumn(label: Text(" ", style: estilo)),
  215. DataColumn(
  216. label: Text("ID", style: estilo)),
  217. DataColumn(
  218. label: Text("NOMBRE", style: estilo)),
  219. ],
  220. rows: registros,
  221. ),
  222. ),
  223. ),
  224. ),
  225. );
  226. }),
  227. ],
  228. ),
  229. ),
  230. const SizedBox(
  231. height: 15,
  232. ),
  233. if (!model.isLoading) ...[
  234. Row(
  235. mainAxisAlignment: MainAxisAlignment.center,
  236. children: [
  237. TextButton(
  238. onPressed:
  239. model.currentPage > 1 ? model.previousPage : null,
  240. child: Text('Anterior'),
  241. style: ButtonStyle(
  242. backgroundColor:
  243. MaterialStateProperty.resolveWith<Color?>(
  244. (Set<MaterialState> states) {
  245. if (states.contains(MaterialState.disabled)) {
  246. return Colors.grey;
  247. }
  248. return AppTheme.tertiary;
  249. },
  250. ),
  251. foregroundColor:
  252. MaterialStateProperty.resolveWith<Color?>(
  253. (Set<MaterialState> states) {
  254. if (states.contains(MaterialState.disabled)) {
  255. return Colors.black;
  256. }
  257. return Colors.white;
  258. },
  259. ),
  260. ),
  261. ),
  262. SizedBox(width: 15),
  263. Text(
  264. 'Página ${model.currentPage} de ${model.totalPages}'),
  265. SizedBox(width: 15),
  266. TextButton(
  267. onPressed: model.currentPage < model.totalPages
  268. ? model.nextPage
  269. : null,
  270. child: Text('Siguiente'),
  271. style: ButtonStyle(
  272. backgroundColor:
  273. MaterialStateProperty.resolveWith<Color?>(
  274. (Set<MaterialState> states) {
  275. if (states.contains(MaterialState.disabled)) {
  276. return Colors.grey;
  277. }
  278. return AppTheme.tertiary;
  279. },
  280. ),
  281. foregroundColor:
  282. MaterialStateProperty.resolveWith<Color?>(
  283. (Set<MaterialState> states) {
  284. if (states.contains(MaterialState.disabled)) {
  285. return Colors.black;
  286. }
  287. return Colors.white;
  288. },
  289. ),
  290. ),
  291. ),
  292. ],
  293. ),
  294. ],
  295. const SizedBox(
  296. height: 15,
  297. ),
  298. ],
  299. ),
  300. ),
  301. ],
  302. ),
  303. floatingActionButton: FloatingActionButton.extended(
  304. onPressed: () async {
  305. Mesa nuevaMesa = Mesa();
  306. Navigator.push(
  307. context,
  308. MaterialPageRoute(
  309. builder: (context) => MesaForm(mesa: nuevaMesa),
  310. ),
  311. ).then((_) => Provider.of<MesaViewModel>(context, listen: false)
  312. .fetchLocalAll());
  313. },
  314. icon: Icon(Icons.add, size: 30, color: AppTheme.quaternary),
  315. label: Text(
  316. "Agregar Mesa",
  317. style: TextStyle(fontSize: 18, color: AppTheme.quaternary),
  318. ),
  319. shape: RoundedRectangleBorder(
  320. borderRadius: BorderRadius.circular(8),
  321. ),
  322. materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
  323. backgroundColor: AppTheme.tertiary,
  324. ),
  325. );
  326. }
  327. Widget busquedaTextField() {
  328. return Row(
  329. children: [
  330. Expanded(
  331. flex: 3,
  332. child: AppTextField(
  333. prefixIcon: const Icon(Icons.search),
  334. etiqueta: 'Búsqueda por nombre...',
  335. controller: _busqueda,
  336. hintText: 'Búsqueda por nombre...',
  337. ),
  338. ),
  339. const SizedBox(width: 5),
  340. ],
  341. );
  342. }
  343. Widget botonBuscar() {
  344. return Expanded(
  345. flex: 2,
  346. child: Row(
  347. children: [
  348. Expanded(
  349. flex: 2,
  350. child: Padding(
  351. padding: const EdgeInsets.only(top: 30),
  352. child: ElevatedButton(
  353. onPressed: clearSearchAndReset,
  354. style: ElevatedButton.styleFrom(
  355. shape: RoundedRectangleBorder(
  356. borderRadius: BorderRadius.circular(20.0),
  357. ),
  358. backgroundColor: AppTheme.tertiary,
  359. padding: const EdgeInsets.symmetric(vertical: 25),
  360. ),
  361. child: Text('Limpiar',
  362. style: TextStyle(color: AppTheme.quaternary)),
  363. ),
  364. ),
  365. ),
  366. const SizedBox(width: 8),
  367. Expanded(
  368. flex: 2,
  369. child: Padding(
  370. padding: const EdgeInsets.only(top: 30),
  371. child: ElevatedButton(
  372. onPressed: () async {
  373. if (_busqueda.text.isNotEmpty) {
  374. await Provider.of<MesaViewModel>(context, listen: false)
  375. .fetchLocalByName(nombre: _busqueda.text.trim());
  376. } else {
  377. ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
  378. content: Text('Introduce un nombre para buscar.')));
  379. }
  380. },
  381. style: ElevatedButton.styleFrom(
  382. shape: RoundedRectangleBorder(
  383. borderRadius: BorderRadius.circular(20.0),
  384. ),
  385. backgroundColor: AppTheme.tertiary,
  386. padding: const EdgeInsets.symmetric(vertical: 25),
  387. ),
  388. child: Text('Buscar',
  389. style: TextStyle(color: AppTheme.quaternary)),
  390. ),
  391. ),
  392. ),
  393. ],
  394. ));
  395. }
  396. }