pedido_form.dart 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761
  1. import 'package:flutter/foundation.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:yoshi_papas_app/widgets/widgets_components.dart';
  4. import '../../themes/themes.dart'; // Asegúrate de tener este archivo con los temas correctos
  5. import '../../models/models.dart'; // Tus modelos de datos
  6. import '../../viewmodels/viewmodels.dart'; // Tus ViewModels
  7. import 'package:collection/collection.dart';
  8. class PedidoForm extends StatefulWidget {
  9. @override
  10. _PedidoFormState createState() => _PedidoFormState();
  11. }
  12. class ItemCarrito {
  13. Producto producto;
  14. int cantidad;
  15. Map<String, dynamic>? customizaciones;
  16. ItemCarrito(
  17. {required this.producto, this.cantidad = 1, this.customizaciones});
  18. }
  19. class _PedidoFormState extends State<PedidoForm> {
  20. CategoriaProductoViewModel cvm = CategoriaProductoViewModel();
  21. ProductoViewModel pvm = ProductoViewModel();
  22. bool _isLoading = false;
  23. ScrollController horizontalScrollController = ScrollController();
  24. CategoriaProducto? categoriaSeleccionada;
  25. List<CategoriaProducto> categorias = [];
  26. List<Producto> productos = [];
  27. List<ItemCarrito> carrito = [];
  28. TopingViewModel _topingViewModel = TopingViewModel();
  29. TopingCategoriaViewModel _categoriaTopingViewModel =
  30. TopingCategoriaViewModel();
  31. List<Toping> _topingsDisponibles = []; // Lista de topings disponibles
  32. Producto? _productoActual; // Producto actual seleccionado para customización
  33. bool isCustomizingProduct = false;
  34. Map<String, dynamic>? currentProductForCustomization;
  35. String? selectedBase;
  36. List<String> selectedSauce = [];
  37. List<String> selectedDressing = [];
  38. List<String> selectedToppings = [];
  39. Map<String, bool> baseOptions = {};
  40. Map<String, bool> sauceOptions = {};
  41. Map<String, bool> dressingOptions = {};
  42. Map<String, bool> toppingOptions = {};
  43. List<TopingCategoria> categoriasDeTopings = []; // Añade esta línea
  44. TopingCategoria? _currentTopingCategoriaSeleccionada; // Añade esta línea
  45. List<Toping> _topingsFiltrados = []; // Añade esta línea
  46. Map<int, List<String>> selectedToppingsByCategory = {};
  47. double calcularTotalPedido() {
  48. double total = 0;
  49. for (var item in carrito) {
  50. total += double.parse(item.producto.precio!) * item.cantidad;
  51. }
  52. return total;
  53. }
  54. @override
  55. void initState() {
  56. super.initState();
  57. cargarCategoriasIniciales();
  58. _cargarCategoriasDeTopings();
  59. _cargarTopingsDisponibles();
  60. }
  61. void _cargarCategoriasDeTopings() async {
  62. var categoriasObtenidas = await _categoriaTopingViewModel.fetchRegistros();
  63. setState(() {
  64. categoriasDeTopings = categoriasObtenidas;
  65. });
  66. }
  67. // Función para cargar topings disponibles
  68. void _cargarTopingsDisponibles() async {
  69. _topingsDisponibles = await _topingViewModel.fetchRegistros();
  70. var categorias = await _categoriaTopingViewModel.fetchRegistros();
  71. var categoriaBase = categorias.firstWhereOrNull(
  72. (cat) => cat.nombre == 'Base',
  73. );
  74. if (categoriaBase != null) {
  75. var topingsBase = _topingsDisponibles.where((toping) {
  76. return toping.idCategoria == categoriaBase.id;
  77. }).toList();
  78. setState(() {
  79. baseOptions = {
  80. for (var toping in topingsBase) toping.nombre!: false,
  81. };
  82. });
  83. }
  84. }
  85. void _onTopingCategoriaSelected(TopingCategoria categoriaSeleccionada) {
  86. setState(() {
  87. _currentTopingCategoriaSeleccionada = categoriaSeleccionada;
  88. // Filtrar y actualizar la lista de toppings para mostrar basada en la categoría seleccionada
  89. _topingsFiltrados = _topingsDisponibles
  90. .where((t) => t.idCategoria == categoriaSeleccionada.id)
  91. .toList();
  92. });
  93. }
  94. void _onTopingSelected(Toping toping) {
  95. print("Topping seleccionado antes de setState: ${toping.nombre}");
  96. setState(() {
  97. // Obtiene la lista actual de toppings seleccionados para la categoría
  98. final currentSelections =
  99. selectedToppingsByCategory[_currentTopingCategoriaSeleccionada!.id] ??
  100. [];
  101. if (currentSelections.contains(toping.nombre)) {
  102. // Si el topping ya está seleccionado, lo deselecciona
  103. currentSelections.remove(toping.nombre);
  104. } else {
  105. // Comprobar las reglas de selección
  106. if (_currentTopingCategoriaSeleccionada!.nombre == 'Base' ||
  107. currentSelections.length < 2) {
  108. // Si es la categoría 'Base' o hay menos de 2 selecciones, selecciona el topping
  109. if (_currentTopingCategoriaSeleccionada!.nombre == 'Base') {
  110. // Si es 'Base', solo permite una selección
  111. currentSelections.clear();
  112. }
  113. currentSelections.add(toping.nombre!);
  114. }
  115. // De lo contrario, no se permite la selección y puedes mostrar un mensaje o algo similar
  116. }
  117. // Actualiza el mapa con las nuevas selecciones
  118. selectedToppingsByCategory[_currentTopingCategoriaSeleccionada!.id] =
  119. currentSelections;
  120. });
  121. print(
  122. "Toppings seleccionados después de setState: ${selectedToppingsByCategory[_currentTopingCategoriaSeleccionada!.id]}");
  123. }
  124. @override
  125. void dispose() {
  126. horizontalScrollController.dispose();
  127. super.dispose();
  128. }
  129. Future<void> cargarCategoriasIniciales() async {
  130. setState(() => _isLoading = true);
  131. var categoriasObtenidas = await cvm.getCategoriaProducto();
  132. setState(() {
  133. categorias = categoriasObtenidas;
  134. _isLoading = false;
  135. // Selecciona la primera categoría por defecto si hay categorías disponibles
  136. if (categorias.isNotEmpty) {
  137. categoriaSeleccionada = categorias.first;
  138. seleccionarCategoria(categoriaSeleccionada!);
  139. }
  140. });
  141. }
  142. Future<void> seleccionarCategoria(CategoriaProducto categoria) async {
  143. if (!mounted) return;
  144. setState(() {
  145. _isLoading = true;
  146. categoriaSeleccionada = categoria;
  147. });
  148. productos = await pvm.fetchRegistros(categoriaProducto: categoria);
  149. if (!mounted) return;
  150. setState(() => _isLoading = false);
  151. }
  152. void agregarAlCarrito(Producto producto) {
  153. setState(() {
  154. var indice =
  155. carrito.indexWhere((item) => item.producto.id == producto.id);
  156. if (indice != -1) {
  157. carrito[indice].cantidad++;
  158. } else {
  159. // Se agrega el nuevo producto al carrito con cantidad inicial de 1
  160. carrito.add(ItemCarrito(producto: producto));
  161. }
  162. });
  163. }
  164. void quitarDelCarrito(Producto producto) {
  165. setState(() {
  166. // Comienza con setState por la misma razón
  167. var indice =
  168. carrito.indexWhere((item) => item.producto.id == producto.id);
  169. if (indice != -1) {
  170. if (carrito[indice].cantidad > 1) {
  171. carrito[indice].cantidad--;
  172. } else {
  173. carrito.removeAt(indice);
  174. }
  175. }
  176. });
  177. }
  178. void addToCart(Producto producto, {Map<String, dynamic>? customizations}) {
  179. var existingIndex =
  180. carrito.indexWhere((item) => item.producto.id == producto.id);
  181. if (existingIndex != -1) {
  182. carrito[existingIndex].cantidad++;
  183. } else {
  184. carrito.add(
  185. ItemCarrito(producto: producto, customizaciones: customizations));
  186. }
  187. setState(() {});
  188. }
  189. void customizeProduct(Producto producto) {
  190. // Asumimos que este producto requiere customización
  191. setState(() {
  192. _productoActual = producto;
  193. isCustomizingProduct = true;
  194. if (categoriasDeTopings.isNotEmpty) {
  195. // Selecciona la primera categoría de toppings automáticamente
  196. _currentTopingCategoriaSeleccionada = categoriasDeTopings.first;
  197. // Filtra los toppings basado en la primera categoría
  198. _topingsFiltrados = _topingsDisponibles
  199. .where((toping) =>
  200. toping.idCategoria == _currentTopingCategoriaSeleccionada!.id)
  201. .toList();
  202. }
  203. });
  204. }
  205. void finalizeCustomization() {
  206. if (_productoActual != null) {
  207. Map<String, dynamic> customizations = {
  208. 'base': selectedBase,
  209. 'sauce': selectedSauce,
  210. 'dressing': selectedDressing,
  211. 'toppings': selectedToppings,
  212. };
  213. addToCart(_productoActual!, customizations: customizations);
  214. setState(() {
  215. isCustomizingProduct = false; // Salir del modo de customización
  216. _productoActual = null; // Resetear el producto actual
  217. // También resetear las opciones de customización aquí si es necesario
  218. });
  219. }
  220. }
  221. @override
  222. Widget build(BuildContext context) {
  223. return Scaffold(
  224. appBar: AppBar(
  225. title: Text("Crear Pedido"),
  226. ),
  227. body: Row(
  228. children: [
  229. Flexible(
  230. flex: 3,
  231. child: _buildCartSection(),
  232. ),
  233. SizedBox(width: 35),
  234. Flexible(
  235. flex: 7,
  236. child: isCustomizingProduct
  237. ? buildCustomizationOptions()
  238. : _buildProductsSection(),
  239. ),
  240. ],
  241. ),
  242. );
  243. }
  244. Widget _buildTotalSection() {
  245. double total =
  246. calcularTotalPedido(); // Aquí llamarías a la función calcularTotalPedido
  247. return Padding(
  248. padding: const EdgeInsets.symmetric(horizontal: 8.0),
  249. child: Row(
  250. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  251. children: [
  252. const Text('Total',
  253. style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18)),
  254. Text("\$${total.toStringAsFixed(2)}",
  255. style:
  256. const TextStyle(fontWeight: FontWeight.bold, fontSize: 18)),
  257. ],
  258. ),
  259. );
  260. }
  261. Widget _buildCartSection() {
  262. return Card(
  263. margin: const EdgeInsets.all(8.0),
  264. child: Column(
  265. children: [
  266. const Padding(
  267. padding: EdgeInsets.symmetric(horizontal: 8.0, vertical: 16.0),
  268. child: Row(
  269. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  270. children: [
  271. Text('Producto',
  272. style:
  273. TextStyle(fontWeight: FontWeight.bold, fontSize: 18)),
  274. Text('Cantidad',
  275. style:
  276. TextStyle(fontWeight: FontWeight.bold, fontSize: 18)),
  277. ],
  278. ),
  279. ),
  280. Expanded(
  281. child: ListView.builder(
  282. itemCount: carrito.length,
  283. itemBuilder: (context, index) {
  284. final item = carrito[index];
  285. return ListTile(
  286. title: Text(item.producto.nombre!),
  287. subtitle: Text('\$${item.producto.precio}'),
  288. trailing: Row(
  289. mainAxisSize: MainAxisSize.min,
  290. children: [
  291. IconButton(
  292. icon: const Icon(Icons.delete, color: Colors.red),
  293. onPressed: () => eliminarProductoDelCarrito(index),
  294. ),
  295. IconButton(
  296. icon: const Icon(Icons.remove),
  297. onPressed: () => quitarDelCarrito(item.producto),
  298. ),
  299. const SizedBox(width: 5),
  300. Text(
  301. '${item.cantidad}',
  302. style: const TextStyle(
  303. fontWeight: FontWeight.bold, fontSize: 14),
  304. ),
  305. const SizedBox(width: 5),
  306. IconButton(
  307. icon: const Icon(Icons.add),
  308. onPressed: () => agregarAlCarrito(item.producto),
  309. ),
  310. // Botón para eliminar el producto del carrito
  311. ],
  312. ),
  313. );
  314. },
  315. ),
  316. ),
  317. const Divider(
  318. thickness: 5,
  319. ),
  320. _buildTotalSection(),
  321. const SizedBox(height: 25),
  322. Padding(
  323. padding: const EdgeInsets.all(8.0),
  324. child: ElevatedButton(
  325. onPressed: () {
  326. // Aquí implementarías la lógica para finalizar el pedido
  327. },
  328. style: ElevatedButton.styleFrom(
  329. primary: AppTheme.primary,
  330. onPrimary: AppTheme.secondary,
  331. textStyle: const TextStyle(fontSize: 22),
  332. fixedSize: const Size(250, 50)),
  333. child: const Text('Finalizar Pedido'),
  334. ),
  335. ),
  336. ],
  337. ),
  338. );
  339. }
  340. void eliminarProductoDelCarrito(int index) {
  341. setState(() {
  342. carrito.removeAt(index);
  343. });
  344. }
  345. Widget _buildProductsSection() {
  346. // Asumiendo que tienes un método para obtener los productos según la categoría seleccionada
  347. return Column(
  348. children: [
  349. _buildCategoryButtons(),
  350. const SizedBox(height: 10),
  351. Expanded(
  352. child: GridView.builder(
  353. gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
  354. crossAxisCount: 3, // Número de columnas
  355. childAspectRatio: 3 / 2, // Proporción de cada tarjeta
  356. ),
  357. itemCount: productos.length,
  358. itemBuilder: (context, index) {
  359. final producto = productos[index];
  360. return Card(
  361. child: InkWell(
  362. onTap: () {
  363. // Modifica esta parte para verificar si el producto necesita customización
  364. if (producto.toping == 1) {
  365. // Asume que `toping` es 1 si necesita customización
  366. customizeProduct(producto);
  367. } else {
  368. // Si no requiere customización, agrega directamente al carrito
  369. agregarAlCarrito(producto);
  370. }
  371. },
  372. child: Column(
  373. mainAxisAlignment: MainAxisAlignment.center,
  374. children: [
  375. const Icon(Icons.fastfood, size: 80),
  376. const SizedBox(height: 8),
  377. Padding(
  378. padding: EdgeInsets.fromLTRB(30, 0, 30, 0),
  379. child: Text(
  380. producto.nombre ?? '',
  381. style: const TextStyle(
  382. fontSize: 16, fontWeight: FontWeight.bold),
  383. )),
  384. const SizedBox(height: 8),
  385. Text(
  386. '\$${producto.precio}',
  387. style: const TextStyle(
  388. fontSize: 16,
  389. fontWeight: FontWeight.bold,
  390. color: Color(0xFF008000)),
  391. ),
  392. ],
  393. ),
  394. ),
  395. );
  396. },
  397. ),
  398. ),
  399. ],
  400. );
  401. }
  402. Widget _buildCategoryButtons() {
  403. return Column(
  404. mainAxisSize: MainAxisSize.min,
  405. children: [
  406. SizedBox(
  407. height: 50, // Altura para los botones
  408. child: Scrollbar(
  409. controller: horizontalScrollController,
  410. thumbVisibility: true,
  411. thickness: 5.0,
  412. child: ListView.builder(
  413. scrollDirection: Axis.horizontal,
  414. itemCount: categorias.length,
  415. controller: horizontalScrollController,
  416. itemBuilder: (context, index) {
  417. final categoria = categorias[index];
  418. bool esSeleccionada = categoriaSeleccionada?.id == categoria.id;
  419. return Padding(
  420. padding: const EdgeInsets.symmetric(horizontal: 4.0),
  421. child: ElevatedButton(
  422. onPressed: () => seleccionarCategoria(categoria),
  423. style: ElevatedButton.styleFrom(
  424. primary: esSeleccionada
  425. ? const Color(0xFFFF848F)
  426. : Colors.white,
  427. onPrimary: Colors.black,
  428. textStyle: const TextStyle(
  429. fontWeight: FontWeight.bold,
  430. ),
  431. shape: RoundedRectangleBorder(
  432. borderRadius: BorderRadius.circular(18.0),
  433. side: BorderSide(
  434. color: esSeleccionada ? Colors.black : Colors.grey),
  435. ),
  436. ),
  437. child: Text(categoria.nombre ?? 'Sin nombre'),
  438. ),
  439. );
  440. },
  441. ),
  442. ),
  443. ),
  444. ],
  445. );
  446. }
  447. Widget buildCustomizationOptions() {
  448. return Container(
  449. margin: const EdgeInsets.all(8.0),
  450. child: Card(
  451. child: Column(
  452. children: [
  453. Expanded(
  454. child: Row(
  455. children: [
  456. Expanded(
  457. flex: 4,
  458. child: ListView.builder(
  459. itemCount: categoriasDeTopings.length,
  460. itemBuilder: (context, index) {
  461. final categoria = categoriasDeTopings[index];
  462. return ListTile(
  463. title: Text(
  464. categoria.nombre ?? 'N/A',
  465. style: TextStyle(
  466. fontSize: 16, fontWeight: FontWeight.bold),
  467. ),
  468. selected:
  469. _currentTopingCategoriaSeleccionada == categoria,
  470. trailing: Image.network(
  471. categoria.mediaTopingCategoria[0].media!.ruta!,
  472. width: 80,
  473. ),
  474. onTap: () {
  475. setState(() {
  476. _currentTopingCategoriaSeleccionada = categoria;
  477. _topingsFiltrados = _topingsDisponibles
  478. .where((t) => t.idCategoria == categoria.id)
  479. .toList();
  480. });
  481. },
  482. );
  483. },
  484. ),
  485. ),
  486. const VerticalDivider(width: 0),
  487. Expanded(
  488. flex: 6,
  489. child: GridView.builder(
  490. gridDelegate:
  491. const SliverGridDelegateWithFixedCrossAxisCount(
  492. crossAxisCount: 3,
  493. ),
  494. itemCount: _topingsFiltrados.length,
  495. itemBuilder: (context, index) {
  496. final toping = _topingsFiltrados[index];
  497. return Card(
  498. child: InkWell(
  499. onTap: () => _onTopingSelected(toping),
  500. child: Column(
  501. mainAxisAlignment: MainAxisAlignment.center,
  502. children: [
  503. // Aquí iría la imagen del toping, asegúrate de manejar errores de red.
  504. Image.network(
  505. toping.mediaToping[0].media!.ruta!,
  506. width: 80,
  507. ),
  508. Text(toping.nombre!),
  509. ],
  510. ),
  511. ),
  512. );
  513. },
  514. ),
  515. ),
  516. ],
  517. ),
  518. ),
  519. Container(
  520. padding: EdgeInsets.symmetric(vertical: 8.0),
  521. alignment: Alignment.centerRight,
  522. // El botón para finalizar la personalización
  523. child: _buildConfirmButton(),
  524. ),
  525. ],
  526. ),
  527. ),
  528. );
  529. }
  530. Widget _buildBaseOptions() {
  531. return GridView.count(
  532. crossAxisCount: 3,
  533. children: baseOptions.keys.map((String key) {
  534. bool isSelected =
  535. baseOptions[key] ?? false; // Determina si está seleccionado
  536. return GestureDetector(
  537. onTap: () {
  538. setState(() {
  539. baseOptions.keys.forEach(
  540. (k) => baseOptions[k] = false); // Desmarca todos primero
  541. baseOptions[key] = true; // Marca el seleccionado
  542. });
  543. },
  544. child: Container(
  545. decoration: BoxDecoration(
  546. color: isSelected ? AppTheme.primary : const Color(0xFFF4F4F4),
  547. border: Border.all(color: isSelected ? Colors.red : Colors.grey),
  548. ),
  549. child: Center(
  550. child: Text(key,
  551. style: TextStyle(
  552. color: isSelected ? Colors.white : Colors.black)),
  553. ),
  554. ),
  555. );
  556. }).toList(),
  557. );
  558. }
  559. Widget _buildSauceOptions() {
  560. return GridView.count(
  561. crossAxisCount: 3,
  562. children: sauceOptions.keys.map((String key) {
  563. bool isSelected = sauceOptions[key] ?? false;
  564. return GestureDetector(
  565. onTap: () {
  566. int selectedCount = sauceOptions.values.where((b) => b).length;
  567. setState(() {
  568. // Si la salsa ya está seleccionada, la deselecciona.
  569. if (isSelected) {
  570. sauceOptions[key] = false;
  571. } else {
  572. // Si se están seleccionando menos de 2 salsas, permite esta selección.
  573. if (selectedCount < 2) {
  574. sauceOptions[key] = true;
  575. } else {
  576. // Si ya hay 2 salsas seleccionadas, primero deselecciona la primera seleccionada.
  577. final firstSelected = sauceOptions.keys.firstWhere(
  578. (k) => sauceOptions[k]!,
  579. orElse: () => '',
  580. );
  581. if (firstSelected.isNotEmpty) {
  582. sauceOptions[firstSelected] = false;
  583. sauceOptions[key] = true;
  584. }
  585. }
  586. }
  587. });
  588. },
  589. child: Container(
  590. decoration: BoxDecoration(
  591. color: isSelected ? AppTheme.primary : const Color(0xFFF4F4F4),
  592. border: Border.all(color: Colors.grey),
  593. ),
  594. child: Center(
  595. child: Text(key,
  596. style: TextStyle(
  597. color: isSelected ? Colors.white : Colors.black)),
  598. ),
  599. ),
  600. );
  601. }).toList(),
  602. );
  603. }
  604. Widget _buildDressingOptions() {
  605. return GridView.count(
  606. crossAxisCount: 3,
  607. children: dressingOptions.keys.map((String key) {
  608. bool isSelected = dressingOptions[key] ?? false;
  609. return GestureDetector(
  610. onTap: () {
  611. int selectedCount = dressingOptions.values.where((b) => b).length;
  612. setState(() {
  613. // Si la salsa ya está seleccionada, la deselecciona.
  614. if (isSelected) {
  615. dressingOptions[key] = false;
  616. } else {
  617. // Si se están seleccionando menos de 2 salsas, permite esta selección.
  618. if (selectedCount < 2) {
  619. dressingOptions[key] = true;
  620. } else {
  621. // Si ya hay 2 salsas seleccionadas, primero deselecciona la primera seleccionada.
  622. final firstSelected = dressingOptions.keys.firstWhere(
  623. (k) => dressingOptions[k]!,
  624. orElse: () => '',
  625. );
  626. if (firstSelected.isNotEmpty) {
  627. dressingOptions[firstSelected] = false;
  628. dressingOptions[key] = true;
  629. }
  630. }
  631. }
  632. });
  633. },
  634. child: Container(
  635. decoration: BoxDecoration(
  636. color: isSelected ? AppTheme.primary : const Color(0xFFF4F4F4),
  637. border: Border.all(color: Colors.grey),
  638. ),
  639. child: Center(
  640. child: Text(key,
  641. style: TextStyle(
  642. color: isSelected ? Colors.white : Colors.black)),
  643. ),
  644. ),
  645. );
  646. }).toList(),
  647. );
  648. }
  649. Widget _buildToppingsOptions() {
  650. return GridView.builder(
  651. key: ValueKey(_topingsFiltrados.length),
  652. gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
  653. crossAxisCount: 3,
  654. ),
  655. itemCount: _topingsFiltrados.length,
  656. itemBuilder: (context, index) {
  657. final toping = _topingsFiltrados[index];
  658. // Verifica si el topping está seleccionado consultando el mapa
  659. final isSelected =
  660. selectedToppingsByCategory[_currentTopingCategoriaSeleccionada!.id]
  661. ?.contains(toping.nombre) ??
  662. false;
  663. return Card(
  664. color: isSelected
  665. ? Colors.red
  666. : Colors.white, // Cambia el color si está seleccionado
  667. child: InkWell(
  668. onTap: () => _onTopingSelected(toping),
  669. child: Column(
  670. mainAxisAlignment: MainAxisAlignment.center,
  671. children: [
  672. Image.network(
  673. toping.mediaToping[0].media!.ruta!,
  674. fit: BoxFit.cover,
  675. width: 80,
  676. height: 80,
  677. ),
  678. Text(
  679. toping.nombre!,
  680. style: TextStyle(
  681. color: isSelected ? Colors.white : Colors.black,
  682. ),
  683. ),
  684. ],
  685. ),
  686. ),
  687. );
  688. },
  689. );
  690. }
  691. Widget _buildToppingOptionsForCategory(int idCategoria) {
  692. var toppingsDeCategoria =
  693. _topingsDisponibles.where((t) => t.idCategoria == idCategoria).toList();
  694. return GridView.count(
  695. crossAxisCount: 3,
  696. children: toppingsDeCategoria.map((topping) {
  697. return GestureDetector(
  698. onTap: () {
  699. // Lógica para seleccionar/deseleccionar topping
  700. },
  701. child: Container(
  702. // Construye tu widget de topping aquí, podrías incluir la imagen y el nombre
  703. ),
  704. );
  705. }).toList(),
  706. );
  707. }
  708. Widget _buildConfirmButton() {
  709. return ElevatedButton(
  710. style: ElevatedButton.styleFrom(
  711. primary: AppTheme.primary, // Color del botón
  712. onPrimary: Colors.black,
  713. textStyle: const TextStyle(fontSize: 22),
  714. padding: const EdgeInsets.fromLTRB(80, 20, 80, 20)),
  715. onPressed: () {
  716. finalizeCustomization(); // Este método creará el pedido personalizado
  717. },
  718. child: Text('Confirmar Pedido'),
  719. );
  720. }
  721. }