MVC in Flutter Part 1

Greg Perry
Flutter Community
Published in
16 min readJul 26, 2019

Using MVC Design Pattern in Flutter’s Stocks app example.

When you download Flutter, some example apps are downloaded along with the SDK. In the directory where you installed Flutter, you’ll find a directory called, examples. In there, you’ll find yet another directory called, stocks. The Stocks app is an example of an app written in Flutter, and in this article, we’re going to review it — but with a twist.

MVC
Stocks Example App

We’re going to review the original Stocks example app and the one that I then rewrote. I rewrote the Stocks app to instead incorporate the MVC design pattern and renamed it, Stocks_MVC. You see below there’s little difference between the two.

You’ll notice the ‘debug’ banner is turned off in the MVC version displayed on the right. That simply means the parameter, debugShowCheckedModeBanner, passed to a MaterialApp widget was set to false. Of course, you have access to the Stocks_MVC repository for you to review.

I Like Screenshots. Click For Gists.

As always, I prefer using screenshots over gists to show concepts rather than just showing 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.

Let’s begin.

Other Stories by Greg Perry

The MVC Approach

As you may know, the MVC design pattern is an explicit effort to separate three aspects of a software application. In this case, Model-View-Controller describes the separation of the app’s data (Model) from the app’s interface (View) from the app’s logic (Controller). If you haven’t already, there’s an article explaining my interpretation of MVC in Flutter for your reference:

Flutter + MVC at Last!

The Stocks app was rewritten using the Dart package, mvc_application. This package serves as an application framework offering common functions and features found in a typical Flutter app yet utilizing the MVC design pattern.

Dart Package mvc_application

Look and Compare

In this article, we will walk through this new Stocks app and do a code comparison of this variation with the original version included with Flutter. The goal here is to highlight the benefits gained when using the MVC design pattern. Frankly, the benefits gained when using any design pattern as opposed to simply implementing the ‘vanilla’ version represented by Flutter’s example apps.

Their Beginnings

Let’s take a look at each app’s main point of entry. The main.dart file. Note, it doesn’t have to be given that name. As long at there’s a main() function inside, the file could have been named, albuquerque.dart, for all that matters. Their main() functions are listed in screenshots below.

main.dart in Stock and main.dart in Stocks_MVC

It is a little deceiving. The second screenshot is that of the main.dart file for the Stocks_MVC application. The first is of the original Stocks app, and it looks like there’s not much to the original’s main.dart file. However, that’s only because I took a screenshot of only its main() function — so to compare the ‘entry point’ of both versions side-by-side.

There’s a lot more to that file in fact. Since the Stocks app example is a rather simple app, the writers chose to place much of its code in the main.dart file leaving the main() function to be listed at the very end.

main.dart file in Stocks Flutter app example.

With the MVC version, however, what you see is what you get in its main.dart file. I’ve included the show clause in its import statements to give you insight as to what class is coming from where. What you see in that main.dart file is pretty much the extent of the code found in all the main.dart files in all my apps using the MVC framework, mvc_application.

Run The App

The StocksApp class is passed to the runApp() function in the original Stocks example app. The MVC version, of course, uses a framework and so extends the class, App, to then implement the createView() function.

main in Stocks and main in Stocks_MVC. Both call a class called StocksApps

A little more work there, but that’s quickly justified when comparing the StocksApp class in each version respectively. As you see below, much of the code in the original version has gone away in the MVC version — taken up by the underlining framework. Hence, the StocksApp class is shorter in length in the MVC version.

Comparing each of the screenshots below, you can find what they have in common. The red arrows highlight where both define their app’s title as well as their routes for example. As for the MVC version, however, the rest of the code has been distributed to other parts of the app or is delegated to the underlining MVC framework itself.

For example, you’ll note the MaterialApp widget called upon near the end in the first screenshot. The MVC version also has a MaterialApp widget, but it’s deep in the MVC framework. Up here in the second screenshot, however, you can still pass in MaterialApp parameter values. Either as a parameter to the parent constructor or by overriding particular functions.

StocksApp class in Stocks and StocksApp class in Stocks_MVC

Going Home

Now let’s go to the ‘home screen’ for each version. Both have a StockHome class that displays the home screen. Below is a screenshot of the StockHome class found in the MVC version. Like the original, it’s defined in the file, stock_home.dart. Now like in any design pattern, the fundamental approach of MVC is to ‘break up’ code into separate parts based on the specific areas of responsibility described earlier: Model-View-Controller.

Below is the result of such an attempt. What you see below is a file containing the ‘View’ aspect of the home screen. However, in the class’ constructor, you see the ‘Controller’ aspect of the home screen is being injected into the code. Therefore, looking closely at the build() function below, you can see it’s the ‘Controller’ that is supplying the data to be displayed by this View.

StockHome class

Below is a screenshot of the file above, but now listed on the right alongside the stock_home.dart file from the original Stocks version. In this case, much of the code found in the original version has been split out in the MVC version into the ‘Controller’ being injected in from the constructor. Again, the pursuit here is to separate the code into its primary areas of responsibility.

It’s All By Degree

Now there’s a degree in which you can delegate here in Flutter. Flutter is made up of Widgets you see. Widgets taking in Widgets. To demonstrate, the StockHome class is listed again in two variations below. Note, both produce the exact same home screen, and yet looking closely, you can see the screenshot on the right has the Controller object doing a little more of the ‘presentation’ of data than the version displayed on the left.

build() function in Stocks_MVC

You can see the right-hand version is supplying the ListTile widgets while the left-hand version appears to be more of the ‘pure’ MVC approach. Instead, it’s dictating what particular data item to display and what to do when events occur (i.e. a title and an event handler). In Flutter, with all its Widgets, there’s this ‘degree of delegation’ available to you. That degree will depend on whether you’re a lone developer or part of a team of developers.

A Team Player

To what degree then does the ‘View’ team take up responsibility in how and what data is displayed? How much responsibility is then left to the ‘Controller’ team when it comes to how and what they display? Interesting, no?

I mean, this is a simple app — maybe too simple to convey what I’m trying to explain here, but when using Flutter, there’s the ability to set the degree to which the ‘areas of responsibility’ are broken up. I mean, look below at a third variation of the StockHome class. In this version, it would appear the ‘View’ team merely has control over the Scaffold widget while the primary components or attributes of the Scaffold widget are all supplied by the ‘Controller’ team. Regardless, this results in the exact same home screen. Do you understand what I’m getting at here?

build() ver. 2 in Stocks_MVC

It is a profound degree of decoupling, isn’t it? The View here supplies the means to display a Scaffold widget, but the Controller supplies the appBar, the drawer, and the body to name a few. Does the View know their contents? No. Does it have to? No. Does this allow for the data to be readily switched out without affecting this code? It most certainly does. Maybe this is the degree in which the View and the Controller should separate responsibility? Maybe.

Looking below, I’ve listed all three variations just to convey the ‘amount of code’ found in each variation of the View. In other words, it conveys how much of the responsibility the Controller takes up in what and how the data is displayed. This is not a conundrum, but a wonderful feature of MVC in Flutter! So to what degree are you then to delegate between the View and the Controller? I would say, it depends on the situation. This approach allows you options, and we developers just love options! Right?

I would suggest a large app and or a large development team would benefit from the left-most approach listed above. It allows for more refined control and a higher gradient between ‘the data’ and ‘how it’s displayed.’ The left-most approach allows for the ‘View’ team more room to play with the interface while it’s the right-most approach that allows the ‘Controller’ team more room to dictate what to display and how to display it. The right-most approach is maybe more appropriate for smaller apps and or smaller teams. Yes? No? Regardless! You’ve got options! We developers love options!

Get Your Data

Let’s see how the View’s data is conceived from its corresponding Controller. As you may have guessed looking below, the use of getters is involved here. For example, looking at the getter, con.widget.appBar, we’ll examine what exactly is involved in bringing this expression about.

build() ver. 2 in Stocks_MVC

See below. The getter, widget, is retrieving a ‘library-private’ variable containing the object for the class, _Widgets. The class is named with a leading underscore, and so you know that this class also resides in the very same library file, controller/stock_home.dart.

StockHome class

Looking further up the class in question, we see where that ‘library-private’ variable, _widget, is being initialized. At a glance, you can see the class, StockHome, is a Controller object (extends ConrollerMVC), and its initState() function is instantiating a number of classes passing itself to their constructors.

Also, note the Singleton pattern is being implemented here. The factory constructor is used to ensure only one instance of this class. After all, is there a need to have more than one instance of this Controller in this app? No.

StockHome class

In the screenshot below, the first portion of the class, _Widgets, is displayed. You can see the getter, appBar, is conceived here. Note the many getters returning the generic type, Widget. (It’s all Widgets! Allowing you to switch things out easier) You can see in the constructor below, there’s still further classes created that are then referenced in getters. All are being passed a reference to the Controller class, StockHome, listed above. The plot thickens!

StockHome class

The last five getters above access the properties from the class, _ListTiles. For example, the value,con.widget.stockList, is a getter retrieving the property, stockList, from the class, _ListTiles. See below.

StockHome class

And so, in the second variation of the View, you now see where the text, ‘Stock List’, is coming from. It’s merely an example of how the Controller is delegated by a specific degree to supply ‘what’ and ‘how’ data is displayed.

Again, you’re free to turn the delegation up a notch! For example, the screenshot below lists the very same build() function in the screenshot above. However, it’s changed! There’s no widget displaying, ‘Stock List’, anymore. That’s instead deep in the Widget,con.widget.drawer , passed to the parameter, drawer. However, the exact same drawer is displayed as you see above. Now, why would I do that? Well, I’ll tell you.

StockHome class

All this makes for a ‘clean’ API between the View and the Controller. With this last variation, for example, you can leave the View code alone as the app changes and evolves. Weeks, months, years later, it may be a completely different data source supplied by the backend, (through the Controller) and the View is none the wiser. All the Views knows is that there are Controller properties: scaffoldkey, appBar, floatingButton, drawer, and body. All named after the named parameters for its Scaffold widget, and that’s all it needs to know. Decoupled. Versatile. Manageable. The Flutter’s own API is used as the API for the Model, for the View, and for the Controller. Nice.

Retrieve the Data

The HttpClient is used in both versions to actually retrieve the current stock prices for a finite list of assets. In fact, the MVC version uses the very same code responsible for doing so and has not changed it at all from the original. In other words, the very code below is the exact same in both versions — reflecting again the ability of the MVC design pattern to decouple its areas of responsibility. Months from now, for example, we could switch out this HttpClient for something else.

_fetchNextChunk in Stocks and _fetchNextChunk Stocks_MVC

It’s All In The Delivery

Let’s compare the two versions by listing their corresponding build() functions. Both screenshots have highlighted with a little red arrow the location where the data is retrieved from the Internet. As you can see in the MVC version, the data is retrieved from the Controller.

StockHome in Stocks and StockHome in Stocks_MVC

In the MVC version, again, there is the delegation of work split up into its three areas. And so, in this case, it is the ‘app’ controller where the data comes from. With that, the three screenshots below actually make up the getter, con.widget.marketTab. In the first screenshot, you see the ‘app’ controller is calling its own getter, stocks. This getter returns the instantiated object for the class, StockData. And again, the class StockData, is left unchanged from the original Stocks app, and so the getter, allSymbols, is used to retrieve the data.

home/controller/stock_home.dart StockHome class and app/controller/stocks.dart AppStocks class and app/model/stock_data.dart StockData class

Looking back at the original version, you can see the StockData class is indeed the same one used in the MVC version. The difference being the intermittent Controller is introduced in the MVC version allowing for further decoupling.

StockHome class and StockData class

Develop And Compare

Let’s compare the directory structure of this MVC version of the Stocks app with the original Stocks app. Each file found in the new version continues the same function as in the original version. However, unlike the original version, they’ve been placed in the directory that best represents their function in the app and not just all stored together in one directory.

The first screenshot above is the MVC version. The common practice of ‘hiding’ Dart code in the directory, src, is utilized in this approach. Further, you can see at a glance, there are two additional directories, app, and home. With these two directories, the distinction is being made for code used just for the ‘home screen’ and for code used for the whole app overall. We’ll get to the three “MVC” files, controller.dart, model.dart and view.dart, a little bit later.

Let’s open up those two directories, app and home, in the MVC version and reveal their contents. You can see there are the additional directories, controller, model, and view in those directories. Well, there are only two in the directory, home. However, in the directory, app, these three directories categorize and describe the role played by the source code.

Like any good design pattern, the MVC approach strives to organize the source code into the three aforementioned areas of responsibility. You can see the implementation taken even has the directory structure arranged in such a way to readily display the three areas involved in different parts of the app.

The only ‘additional’ files made in the MVC version were the files, stocks.dart, and stock_home.dart. Each is found in their respective ‘controller’ directories, and each is splitting out the code found in the original version’s main.dart and stock_home.dart files respectively. The code is organized into directories, and not all just in one directory.

The Setup

Let’s go back again to the StockApp class. In both versions, it is this class that is passed to Flutter’s runApp() function. Further, in both versions, it is here where the app’s overall theme is set up. The app’s localization settings are established here, the routes to be taken are defined here, and the means of configuration of the app is established here. All are highlighted by arrows below.

In the original version, there is a class created to perform the configuration in the app’s settings screen. It’s highlighted by the first red arrow below and is called, StockConfiguration. The first red arrow on the second screenshot highlights the means to configure the app in the MVC version. It’s the Controller object for the whole app itself and is called, AppStocks.

StockApp class and StockApp class

How You Configure

Below, we again see the original StockApp class, but beside it, in the second screenshot, is the AppStocks class for the MVC version. It is in the AppStocks class where the initState() function is called to access the app’s data source, StockData. It is also in this class where, like the original StockApp class, you can retrieve the current theme for the app.

StocksAppState class and AppStocks class

Note, I’ve replaced the StockConfiguration class with the use of the AppStocks class in the MVC version. I decided to keep the file, stock_types.dart, to allow for an implementation comparison, but you’ll see in the MVC version I’ve literally commented out the ‘configuration’ mechanism utilized in the original Stocks app. It’s not needed. Besides, I didn’t particularly like its implementation.

stock_types.dart and stock_types.dart

So when you’ve changed one of the app settings, both versions now have their own approach to execute and to reflect that change.

It makes sense that such events are addressed by the ‘Controller’ in the Stocks_MVC example app. Unlike the original Stocks app, instead of jury-rigging a ‘configuration’ solution with a ‘StockConfiguration’ class and ‘Updater’ class, the App’s controller is in a convenient position to work with the app’s settings.

As demonstrated in the screenshot above, the setting, stockmode, is used to convey either an optimistic or pessimistic mode (White interface for optimistic; a Black interface for pessimistic). Looking below, the MVC approach in the second screenshot is pretty straightforward while the original version makes the extravagant effort to create a whole new copy of the ‘configuration’ object to reflect this new setting.

StockHomeState class & onChanged() and StockHomeState class & onChanged()

The MVC version makes for a more straightforward approach overall. In the first screenshot below, I’ve preserved the function names called in the original Stocks example when making a change in the settings. In the original, the two interim components, widget.configuration, and widget.updater, would then be called to assign that change. The setState() function is eventually called in the underlining State object to then reflect that change. In the MVC version, it’s more straightforward. We see in the second screenshot the underlining State object is called directly to assign the change (eg._state?.showSemanticsDebugger = v) and then reflect that change (eg._state?.refresh()) in the app.

app/view/stock_settings.dart StockSettings class and app/controller/stocks.dart AppStocks class

What’s Next

In Part 2, we further examine the MVC design pattern as it is interpreted in the Flutter framework, mvc_application. We continue examining the example app, Stocks, but we will also look at two other apps written with the MVC framework. We’ll review them and compare them to the Stocks app as well as with each other.

MVC in Flutter Part 2

Cheers.

→ Other Stories by Greg Perry

DECODE Flutter on YouTube

--

--