Flutter Architecture : Implement MVP Pattern

mcfly
mcfly
Aug 25, 2018 · 5 min read

MVP, MVVM… all different pattern but with a common purpose: to have a clean and easily testable code.
As you code for mobile or web, we want to split UI components from our business.

MVP for Model View Presenter

From wikipedia here is a quick definition of the MVP:

  • The model is an interface defining the data to be displayed or otherwise acted upon in the user interface.
  • The view is a passive interface that displays data (the model) and routes user commands (events) to the presenter to act upon that data.
  • The presenter acts upon the model and the view. It retrieves data from repositories (the model), and formats it for display in the view.

A design pattern star on Android

Google encourage MVP on its Android documentation, so this is a very common pattern on it. (STOP putting all your code in activities ! … )

Implement the MVP on Flutter

Flutter has no activity or fragment but the MVP pattern is still really interesting to architect our application code.
Here is a quick view of how it works.

Lets create a new flutter project with command flutter create mvp_pattern_tutorial

We have the flutter example code that show a button to push and increment a counter. Perfect for us, we are going to transform it.

Our presenter must stay as much as possible testable without any library. So lets create it for our demo.

import 'package:mvp_flutter_tutorial/ui/counter/viewmodel/counter_viewmodel.dart';

class CounterPresenter {
void onButton1Clicked() {}
}

class BasicCounterPresenter implements CounterPresenter {

CounterViewModel _counterViewModel;

@override
void onButton1Clicked() {
// TODO: implement onButton1Clicked
}
}

The presenter is responsible of what a button or any action on the UI will do on our ViewModel

Our main.dart, that will start our application will now look like this

import 'package:flutter/material.dart';
import 'package:mvp_flutter_tutorial/ui/counter/presenter/counter_presenter.dart';
import 'package:mvp_flutter_tutorial/ui/counter/views/counter_component.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(new BasicCounterPresenter(), title: 'Flutter Demo Home Page'),
);
}
}

You see now MyHomePage has a Presenter in it’s constructor.

Well now we want our View (MyHomePage) be as dumb as possible, and the presenter do the logic without using any UI library (not even flutter).
Here is the full view code.

import 'package:flutter/material.dart';
import 'package:mvp_flutter_tutorial/ui/counter/presenter/counter_presenter.dart';
import 'package:mvp_flutter_tutorial/ui/counter/viewmodel/counter_viewmodel.dart';
import 'package:mvp_flutter_tutorial/ui/counter/views/counter_view.dart';


class MyHomePage extends StatefulWidget {
final CounterPresenter presenter;

MyHomePage(this.presenter, {Key key, this.title}) : super(key: key);

final String title;

@override
_MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> implements CounterView {

CounterViewModel _viewModel;

@override
void initState() {
super.initState();
this.widget.presenter.counterView = this;
}

@override
void refreshCounter(CounterViewModel viewModel) {
setState(() {
this._viewModel = viewModel;
});
}

@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text(
'You have pushed the button this many times:',
),
new Text(
_viewModel?.counter.toString(),
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: new FloatingActionButton(
onPressed: () => this.widget.presenter.onButton1Clicked(),
tooltip: 'Increment',
child: new Icon(Icons.add),
),
);
}
}

Key points here are :
- we bind the interface of the view to the Presenter in the initState method
- on click we call the action “onButtonClick” of the presenter
- presenter now just have to increment the counter in the viewModel and call a refresh of the view

Our presenter implements an interface too. Imagine you want to do another same UI with just a few modification on how you handle events ? Easy you just create another implementation of the presenter.

So,
The presenter init the ViewModel on it’s constructor.
We have bind the CounterView interface on the presenter to refresh viewModel or show a message… But here still, we don’t have any flutter lib, we just know we have a method to do it, not the implementation.

import 'package:mvp_flutter_tutorial/ui/counter/viewmodel/counter_viewmodel.dart';
import 'package:mvp_flutter_tutorial/ui/counter/views/counter_view.dart';

class CounterPresenter {
void onButton1Clicked() {}
set counterView(CounterView value) {}
}

class BasicCounterPresenter implements CounterPresenter {

CounterViewModel _counterViewModel;
CounterView _counterView;

BasicCounterPresenter() {
this._counterViewModel = new CounterViewModel(0);
}

@override
void onButton1Clicked() {
this._counterViewModel.counter++;
this._counterView.refreshCounter(this._counterViewModel);
}

@override
set counterView(CounterView value) {
_counterView = value;
this._counterView.refreshCounter(this._counterViewModel);
}


}

Note : we could throw an error if the view is null on the refreshCounter call.

Just a counter here but we could imagine that attributes :
- messageViewModel
- errorViewModel
- delayBetweenClicks

class CounterViewModel {
int counter = 0;

CounterViewModel(this.counter);
}

Conclusion

Our demo works now with the MVP pattern, our code is cleaner, more testable and i love that.

The flutter exemple app works after moving to MVP pattern

Some people might think this is a huge lost of time, but this is mostly because you never had to maintain a big application. Respect this kind of architecture helps you grow your application big over the years, bring new developpers on it, test it easily (TDD if you want).

That’s all, will put the code on GitHub if anyone want, hope you enjoy it. If you have found this interesting don’t forget to clap ;).
If you have any improvement ideas, would love to discuss about it.

Edit :
Github repository: https://github.com/macfleid/flutter_mvp_design_tutorial

Next ?
You can check how to drive test with this pattern


The Flutter Pub is a medium publication to bring you the latest and amazing resources such as articles, videos, codes, podcasts etc. about this great technology to teach you how to build beautiful apps with it. You can find us on Facebook, Twitter, and Medium or learn more about us here. We’d love to connect! And if you are a writer interested in writing for us, then you can do so through these guidelines.

FlutterPub

The Pub(lication) for all about the Flutter and its magic

mcfly

Written by

mcfly

Currently co-creating my own dev agency dedicated to mobile applications.

FlutterPub

The Pub(lication) for all about the Flutter and its magic

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade