IIn keeping with the “KISS Principle”, this attempts to offer the MVC design pattern in an intuitive fashion incorporating much of the Flutter framework itself.

All in one lone Flutter Package:

Some Github examples right off the hop!

Right-click and save these examples as bookmarks. You may want to try out these examples later. They use this MVC implementation.

Counter app example — the counter app first created with a new Flutter project. This one, however, will use the MVC package. Startup_namer example — ‘Your First Flutter App’ but with this package.

Forget Everything I’ve Told You!

By the way, if you’ve been following me on Medium.com, forget the other two articles I wrote on the subject! Don’t read them! (Well, maybe the first bit of An MVC approach to Flutter.) I’ve moved on since then having learned a great deal and experimented on how to marry MVC with Flutter. I’ve made a Flutter Framework that I now use with all my apps! I’m satisfied for now. So much so, I’ve made it into this package and use it as the basis for all my apps.

I Like Screenshots. Click For Gists.

As always, I prefer using screenshots over gists to show concepts rather than just show code in my articles. I find them easier to work with. However, you can click/tap on them to get at the code in a gist or in Github if you must. Ironically, it’s better to read this article about mobile development on your computer than on your phone. Besides, we program on our computers; not on our phones. For now.

No Moving Pictures No Social Media

Note, there will be gif files in this article demonstrating aspects of the topic at hand. However, it’s said viewing such gif files is not possible when reading this article on platforms like Instagram, Facebook, etc. Please, be aware of this and maybe read this article on medium.com

Let’s begin.

Like many design patterns, MVC aims to separate ‘the areas of work.’ It’s meant to separate ‘the areas of responsibility’ when developing software. With MVC, this means separating ‘the interface’ from ‘the functionality’ from ‘the data’ that makes up the application. To decouple these three major areas in the software allows for efficient modular coding, code reuse, and parallel development:

Model — the source for data. In some implementations, it contains the business logic.

View — concerned with how data (not necessarily that in the Model) is displayed in the user interface.

Controller — controls what data is displayed, answers to both system or user events, and, in some implementations, contains the business logic.

“The model is. The view shows (what the model is). The controller changes (what the model is or what the view shows).” — Jon Purdy Jun 8 ’11 at 1:51

The version of MVC conveyed in this implementation is actually more of a PAC (Presentation-abstraction-control) pattern instead of the classic MVC. In the PAC pattern, the Controller contains the business layer while the Model is not explicitly defined but an abstraction that ‘stores data.’ The code in the ‘View’ does not directly affect or interfere with the code involved in the ‘Model’ and vice versa. While the ‘Controller’ controls the ‘state’ of the application during its life cycle. The View can ‘talk to’ the Controller; The Controller can ‘talk to’ the Model, but again, the View is unaware of the Model and vice versa.

However, this particular MVC implementation allows one to also implement the more classical pattern. One that allows the View and the Controller to ‘talk to’ each other, as well as ‘talk to’ (by calling public functions and public properties) the Model.

By Design, You Chose the Design

It’s to the developer to pick the appropriate approach depending on the circumstances presented to them during development. That’s what design patterns, frameworks, architectures, or whatever you want to call them are for. They are to allow you to organize the code, to impose your chosen structure, to allow for some conformity and some consistency.

The idea is that doing so will then allow a developer new to the development team, who is familiar with this package or at least familiar with the MVC design pattern, to walk in and already know where all the code is. The developer, with task in hand by their manager, can ‘hit the ground running’ and go straight into the application knowing how the code is arranged and how the boilerplate works — what little boilerplate there is in this case.

Note, there’s no Model

Note, I’ve purposely left any implementation of a Model out of this implementation of the MVC. Again, more in keeping with the PAC design pattern. You won’t find the concept of a Model in this package. There’s a ‘View Class’, and there’s a ‘Controller Class’, but there’s no ‘Model Class.’ If and how a Model is implemented, I leave to the developer.

When it comes to a Model, you may even choose to implement a hybrid of sorts (and all that entails). For example, one Dart file could contain what would be defined as both the Controller and the Model.

Maybe your app has no data! Maybe your app has lots of data! (i.e. any number of Models). Who knows! This package will allow for that — by not caring either way frankly.

The last graphic demonstrates another possible purpose for a Model. There may be times your app works with another app’s data source. Your Model serves to ‘convert’ the other app’s data to a format suitable to your app.

This is actually demonstrated by this library package contribution to the Flutter Architecture Samples Project. You’ll find the model in the MVC Example merely ‘relays’ the data requests to models used by yet another architecture design pattern. In this case, the Scoped Model.

In Flutter, There Are Many Views

From the beginning, the intent for this package was not only to allow the developer the freedom to write their code in the best way to meet their needs but also in a way that best utilizes the underlying Flutter framework.

In Flutter, a ‘Navigator’ is assigned its routes and is used to go ‘up the tree’ of Widgets to view one screen after another. Using this framework, a Controller can ‘follow along’ the list of possible Widgets (Views). Note, however, it can only access one View at any one time. In other words, a Controller can be assigned to any number of Views in the course of its own lifecycle, each one representing a Widget with its build() function, but can only directly access the ‘last’ View assigned. Understand? Don’t worry, this will be demonstrated in subsequent articles.

Note also, as any good MVC design should, you are allowed as many Controllers as you like to have access to a particular View. Each ready to react to any ‘events’ that originate from that View. Oh yes, there are many events fired in Widgets!

And so, if there is a number of Controllers assigned to a View, and when ‘an event’ occurs in that View, the Controllers will fire, in turn, in the order they were assigned.

Again, there are events fired in the course of a Flutter app’s lifecycle. See Listeners below.

How Does It Work?

So, how do you use this package? Well, let’s fall back on the ol’ ‘Counter app’ first introduced to you every time you create a new Flutter project. To utilize the package in the Counter app, I changed three things. I extended the Class, _MyHomePageState, with the Class StateMVC, I introduced a ‘Controller’ Class that extends ControllerMVC, and I then introduced a static instance of that Controller Class to the build() function. Done!

Now, of course, there’s a lot of things going on here, but you don’t see that. As any good library package should, a lot is kept in the background. At a glance, however, you can see there is now a separation of the ‘Interface’ and the ‘data.’ The build() function (the View) is concerned solely with the ‘look and feel’ of the app’s interface — how things are displayed. In this case, it is the Controller that’s concerned with what is displayed.

What data does the View display? It doesn’t know nor does it care. It ‘talks to’ the Controller instead. Again, it is the Controller that determines ‘what’ data the View displays. In this case, it’s the app’s title and a counter. Even when a button is pressed, the View turns to the Controller to address the event by calling one of the Controller’s public functions, incrementCounter().

How Do Things Relate?

Notice that in this arrangement, the Controller is ‘talking back’ to the View by calling the View’s function, setState(), to tell it to ‘rebuild.’

Maybe you don’t want that. Maybe you want the View to be solely concern with the interface, and it alone determines when to rebuild or not. It’s a simple change.

You see the function, setState(), is now called in ‘the View’ (in the build function) and not in the Controller. Doing so does separate the ‘roles of responsibility’ a little more, doesn’t it? After all, it is the View that’s concerned with the interface. It may know best when to rebuild, no? Regardless, with this package, such things are left to the developer. Also, notice what I did with the app’s title? I created a static String field in the MyApp class called, title. It is the app after all. It should know its own title.

How about Model?

Currently, in this example, it’s the Controller that’s containing all the ‘business logic’ for the application. However, in some MVC implementations, it’s the Model that contains the business rules for the application. So what would that look like? Well, maybe it could look like this:

I decided to make the Model’s API a little cleaner with the use of static members. As you can see, the changes were just made in the Controller. The View doesn’t even know the Model exists. It doesn’t need to. It still ‘talks to’ the Controller, but it is now the Model that has all the ‘brains.’

However, what if you want the View to talk to the Model? Maybe because the Model has zillions of functions, and you don’t want to write the very same functions in the Controller only ‘to relay’ the Model’s functions and properties over to the View. You could simply provide the Model directly to the View. The View could then calls the Model’s properties and functions.

Not particularly pretty. I would have just kept the Static members in the Model, and have the View call them instead (or not do this at all frankly), but I’m merely demonstrating the possibilities. With this MVC implementation, you have options, and developers love options.

How Does It Works?

When working with is MVC implementation, you generally override two classes: The Controller (Class ControllerMVC) and the StateView (Class StateMVC). Below is a typical approach to overriding the Controller.

The Controller has ‘direct access’ to the View (aka. the Class StateMVC). This is represented by the property, stateMVC, in the Class, ControllerMVC. In this example, a ‘static’ reference to the View. A ‘static’ reference to the Controller itself is made as well, in the Constructor. This example conveys a generally good approach when dealing with the application’s Controller because you then have easy access to your Controller throughout the app. It’s now in a static field called con. See how it’s easily accessed now in another Dart file below:

Now, in any View or any Widget for that matter, you can access the app’s data or business logic easily and cleanly through its Controller. For example, you can then reference the Controller in a build() function using the field, con.

Note, the ‘StateView’, like the State Class is abstract and must be extended to implement its build() function. So, in fact, ‘the View’, in this MVC implementation, is simply the State’s object’s build() function! Surprise!

The Controller

Note, the Controller class (ControllerMVC) looks pretty thin at first glance. I mean the code you see there is only if a ‘StateMVC’ object is passed to the constructor. The Controller is then ‘assigned’ that State object. In most projects, the Controller will serve as your ‘workhorse’ containing most of the ‘logic’ for the app.

Set the State

The StateMVC class, of course, has the setState() function. It’s a State object after all. However, with this MVC implementation, you also have the setState() function in your Controller! That will prove to be very useful in development.