A Design Pattern for Flutter

MVC
Weather App with “mvc_pattern”

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.

Other Stories by Greg Perry

The MVC Approach

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 as it was implemented in the Flutter package, mvc_pattern:

Flutter + MVC at Last!

An MVC Example

Now in this article, the three aspects of the ‘sample app’ are separated even further — incorporating and integrating even further Flutter’s own API and framework. This sample app has its own Github repository called, Weathercast, and is also available on Google Play.

Structure By Design

Design patterns provide structure. They provide a means to organize your code. Doing so, it’s hoped, allows developers familiar with the design pattern to ‘hit the ground running’ when assigned to a project. We’re going to see how a design pattern can help organize your code, your files, and even the directories that make up your project.

An MVC Directory Structure

In this sample app, the MVC design pattern is even conveyed in its directory structure. Looking at the three screenshots below, you’re looking at the directory structure that stores the sample app. Each screenshot is the previous one but with subdirectories opened further. Notice there are some files and even directories named, model, view, and controller. Therefore, even at a glance, you know what ‘type’ of code is in what file and in what directory.

The directories of a Flutter project

Export MVC

Note, in the last two screenshots above, the three library files, model.dart, view.dart, and controller.dart are highlighted with little red arrows. They’re now listed below, and you’ll discover each contains a list of export statements. Each represents the ‘three aspects’ of the MVC design pattern. With that, guess what’s contained in the library file, controller.dart. Look closely.

controller.dart and model.dart and view.dart

Everything In Its Place

So, all the source code located under a ‘controller’ directory is listed in the library file, controller.dart. All the source code considered part of ‘The View’ in this app is found in the library file, view.dart. Finally, the file, model.dart, contains references to all the code concerning the app’s data. All this organizing of the code in this way is so to expedite the development process. Time is money after all.

Get On With It

As an example, you see those three files listed above now referenced in the import statements highlighted by three red arrows in the screenshots below. While developing, the practice of automatically supplying these three import statements right away after you create a ‘new Dart file’ ensures you as a developer that you’ll have all dependencies necessary to get on with your work. Near the end of development, I would then include the ‘show’ directive for future reference (the second screenshot). With that, weeks, months, years from now, I or another developer can then return to this library file, and readily see the dependencies involved.

Class WeatherCon

A Flutter Directory Structure

Notice there’s a directory called, home, in the app’s directory structure. This tells you, at a glance, where the code for the ‘Home’ widget is located. The ‘Home’ widget, as you may know, is usually the main screen for the mobile app in most cases. It’s named after the property found in the class, MaterialApp.

MVC within MVC

Notice in the last screenshot, under the home directory, there’s another set of directories named, model, view, and controller respectively. They’re all pertaining to the ‘Home’ widget. Guess what’s under each directory? See where I’m going with this? At a glance, you can see what code does what.

Again, Everything In Its Place

At a glance, you can see there’s a drawer widget accessed in the ‘Home’ widget. Look at the last screenshot above. See where the drawer code is located? A pretty logical place for it, no? So where’s the data accessed by the ‘Home’ widget, I wonder? Under the directory, model, of course.

In The Beginning

So let’s step back a bit, and ask where is this ‘Home’ Widget called in this app? Like with most Flutter apps, you can guess it’s being passed to the ‘home’ property of some MaterialApp widget somewhere. But where? Look below, and you’ll see where.

Class WeatherApp

What’s All This?

So there’s the home property being passed the class object, Weather. Where is this class, Weather? Where is this class, WeatherApp? What’s this AppView class that extends WeatherApp? What’s this method, onTheme()? And finally, what’s this con.WeatherApp passed to the property, con? A lot is going on here, uh. We’ll walk through this one step at a time.

Where’s Waldo…I mean Weather?

First of all, where is this class, Weather? Well, you already have a good idea where that is. It’s the ‘Home’ widget after all. More specifically, it’s the screen or the interface for the ‘Home’ widget. You know where the ‘Home’ widget code lives, and you know the ‘interface stuff’ for the ‘Home’ widget is under the directory called, view. Looking there, you’ll see a library file called, of all things, home.dart. I bet it’s in there.

Class Weather in home.dart

“The Export Files” An Applied Example

As an aside, let’s perform a little exercise demonstrating the benefits of using those three export files. Again, it has proven helpful for at least smaller projects. Also, in this little exercise, I‘ll demonstrate the dedicated practice of keeping things consistent. So, here we go.

Both ‘secondary’ widgets are applied to a ‘child’ property.

Oops. Broke It.

Now, look at what happens when I do rename the directory to, child. We lose some dependencies in parts of the source code. Makes sense, but then see how easily it’s resolved. I don’t have to go around to all the individual files in the project and update their import statements. I just have to go to the one ‘view’ export file and update four lines, and I’m done. See the screenshots below.

The ‘View Export File’ before updating the four lines.
The ‘View Export File’ after updating the four lines.

It Is All In The Name

So let’s get back to it. Now, where is this class, WeatherApp? It appears to be the start of this app. Note, the class name, WeatherApp, has the word, ‘App’, appended on the end. That should give you a clue as to where this code could be found. Let’s take a look at the sample app’s directory structure again. Notice there’s a directory called, app. Bet, you can guess what’s in there. Yup, three dire…Oops, two directories! Guess the app doesn’t need to access any data. That’s fine.

Again And Again, Everything In Its Place

So, where is this class, WeatherApp? Let’s look at the three screenshots below. Ok, we can figure this out. It’s part of the interface. Hence, it should be under a directory called, view. Looking in the directory, view, we see a library file called, weatherapp.dart. Look’s promising. Let’s open that one up.

Class WeatherApp in WeatherApp.dart

In The Beginning?

So is this the start of the app? How about we look at the library file, main.dart. By convention, this is the start of most Flutter applications because it contains the function, main().

main.dart

The App Layer

So, what do you see? You see a class called, App, being instantiated from the library package, mvc_application. Those familiar with my past articles may recognize this as part of my up-and-coming package release to develop Flutter apps.

package release to develop Flutter apps
Class App and Class AppMVC
Class WeatherApp

Import Control

Notice, I don’t bother passing the Controller as a parameter inside the file, main.dart. Why bother? Make your code more modular, and instantiate the Controller class inside the View in the form of con.WeatherApp().

Class WeatherApp

The App’s Controller

The purpose of this ‘App layer’, by the way, is to set up the ‘environment’ for the app to run on. What you see below is the Controller for the app first instantiated above in the form of, con.WeatherApp().

Class WeatherApp

The App’s View

Let’s return to the app’s View, also called WeatherApp, for a moment. It extends the class, AppView, and is first instantiated in the file, main.dart. Remember?

Class WeatherApp

The View Under The Hood

Looking at a truncated screenshot of the class, AppView. Here, you can see its build() function. And look! There’s that MaterialApp we’ve been looking for!

Class AppView

Keep It Static If You Can

By the way, I noted while writing this article that I made a misstep. It’s always good programming practice to keep your code static and immutable if you can. Since there should only be ‘one instance’ of these two classes at the ‘app level,’ the use of static factories make sense, and so the two classes we just covered were changed accordingly:

Class WeatherApp and Class WeatherApp

What’s In The Drawer?

Ok, back to it. Now, there’s a drawer used in this app. Remember? How do know? Again, we saw it in the directory structure under the directory, view. See below. Nice.

mvc.dart
weather_locations.dart

It Is All In The Name

Note the name of the Controller class. It has the abbreviation, Con, append on the end. Developers will then recognize this class as a Controller. (Of course, you could call it anything you want, but there it is.) Note the import statements. They’re using the ‘show’ directive so the next developer who happens to open up this file can readily see what is used and where it lives. Notice the last import statement is that mvc.dart file. All this is an attempt to impose a standard, conformity, a means to help the next developer ‘hit the ground running’ when this project is handed off to them next.

weather_locations.dart
weather_locations.dart

Location! Location! Location!

Because of the way the directory structure is arranged, we can ‘walk this back’ and take an educated guess as to where this high-level function is called. Neat!

Who Calls Who

The first screenshot below is the file, settings_drawer.dart. You can see the function call, LocationCon().listLocations(). This function, in turn, is found in the Controller class, LocationCon. Now that makes sense since, in this MVC arrangement, the View doesn’t directly ‘talk to’ the Model. Instead, your access to the high-level function is through the Controller. Looking at the second screenshot, you can see the high-level function we’ve been looking for, weatherLocations, is indeed accessed in the Controller.

Class SettingsDrawer and Class LocationCon

Step Back To See The Bigger Picture

Let’s step back now and look at one particular feature of this sample app. With every weather condition reported, a ‘color theme’ is presented to represent a particular ‘type’ of weather. Gives a little splash to the app.

The Color Theme Changes

What’s The Theme

To be able to ‘dynamically’ change the app’s color theme, you need to be able to call the setState() function on the State object that contains the theme. In other words, the State object that has the class, MaterialApp, in its build() function.

theme.dart
Class WeatherApp

All Starts Under The Hood!

Let’s step back all the way to the beginning. Remember the ‘App’ object that’s passed to the function, runApp()? It’s a StatefulWidget, remember? That means it creates a State object. It creates the app’s State object. Both that StatefulWidget, and its State object, call those two ‘init’ functions respectively. It’s all part of the process of setting up the environment to run your app. Let’s take a peek.

main.dart

Follow the breakpoints

Below are screenshots of my IDE where the execution of the sample app was paused at specific breakpoints. The first screenshot is stopped in the app’s State object’s initState() function. Look at the name of the StatefulWidget function being called there. It too is called, initApp(). Consistency.

Class _AppState and Class App
Class WeatherApp

Wait For The Future!

With any computer program, a lot of things have got to happen before it’s ready for the user to…well, use. There’s a lot of ‘initializing’ to be done first. This is a simple little sample app, but it too has to ‘get ready’ first before it’s, well…ready. Let’s take a further peek at how this Flutter app gets ready.

Class App
Class AppView and Class AppViewState
Class WeatherApp and Class AppController

Back To Our Theme!

Let’s get back now to how this app changes its color theme on command. The app’s View has a function called, onTheme(). Remember, using such functions allows the MaterialApp to dynamically change its properties the next time it ‘rebuilds’ its Widget tree (i.e. the next time the setState() function is called.) It’s all coming together!

Class WeatherApp
Class ThemeCon
Class AppView

How’s It Works…Really

This article is turning into a novel, I know. However, there are two more things I want to cover here. Firstly, how does the framework work in performing the main function of this small simple sample app: Get the weather for a city. Let’s do a quick ‘walk-through’ of that. If you’ve read Felix Angelov’s story, Weather App with “flutter_bloc”, you know how it goes. You type in the city’s name, it gets the weather.

Follow The Code

Ok, here we go. In the ‘Home’ widget file, home.dart, there’s a State object there that allows you to click on a magnifying glass and type in a city name to get its current weather. Below (just above the little red arrow) there is an IconButton widget that, when pressed, returns a city in a variable called, city. That variable is passed to the ‘Weather Controller.’ See below.

Class _WeatherState in home.dart
WeatherCon in home.dart

What’s Your Preference?

The last thing I want to cover quickly is app preferences. If and when you try out this sample app, you’ll notice it’ll ‘remember’ the last city you picked, and will, in fact, bring up it’s current weather conditions the next time you start up the app. That’s because the ‘last city’ entered is recorded in the app’s preferences — with help from the framework.

It’s All In The Init

The ‘Weather Controller’, WeatherCon, will go and fetch the last city if any in its initState() function. There, it calls the function, initFetch(). Below is a truncated screenshot of the controller, WeatherCon. The red arrow shows you where the app’s preferences are accessed.

Class WeatherCon

Saved For Later

You see in the screenshot above where the ‘last city’ is retrieved and, in fact, where the ‘last city’ is recorded — in the function, fetchWeather(). Remember, the function, fetchWeather(), is eventually called every time the user clicks the magnifying glass, and the function, onPressed(), is executed in the View’s code and then in the Controller’s code.

The Prefs

That Prefs class you see in the screenshot above is a library I wrote a few months after first discovering Flutter. I had a need for such a library in my projects. The Prefs library is actually part of the framework I’m working on, mvc_application, and you’ve already seen in being initialized above, in the class AppController, when the app was setting up its environment. I decided to publish Prefs as a library package! I just did it! Just now! Here you go: prefs

Conclusion

Ok, that was a bit of a read. I’m sure you’re hungry, and or want to get on with your Life. However, I will be following up on this article as there is much more one has to consider when developing software as you know. It just happens, this framework will allow you to do so, and I’ll show you how in other articles.

DECODE Flutter on YouTube
Questions?

--

--

Freelance Developer

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store