Question #70EasyArchitecture

What is MVP ?

#mvp

Answer

Overview

MVP (Model-View-Presenter) is an architectural pattern derived from MVC. It improves on MVC by making the View passive and completely decoupling the Presenter from any Android/Flutter UI framework, making it highly testable.


The Three Components

ComponentRoleResponsibility
ModelData layerBusiness logic, data fetching, state
ViewUI layerRenders UI; delegates all logic to Presenter
PresenterLogic layerRetrieves data from Model; updates View via interface

Key Difference from MVC

In MVC, the Controller can directly manipulate the View. In MVP, the Presenter never directly touches the View — it only communicates through an interface (contract).

text
User Input → View → Presenter → Model
                ↑         ↓
           View Interface (IView)

MVP in Flutter (Example)

1. Define the Contract (Interface)

dart
// View interface — what the Presenter can tell the View to do
abstract class IUserView {
  void showLoading();
  void hideLoading();
  void showUser(UserModel user);
  void showError(String message);
}

2. Model

dart
class UserModel {
  final String name;
  final String email;

  UserModel({required this.name, required this.email});
}

class UserRepository {
  Future<UserModel> fetchUser(int id) async {
    // Simulate API call
    await Future.delayed(Duration(seconds: 1));
    return UserModel(name: 'Alice', email: 'alice@example.com');
  }
}

3. Presenter

dart
class UserPresenter {
  final IUserView _view;
  final UserRepository _repository;

  UserPresenter(this._view, this._repository);

  Future<void> loadUser(int id) async {
    _view.showLoading();
    try {
      final user = await _repository.fetchUser(id);
      _view.showUser(user);
    } catch (e) {
      _view.showError('Failed to load user: $e');
    } finally {
      _view.hideLoading();
    }
  }
}

4. View (Flutter Widget)

dart
class UserScreen extends StatefulWidget {
  
  _UserScreenState createState() => _UserScreenState();
}

class _UserScreenState extends State<UserScreen> implements IUserView {
  late UserPresenter _presenter;
  bool _isLoading = false;
  UserModel? _user;
  String? _error;

  
  void initState() {
    super.initState();
    _presenter = UserPresenter(this, UserRepository());
    _presenter.loadUser(1);
  }

  
  void showLoading() => setState(() => _isLoading = true);

  
  void hideLoading() => setState(() => _isLoading = false);

  
  void showUser(UserModel user) => setState(() => _user = user);

  
  void showError(String message) => setState(() => _error = message);

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('MVP Example')),
      body: _isLoading
          ? Center(child: CircularProgressIndicator())
          : _error != null
              ? Center(child: Text(_error!))
              : Center(child: Text('Hello, ${_user?.name}')),
    );
  }
}

Pros and Cons

ProsCons
✅ Highly testable — Presenter has no UI dependency❌ More boilerplate (interfaces needed)
✅ View is completely passive (easy to swap)❌ One-to-one View ↔ Presenter coupling
✅ Clear separation of concerns❌ Can still become complex in large apps
✅ Good for unit testing Presenter❌ Harder to reuse Presenter across multiple Views

Testing the Presenter (Unit Test)

dart
class MockUserView implements IUserView {
  bool loadingShown = false;
  UserModel? displayedUser;

   void showLoading() => loadingShown = true;
   void hideLoading() => loadingShown = false;
   void showUser(UserModel user) => displayedUser = user;
   void showError(String message) {}
}

void main() {
  test('UserPresenter loads user correctly', () async {
    final mockView = MockUserView();
    final presenter = UserPresenter(mockView, UserRepository());

    await presenter.loadUser(1);

    expect(mockView.displayedUser?.name, 'Alice');
    expect(mockView.loadingShown, false); // hideLoading was called
  });
}

MVC vs MVP vs MVVM

FeatureMVCMVPMVVM
View dependencyController knows ViewPresenter uses interfaceViewModel has no View reference
TestabilityModerateHighHigh
Data bindingManualManualAuto (reactive)
BoilerplateLowMediumMedium-High
Flutter fit⚠️ Okay✅ Good✅✅ Best

In Flutter, MVP is a solid, testable pattern. However, MVVM with

text
ChangeNotifier
,
text
Riverpod
, or
text
BLoC
tends to be more idiomatic because Flutter's reactive model is built for data binding.