What is the difference between MVC and MVVM (for Flutter)?
Posted by Superadmin on July 31 2023 02:59:33

What is the difference between MVC and MVVM (for Flutter)?

·

If you ask n people about the difference between MVC (Model-View-Controller) and MVVM (Model-View-ViewModel) you get at least n^n different opinions of what it is. Even Allen Wyma, the host of the podcast “Flying high with flutter,” and his guest Richard Coutts, the creator and maintainer of the MVVM+ package (a package that implements the MVVM pattern for Flutter), admit that even for them it is hard to get the difference. But how should a student, aspiring coder, or entrepreneurial tech enthusiast understand this topic, if even experts find it hard to articulate the difference?

In this article, I will give you a better understanding of MVVM and MVC by explaining both patterns and giving you a counter-app implementation of both patterns in Flutter.

Settling on definitions

One issue with understanding MVC and MVVM is that different definitions of each pattern exist which vary in minor and major aspects. To understand differences we need to settle on one definition for each pattern that we compare.

Following, I will refer to the MVC definition by Glenn E. Krasner and Stephen T. Pope (1988) who further developed the original pattern coined by Trygve Reenskau (1979). I choose not to take the original creation by Reenskau because it differs from the modern understanding of MVC, e.g. by including the editor as one of the four important parts of the pattern.

For MVVM, I refer to the definition of Josh Smith (2009) who worked at Microsoft where the MVVM principle was invented. Here, I chose the definition by Smith because it is better documented than the inventor’s definition by Ken Cooper and Ted Peters (2005).

General definitions

So, let’s start with some general definitions:

MVVM and MVC are both architectural patterns used in software development. Architectural patterns guide you on how to structure your code and how to separate different levels of concern like the user interface (UI) and the business logic.

Both patterns have a View and a Model which are in most ways similar. The Model represents the data and the business logic of the application. It is responsible for retrieving and storing data and performing any necessary calculations or transformations on that data. The View is responsible for displaying the (dumb) data to the user. The View should not contain any business logic and should not modify the data from the Model in any way.

Even though the View and the Model have similar responsibilities in both patterns, there is a fundamental difference in how they interact.

MVVM

In MVVM, the View is connected to a ViewModel, which is a representation of the View’s state, and the ViewModel is related to the Model. The View updates itself based on changes to the ViewModel, rather than directly to the Model. The ViewModel acts as an intermediary between the View and the Model, and it is responsible for coordinating the flow of data between the two. The ViewModel is responsible for holding the state of the View, the View’s behavior, and the logic that interacts with the Model. It is also responsible for translating the data from the Model into a format that the View can understand.

Data binding between the View and the Model assures that data between them is synchronized. For this, the observer pattern can be used which notifies the View when the Model changes. Furthermore, the View can also notify the ViewModel when the user interacts with it. This allows the ViewModel to update the Model, and in turn, the View.

MVC

In MVC, the View directly accesses the Model to get the data and updates the data based on user actions. The View is dependent on the Model. The Controller is the component that sits between the View and the Model. It is responsible for interpreting the user input and then taking the appropriate action on the Model and/or the View. It also acts as a decision maker for what should be displayed in the View, it can decide to update the View or change the View based on a specific user action.

In summary, The ViewModel in MVVM and the Controller in MVC are similar in that they both act as intermediaries between the View and the Model, but the ViewModel is more focused on holding the state of the View, while in MVC the View gets its data directly from the Model.

Code Example: Counter app

As an exemplary implementation, I chose a simple counter app with two buttons and text with the current counter. One button increases the counter by one and the other increases the counter by two. I chose an example with two buttons to emphasize that input processing works differently. For state management, I used the ChangeNotifier from the Provider package.

Screenshot of example counter app

Code example MVC

The following code shows the implementation of the counter app for the MVC. The main flow starts in the View where one of the two buttons gets triggered in their onPressed method (1). Then, the Controller calls the appropriate increment function of the Model (2). Finally, the Model updates itself (3) and then notifies the View to update the UI (4).

The original documents of MVC show that the user input is directly received by the Controller. In Flutter, it is not possible to receive the input directly on a Controller (tell me in the comments if I have overseen something), but only through the View. Similar behavior is imitated by directly delegating the input from the View to the Controller with its own function for every possible input (1).

You find the complete code that can be directly run in this DartPad.


class MyModel with ChangeNotifier {
int _counter = 0;
int get counter => _counter;

void incrementCounter(int increment) {
_counter += increment; // (3)
notifyListeners(); // (4)
}
}

class MyView extends StatelessWidget {
final MyController controller;
final MyModel model;

const MyView({super.key, required this.controller, required this.model});

@override
Widget build(context) {
return ChangeNotifierProvider<MyModel>(
create: (_) => model,
builder: (_, __) => Consumer<MyModel>(
builder: (_, model, __) => Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Counter: ${model.counter}'), // (4)
TextButton(
onPressed: controller.onButtonIncrement1Pressed, // (1)
child: const Text('Increment by 1'),
),
TextButton(
onPressed: controller.onButtonIncrement2Pressed, // (1)
child: const Text('Increment by 2'),
),
],
),
),
),
),
);
}
}

class MyController {
final MyModel model;

MyController({required this.model});

void onButtonIncrement1Pressed() {
model.incrementCounter(1); // (2)
}

void onButtonIncrement2Pressed() {
model.incrementCounter(2); // (2)
}
}

Code example MVVM

In the MVVM code, the user input is registered by the View. The View calls the corresponding function in the ViewModel (1). The ViewModel then calls the function to increment the counter in the Model (2). Afterward, the ViewModel notifies all listeners (3). The View updates itself because the Model value gets passed to it through the ViewModel (4).

You can see and run the complete code in this DartPad.

class MyModel {
int _counter = 0;
int get counter => _counter;

void incrementCounter(int increment) {
_counter += increment;
}
}

class MyView extends StatelessWidget {
final MyViewModel viewModel;

const MyView({super.key, required this.viewModel});

@override
Widget build(context) {
return ChangeNotifierProvider(
create: (_) => viewModel,
child: Consumer<MyViewModel>(
builder: (_, viewModel, __) => Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Counter: ${viewModel.counter}'),
TextButton(
onPressed: () => viewModel.incrementCounter(1),
child: const Text('Increment by 1'),
),
TextButton(
onPressed: () => viewModel.incrementCounter(2),
child: const Text('Increment by 2'),
),
],
),
),
),
),
);
}
}

class MyViewModel with ChangeNotifier {
final MyModel model;
int get counter => model.counter;

MyViewModel({required this.model});

void incrementCounter(int increment) {
model.incrementCounter(increment);
notifyListeners();
}
}

The main differences in the implementations of MVC and MVVM are how the input is handled and the connections between the elements. By the definition of Krasner and Pope, the input should be received by the Controller. This behavior can be mocked but is not truly possible to implement in Flutter. The most important difference is that in MVC the Model is connected to the View but in MVVM they are disconnected.

On the internet, lots of resources exist that disconnect the View and the Model in MVC. With time the definition of MVC approached more and more in the direction of MVVM. Even though every person can interpret architectural patterns in their own way this, in my opinion, is the main reason why many people do not see a difference between both patterns.

Furthermore, it should be mentioned that MVVM and MVC do not necessarily exclude each other. If you are interested to dig deeper I recommend reading about MVCVM. An approach that combines ModelViewController, and ViewModel but this will not be covered here.

Hopefully, you now understand the difference between MVC and MVVM better and can use it to better articulate software architectural topics in your projects with your team. Any feedback is highly appreciated.