Comparing three app’s that use the MVC design pattern.
This is a continuation of the three-part series describing the use of the MVC design pattern when developing your Flutter apps. It comes in the form of a Flutter framework supplied by the Dart package, mvc_application.
Part 1 of the series incorporated this design pattern to the app, Stocks, originally included as an example app when you download Flutter. In this article, we’ll continue looking at this rewrite as well as two other apps also using the Dart package, mvc_application: weather_cast & contact services.
A repository containing the modified version of the Stocks app is of course available. I invite you to download the source code of all three example apps to follow along and get a better understanding of how the MVC design pattern implemented in each app.
Screenshots! Not Gists!
As always, I prefer using screenshots over gists to show code in my articles. I find them easier to work with, and easier to read. However, you can click/tap on them to see the code as a gist or in Github. Ironically, it’s better to read this article about mobile development on your computer than on your phone. Besides, we program mostly on our computers; not on our phones. For now.
In the Beginning
As in Part 1, we’ll take a quick look again at how all four apps start up. The original Stocks version supplied by Flutter is displayed last in the lower-right. Notice the remaining three files have a consistency in their appearance. That’s what design patterns do — provide consistency. In this case, it’s providing a consistent pattern as to how they start up an app.
Location! Location! Location!
The four screenshots below present the directory structures that make up each app. Each with their main.dart file highlighted with an little read arrow. Again, the original Stocks app is listed last. As you can see in that last screenshot, it’s the name of its files that give you an indication as to their contents if not to their function. Also note they’re all piled into the one directory, lib. Leaving only the files generated for Localization in their folder called, i18n. In the first three screenshots, however, you see there’s additional directories used to organize the source code. Again, you see consistency.
There’s A Pattern To It
Like many design patterns, MVC aims to separate ‘the areas of work’ or ‘the areas of responsibility’ when developing software. With MVC, this means separating ‘the interface’ from ‘the functionality’ from ‘the data’ that makes up an application. Like most things in Life, it’s generally better if you break up a project into its separate working parts. In the case of MVC, these three separate working parts are given specific names:
- Model — the source for data. In some implementations, it may also 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 events or user events, and, in many cases, contains the business logic.
The Lines Of Communications
Decoupling these three major areas in software allows for more modular code, for more code reuse and for parallel development. With a decoupled system, one can easily change any part of that system without effecting the other parts of the system. Of course, there has to be a means of communicating between them; a consistent API that, in the case of MVC, allows the View and the Controller to ‘talk to’ each other, as well as ‘talk to’ the Model through the use of public functions calls and public class properties.
Note, in many configurations, the View can ‘talk to’ the Controller; the Controller can ‘talk to’ the Model, but the View is unaware of the Model and vice versa enforcing even further decoupling of the system.
In many such instances, the Controller is likely the brains of the app. It reacts to events from the system or from the user’s input and dictates how such events effect the model’s data source as well as determine what data is eventually displayed in the View. The View and the Model are each tightly coupled to the Controller (i.e. The View knows how to ‘talk to’ the Controller, and the Controller knows how to ‘talk to’ the Model ), however each has no idea of the other’s existence. They do worked together, but indirectly — through the Controller.
Such an arrangement allows one, for example, to switch out the Model and put in a different one supplying a completely different data source for example without one character change to the remaining code. The new Model would only have to conform to the API requirements allowing the Controller to ‘talk to’ it correctly. So too with the case of the View. One should be able to switch out the View with little consequence to the app’s remaining code.
Everything In Its Place
Design patterns provide structure. They provide a means to organize your code allowing developers familiar with the design pattern to ‘hit the ground running’ when assigned to the project. A design pattern can organize your code, your files and even the directories that make up your project.
In the original Stocks example, everything is just in one directory. Now let’s take a look at the other three apps and their directories below. Now since you know the meaning behind the words, Model, Controller and View, I bet you can tell at a glance what files are concerned with what in these apps, and in what directories those files reside. See? Organized. Consistent. Concise.
“Break things up to make things easier,” is the simple phrase I can use here. You could possibly deduce the app depicted in the middle screenshot might be a little simpler than the others — there’s fewer directories. The other two apps have the directories: app and home. The directory, home, contains the ‘home screen’ and its accompanying code. There’s also the consistent use of three directories named, controller, model and view in all of the apps.
The first app, weather_cast, has separated the overall ‘app’ portion of the app from the ‘home screen’ portion of the app. The red arrow below highlights the class, Weather, that’s defined in a Dart file residing in the directory, home. It’s instantiated and passed to the ‘app’ portion of the app using the parameter, home. See below. Do you see the connection?
The class, WeatherApp, listed above is found in a Dart file stored in the directory, app. While the class, Weather, is found in the directory, home. See how that’s organized? In the first screenshot below, the class, WeatherApp, is found in a Dart file by the same name and is not only under the directory, app, but is actually stored in the directory, view. It’s very location describes its role in the app. It’s the start of the app (and so it’s under the app directory) but most of it does involve the interface (and so it’s under the view directory). Now, how about the class, Weather, assigned to that parameter, home?
The class, Weather, can be found defined in the Dart file, home.dart, and that file is located in the directory, view, under the directory, home. See below.
This class extends the class, Widget. Makes sense since the parameter, home, in the WeatherApp class, is of type, Widget. Like the home parameter in the MaterialApp widget, this parameter takes in a widget to be the ‘home screen’ of the app. As a matter of fact, the class, Weather, here is indeed assigned to the home parameter of a MaterialApp widget created deep in the MVC framework. And so, inline with its role, this class is also stored in a directory called, view. Note, however, the name of its Dart file is named, home.dart and not weather.dart. Why?! Why not?! It imposes some standards.
In fact, the other file, weatherapp.dart, should be named, app.dart. No? That’s a wonderful change from Java! In Dart, you don’t have to name the file after the class defined inside. In fact, the Dart library file can contain any number of classes, any number of high level functions, and any number of high level variables. That’s huge! Gives the developer options. Love it.
As it is, when it came to rewriting the Stock app using MVC, I decided to keep the file names used in the original Stocks app — to highlight their differences when it came to directory structure. But as you see in the second screenshot above, the two files indicated could have just as easily been named app.dart and home.dart respectively instead.
The Details They Tell
Looking again at the directory structure of the two app: weather_cast & stocks_mvc, the standard practice of grouping the source code in the appropriate directories (controller, model and view) allows you (or another developer) some time in the future to readily see what’s involved in different parts of app at a glance. Let’s take a look at the screenshots below.
You can readily see there’s a database (or data source) involved with the ‘home widget’ in the first app, weather_cast. That’s because you see there’s a directory, model, under the directory, home. I can also tell you at a glance there’s a Drawer widget displayed in the home widget because of the directory, drawer. Further, as you can readily see, there’s a ‘application-wide’ database in the other app, stocks_mvc. That’s because there’s no directory, model, under the ‘home’ directory, but under the directory, app. You know all this by simply looking at the directory structure. Organized. Concise.
All At A Glance
Let’s open up all the ‘MVC’ directories in each app now. All the source code that makes up the ‘Controller’, the ‘Model’, and the ‘View’ of each app is now readily on display below. Tell me, what do you see?
Well, knowing what we know about MVC, we then also know in the first screenshot, the class in the file, view/weatherapp.dart, can talk to the class in the file, controller/weatherapp.dart. We know the file, view/home.dart, can talk to the file, controller/home.dart, and the file, controller/home.dart, can talk to the file, model/weather_model.dart. We know all this by just looking at the directories. Such a relationship exists as well in the second screenshot.
What else do we know? We know there’s two separate views or screens involved with the ‘home’ widget. One is the home screen itself (home.dart) while there’s a secondary screen (location.dart) that appears to involve ‘locations.’ As I mentioned earlier, we can see the home widget uses a Drawer as part of the interface. There’s also a directory called, child, found with the home widget. The home widget’s build() function is listed below highlighting where the widgets found in this child directory are actually instantiated— many are assigned to a parameter named, child. See the connection now?
Below is the contents on that directory, child. You can see the name of each file, in this case, match up with the name of the residing class. As such, it allows you to see at a glance what class is defined where.
What’s In The Drawer
Looking at the second screenshot above, we see there’s a subdirectory found in the directory, drawer. The name of the directory is called, weather_locations. You can readily see it contains its own set of “MVC” components. So, what does that tell you? It tells you ‘weather locations’ contains its own screen, its own database and its own Controller. All with a glance. Nice.
Three Exports All Round
Now you’ve no doubt noticed the three Dart files consistently listed in each of the app’s src directories. What’s that all about, do you think? Well, it’s all about organizing your code, about allowing the expedition of development, and about the ease of code maintenance in future. All traits to aspire to when developing apps, no?
The approach I’m about to detail now was, in fact, conceived out of the Dart documentation itself, organizing a library package. In involves the common practice of using a single Dart file residing in the app’s directory, lib, to dictate what will make up it’s public API by ‘exporting’ still other Dart files hidden in the subdirectory, lib/src. This allows a user to get at an app’s functionality by importing just this one file or possibly just a few other files instead of importing many many Dart files. It provides a level of control as to what is publicly accessible when it comes to the app’s source code. This approach has also been applied when developing apps with this MVC framework.
Export The Weather
The first example app, weather_cast, has those three files under the directory, lib. As you may have guessed, one exports all the Controller code, one exports all the Model code, and one exports all the View code — or you could say, ‘exposes’, all the code in those three areas of responsibility that make up the app. All three are listed below. In fact, all three “MVC” export files from all three example apps are listed below.
What’s It All Good For?
The home screen in the first example app, weather_cast, is defined in the Dart file, view/home.dart. Below is a screenshot of its import statements. You can see it merely imports the three “MVC” files and gets on with it. The second screenshot introduces the show clause so to readily show you what particular classes are called upon in this particular Dart file. The idea is, while developing, just paste those three import statements in any new Dart file, and get on with your coding — you’ve supplied all the dependencies needed beforehand into those three files already. Get it? I’ll continue.
Looking below at other prominent Dart files that make up the example app, weather_cast, you see they too have those three import statements. Well, the first screenshot apparently doesn’t need to have access to the ‘model’ (data source) aspect of the app and so only has two, but that’s good too. You know what it uses; what it doesn’t. Again with the show clause, you can see what classes are involved in each file. Examining those clauses closely, and you’ll notice they’re different from the classes listed in the screenshots above.
Importing the same Dart files — yet using different classes! Sounds efficient.
This whole exercise is so to organize your source code, to expedite development and to ease its maintenance. You’ll find that weeks, months, or years later you can more readily ‘get reacquainted with the code’ with a simply glance at the import statements. It’s also helpful when the app is being maintained by ‘many different hands over time.’
With these three “MVC” files, you’ve categorized the source code into its three possible roles. Later, you or another developer can examine a particular file and readily and easily find the code requiring changes when necessary. Nice.
Looking below at two of the “MVC” files that make up the example app, weather_cast, even the directory paths themselves provide you with some insight as to how the app is organized and even what makes up the app. Again, you can see there’s some ‘child’ screens accompanying the ‘home’ screen; there’s a drawer found in the ‘home’ screen. There’s even Admob Ads included in the app. Lastly, this arrangement and the use of import statements (with their clauses ‘as’ and ‘show’) gives you control over what is accessible where. All three example apps utilizes this approach. Delete one of the lines as an experiment and see what happens; see for yourself the benefits of such files.
Those That Came Before
This article is an culmination of work conveyed in past articles. I would suggest reading two such articles, in turn, to get a more well-rounded appreciation of the approach taken to supply a framework to Flutter apps, and how it’s evolved in the past year alone. You’ll find the two example apps presented here are found in each.
In the third and final article of this series, we focus our attention more so on the Dart package itself, mvc_application. We’ll perform a code walkthrough much of the important aspects of this software framework. A framework designed to handle the more common functions and features of a Flutter app leaving the developer to work on the code that needs to get done…yesterday.