Your Next Flutter Project

Greg Perry
Follow Flutter
Published in
33 min readMay 8, 2020

Use this MVC Project Template to Quickly Build Your Next Flutter App

Download this zip file, and you’ll have a ready-made Flutter project — but with a twist. It‘s interlaced with a sample app that runs when you start up the project. The purpose here is so you can then better understand how the underlying framework works. It’s a framework that utilizes the MVC design pattern. In fact, you’ll find two sample apps in this Flutter project template. Both, use the very same library package that is an MVC framework called, mvc_application. The framework, in turn, uses the library package, mvc_pattern, which is the core of the MVC architecture implementation in Flutter.

Download this other zip file to attain the project template alone without any sample apps and use that project, again and again, to write your next Flutter app. It provides all the files and directories needed to best utilize this MVC architecture specifically designed to work with Flutter.

Unlike other architectures offered, it doesn’t just ‘sit on top’ of Flutter but instead works with Flutter. It’s incorporated into Flutter to a degree. This architecture actually uses the many classes and high-level functions offered by the Flutter framework itself. Further, it doesn’t try to impose its own API but instead shares the many parameter and function names found in the underlying Flutter framework. It also installs and works with many of the more popular library packages out there that provide functions and features commonly needed by all Flutter apps. For example, Shared Preferences capability is readily available for use. Lastly, an extensive Error Handling capability is offered to developers from the get-go.

True, the Provider framework is officially supported by Google, but like the others, Provider ‘sits on top’ of the existing Flutter framework very much a separate layer with its own API. And so, to work with it, you have to learn a whole new list of classes (‘Provider this’ and ‘Provider that’).

And I don’t get me started on Redux. Ironically, it was the first architecture I worked with when I switched over to Flutter. Based on Flex and originally conceived in JavaScript, Redux was developed to work with not just a large website, but with a huge website that is facebook.com! It was designed so that it can easily be maintained by hundreds if not thousands of developers over time. Hence, its use of the many ‘actions’ and ‘reducers’ that make up what essentially is an elaborate messaging system. Imposing this on Flutter, the work just gets bogged down by the ‘sheer weight’ of its boilerplate. Redux was not designed for mobile development.

To use this MVC framework, you do three things. With any StatefulWidget, you would use the StateMVC class instead of the traditional State class. As you may know, it serves as the ‘View’ portion (the interface) of the MVC design pattern. Second, you extend the class ControllerMVC to contain all your business logic as it’s, of course, the ‘Controller’ portion of the design pattern. Lastly, you pass that Controller object to the StateMVC object so the Controller has access to all the abilities and properties of a State object and more, as well as hooks into the app’s very lifecycle — Android developers would appreciate what that means.

Why did I pick MVC? It’s hoped you’ll see why MVC in particular by the end of this article. However, there were many reasons why. First and foremost, following any design pattern has its benefits. But it really came down to the very mechanism the Flutter framework uses behind the scenes in its own ‘State Management.’ The way it does so allows Flutter not only to work on mobile phones but soon just as readily on a desktop computer as wells as on the Web. I’ll explain. No doubt, when you were first started playing around with Flutter, you inadvertently caused that annoying ‘immutable’ message to come up. Look at the two screenshots below of a particular StatefulWidget, and you’ll see what I mean. It’s warning you the instance variable, title, should be declared final.

Now ignoring such a message is a no-no. You’re impeding the very means Flutter uses in its own State Management efforts. The code will work, sure, but you’re really reducing the overall performance of your Flutter app in doing so. You don’t realize how often a StatefulWidget and a StatelessWidget are created over and over again in the life of a typical Flutter app. They are the ‘building blocks’ of your Flutter app and used all the time, and so it’s optimal to keep everything within such objects static, unchanging, and immutable. There’s this ‘paywall’ article that explains further:

An in-depth look at Flutter’s StatefulWidget and its Lifecycle.

Well, at first thought, that means all the mutable stuff, all ‘the guts’, all the logic of a particular app would have to be stored in the app’s State object. Alternatively, it would have to be stored in Dart files called by code in the State object. It would have to be stored in Dart files called by code within the build() function found in the State object. It results in some pretty ‘busy looking’ State objects. But again, placing much of the code in another Dart file is a good play because, unlike Java, there’s a particular ability privy to you when using Dart.

More Than A File. It’s A Library!

“Some languages, such as Java, tie the organization of files to the organization of classes — each file may only define a single top level class. Dart does not have that limitation.” — Effective Dart: Design

A Dart file is called a library file. I would suggest that’s because you can declare any number of classes in the same file! That’s huge! A single Dart file can contain any number of classes, any number of top-level variables, and any number of top-level functions. That’s the hugest! (word?) That’s powerful! That can be very helpful in making industrial-strength apps — separating aspects of software into more manageable parts for example. As is the case with MVC, separating ‘the data’ from ‘the interface’ and from ‘the logic’ that essentially makes up every app. I’ll take ‘the long way round’ now in explaining further by introducing what you get when you do work with these project templates.

The Big Picture

Below is a series of screenshots of an installed project template. In this architecture, you will consistently see the ‘model-view-controller’ labelling of even its file and folders to make the distinction between the app’s data, the app’s interface, and the app’s logic. This is the hallmark of the MVC architecture. What you do then is fill it up with your code to make your next Flutter app.

A Framework Is A Foundation

A framework is to serve you. It is to make building an app that much easier. A framework is to provide structure; a guide to go by. It is to convey a consistent approach allowing for quick development yet still be adaptive to each individual app’s unique requirements. Using such a framework encourages efficient code reuse and allows for parallel development. It offers a way to organize your code, it offers some conformity and some consistency. Having developers familiar with such an architecture, enables them to walk in on an ongoing project and know exactly where all the code is located and what code does what. A new developer can ‘hit the ground running.’

If the reader is not readily familiar with the MVC design pattern and how it’s being used in Flutter. There is a supplementary article called, MVC in Flutter, which lists all the articles currently available on the subject. It serves to get the reader ‘up to speed’ on the MVC interpretation currently offer to the Flutter community.

Screenshots Only. 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, and easier to read. However, you can click/tap on them to see the code in 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.

No Moving Pictures, No Social Media

There will be a number of 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.

Other Stories by Greg Perry

The main() function is the starting point for a Flutter app. The project templates, of course, provide you with one, and in most cases, you will have no real reason to even visit it. It’s all ready to go. There’s a screenshot below of the main.dart file in the project template. You can see the runApp() function and all the class references come from one import statement. You can see the framework implements the MVC design pattern right from the start as it is the class, MyView, where you’d actually go to begin writing code and start building your app.

main.dart

Note, the runApp() function doesn’t directly call the ‘View’ (the interface) that will be your app but instead takes in the class, MyApp, that is here at the ‘app level.’ It is the View for the framework itself and not necessarily your particular app. You see, there’s always a lot of preparation needed to set up a Flutter app — it is at this level where all that is done. Note, throughout the template, all the parameters available to you will be highlighted by simply passing null. Of course, you can ignore them or even remove them if you like, but again, this is done so you can readily see what’s there and available to you. At this point, for example, the app is only concerned with the loading screen to display at startup.

Further, you can see error handling is paramount right from the start with the last three parameters dealing with how to respond and what to display if and when an error occurs. Of course, like any good framework, if such parameters do not explicitly receive values (or are passed null), no loading screen is displayed and the default error routines used. Again, your attention would likely be directed to the MyView class instead. We’ll get there soon enough.

V-C-M in MVC

Staying here in the main.dart file, I’ll introduce you to a common pattern you will see throughout the framework. This is with regards to the lines of communication and consequently the workflow between the three aspects that make up an app using this MVC design pattern. You will see the Model (data) component in many instances is passed as a parameter to Controller (event logic) component which, in turn, is passed as a parameter to the View (interface) component. This means, in other words, the View can ‘talk to’ the Controller; the Controller can ‘talk to’ the Model.

It begins even here in the main.dart file. You can see the class, MyApp, extends the class, App, which again is the View component here at the ‘app level.’ Although not commonly the case in the main.dart file, even this View object has a named parameter called, con, to take in a Controller as a parameter. For example, in the screenshot below on the right-hand side, I’ve demonstrated this with a variation to the main.dart. Instead of passing null, the parameter, con, receives an instance of a class called, AppCon, which extends the framework Controller class, AppConMVC. Lastly, because this interpretation of the MVC design pattern designates the StatefulWidget as part of the View, the parameter, key, will always be available in such an object’s list of parameters.

Error Handling At The Start

Again, error handling is a paramount responsibility of the MVC framework. It should be for any framework frankly. It even has its own runApp() function because of this. The runApp() function you see in the main.dart file above isn’t the one you’re familiar with — it’s the framework’s version. It calls the original runApp() function, but not before wrapping it and your very app itself in error-handling routines. The screenshot below on the right-hand side is the framework’s runApp() version. Of course, like any good framework, that fact is not readily advertised and instead leaves the developer to get on with building their app. Many may even recognize the implementation as it’s the same approach taken by the popular library package, Catcher.

Import Your View

Let’s take a look again at that lone import statement in the main.dart file. Examining its path, we see this Dart file is located under the folder, src. As you may know, in Dart, such files in the src folder are not publically available like those in the lib folder. In this MVC architecture, the file, view.dart, is concerned with the View or interface of your app. We going to take a quick peek inside that file.

import 'package:mvc_template/src/view.dart'

Inside the file, view.dart, we see it contains a list of export statements. Looking closer, you see the files exported are all concerned with the interface (the View) of the app. In the screenshot below of the project’s directory structure, all those files are highlighted. Again, frameworks are to provide consistency in implementation. They are to provide structure. In the case of an MVC architecture, the source code is consistently segregated into three aspects of a typical mobile app. One is concerned with the data (Model), one is concerned with the interface (View), and one is concerned with the app’s event handling and logic (Controller). And so, like with regards to the interface for the app with its file, view.dart, there is a dedicated file for the app’s logic called, controller.dart and a dedicated file for the app’s data called, model.dart. All under the folder, src.

The ‘import files’ for MVC

Note, the first export statement in the screenshot above references the ‘ view file’ found directly in the MVC framework itself. Note, it’s that file that supplied the runApp() function and the class, App , in the main.dart file. The rest of those export statements above are specific to this project. In this article, you’ll be visiting each of them in turn.

Your App’s View

Back in the main.dart file, we’ve examined the class, MyApp. Let’s now look that the class, MyView. It’s instantiated in the function called, createView() — sort of in homage to Flutter’s own function, createState(). The MyView class allows you to extensively set up your app’s interface. In the screenshot below, you can see what file contains this class, and where it’s referenced in the src file, view.dart. It’s concerned with the whole app and so it’s under the folder, app. As it happens, its file name is also, view.dart.

Note, the use of these ‘import files’ with their many export statements is an approach recommended by Google itself. There’s not re-inventing the wheel here. The architecture always tries to keep in line with Flutter’s own approach. More information on export statements is in the documentation, organizing a library package.

Below is a screenshot of the class, MyView. Again, the many named parameters are listed out with their redundant null values merely for your benefit. Further, in keeping with the effort to smoothly integrate this framework with Flutter, many of those parameters you’ll recognize as those you’d pass to the MaterialApp widget, or to the CupertinoApp widget.

view.dart

As you can see in the screenshot below, the MyView class extends the class, AppView. Again, this is a View component in MVC. Hence, it too takes in a Controller object using the named parameter, con. It’s the Controller for your particular app. Let’s take a quick peek at this ‘App Controller.’

In the screenshot above on the right-hand side, we see the framework’s AppView class. If there’s no ‘App Controller’ passed to your ‘App View,’ the framework instead takes in one called, AppController. Now, what does that Controller do? Well, it’s purpose is to initialize functions and features commonly required by a Flutter app.

In the screenshot below, for example, you can see the default AppController provides the app’s Stored Preferences feature. You can see it sets up things in its initAsync() function and cleans things up in its dispose() function when the app shuts down. You are to override the initAsync() function to perform any and all ‘asynchronous’ operations that need to be performed before your app starts up. Further, another function called, initApp(), is available to you to override and perform any and all ‘synchronous’ operations that also needs to be done before your app starts up. Finally, you can see below on the right-hand side a screenshot of the App class (introduced in main.dart) calling those very functions. You can also see the createView() function first introduced to you in the main.dart file. Do you see how this is all coming together? Again, this is a framework, and so you don’t even need to know the inner works here. Just know it’s doing a lot of the ‘heavy lifting’ for you required of every Flutter app.

Like any good framework, you’re free to create your own ‘App Controller’ specifying your own ‘functions and features’ unique to the needs of your app. Possibly you’ll supply your app with capabilities never seen before. Of course, your app will meet with great success and propel you to fame and fortune! However, if you don’t bother, the framework will provide you instead with some of the fundamental capabilities needed for a typical app.

Your Functions Function

The remaining screenshots below are the many functions available to you when you work with your ‘MyView’ class. It extends the class, AppView, and again allows you to get really particular when it comes to setting up the ‘look and feel’ of your app. You’ll find many listed below are the ‘functional equivalent’ of the many parameters you can pass to the class, AppView.

That’s because, like any good framework, it can’t anticipate what logic you will use to pass particular values to those many parameters, and so it offers you equivalent functions to allow for that. I’ll demonstrate.

Below is a simple example involving the named parameter, title. It’s one of the few parameters found in this MyView class that is passed a legitimate value. You can see below it’s a pretty straightforward value at that. However, know that instead of explicitly passing a title to the AppView class as a parameter, you could have overridden the function, onTitle(), instead and conceive a title in that fashion. The screenshot on the right-hand side is code deep inside the MVC framework. You can see if there’s no value passed to the title as a parameter or there’s no function written to produce a title, an empty string is provided instead. This arrangement is available to most of the other parameters as well.

And so, again, all the functions available to you when you use this project template are overridden and merely returning null. When implementing your own app with this template, I would suggest you simply delete most if not all the functions you won’t be using — clean up the unnecessary clutter.

Do note, in time, when you’re more familiar with the framework, it’s hoped I’ll have an alternative so to delivering the MVC project template. One integrated into the IntelliJ and the Android Studio IDE’s themselves allowing you to create a new ‘MVC Flutter project’ with a click of the mouse. Well, it’s hoped if I don’t, someone else will. More on that later.

Again, the screenshot below continues with the functions available to you when working with the MyView class. As always, you can tap or click on the screenshots to examine its gist or Github equivalent. You’ll come to realize this framework is really a lot of ‘parameter testing’ and ‘potential functionality.’ It’s really rather ugly at first glance. However, that’s what a framework is all about. Ugly on the inside and sleek on the outside making life easier for a developer.

view.dart

Again, as you get more and more familiar with Flutter itself and with the MaterialApp widget and the CupertinoApp widget, you’ll readily recognize what each and every function listed here can do for you. You’ll then better take advantage of them when the need arises.

view.dart
view.dart
view.dart

Yes, there’s a lot of them. However, that’s another attribute of a good framework — there’s a lot of options for the developer.

view.dart

Go Home

In your own Flutter apps, you’ve no doubt specified a widget in the home parameter of a MaterialApp widget or a CupertinoApp widget. Well, it is here in the class, MyView, where you again provide a widget to the named parameter, home. It is here where the ‘first screen’ for your app is instantiated so it’s eventually displayed. As you see, a lot is done behind the scenes before even your home screen is first displayed. Of course, if you're familiar with working with the MaterialApp widget or CupterinoApp widget, you’ll be fine working with this class because this framework ‘works with’ the underlying Flutter framework providing the very same named parameters.

As for the ‘home’ widget used in this template, it’s merely a shell displaying an empty Container widget — not much to it. The template provides it merely as a placeholder really. However, if we take a look at the MyHome class, we see it does offer you an approach to separate the interface for both the Android platform or the iPhone platform. You are to create two separate State objects for each platform. Note, it’s merely a suggested approach. The idea, of course, is when you do use this template yourself you would completely overwrite this StatefulWidget and its accompanying State object with all your code — all in the course of making your app’s main screen. Of course, in line with MVC, the code should only be concerned with the app’s interface at that point, right?

view.dart and view.dart

For example, let’s take a look at one of those State objects we see in the screenshot above. We’ll look at the one called, MyAndroid. In its screenshot below, you can see it extends the class, StateMVC — the name gives away that it’s part of the MVC architecture. Of course, it’s like a State object but more. As you may know, in this interpretation of the MVC design pattern, the build() function inside a State object returns what is considered the ‘View’ in the MVC design pattern. And so, for all intents and purposes, every State object in this framework is a ‘View’ object.

When you extend the class, StateMVC, you retain all the functions and features that make up a traditional State object and more. Take the time to examine the screenshot below — tap or click on the screenshots for its corresponding gists or Github copy.

android.dart

For Each Controller

A quick peek at the dispose() function in the parent class, StateMVC, reveals the ‘dependency’ between the State object that is the ‘View’ and each and all of its Controllers. I’ve commented out most of the code to highlight the fact when you call this particular State object’s dispose() function; you’re calling each and every dispose() function of every Controller associated with it — if any. Note, the State object when terminating also returns to the app’s original error routine as well. That’s right, each State object can have its own dedicated error handling if you want. You can tell I’m big on error handling.

mvc_pattern.dart

And so, again in keeping with the protocol regarding the three components that make up the MVC architecture, every View can take in a Controller. With the class, MyAndroid, there’s no exception. You can see below a Controller unimaginatively called Controller is instantiated and passed to the View object, MyAndroid.

android.dart

Every function you find in the View object, MyAndroid, you can find in the Controller again called…Controller. Let’s take a look now at this Controller class. Its screenshot below reveals it extends the MVC class, ControllerMVC. It’s optional, but you’ll notice a factory constructor is being used there. In my own experience, I’ve had no reason to have more than one instance of a Controller and so my Controllers have always used a factory constructor.

All the functions here, you’ll also find in the MyAndroid class. Again, the StateMVC object calls all its Controller objects in turn when one of its own functions is called. Such an arrangement allows the Controller to have all the functionality of a traditional State object (e.g. setState() and BuildContext). Each Controller will tap into that State object’s own lifecycle and error handler as well.

Each Controller has access to, properties, and capabilities of ‘the last’ View object it was passed to. And of course, one View can have as many Controllers you required at any one time. Breaking up code into separate Controllers will reduce ‘Controller bloating’ — a common criticism to MVC.

One to many and Last-In-First-Out
controller.dart

Learn A Framework By Example

When you downloaded that second zip file of the project template, you’ll get two working examples that demonstrate how to take advantage of the MVC framework’s many functions and features.

Location of Sample Apps

Main With A Difference

When you open this sample template up, you’ll find a bit more code residing in the main.dart file as well. It’s to demonstrate one of the few times you may require an ‘App Controller’ here in the main() function. In this case, it’s when you want to use ‘hot reload’ to switch back and forth between an Android Interface (Material) and an iOS Interface (Cupertino). Another requirement demonstrated here is to use ‘hot reload’ to switch to an entirely new app altogether. In this case, it’s to the Shrine sample app Google offers in its Flutter Gallery package.

I know, not great examples. I mean, why would you ever want such a feature since hot reload is not available in production?? Ok, I just thought this up while writing this article. Go away. Leave me alone…I’ll continue.

In the second screenshot on the right-hand side, I’ve simply removed the comments and the parameters passed null. You wouldn’t have that clutter up your code when you start using this template, and so I removed them for you.

So how does this work? In the gif presented below on the lower right-hand side, you can see the sample app in action. Note, the ‘switching of the interface’ you see is the result of a ‘hot reload’ of this project sample after the appropriate switch was toggled. The AppCon object instantiated in the createView() function is the fundamental piece making this all possible. That’s because, as you can see in the screenshot above, a number of AppCon’s properties are referenced in the createView() function.

If you haven’t guessed already, a ‘hot reload’ will cause that createView() function to be called again. The idea is those properties will have been set by a toggled switch somewhere and voila! — the interface will change accordingly. For example, in the createView() function there is the following statement,

if (app.shrine) {

You can see if that’s set to true, then the ShrinApp() function will return a different widget then expected. The shrine property is of a boolean data type defined in the AppCon class. A screenshot of that class is below.

On Hot Reload

On a hot reload, your MyView class (which extends AppView) is a State object and its reassemble() function is called as a result. A screenshot of that function below reveals a boolean property in the App class is set to true.

app.dart

Another consequence of this hot reload is that the App class (which happens to be yet another Flutter State object) is then rebuilt — calling its build() function again. A screenshot of that class is displayed below. The build() function calls its initAsync() function again. And in that function with the App’s property, _hotReload, set to true, you see the function createView() is called again. Remember that function?

Yes, you first saw that function in the main.dart file. See now how all this works? Note, because the AppCon Controller object is instantiated with a factory constructor, despite the additional calls to createView(), the Controller is not instantiated all over again and so that Controller’s property values are preserved with their new settings. Nice work.

app.dart

A Different View

The sample template copy will run the ‘Contacts’ sample app when you first start it up. This is done by simply commenting out the original MyAndroid class (see below) and replacing it with a function returning a State object actually found in the Contacts app. Admittedly, this is a quick and dirty way to accomplish the effect I was looking for. It’s certainly not a recommended approach. It merely serves to help further demonstrate the MVC framework.

In the MyHome class, the MyAndroid() function is referenced in the StatefulWidget. Again, not ideal, but this gets us the ‘Contacts’ interface — and thus integrating a ‘third-party’ into this project.

android.dart

Compose A Controller

ok, that’s how I jury-rigged things on the ‘View’ side of things. However, I feel I accomplished the same effect on the Controller side but with a more elegant approach. Composition is a recognized and, in fact, recommended alternative to inheritance when it comes to working with object-oriented programming languages. Ironically, its use here is actually a bit of overkill as, of course, the original intent when using this project template is that you would ‘overwrite’ this class with all the code relevant to your own app. However, Composition is used here to integrate a third-party for demonstration purposes.

And so, the Controller below instantiates yet another Controller called, Contacts, in its constructor, and then uses that Controller in all its corresponding functions and properties. This is an example of Composition. Tap or click on the screenshot below and see for yourself.

controller.dart

Again, simply by looking at the directories and files that make up the Contact example app (see below), you can tell it’s a self-contained app in its own right — it’s using the MVC design pattern. At a glance, you can tell it has its own main.dart file, it has its own screens, its own ‘business logic’, and its own data. Do you see that?

The Contacts app

A Model Composition

The sample app, Contacts, has a database! It has data to deal with, and so that means a Model component is involved because we’re using the MVC design pattern. As with the Controller, you can see in the screenshot below I’ve introduced the Contact app’s own model to this template’s counterpart using Composition. Every function in this Model then mirrors a function found in the Contact app's own Model class to achieve the same functionality.

model.dart

One Import Imports Many

Notice the one lone import statement in the screenshot above. Makes sense — it’s the ‘import file’ for all that involves data. Hence, it’s named model.dart. It’s the same model.dart file found in the ‘empty’ Flutter project template, but you can guess this one has additional entries to take in any and all of the ‘data element’ found in the Contacts and Shrine sample apps. The idea here is that you leave that one lone import statement alone. Any new capabilities, of course, continue to be introduced to project using Dart’s import statement, but it’s all done in that one ‘import file’, model.dart. The code in the class above (and at times the developers working on that code) are none the wiser. This encourages a modular approach to the development and maintenance of code.

For example, below is the contents of the model ‘import file’ for both the empty Flutter project template and this template with its two sample apps. At a glance, you can see what’s what. There are additional lines in the second file because there are additional ‘data functions and features’ in the second project. Again, changes and updates down the road can involve only this file in many cases without disturbing the rest of the app. Modular. Succinct. Nice.

Model In The Machine

Note, the first two lines will be a common sight in such files. The first line imports all the ‘Model stuff’ from the MVC framework itself. That being functions and features a developer will commonly find useful when working with databases, files, and such in their own Flutter app. A screenshot of that very file is below — look at all goodies you’re privy to. The next import statement is this project's own Model file. For example, when you make such a model ‘import file’ you can then include that file to any part of your app using an import command when it requires the functions and features you’ve defined in your own Model file:

import 'package:your_project/src/model.dart';

Note, the first two import statements in the screenshot below, at first glance, have really no relation to databases or any ‘data aspect’ of a possible app, but are the fundamental library packages used to support the Material theme and the Cupertino theme in the user-interface!? That was a deliberate move on my part. You’ll find those two export statements in both the View ‘import file’ and the Controller ‘import file’ as well. You see, a framework is to make ‘less work’ not more — a developer needs not even import the material.dart file or the cupertino.dart file. They’re available in these ‘import files!’ Admittedly, such a ‘feature’ may not be a practical approach, but… “no place for the meek!”

model.dart

On The iOS Side

By the way, on the iOS side of this, you’ll notice that yet another approach was taken when it came to integrating the Contacts app’s iOS interface to this template. You’ll discover the very elements in the Contacts ios folder are peppered here and there in this app’s View.

ios.dart

If you tap or click on the screenshot above and see the whole class, you’ll see the highlighted elements are actually found in the Contacts app. Further, the scaffoldKey is a property from the Contacts app’s own Controller.

A follow-up to this article is the article, MVC in Flutter, which details even further the inner workings of the sample app, Contacts, for your review.

An MVC Approach to Flutter

pubspec.yaml

Below are screenshots of the two project templates’ pubspec.yaml files respectively. The first one, of course, belongs to the ‘empty’ project template — its only unique entry is the MVC framework library package. The second file, with its two sample apps, has another five more entries of library packages. Two of which I’ve also written.

Click ‘New MVC Flutter Project’

You know, I could do better. Last year, I spent a month going down a rabbit hole on how to make a File option called, ‘New MVC Flutter Project,’ in Android Studio.

III reached out to Phil Quitslund as he was responsible for the “New Flutter Project…” Template/Wizard found in Android Studio. I quickly came to appreciate the immense effort required to provide a similar option, and so, I’ve turned to another approach.

I’m now looking at something like Tomoaki Imai’s “Android Best Practices Template” approach. He includes a new Android project under Android Studio’s ‘New Project…’ option conforming to a particular framework structure. It looks familiar to my own pursuits.

Android Best Practices Template

It would create all the files and directories necessary for an effective boilerplate for the next Android project supplying as well as all the ‘platform-specific’ files required (eg. AutoMandfest.xml file).

Android Best Practices Layout

Some medium.com articles appear to be suitable as a starting point: Mastering Android Studio Templates Creation and How to speed up development by using MVP templates for Android studio. However, I would love to ‘delegate’ this to another as I’m currently swamped. If you’re interested — let me know.

You know, at the end of all this, I’ve done nothing. Of course, I’m eternally grateful for the amount of positive feedback received since the MVC framework’s release. However, I merely married the concept that is the MVC design pattern to the approach taken by Flutter. The intent from the start was to ensure a low learning curve. In truth, it was the Google team that came up with Flutter that made this all possible. And, of course, Trygve Reenskaug, who conceived of MVC going on some 40 years ago.

That’s why, when I first turned to Flutter, I had to step back when I reviewed the architecture solutions currently on offer. You see, it was around eighteen years ago when they began using the term, ‘State Management.’ It came with the advent of ‘Web Apps’ that could run on the Internet. It came because one of the more challenging tasks at that time was the ‘persisting of state’ during the lifecycle of such apps:

“State management is the process of maintaining the state of a Web page across round trips….because of the disconnected nature of the HTTP protocol, state management is a big issue for Web applications.”

— MCAD/MCSD Training Guide (70–305): Developing and Implementing Web Applications with Visual Basic.NET and Visual Studio.NET
Published Dec 31, 2002

Before such apps, state management was simply a ubiquitous innate ability in all desktop applications and standalone executables. The ‘maintaining of state’ was not something a developer had to worry about.

The ‘State Management’ concept was carried over to Flutter frankly for the benefit of those developers now coming back from that World. However, Flutter ‘retains the state’ for you now. It’s come full circle. You now have a standalone executable of sorts again — this time it sits in the palm of your hand. To my delight, that means this tried and true design pattern has worked very well for me. I feel it can work for you. You can make something that no one has ever seen before — propelling you to fame and fortune.

Now go away. Leave me alone...

Cheers.

TL;DR

You’ve Got Class!

As a further supplement to this article, let’s list out all the public classes that are available in the MVC framework. Note, however, you will likely only use the first two.

StateMVC — The View’s State object.

ControllerMVC — The Controller responding to both system and user events.

AppView — The View for the app. Sets the ‘look and feel’ for the whole app.

StateListener — Mixin giving you listeners to the State object’s lifecycle.

SetState — Allow for spontaneous ‘rebuilds’ in your app without a State object.

ViewMVC — The MVC State object for the whole app.

AppConMVC — A Controller object for the whole app.

AppMVC — Passed to the runApp() function.

Uuid — A UUID generator, useful for generating unique IDs.

State The State

We’re running in Flutter. Might as well use it. You’ll find the Controller class and View class (StateMVC) in this framework will use all the functions and properties that Flutter has to offer. For example, with regards to the lifecycle, this framework takes advantage of Flutter’s own WidgetsBindingObserver class to respond to the app’s lifecycle. Any seasoned Flutter developer will recognize most of the functions offered by a StateMVC object:

context — The BuildContext passed in the build() function.

mounted — A boolean value indicating if the State object is terminating or not.

widget — The associated ‘StatefulWidget’ object.

initState() — Called exactly once when the State object is first created.

dispose() — Called when the State object will never build again. Its terminated.

deactivate() — Called when the State object is removed from the Widget tree.

setState() — Notify the framework to call the State object’s build() function.

didChangeAccessibilityFeatures() — Called when the system changes the set of active accessibility features.

didChangeAppLifecycleState(AppLifecycleState state) — Called when the system puts the app in the background or returns the app to the foreground.

didChangeDependencies() — Called when a dependency of this State object changes.

didChangeLocale(Locale locale) — Called when the system tells the app that the user’s locale has changed.

didChangeMetrics() — Called when the application’s dimensions change. For example, when a phone is rotated.

didChangeTextScaleFactor() — Called when the platform’s text scale factor changes.

didHaveMemoryPressure() — Called when the system is running low on memory.

didUpdateWidget(StatefulWidget oldWidget) — Override this method to respond when the Widget changes (e.g., to start implicit animations).

onError(FlutterErrorDetails details) — Allows one to define their own Error Handler. The default routine is to dump the error to the console.

reassemble() — Called whenever the application is reassembled during debugging, for example during hot reload.

addBeforeListener(StateListener listener) — Add a listener fired ‘before’ the main controller runs.

addAfterListener(StateListener listener) — Add a listener fired ‘after’ the main controller runs.

beforeListener(String key) — Returns a listener by its unique id. Listeners fire ‘before’ the Controller runs.

afterListener(String key) — Returns a listener by its unique id. Listeners fire ‘after’ the Controller runs.

beforeContains(StateListener listener) — Returns true if contains specified ‘before’ listerner

bool afterContains(StateListener listener) — Returns true if contains specified ‘after’ listerner

ControllerMVC Class

Everything the State object can do, the Controller can do. It even has access to the State object:

context — The BuildContext from the associated MVC State object.

stateMVC — A reference to the associated MVC State object.

setState() — Calls setState() from the associated MVC State object.

rebuild() — Calls setState() from the associated MVC State object.

refresh() — Calls setState() from the associated MVC State object.

notifyListeners() — Calls setState() from the associated MVC State object.

states — Returns a Set of the State objects the Controller is currently associated with.

initState() — Called exactly once when the State object is first created.

dispose() — Called when the State object will never build again. Its terminated.

initAsync() — Initialize any asynchronous operations.

deactivate() — Called when the State object is removed from the Widget tree.

didChangeAccessibilityFeatures() — Called when the system changes the set of active accessibility features.

didChangeAppLifecycleState(AppLifecycleState state) — Called when the system puts the app in the background or returns the app to the foreground.

didChangeDependencies() — Called when a dependency of this State object changes.

didChangeLocale(Locale locale) — Called when the system tells the app that the user’s locale has changed.

didChangeTextScaleFactor() — When the phone’s text scale factor changes.

didHaveMemoryPressure() — When the system is running low on memory.

didUpdateWidget(StatefulWidget oldWidget) — Override this method to respond when the Widget changes (e.g., to start implicit animations).

reassemble() — Called whenever the application is reassembled during debugging, for example during hot reload.

didPopRoute() — Called when the system pops the current route.

didPushRoute(String route) — Called when the system pushes a new route.

didChangeMetrics() — Called when the phone is rotated.

didChangePlatformBrightness() — Phones changes its brightness.

onError(FlutterErrorDetails details) — Allows one to define their own Error Handler. The default routine is to dump the error to the console.

keyId — Returns a Sting object uniquely identifying this Controller.

beforeListener(String key) — Returns a listener by its unique id.

afterListener(String key) — Returns a listener by its unique id.

Your App Knows All

Like any good framework, you can readily get a lot of information that involves the whole app. It’s as easy as prefixing the sought information with ‘App.’ and you have that information wherever you are in your app. Admittedly, the volume of information may be a case of overkill and reduced in subsequent revisions of the framework, but for now, here you are:

vw — Gives access to the App’s View. The ‘MyView’ you first work with.

snapshot — The snapshot used by the App’s View.

isInit — Determine if the App initialized successfully without error.

useMaterial — Indicates if the App is running the Material interface theme.

useCupertino— Indicates if the App is running the Cupertino interface theme.

navigatorKey — Return the navigator key used by the App’s View.

routes — Returns the routes used by the App’s View.

initialRoute — Returns to the initial route used by the App’s View.

onGenerateRoute — The route generator used when the app is navigated to a named route.

onUnknownRoute — Called when [onGenerateRoute] fails except for the [initialRoute].

navigatorObservers — The list of observers for the [Navigator] for this app.

builder — if neither [home], [routes], or [onGenerateRoute] was passed.

title — Returns the title for the App’s View.

onGenerateTitle — Routine used to generate the App’s title.

theme — The App’s current theme.

color — Returns the Color passed to the App’s View.

locale — Returns the App’s current locale.

localizationsDelegates — Returns the App’s current localizations delegates.

localeResolutionCallback — Resolves the App’s locale.

supportedLocales — Returns an iteration of the App’s locales.

debugShowMaterialGrid —It paints a grid overlay on Material apps.

showPerformanceOverlay —It turns on a performance overlay.

checkerboardRasterCacheImages —Checkerboard raster cache to speed up overall rendering.

checkerboardOffscreenLayers —Checkerboard layers rendered offscreen bitmaps.

showSemanticsDebugger —Shows an overlay of accessibility information.

debugShowCheckedModeBanner — Shows a little “DEBUG” banner in checked mode.

debugPaintSizeEnabled — Each RenderBox to paint a box around its bounds.

debugPaintBaselinesEnabled — RenderBox paints a line at its baselines.

debugPaintPointersEnabled — Objects flash while they are being tapped.

debugPaintLayerBordersEnabled — Layer paints a box around its bound.

debugRepaintRainbowEnabled — Overlay a rotating set of colors when repainting layers in checked mode.

context — The BuildContext for the App’s View.

appName — The Name of the App.

packageName — The ‘Package Name’ of the App.

version — The current version of the App.

buildNumber — The build number of the App.

inDebugger — True, if in check mode or debug mode.

scaffold — The Scaffold object for this App’s View.

colorTheme — The ColorSwatch for this App’s menu.

filesDir — The local directory for this App.

connectivity — Returns the connection status of the device.

isOnline — Indicates if the app has access to the Internet.

installNum — The id for this App’s particular installation.

addConnectivityListener() — Add a Connectivity listener.

removeConnectivityListener() — Remove a Connectivity listener.

clearConnectivityListener() — Clear Connectivity listeners.

Cheers.

→ Other Stories by Greg Perry

DECODE Flutter on YouTube

--

--