Análisis y Desarrollo de Software
Taller Individual · Flutter · Proyecto "Café Místico"
Fase 0 · Requisitos Fase 1 · Proyecto y assets Fase 2 · Modelo y backend Fase 3 · Componentes Fase 4 · Pantallas ✅ Checklist
📱 TALLER INDIVIDUAL · ADSO · FASE 2 DE 5

Modelo y conexión
con el backend

El modelo Product, la configuración de la URL de la API según la plataforma y el servicio que consulta Node/Express + MongoDB.

📅 Sesión del 27 de julio de 2026
☕ ecommer_app
🔒 Código protegido, solo lectura
🔒
Código de solo lectura: no se puede seleccionar, copiar ni pegar (tampoco desde el inspector del navegador). Debes escribirlo tú mismo, línea por línea, en tu editor.
En cada paso encontrarás: 💻 Código 🧪 Prueba 🔒 Sin copiar
🔄 Mapeo Mongoose ➔ Modelo Dart
MongoDB (Mongoose Schema)Flutter (Product Model)
productId (String)product.id
Nombre (String)product.title
Descripcion (String)product.description
Precio (Number)product.price
Image (String)product.imageUrl
2
FASE 2
Modelo y conexión con el backend
PASO 5
Modelo de datos (models/product.dart)
💻 Código
Primero, los campos y el constructor:
dart🔒 class Product { final String id; final String title; final String description; final double price; final String imageUrl; Product({ required this.id, required this.title, required this.description, required this.price, required this.imageUrl, });
Ahora, el factory que convierte el JSON del backend en un Product (va dentro de la misma clase, después del constructor):
dart🔒 factory Product.fromJson(Map<String, dynamic> json) { return Product( id: json['productId'] ?? '', title: json['Nombre'] ?? 'Sin Nombre', description: json['Descripcion'] ?? 'Sin descripción', price: (json['Precio'] as num?)?.toDouble() ?? 0.0, imageUrl: json['Image'] ?? '', ); } }
🧪 Prueba este paso

En main(), antes de runApp, crea un producto de prueba: final p = Product(id: '1', title: 'Test', description: 'x', price: 1.0, imageUrl: ''); seguido de print(p.title);. Corre en modo debug y revisa la consola: debe imprimir Test. Luego borra esas dos líneas.

PASO 6
Configuración de la API (services/api_config.dart)
💻 Código
Esta clase decide la URL del backend según la plataforma donde corre la app:
dart🔒 import 'dart:io'; class ApiConfig { static String get baseUrl { if (Platform.isAndroid) { // Emulador de Android return 'http://10.0.2.2:3000/api'; } else if (Platform.isIOS) { // Simulador de iOS (usa localhost directamente) return 'http://localhost:3000/api'; } // Fallback para Web, macOS, Windows, etc. return 'http://localhost:3000/api'; } }
🧪 Prueba este paso

Importa api_config.dart en main.dart y agrega temporalmente print(ApiConfig.baseUrl); dentro de main(). Corre la app: en consola debe imprimir la URL correspondiente a tu emulador. Luego borra esa línea.

PASO 7
Servicio de productos (services/product_service.dart)
💻 Código
Este servicio consulta el backend Node/Express + MongoDB. Empecemos por el GET, que trae la lista completa:
dart🔒 import 'dart:convert'; import 'package:http/http.dart' as http; import '../models/product.dart'; import 'api_config.dart'; class ProductService { // GET: Obtener todos los productos static Future<List<Product>> getProductos() async { final response = await http.get( Uri.parse('${ApiConfig.baseUrl}/productos'), headers: { 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', }, ); if (response.statusCode == 200) { List<dynamic> body = jsonDecode(response.body); return body.map((item) => Product.fromJson(item)).toList(); } else { throw Exception('Error al cargar productos (Código: ${response.statusCode})'); } }
Ahora las operaciones para crear y actualizar un producto, dentro de la misma clase (usan los mismos nombres de campo que ya viste en el mapeo: Nombre, Descripcion, Precio, Image):
dart🔒 // POST: Crear un nuevo producto static Future<Product> crearProducto({ required String nombre, required String descripcion, required double precio, required String image, }) async { final response = await http.post( Uri.parse('${ApiConfig.baseUrl}/productos'), headers: {'Content-Type': 'application/json'}, body: jsonEncode({ 'Nombre': nombre, 'Descripcion': descripcion, 'Precio': precio, 'Image': image, }), ); if (response.statusCode == 200 || response.statusCode == 201) { return Product.fromJson(jsonDecode(response.body)); } else { throw Exception('Error al crear el producto (Código: ${response.statusCode})'); } } // PUT: Actualizar un producto existente static Future<Product> actualizarProducto({ required String productId, required String nombre, required String descripcion, required double precio, required String image, }) async { final response = await http.put( Uri.parse('${ApiConfig.baseUrl}/productos/$productId'), headers: {'Content-Type': 'application/json'}, body: jsonEncode({ 'Nombre': nombre, 'Descripcion': descripcion, 'Precio': precio, 'Image': image, }), ); if (response.statusCode == 200) { return Product.fromJson(jsonDecode(response.body)); } else { throw Exception('Error al actualizar el producto (Código: ${response.statusCode})'); } }
Y para cerrar la clase, eliminar un producto:
dart🔒 // DELETE: Eliminar un producto static Future<void> eliminarProducto(String productId) async { final response = await http.delete( Uri.parse('${ApiConfig.baseUrl}/productos/$productId'), ); if (response.statusCode != 200 && response.statusCode != 204) { throw Exception('Error al eliminar el producto (Código: ${response.statusCode})'); } } }
🧪 Prueba este paso

Con el backend Node/Express y MongoDB corriendo, llama ProductService.getProductos() desde un FutureBuilder simple en main.dart y revisa que no aparezca ninguna excepción en consola. Si el backend aún no está listo, confirma que el estado de error se muestre controlado (sin cerrar la app). Las operaciones crearProducto, actualizarProducto y eliminarProducto las probarás más adelante, cuando construyas las pantallas que las usan (por ejemplo, un panel de administración de productos).

← Fase 1 · Proyecto y assets Siguiente: Fase 3 · Componentes →