MVVM in Flutter: A Beginner’s Guide

Affan Minhas
Blocship
Published in
4 min readApr 9, 2023

Model-View-ViewModel (MVVM) is a popular design pattern in software development, which separates an application’s data model, user interface, and application logic into distinct layers. The MVVM pattern is widely used in many platforms, including mobile development, to improve code organization, reusability, and maintainability.

In this article, we will discuss how to implement MVVM in Flutter, a popular mobile development framework that uses the Dart programming language. We will also cover the necessary steps to build a simple Flutter application using the MVVM architecture.

The MVVM Architecture

Before we dive into implementing MVVM in Flutter, let’s take a quick look at the three main components of the MVVM architecture:

  1. Model: The data model represents the data and business logic of the application. It encapsulates the data and provides methods to manipulate it.
  2. View: The view is the user interface (UI) of the application. It displays the data and allows the user to interact with it.
  3. ViewModel: The ViewModel is responsible for connecting the Model and the View. It provides data to the view and communicates user interactions to the model.

Implementing MVVM in Flutter

To implement MVVM in Flutter, we need to create three main components: the Model, the View, and the ViewModel. Here are the steps to build a simple Flutter application using MVVM:

Step 1: Create the Model

The first step is to create the data model for our application. In Flutter, we can define a simple model class that holds the data we want to display in the UI. For example, let’s create a Person class with two properties: name and age.

class Person {
String name;
int age;

Person({required this.name, required this.age});
}

Step 2: Create the ViewModel

Next, we need to create the ViewModel class. In Flutter, we can define a simple ViewModel class that holds the data and provides methods to manipulate it. For example, let’s create a PersonViewModel class that holds a list of Person objects and provides methods to add, remove, and update persons.

import 'package:flutter/material.dart';

import 'person.dart';

class PersonViewModel extends ChangeNotifier {
List<Person> _persons = [];

List<Person> get persons => _persons;

void addPerson(Person person) {
_persons.add(person);
notifyListeners();
}

void removePerson(Person person) {
_persons.remove(person);
notifyListeners();
}

void updatePerson(Person oldPerson, Person newPerson) {
final index = _persons.indexOf(oldPerson);
_persons[index] = newPerson;
notifyListeners();
}
}

The ViewModel class extends the ChangeNotifier class, which is a built-in Flutter class that provides a way to notify the UI when the data changes.

Step 3: Create the View

The final step is to create the UI, which is the View component. In Flutter, we can create the UI using the widgets provided by the framework. For example, let’s create a simple ListView widget that displays a list of persons and allows the user to add, remove, and update them.

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

import 'person.dart';
import 'person_view_model.dart';

class PersonListView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Person List')),
body: Consumer<PersonViewModel>(
builder: (context, viewModel, child) {
return ListView.builder(
itemCount: viewModel.persons.length,
itemBuilder: (context, index) {
final person = viewModel.person
return ListTile(
title: Text(person.name),
subtitle: Text('Age: ${person.age}'),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () => viewModel.removePerson(person),
),
onTap: () => _showEditDialog(context, viewModel, person),
);
},
);
},
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () => _showAddDialog(context),
),
);
}
void _showAddDialog(BuildContext context) {
final nameController = TextEditingController();
final ageController = TextEditingController();
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Add Person'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: nameController,
decoration: InputDecoration(labelText: 'Name'),
),
TextField(
controller: ageController,
decoration: InputDecoration(labelText: 'Age'),
keyboardType: TextInputType.number,
),
],
),
actions: [
TextButton(
child: Text('Cancel'),
onPressed: () => Navigator.of(context).pop(),
),
TextButton(
child: Text('Add'),
onPressed: () {
final name = nameController.text;
final age = int.parse(ageController.text);
final person = Person(name: name, age: age);
final viewModel =
Provider.of<PersonViewModel>(context, listen: false);
viewModel.addPerson(person);
Navigator.of(context).pop();
},
),
],
),
);
}
void _showEditDialog(
BuildContext context, PersonViewModel viewModel, Person person) {
final nameController = TextEditingController(text: person.name);
final ageController = TextEditingController(text: person.age.toString());
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Edit Person'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: nameController,
decoration: InputDecoration(labelText: 'Name'),
),
TextField(
controller: ageController,
decoration: InputDecoration(labelText: 'Age'),
keyboardType: TextInputType.number,
),
],
),
actions: [
TextButton(
child: Text('Cancel'),
onPressed: () => Navigator.of(context).pop(),
),
TextButton(
child: Text('Save'),
onPressed: () {
final name = nameController.text;
final age = int.parse(ageController.text);
final oldPerson = person;
final newPerson = Person(name: name, age: age);
viewModel.updatePerson(oldPerson, newPerson);
Navigator.of(context).pop();
},
),
],
),
);
}
}

The View component uses the Provider package to listen to changes in the ViewModel and rebuild the UI when necessary. The ListView widget displays a list of persons using the itemBuilder method, and each item in the list is a ListTile widget. The floatingActionButton widget displays a button that allows the user to add a new person to the list.

Conclusion

In this article, we discussed how to implement MVVM in Flutter, a popular mobile development framework that uses the Dart programming language. We also covered the necessary steps to build a simple Flutter application using the MVVM architecture. By using the MVVM architecture, we can improve code organization, reusability, and maintainability, making our application easier to develop and maintain in the long run.

--

--