Skip to main content
Este ejemplo permite cargar clientes, validar nombre/correo/tipo, crear uno y mostrar confirmación. El repositorio es de demostración: guarda en memoria y simula una espera breve. El contrato puede implementarse con una API real sin modificar la vista. Parte de los archivos de la primera app, conserva su AppIsselController y añade los archivos siguientes. El nuevo main.dart abre CustomersDemoApp.

Entidad y borrador

La creación recibe un borrador; el id se asigna en datos. Dominio no importa Flutter.

Contrato de repositorio

Fuente en memoria

No hay JSON ni SDK externo, de modo que no se añaden modelos de transporte o datasources vacíos. La validación de correo mostrada es básica para el ejemplo; adapta la regla del producto.

Controlador

El controlador devuelve AppResult y además mantiene lista, carga y guardado. load impide solaparse con otra operación; si el usuario pulsa Guardar repetidamente, createCustomer rechaza duplicados aunque la vista ya deshabilite el botón. No conoce contextos, toasts ni rutas.
El guardado puede terminar después de dispose; el resultado se devuelve sin mutar ni notificar el controlador. Los fallos esperados llegan como AppError; los errores de programación se propagan. finally sólo actualiza una instancia activa.

Vista

La vista es dueña de su controlador de pantalla y de los controllers de texto. Los campos viven dentro de una superficie surface, con bloques surfaceContainer. El callback de Guardar comprueba mounted antes de mostrar feedback. No se recrean Futures desde build.
CustomersView recibe un repositorio estable para toda su vida. Si tu composición necesita sustituirlo en una vista montada, implementa el cambio y la liberación del controlador mediante didUpdateWidget, o crea una vista con una clave nueva.

Composición y arranque

La raíz mantiene el repositorio de demostración durante su vida. Reconstruir el tema conserva los clientes almacenados en esa instancia.

Recorrido y variantes

Ejecuta dart format lib, flutter analyze y flutter run. Comprueba el estado vacío, envío incompleto, creación válida, correo duplicado, dos pulsaciones durante el guardado y salida de la pantalla con una operación pendiente. Para verificar error de carga inyecta un repositorio que devuelva AppResult.error; la fuente en memoria normal siempre carga con éxito. Para convertir el formulario en diálogo o ruta que devuelve un cliente, conserva el controlador y reemplaza sólo el efecto de éxito de la vista por Navigator.of(context).pop(value) o app.navigation.goBack(value). El caller espera el resultado tipado y actualiza su listado. El dominio no cambia. Para conectarlo con una API, implementa CustomerRepository, serializa el borrador conforme al contrato, convierte la respuesta a CustomerEntity y traduce sólo los fallos esperados. Inyecta esa implementación en la raíz; la app de demostración no afirma persistencia después de cerrar o recargar.