MVC vs. MVVM Architecture in Flutter: A Deep Dive with a To-Do List Example

Abhishek Singh
4 min readSep 23, 2024

--

Flutter MVC & MVVM Design Pattern 2024

Chances are, you’ve stumbled upon terms like MVC and MVVM when diving into app development. At first glance, these architectural patterns can feel overwhelming — especially if you’re new to the scene. But don’t worry, I’m here to break them down and make them easier to grasp.

Understanding MVC (Model-View-Controller) in Flutter

What is MVC?

MVC stands for Model-View-Controller. This design pattern/architecture divides your application into three main components:

Model: Manages the data and business logic.
View: Displays the data to the user and sends user input to the Controller.
Controller: Acts as the mediator between the Model and the View. It updates the Model based on user interactions and refreshes the View accordingly.

Though Flutter doesn’t directly follow the MVC pattern, you can implement it by manually creating separate classes for the Model, View, and Controller.

To-Do List Example in MVC

Model

The ToDoItem class represents each to-do item in the list:

class ToDoItem {
String title;
bool isCompleted;

ToDoItem({required this.title, this.isCompleted = false});

void toggleCompletion() {
isCompleted = !isCompleted;
}
}

Here we have created a class called ToDoItem which has 2 variables and a public function that toggles isCompleted variable.

Controller

The ToDoController manages the list of to-do items and interacts with the ToDoItem model:

class ToDoController {
List<ToDoItem> toDoList = [];

void addItem(String title) {
toDoList.add(ToDoItem(title: title));
}

void toggleItemCompletion(int index) {
toDoList[index].toggleCompletion();
}

void deleteItem(int index) {
toDoList.removeAt(index);
}
}

We have created another class called ToDoController which holds all the functions to manipulate our toDoList.

View

The View in Flutter is simply the widget that displays the data. It listens to the controller for changes.

class ToDoView extends StatefulWidget {
@override
_ToDoViewState createState() => _ToDoViewState();
}

class _ToDoViewState extends State<ToDoView> {
late ToDoController _controller;

@override
void initState() {
super.initState();
_controller = ToDoController(); // Initialize the controller in initState
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('MVC To-Do List'),
),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
decoration: InputDecoration(
labelText: 'Add To-Do Item',
),
onSubmitted: (value) {
setState(() {
_controller.addItem(value);
});
},
),
),
Expanded(
child: ListView.builder(
itemCount: _controller.toDoList.length,
itemBuilder: (context, index) {
final item = _controller.toDoList[index];
return ListTile(
title: Text(
item.title,
style: TextStyle(
decoration: item.isCompleted
? TextDecoration.lineThrough
: TextDecoration.none,
),
),
leading: Checkbox(
value: item.isCompleted,
onChanged: (value) {
setState(() {
_controller.toggleItemCompletion(index);
});
},
),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () {
setState(() {
_controller.deleteItem(index);
});
},
),
);
},
),
),
],
),
);
}
}

Here we are listening to ToDoController controller to display toDoList and appending ToDoListItem. We are also using an inbuilt flutter function called setState((){}) to reflect changes to the UI.

Understanding MVVM (Model-View-ViewModel) in Flutter

What is MVVM?

MVVM stands for Model-View-ViewModel. It’s a refinement of MVC where a ViewModel replaces the Controller. The ViewModel is responsible for managing the state and acts as a bridge between the Model and the View. It allows for a clear separation of concerns and makes testing and maintenance easier.

Model: Manages the data.
View: Displays the data (widgets in Flutter).
ViewModel: Holds the state of the UI, handles business logic, and interacts with the Model.

In Flutter, MVVM can be implemented using state management tools like ChangeNotifier, Provider, or Riverpod. We will use ChangeNotifier & Provider for this example.

To-Do List Example in MVVM

Model

The ToDoItem model is the same as in the MVC example.

class ToDoItem {
String title;
bool isCompleted;

ToDoItem({required this.title, this.isCompleted = false});

void toggleCompletion() {
isCompleted = !isCompleted;
}
}

ViewModel

The ToDoViewModel manages the state and business logic. It extends ChangeNotifier to notify the UI when data changes.

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

class ToDoViewModel extends ChangeNotifier {
List<ToDoItem> toDoList = [];

void addItem(String title) {
toDoList.add(ToDoItem(title: title));
notifyListeners(); // This reflects the changes to the UI
}

void toggleItemCompletion(int index) {
toDoList[index].toggleCompletion();
notifyListeners();
}

void deleteItem(int index) {
toDoList.removeAt(index);
notifyListeners();
}
}

View

The View listens to changes in the ViewModel using ChangeNotifier Provider and updates the UI accordingly.

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

class ToDoView extends StatelessWidget {
final TextEditingController _textController = TextEditingController();

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('MVVM To-Do List'),
),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: _textController,
decoration: InputDecoration(
labelText: 'Add To-Do Item',
),
onSubmitted: (value) {
Provider.of<ToDoViewModel>(context, listen: false).addItem(value);
_textController.clear();
},
),
),
Expanded(
child: Consumer<ToDoViewModel>( // Spits out changes in a stream
builder: (context, viewModel, child) {
return ListView.builder(
itemCount: viewModel.toDoList.length,
itemBuilder: (context, index) {
final item = viewModel.toDoList[index];
return ListTile(
title: Text(
item.title,
style: TextStyle(
decoration: item.isCompleted
? TextDecoration.lineThrough
: TextDecoration.none,
),
),
leading: Checkbox(
value: item.isCompleted,
onChanged: (value) {
viewModel.toggleItemCompletion(index);
},
),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () {
viewModel.deleteItem(index);
},
),
);
},
);
},
),
),
],
),
);
}
}

Conclusion

Personally, I prefer the MVVM architecture because it offers a more robust and flexible approach to state management. It doesn’t constrain your app’s growth and ensures a clear separation of business logic from UI code — a crucial factor for maintainability and scalability as your application evolves

Keep Fluttering……

--

--

Abhishek Singh
Abhishek Singh

Written by Abhishek Singh

Hi, I am a flutter dev with 5+ years of experience, passionate about scalable apps and business ideas.