Curso de Patrones MVVM en Android

Guardando comida en Room con MVVM

Curso de Patrones MVVM en Android

Guardando comida en Room con MVVM

Resumen

Persistir datos en una app Android con Room y el patrón MVVM te permite construir experiencias offline, reactivas y con feedback claro para el usuario. Aquí vas a ver cómo conectar eventos, estados y una base de datos local para guardar la comida que el usuario selecciona desde una API, paso a paso dentro de un SearchViewModel.

¿Por qué usar Room con MVVM para persistir datos?

Cuando tu aplicación guarda información localmente, ganas independencia del servidor y mejoras la experiencia del usuario. Si la conexión se cae, los datos siguen ahí.

El patrón MVVM combinado con eventos y estados te da una comunicación reactiva entre la UI y los datos. Cada vez que el usuario escribe en una caja de texto o presiona un botón, disparas un evento que actualiza el estado, y la interfaz responde en tiempo real.

¿Qué ventajas tiene persistir datos con Room? Tu app funciona offline, reduce dependencia de servidores y entrega información instantánea al usuario incluso sin conexión.

¿Cómo defino los eventos en SearchEvent?

Dentro de la capa presentation, en el archivo SearchEvent, creas dos data class nuevas que representan las acciones del usuario [03:15].

  • OnTrackFoodClick recibe food: TrackableFood, mealType: MealType y date: LocalDate. Este evento se dispara cuando el usuario confirma que quiere guardar la comida.
  • OnAmountForFoodChange recibe food: TrackableFood y amount: String. Controla el cambio de estado mientras el usuario escribe la cantidad en la caja de texto.

Ambas clases heredan de SearchEvent, lo que te permite manejarlas de forma centralizada en el when del ViewModel.

¿Cómo conecto los eventos al SearchViewModel?

En el SearchViewModel agregas los nuevos branches al bloque when. Para OnAmountForFoodChange, actualizas el estado mapeando la lista trackableFood: si la comida coincide con la del evento, aplicas filterOutDigits sobre el amount para limpiar dígitos no válidos; si no coincide, retornas la misma comida [05:40].

Para OnTrackFoodClick creas una función privada llamada trackFood. Como las operaciones de Room son asíncronas y se ejecutan en funciones de suspensión, necesitas envolver la lógica en viewModelScope.launch.

Dentro de la corrutina:

  1. Buscas en el estado la comida que coincide con la del evento.
  2. Conviertes el amount a Int o haces return@launch si es nulo.
  3. Llamas al use case trackFoodUseCase pasando food, amount, mealType y date.
  4. Disparas un evento que devuelve la navegación a la home screen cuando termina el guardado.

¿Por qué Room necesita corrutinas? Porque sus operaciones son asíncronas y usan funciones suspend, evitando bloquear el hilo principal de la UI.

¿Qué hace filterOutDigits dentro del estado?

Es un use case que limpia el texto que entra a la caja: descarta caracteres no numéricos y decimales innecesarios, asegurando que el amount sea válido antes de guardarlo.

¿Cómo enlazo el SearchScreen con el ViewModel?

En la SearchScreen tenías lambdas vacías esperando los eventos. Ahora las conectas al searchViewModel.onEvent.

  • En onAmountChange envías SearchEvent.OnAmountForFoodChange(food, it).
  • En onTrack primero ocultas el teclado y luego disparas SearchEvent.OnTrackFoodClick pasando food, MealType.fromString(mealName) y un LocalDate.of(año, mes, día).

También corriges el BasicTextField dentro de TrackableFoodItem: el valor estaba fijo en vacío, así que lo reemplazas por trackableFoodUiState.amount para que la caja refleje lo que el usuario escribe.

¿Cómo verifico que la base de datos se creó correctamente?

Lanzas el emulador, buscas un alimento (por ejemplo pizza), eliges una opción como Homemade Style, escribes una cantidad como 200 y presionas el check [12:50].

Desde App Inspection en Android Studio confirmas que se generó TrackerDB con la tabla TrackerFoodEntity, donde aparecen el nombre del alimento, carbohidratos, proteínas, grasas y la imagen. Las fechas (día, mes, año) se ajustarán en una clase posterior.

¿Cómo cambio el teclado a numérico en la caja de texto?

Un detalle de UX importante: cuando el usuario abre la caja para escribir cantidades, el teclado por defecto no es numérico. Vas al componente donde defines el KeyboardOptions y agregas keyboardType = KeyboardType.Number junto al ImeAction existente.

Con ese cambio mínimo, al abrir la caja aparece directamente el teclado numérico y el usuario evita saltar entre layouts de teclado.

Conceptos clave que aplicaste en esta integración

  • MVVM: separa la lógica de presentación (ViewModel) de la UI (SearchScreen) y comunica ambas con eventos y estados.
  • Room: framework de persistencia local de Android que requiere funciones suspend para operaciones asíncronas.
  • viewModelScope.launch: corrutina ligada al ciclo de vida del ViewModel, ideal para llamadas a base de datos.
  • TrackableFood y TrackableFoodUiState: modelos que viajan entre la API, el estado y la UI.
  • MealType.fromString y LocalDate.of: utilidades que transforman strings y fechas al formato que espera la entidad de Room.
  • App Inspection: herramienta de Android Studio para inspeccionar bases de datos en tiempo real durante el desarrollo.

¿Ya probaste guardar varios registros seguidos y revisarlos en App Inspection? Cuéntame en los comentarios qué cambios harías para manejar errores de inserción.