Overview
MVC (Model-View-Controller) is one of the most widely used architectural design patterns in software development. It separates an application into three interconnected components, making code more organized, maintainable, and testable.
The Three Components
| Component | Role | Responsibility |
|---|
| Model | Data layer | Holds business logic, data, and state |
| View | UI layer | Displays data; what the user sees |
| Controller | Logic layer | Bridges Model and View; handles user input |
How It Works
User Input → Controller → Model (update data) → View (re-renders)
- User interacts with the View (e.g., taps a button)
- The View notifies the Controller
- The Controller updates the Model
- The Model notifies the View to refresh
- The View re-renders with new data
MVC in Flutter (Example)
Model
class UserModel {
String name;
String email;
int age;
UserModel({
required this.name,
required this.email,
required this.age,
});
// Business logic lives here
bool isAdult() => age >= 18;
// JSON conversion
factory UserModel.fromJson(Map<String, dynamic> json) {
return UserModel(
name: json['name'],
email: json['email'],
age: json['age'],
);
}
}
Controller
class UserController {
UserModel? _user;
UserModel? get user => _user;
// Fetch user from API
Future<void> fetchUser(int id) async {
final response = await ApiService.getUser(id);
_user = UserModel.fromJson(response);
}
// Update user name
void updateName(String newName) {
_user?.name = newName;
}
bool isUserAdult() => _user?.isAdult() ?? false;
}
View
class UserView extends StatefulWidget {
@override
_UserViewState createState() => _UserViewState();
}
class _UserViewState extends State<UserView> {
final UserController _controller = UserController();
@override
void initState() {
super.initState();
_loadUser();
}
Future<void> _loadUser() async {
await _controller.fetchUser(1);
setState(() {}); // Re-render with updated data
}
@override
Widget build(BuildContext context) {
final user = _controller.user;
return Scaffold(
appBar: AppBar(title: Text('MVC Example')),
body: user == null
? CircularProgressIndicator()
: Column(
children: [
Text('Name: ${user.name}'),
Text('Email: ${user.email}'),
Text('Adult: ${_controller.isUserAdult()}'),
],
),
);
}
}
Pros and Cons
| Pros | Cons |
|---|
| ✅ Clear separation of concerns | ❌ Controller can become too large ("Massive Controller") |
| ✅ Easy to understand for beginners | ❌ View and Controller can be tightly coupled |
| ✅ Easy to test Model independently | ❌ Not ideal for complex Flutter state management |
| ✅ Industry standard pattern | ❌ Hard to scale in large apps |
MVC vs Other Patterns in Flutter
| Pattern | Key Difference |
|---|
| MVC | Controller connects View ↔ Model |
| MVP | Presenter is more decoupled than Controller; View is passive |
| MVVM | ViewModel uses data binding; View auto-updates |
| Clean Architecture | Multiple layers with strict dependency rules |
Note: Pure MVC is not commonly used in Flutter. Most Flutter apps use MVVM (with Provider/Riverpod) or BLoC pattern instead, which work better with Flutter's reactive UI model.