MVVM en Flutter: cómo implementar esta arquitectura paso a paso

- Andrés Cruz

EN In english

MVVM en Flutter: cómo implementar esta arquitectura paso a paso

Flutter es una tecnología desarrollada por Google que permite crear aplicaciones para Android, iOS, Web, Windows, macOS y Linux usando una sola base de código. Una de las particularidades de Flutter es que no impone una arquitectura específica para organizar las aplicaciones, a diferencia de otros frameworks que suelen recomendar patrones como MVC.

Cuando empecé a trabajar con Flutter, una de las primeras decisiones importantes fue cómo estructurar el proyecto para que el código fuera escalable y fácil de mantener. Una de las arquitecturas que mejor funciona en este ecosistema es MVVM (Model-View-ViewModel).

En este artículo vamos a ver qué es MVVM, cómo funciona en Flutter y cómo implementarlo usando Provider para la gestión de estado.


Qué es MVVM y por qué usarlo en Flutter

MVVM es un patrón de arquitectura que separa la aplicación en tres componentes principales:

  • Model
  • View
  • ViewModel

El objetivo principal es separar la lógica de negocio de la interfaz de usuario, permitiendo que cada parte tenga una responsabilidad clara.

En Flutter esto es especialmente útil porque las interfaces están formadas por widgets reactivos, y separar la lógica permite mantener el código organizado incluso cuando la aplicación crece.

En mi caso, cuando empecé a desarrollar aplicaciones más grandes en Flutter, noté que mezclar lógica de negocio dentro de los widgets hacía que el código fuera difícil de mantener. Implementar MVVM ayudó a mantener la UI limpia y mover toda la lógica al ViewModel.


Componentes del patrón MVVM

Model

El Model representa los datos de la aplicación.

Se encarga de:

  • Obtener datos desde una API
  • Acceder a bases de datos
  • Convertir respuestas JSON en objetos

Ejemplo simple de modelo:

class Item {
  final String title;
  final String description;
  Item({required this.title, required this.description});
}

El modelo no conoce nada de la interfaz de usuario, solo representa datos.


View

La View es la capa de presentación, es decir, la interfaz que ve el usuario.

En Flutter, esto corresponde a los widgets.

La vista:

  • Muestra datos
  • Escucha cambios en el ViewModel
  • Envía eventos del usuario

Un principio importante es que la vista no debería contener lógica de negocio compleja.


ViewModel

El ViewModel actúa como intermediario entre la vista y el modelo.

Sus responsabilidades incluyen:

  • Gestionar el estado
  • Procesar acciones del usuario
  • Solicitar datos al modelo
  • Notificar cambios a la interfaz

En Flutter, una forma común de implementar un ViewModel es usando ChangeNotifier.

class ItemViewModel extends ChangeNotifier {

  List<Item> _items = [];

  List<Item> get items => _items;

  void fetchItems() {
    // lógica para obtener datos
    notifyListeners();
  }
}

Cada vez que el estado cambia, se llama a notifyListeners(), lo que provoca que la interfaz se actualice.


Beneficios de usar MVVM en Flutter

Implementar MVVM en Flutter ofrece varias ventajas importantes.

1. Separación clara de responsabilidades

La lógica de negocio se mueve al ViewModel y la interfaz se mantiene limpia.

Esto hace que los widgets se enfoquen únicamente en mostrar datos y reaccionar a eventos.


2. Código más fácil de mantener

Cuando la aplicación crece, separar responsabilidades permite modificar partes del código sin afectar otras capas.


3. Mejores pruebas unitarias

Al mover la lógica al ViewModel, es posible probar el comportamiento sin depender de la interfaz.

Esto facilita la creación de tests automatizados.


4. Reutilización de lógica

Un ViewModel puede ser reutilizado por diferentes vistas, evitando duplicación de código.


Estructura de carpetas para una arquitectura MVVM en Flutter

Una buena organización del proyecto es clave para mantener la arquitectura clara.

Una estructura común podría ser: 

lib/
│
├── network/
│   └── api_client.dart
│
├── models/
│   └── item.dart
│
├── repository/
│   └── item_repository.dart
│
├── view/
│   └── item_list_view.dart
│
├── viewmodel/
│   └── item_viewmodel.dart
│
├── utils/
│
└── res/

Qué hace cada carpeta

network

Contiene las clases encargadas de realizar llamadas HTTP o acceder a bases de datos.

models

Define las estructuras de datos usadas en la aplicación.

repository

Actúa como intermediario entre el ViewModel y las fuentes de datos.

view

Contiene los widgets y pantallas.

viewmodel

Gestiona el estado y la lógica de negocio.

Esta estructura ayuda a mantener una separación clara entre capas.


Cómo funciona el flujo de datos en MVVM

El flujo de datos en MVVM sigue este proceso:

  1. El usuario interactúa con la View.
  2. La vista envía el evento al ViewModel.
  3. El ViewModel solicita datos al Model o Repository.
  4. El Model devuelve los datos.
  5. El ViewModel actualiza el estado.
  6. La vista se actualiza automáticamente.

Este flujo hace que la interfaz sea reactiva y desacoplada de la lógica.


Implementación de MVVM en Flutter con Provider

Una forma sencilla de implementar MVVM en Flutter es usando Provider como gestor de estado.

Provider permite que los widgets escuchen cambios en el ViewModel.


Crear el modelo de datos

class Item {
  final String title;
  final String description;
  Item({required this.title, required this.description});
}
 

Crear el ViewModel con ChangeNotifier

class ItemViewModel extends ChangeNotifier {
  List<Item> _items = [];
  List<Item> get items => _items;
  void fetchItems() {
    // Simulación de datos
    _items = [
      Item(title: "Item 1", description: "Descripción 1"),
      Item(title: "Item 2", description: "Descripción 2"),
    ];
    notifyListeners();
  }
}
 

Conectar la vista con Provider

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => ItemViewModel(),
      child: MaterialApp(
        home: ItemListView(),
      ),
    ),
  );
}
 

Crear la vista

class ItemListView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final itemViewModel = Provider.of<ItemViewModel>(context);
    return Scaffold(
      appBar: AppBar(title: Text('Lista de elementos')),
      body: ListView.builder(
        itemCount: itemViewModel.items.length,
        itemBuilder: (context, index) {
          final item = itemViewModel.items[index];
          return ListTile(
            title: Text(item.title),
            subtitle: Text(item.description),
          );
        },
      ),
    );
  }
}
 En este ejemplo, la vista escucha los cambios del ViewModel y se actualiza automáticamente.

Buenas prácticas al usar MVVM en Flutter

Para mantener una arquitectura limpia:

  • Mantener los widgets libres de lógica de negocio
  • Usar repositorios para acceder a datos
  • Mantener ViewModels pequeños y específicos
  • Separar modelos de dominio y modelos de API
  • Evitar dependencias directas entre View y Model

Errores comunes al implementar MVVM en Flutter

Al implementar este patrón es común cometer algunos errores.

Mezclar lógica en los widgets

Esto rompe la separación de responsabilidades.


Crear ViewModels demasiado grandes

Cada ViewModel debería tener una única responsabilidad.


No separar la capa de repositorio

Sin repositorios, el ViewModel termina accediendo directamente a la API.


MVVM vs otras arquitecturas en Flutter

MVVM vs MVC

En MVC, el controlador maneja tanto la lógica como la interacción con la vista.

En MVVM, el ViewModel actúa como intermediario y mantiene la vista desacoplada.


MVVM vs Clean Architecture

Clean Architecture es más robusta y escalable, pero también más compleja.

MVVM suele ser más fácil de implementar en proyectos pequeños o medianos.


Conclusión

Flutter ofrece mucha libertad a la hora de estructurar aplicaciones, y elegir una buena arquitectura es clave para mantener el proyecto organizado.

El patrón MVVM en Flutter permite separar la interfaz de usuario de la lógica de negocio, facilitando el mantenimiento, la reutilización del código y las pruebas.

Combinado con herramientas como Provider, es posible implementar una arquitectura limpia y escalable sin añadir demasiada complejidad al proyecto.


FAQ

¿Flutter usa MVVM?

Flutter no impone una arquitectura específica, pero MVVM es uno de los patrones más utilizados.


¿Cuál es el mejor gestor de estado para MVVM en Flutter?

Los más utilizados son:

  • Provider
  • Riverpod
  • Bloc

¿MVVM es mejor que MVC en Flutter?

En muchos casos sí, porque separa mejor la lógica de negocio de la interfaz.

Aprende cómo implementar MVVM en Flutter paso a paso usando Provider. Arquitectura, estructura de carpetas, ejemplos de código y buenas prácticas.

Acepto recibir anuncios de interes sobre este Blog.

Andrés Cruz

EN In english