mesa_screen.dart 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. import 'package:conalep_pos/models/mesa_model.dart';
  2. import 'package:conalep_pos/themes/themes.dart';
  3. import 'package:conalep_pos/viewmodels/mesa_view_model.dart';
  4. import 'package:conalep_pos/views/mesa/mesa_form.dart';
  5. import 'package:conalep_pos/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)
  126. .selectMesa(item);
  127. go(item);
  128. },
  129. ),
  130. DataCell(
  131. Text(item.nombre.toString()),
  132. onTap: () {
  133. Provider.of<MesaViewModel>(context, listen: false)
  134. .selectMesa(item);
  135. go(item);
  136. },
  137. ),
  138. ]));
  139. }
  140. return Scaffold(
  141. appBar: AppBar(
  142. title: Text(
  143. 'Mesas',
  144. style: TextStyle(color: AppTheme.secondary),
  145. ),
  146. iconTheme: IconThemeData(color: AppTheme.secondary),
  147. ),
  148. body: Column(
  149. children: [
  150. Expanded(
  151. child: ListView(
  152. padding: const EdgeInsets.fromLTRB(8, 0, 8, 0),
  153. children: [
  154. const SizedBox(height: 8),
  155. clase.tarjeta(
  156. Padding(
  157. padding: const EdgeInsets.all(8.0),
  158. child: LayoutBuilder(
  159. builder: (context, constraints) {
  160. if (screenWidth > 1000) {
  161. return Row(
  162. children: [
  163. Expanded(
  164. flex: 7,
  165. child: busquedaTextField(),
  166. ),
  167. SizedBox(width: 5),
  168. botonBuscar()
  169. ],
  170. );
  171. } else {
  172. return Column(
  173. children: [
  174. Row(
  175. children: [busquedaTextField()],
  176. ),
  177. SizedBox(height: 15),
  178. Row(
  179. children: [botonBuscar()],
  180. ),
  181. ],
  182. );
  183. }
  184. },
  185. ),
  186. ),
  187. ),
  188. const SizedBox(height: 8),
  189. model.isLoading
  190. ? const Center(child: CircularProgressIndicator())
  191. : Container(),
  192. clase.tarjeta(
  193. Column(
  194. children: [
  195. LayoutBuilder(builder: (context, constraints) {
  196. return SingleChildScrollView(
  197. scrollDirection: Axis.vertical,
  198. child: Scrollbar(
  199. controller: horizontalScrollController,
  200. interactive: true,
  201. thumbVisibility: true,
  202. thickness: 10.0,
  203. child: SingleChildScrollView(
  204. controller: horizontalScrollController,
  205. scrollDirection: Axis.horizontal,
  206. child: ConstrainedBox(
  207. constraints: BoxConstraints(
  208. minWidth: isMobile
  209. ? constraints.maxWidth
  210. : screenWidth),
  211. child: DataTable(
  212. columnSpacing: columnSpacing,
  213. sortAscending: true,
  214. sortColumnIndex: 1,
  215. columns: [
  216. DataColumn(label: Text(" ", style: estilo)),
  217. DataColumn(
  218. label: Text("ID", style: estilo)),
  219. DataColumn(
  220. label: Text("NOMBRE", style: estilo)),
  221. ],
  222. rows: registros,
  223. ),
  224. ),
  225. ),
  226. ),
  227. );
  228. }),
  229. ],
  230. ),
  231. ),
  232. const SizedBox(
  233. height: 15,
  234. ),
  235. if (!model.isLoading) ...[
  236. Row(
  237. mainAxisAlignment: MainAxisAlignment.center,
  238. children: [
  239. TextButton(
  240. onPressed:
  241. model.currentPage > 1 ? model.previousPage : null,
  242. child: Text('Anterior'),
  243. style: ButtonStyle(
  244. backgroundColor:
  245. MaterialStateProperty.resolveWith<Color?>(
  246. (Set<MaterialState> states) {
  247. if (states.contains(MaterialState.disabled)) {
  248. return Colors.grey;
  249. }
  250. return AppTheme.tertiary;
  251. },
  252. ),
  253. foregroundColor:
  254. MaterialStateProperty.resolveWith<Color?>(
  255. (Set<MaterialState> states) {
  256. if (states.contains(MaterialState.disabled)) {
  257. return Colors.black;
  258. }
  259. return Colors.white;
  260. },
  261. ),
  262. ),
  263. ),
  264. SizedBox(width: 15),
  265. Text(
  266. 'Página ${model.currentPage} de ${model.totalPages}'),
  267. SizedBox(width: 15),
  268. TextButton(
  269. onPressed: model.currentPage < model.totalPages
  270. ? model.nextPage
  271. : null,
  272. child: Text('Siguiente'),
  273. style: ButtonStyle(
  274. backgroundColor:
  275. MaterialStateProperty.resolveWith<Color?>(
  276. (Set<MaterialState> states) {
  277. if (states.contains(MaterialState.disabled)) {
  278. return Colors.grey;
  279. }
  280. return AppTheme.tertiary;
  281. },
  282. ),
  283. foregroundColor:
  284. MaterialStateProperty.resolveWith<Color?>(
  285. (Set<MaterialState> states) {
  286. if (states.contains(MaterialState.disabled)) {
  287. return Colors.black;
  288. }
  289. return Colors.white;
  290. },
  291. ),
  292. ),
  293. ),
  294. ],
  295. ),
  296. ],
  297. const SizedBox(
  298. height: 15,
  299. ),
  300. ],
  301. ),
  302. ),
  303. ],
  304. ),
  305. floatingActionButton: FloatingActionButton.extended(
  306. onPressed: () async {
  307. Mesa nuevaMesa = Mesa();
  308. Navigator.push(
  309. context,
  310. MaterialPageRoute(
  311. builder: (context) => MesaForm(mesa: nuevaMesa),
  312. ),
  313. ).then((_) => Provider.of<MesaViewModel>(context, listen: false)
  314. .fetchLocalAll());
  315. },
  316. icon: Icon(Icons.add, size: 30, color: AppTheme.quaternary),
  317. label: Text(
  318. "Agregar Mesa",
  319. style: TextStyle(fontSize: 18, color: AppTheme.quaternary),
  320. ),
  321. shape: RoundedRectangleBorder(
  322. borderRadius: BorderRadius.circular(8),
  323. ),
  324. materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
  325. backgroundColor: AppTheme.tertiary,
  326. ),
  327. );
  328. }
  329. Widget busquedaTextField() {
  330. return Row(
  331. children: [
  332. Expanded(
  333. flex: 3,
  334. child: AppTextField(
  335. prefixIcon: const Icon(Icons.search),
  336. etiqueta: 'Búsqueda por nombre...',
  337. controller: _busqueda,
  338. hintText: 'Búsqueda por nombre...',
  339. ),
  340. ),
  341. const SizedBox(width: 5),
  342. ],
  343. );
  344. }
  345. Widget botonBuscar() {
  346. return Expanded(
  347. flex: 2,
  348. child: Row(
  349. children: [
  350. Expanded(
  351. flex: 2,
  352. child: Padding(
  353. padding: const EdgeInsets.only(top: 30),
  354. child: ElevatedButton(
  355. onPressed: clearSearchAndReset,
  356. style: ElevatedButton.styleFrom(
  357. shape: RoundedRectangleBorder(
  358. borderRadius: BorderRadius.circular(20.0),
  359. ),
  360. backgroundColor: AppTheme.tertiary,
  361. padding: const EdgeInsets.symmetric(vertical: 25),
  362. ),
  363. child: Text('Limpiar',
  364. style: TextStyle(color: AppTheme.quaternary)),
  365. ),
  366. ),
  367. ),
  368. const SizedBox(width: 8),
  369. Expanded(
  370. flex: 2,
  371. child: Padding(
  372. padding: const EdgeInsets.only(top: 30),
  373. child: ElevatedButton(
  374. onPressed: () async {
  375. if (_busqueda.text.isNotEmpty) {
  376. await Provider.of<MesaViewModel>(context,
  377. listen: false)
  378. .fetchLocalByName(nombre: _busqueda.text.trim());
  379. } else {
  380. ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
  381. content: Text('Introduce un nombre para buscar.')));
  382. }
  383. },
  384. style: ElevatedButton.styleFrom(
  385. shape: RoundedRectangleBorder(
  386. borderRadius: BorderRadius.circular(20.0),
  387. ),
  388. backgroundColor: AppTheme.tertiary,
  389. padding: const EdgeInsets.symmetric(vertical: 25),
  390. ),
  391. child: Text('Buscar',
  392. style: TextStyle(color: AppTheme.quaternary)),
  393. ),
  394. ),
  395. ),
  396. ],
  397. ));
  398. }
  399. }