Use a Framework on your Flutter apps!

I’ve been writing in Flutter for going on 8 months now. As time went on, I’ve written and set aside code that I would then use again and again to perform the ‘common operations’ found in all my Flutter apps. In other words, like any developer, I was building up my ‘toolkit’ so to allow for my next project to be done much faster and more effectively.

A Work In Progress

I Like Screenshots. Click For Gists.

Always Start with a Framework!

MVC was my chosen design pattern for building my Flutter apps. I would eventually release a Flutter library package called, mvc_pattern, providing the design pattern to developers. As I progressed in my own journey in Flutter, of course, the library package itself would evolve. As of this writing, it’s up to version 3.2.0.

Other Stories by Greg Perry

You may want to read the article, Flutter + MVC at Last!, first to get some background on the approach I took to implement the MVC design pattern in Flutter. It’s been revised to convey the recent library package’s functions and features. In this article, I’ll be demonstrating mostly its implementation.

Flutter + MVC at Last!

The library package attempts to offer the MVC design pattern in an intuitive fashion incorporating much of the Flutter framework itself. All in one lone Flutter Package:

mvc_pattern

Learn By Doing…or By Showing

Clean up all the Flutter!

Keep It Simple!

main.dart on Github

The ‘Contacts’ app is a small simple app. Above, you see Flutter’s runapp() function is accepting a ‘Widget’ called, MVC. This merely starts the app following the ‘Model-View-Controller’ design pattern. It represents the following composition: MVC(View(Controller(Model())))

You can say it represents the ‘lines of communications’ of the three components involved. For example, you see the class, ContactsExampleApp, comes from a file named, View.dart. Coincidence? I think not!

ContactsExampleApp on Github

Looking at the class, ContactsExampleApp, you see it extends the class, ViewMVC. Small apps generally will have, at most, two screens working on one data set. This View class was written for just such kind of small simple app. In this case, an app for a list of contacts — it’s going to have one ‘home’ screen and one database. There’s not going to be too much code that makes up the app itself. It’s the class, ViewMVC, that does all the ‘groundwork.’ It’s part of the mvc_application library package and is listed below.

View on Github

A Lot Going On Underneath

And so, deep in that mvc_application package, there’s a build() function somewhere that takes in that View’s parameters and passes them on to a MaterialApp object. Here’s a snapshot of it listed below.

build() function on Github

There’s a lot I’m skipping over regarding the framework for now just so to supply a quick overview of ‘the players’ involved in this simple app. We’ll get into the nitty-gritty later.

Your Home

ContactListPage is assigned to the Widget, home
ViewMVC on Github

Note, if you don’t supply a ‘home’ widget, your app will simply convey to the user a loading screen: A circular rotating graphic in the center of the screen.

TTThe class, ViewMVC, has since been modified with the build() function removed and returning to the build() function found in its parent class, AppView. ViewMVC on Github dated Mar. 12.

The ‘C’ in MVC

ContactListPage on Github

The View’s State & The Controller

For clarity, I’ve placed ‘Contacts’ code in the functions initState() and dispose(), but they could have been placed instead ‘inside’ the Controller itself. Inside the Controller’s own initState() and dispose() functions. They would then be called at the line, ‘super.initState()’ and ‘super.dispose()’ respectively. To make a point, for now, they’re outside here in the _ContactListState object — the ‘View’ part of the app. Remember, in my MVC interpretation, every build() function is a State object that serves as ‘the View’.

The ‘M’ in MVC

Contacts on Github

Everything In Its Place

Note, I didn’t have to keep the prefix, ‘Contacts.’, of course, since they’re defined in that library file, but I did merely copy n’ paste after all. It would, however, be better design practice to remove the prefix static references. See below.

The ‘Contacts’ prefix is removed.

Nothing In Its Place

However, I do have a function called, onError(), in there. Now, what’s that all about? I’ll talk about Error Handling a little later.

What’s Your Source?

ContactsService on Github
Flutter and a SQLite Database

Further information on the library file for SQLite in Flutter can be found in this past article.

Divide and Conquer Code

It All Comes Back Around

TTThings have evolved since this writing. The framework has become more layered and more adaptive as you see with the image of the class hierarchy now as of March 12th:

The ‘App’ in MVC

AppView on Github

Above is a truncated screenshot of the ‘App View.’ Again, the class, ViewMVC, extends this class, and so you see the many parameters that will eventually be passed on to the MaterialApp object. It’s an abstract class where its build() function, like its parent class, StateMVC, has to then be implemented to provide ‘the View.’ So, when you create your own screens with the class, StateMVC, you too will have to implement its build() function to provide ‘its View.’

A Controller at the App Level

So for small simple apps, there’s an ‘App Controller’ assigned to your app to do some ‘common operations’ for you that most Flutter apps will require. The class, AppController, is short in length and so easily listed below, but it does a couple of interesting things for you. Note, the arrows below.

AppController on Github

So, what do you see? This class extends the class, ControllerMVC — the ‘work horse’ for the MVC design pattern library package, mvc_pattern. The arrows highlight some functionality included in the app. Again, my ‘toolkit’ is accessed, and at every Prefs reference, it provides the app a means to retrieve and save its preferences; it’s application settings.

Note, the last arrow highlights the ability for the Controller to respond to the system events (for example, of the app is minimized and placed in the background). I’m merely calling the parent class in this screenshot just to highlight the didChangeAppLifecycleState() function. I may have to dedicate a separate article on a Flutter app’s lifecycle. We’ll see.

I also noticed there’s no ‘@mustCallSuper’ annotation on the init() function. I think that’s a bug! Don’t you? I’ll look into it after. ;)

The App’s Assets

An Error In Our Midst!

The class, StateMVC, has an error handler. It’s there where the unexpected happens. By default, if an error does occur in the framework, the response is the old ‘red screen of death.’ However, you're invited to override the error handler (the function, onError()) and implement your own response to possible errors. The State object is exposed to a number of external elements in the course of an app’s lifecycle — you're invited to make it more resilient to that fact.

onError() on Github

Developers should always use try…catch statements judiciously in their code. Of course, like documenting our code, we developers are ‘always’ on top of it when it comes to ensuring our code has sufficient ‘error handling’ capability. *nudge nudge* *wink wink*

So maybe, looking at the responsibility of error handling, you could think of it as it’s not so much your code you have to worry about: It’s the other guy’s! At that State object level, the class, StateMVC, is part of the ‘View’ in this MVC framework. It should have the means to possibly overcome any errors from its Controller(s) or its Listener(s).

It’s In The Framework

Embrace the Flutter!

Like any standard, the framework is made up of a list of explicit rules on how the code is organized. So any developer, knowing those rules, can get to work on the code…fast. I use these rules for my own apps. And so, in my case, week, months, or even years later, when I return to a particular app, I’ll know where everything is, and I can get to work…fast.

“MVC” From the Start

You can readily see the code involving the ‘User Interface’ for this small simple app is under the ‘View’ directory. The code concerning the ‘data source’ is under the directory, Model, and the class, Contacts, that extends the class, ControllerMVC, is under the directory, Controller. Clean. Organized.

The Dart Way

Further, the convention is to now leave the main.dart file all alone and place all the rest of the code that makes up the app into a subdirectory called, src/. Code under lib/src is considered private. The idea is other packages (external programs) should never need access to or need to import from the directory, src/. However, if you want to make some of them public, you are to ‘export’ those lib/src files from a file that’s found directly under lib/ directory. Interesting, no?

Have you been following these rules in your projects? No doubt, yes. Well, let’s now turn to some ‘MVC’ rules. Looking at the small simple app’s directory structure again, you can see I’m holding tough to the Model-View-Controller configuration with even having some directories and files named after these three components.

So looking at the three directories below, again, you can readily see where all the code concerning the app’s ‘data source’ lives. You know where all the ‘interface stuff’ lives, and where all the code controlling the event handling for this app resides. Clear and clean.

Now, how about those three files? Remember that Dart convention: If you want to make private files under the src/ directory publicly available, you’d ‘export’ those files from a file that’s found directly under the lib/ directory. Now guess what’s in those files. Or not, look below:

controller.dart
model.dart
view.dart

At a glance, you can see each individual file export files from their corresponding directory. However, why would one do this? It seems like an unnecessary extra layer of abstract, doesn’t it? Well, there’s a ‘meaning to the madness.’

Import Dependency Injection

The ‘show’ directives indicate what comes from the ‘View’ aspect of the app.
The contents of lib/view.dart

What’s Your View

Note, the first import statements injects the ‘Model’ into this class to be used by the Controller in keeping with the traditional interaction of the three MVC components.

What’s Coming From Where

With the ‘show’ directives removed for now.

‘Show’ When Finished

Subsequent developers can, at a glance, then determine what classes are of which part of the MVC design pattern as well as readily see what classes are involved in that particular library file. Nice.

There’s More Than One Way To Skin A Controller

Turn to the class, StateMVC, for a reference to the Controller called, controller.

Minimize Change

With the name of the class is changed to ‘Controller’, for example, you could switch out different Controllers throughout the lifetime of the app without disturbing the rest of the codebase. Just a thought.

A Final Approach

If you don’t want to use StateMVC’s ‘controller’ property.

An Added Import

The third line tells you the Controllers involved. Again, in this case, it’s one called, Controller. This standard approach with import statements gives the developer some structure to follow, some consistency…a framework.

ContactAddPage on Github
Clean up all the Flutter!

The ListView widget above contains a list of textFormField types you may have never seen before. You can read up on them in a past article provided here.

What’s in a Name? Everything!

Add a Bit of Clarity

Highlighted with arrows, you can see ‘the data’ is actually coming from another class, ContactService. The ‘View’ aspect of the application (the user interface) doesn’t know this, and it doesn’t need to know. Modular.

You can see three ‘classes’ that make up the CRUD aspect of the app, ContactAdd, ContactEdit, and ContactList, all inherit one after the other. The last class, ContactFields, simply lists all the database fields involved. Below is just the first portion of that class.

ContactFields on Github

The Contact is the Data

Take Advantage of The Dart Advantage!

To Come…

Cheers.

Freelance Developer