Walkthrough apps written with an MVC software framework.
Examine this class below. Know that it extends the class, StateMVC, that comes from the Dart package, mvc_pattern, and itself extends the class, State. Note, this class is an abstract class, and so there’s some function(s) to be implemented. And finally, note the many parameters destined for a MaterialApp Widget somewhere.
This class is found in the Dart package, mvc_application, and, as it pertains to the MVC design pattern, is considered the ‘View’ aspect of the app. Looking at the first little red arrow above, you can see there’s a reference to this View’s corresponding Controller. This class is given the very descriptive name, AppViewState, while its Controller is called, AppController. Hence, they are both the ‘View’ and the ‘Controller’ for the app as a whole.
In this article, we’ll examine the Dart package, mvc_application, in some detail. To do so, we’ll walk through the two example apps, weather_cast and then contact services while continually ‘look under the hood’ at this underlining MVC software framework that was used to make them.
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.
This is Part 3 of a ‘free’ three-part series examining an approach to organize and develop your Flutter apps following the concepts behind the MVC design pattern. The series further introduced the Dart package, mvc_application, as it is the software framework providing the generic functionality and standard approach to making a Flutter app — using MVC as a guide.
Now back to the class, AppViewState. It’s an abstract class, and the function, build (), is the function to be implemented. In this interpretation of the MVC design pattern, the contents of the build () function presents ‘the View’ aspect of the design pattern, MVC.
In the example app, weather_cast, in its main.dart file (see above), the ‘View’ of the app is considered the class, WeatherApp. Now, it extends the class, AppView. Well, guess what that class extends? It extends the first class you saw above, AppViewState. The class, WeatherApp, is listed below. Look at the parameter values passed to its parent class, AppView.
In the screenshot above, we see ‘the View’ has passed in the following parameters: the title of the app, it’s Controller and the home screen Widget. Note, this class has also overwritten the function, onTheme (), with what you would guess correctly is a getter from a Controller class, con.ThemeCon.theme.
Further, notice the ‘as’ clause is used because both the View class and the Controller class have the same name, WeatherApp. The clause makes the distinction allowing me to reference properties from both classes.
Let’s continue up the class hierarchy. What follows now is the AppView class. We’re going inside the very software framework. Remember, this class extends the abstract class, AppViewState. It is in this class where the MaterialApp Widget is instantiated; where a great many of those parameters are finally put to use. The first red arrow highlights the fact that the ‘debugPaint’ properties are also utilized here, allowing a developer access to those visual tools while developing their app.
Notice the second arrow above depicts the fact that if there’s no ‘App Controller’ provided, it’s created and supplied to the class, AppViewState. Below, the class continues, and you can see, the MaterialApp widget is indeed instantiated. The next red arrow directs you to the fact that the ?? operator is repeatedly used to supply a non-null parameter value if possible to the MaterialApp widget.
Finally, the last red arrow reveals that if there is no theme parameter value passed to the app, weather_cast, (which there wasn’t) then the function, onTheme (), is called, and if it’s implemented (which it was), it will provide the ‘theme’ of the app. In this case, when the example app changes its location, and consequently its weather conditions, the colour of the app’s interface will change with it because of the overridden onTheme () function.
In the main.dart file, we see the main () function. Of course, it is here where the Flutter app begins. This is the beginning of the underlining software framework as well. You can see the MyApp class extends the App class. The App class comes from the Dart package, mvc_application.
Let’s look under the hood, and examine the App class. It’s not listing the whole class, but the screenshot below displays a good portion of the first half of the class with particular points of interest highlighted with an arrow.
For example, the first arrow points to the abstract function, createView (). You’ve seen it before. Look above. It makes sense it’s abstract. After all, you’re to supply the ‘View’ when you start up your app. Now, the second arrow below shows this function, createView (), is then called in the function initApp ().
It’s the third arrow that highlights a particular point of interest. This framework utilizes a FutureBuilder Widget to ‘set up and ready’ the app. In other words, any asynchronous operations that simply have to complete before the app can proceed are implemented using the FutureBuilder Widget. Note, the parameter, future, takes in the the function call, init ().
The fourth arrow shows you where a little spinner appears in the center of the screen while the asynchronous operations complete. It comes about from the class, LoadingScreen. When they do complete, the StatefulWidget, _AppWidget, is then instantiated. Guess what’s its corresponding State object returns with its createState () function? It’s the class, AppView (the class WeatherApp extends). In other words, the build () function of the class, WeatherApp, originally instantiated back in the main.dart is now run.
Finally, the init () function itself is listed in the App class. It’s displayed again below. The App class’ build () function runs again, by the way, if and when a ‘hot reload’ is issued. The App class has its reassemble () function fired when a ‘hot reload’ is issued. Note, the property, hotLoad, is set to true, and so the createView () function is fired again.
Note, the App class extends the class, AppMVC, and AppMVC class extends StatefulWidget class. Again, it is the App class that is passed to the function, runApp (). Know that its corresponding State object class is _AppState, and it’s in this class’ initState () function that calls the class App’s initApp () function. Essentially getting the ball rolling with the app start up.
In the second screenshot above, the function call,widget.initApp() , takes you now to the function, initApp (), in the App class. See below. Note, in that function, there is the line, _vw.con?.initApp(). When that line fires, it takes us, in the case of example app weather_cast, to the initApp () function in the WeatherApp class. As it happens, in there, this Controller’s State object adds in a second Controller called, ThemeCon. The Controller, ThemeCon, is involved in ‘changing the colour’ of the app when changing to a new weather forecast.
If you’ve read the two previous articles, you know even the directory structure and file names conform to a standard conveying the MVC approach. And so, even the class library file, mvc_application, follows such an approach to organize its files and directories. Like the sample app, weather_cast, the class library listed below on the right also has its “MVC” files and directories.
Design patterns are about a standard way of organizing of code, and it’s about the separation of the work and responsibilities. Therefore, however, it’s then also about delivering a standard API so these separate areas of work and responsibilities can then talk to each other.
In the two examples app, when the Controller is ‘injected’ into the View you see there is a consistent approach. In both, the Controller class is instantiated as a parameter for the non-default superclass constructor. It’s then the software framework that provides an object reference of that Controller with the property, controller. It’s that object that then serves as are part of the API to communicate to the Controller.
In each screenshot above, you’re given a instance reference to the controller. In one example, it’s the instance variable, _weatherCon, of type WeatherCon. In the other example, it’s the instance variable, con, of type Controller. The type for the property, controller, is of type ControllerMVC of course.
You are to use these instance variables to now ‘talk to’ the Controller and retrieve, as it pertains to MVC, the data to be displayed by this View. And so what would the API look like? Envision the ‘View team’ is given a list of function calls from the ‘Controller team’ to be used to access the data. Now what would that list of function calls look like?
One option is for it to be anything you like. Dictated by the functions and features demanded of the project’s specifications, the public functions names and the name of public properties could reflect the functionality required.
For example, in the weather_cast app, we know there is a city name to be entered, and then the current weather conditions for that city is returned. Therefore, in the build () function (i.e. in the View) you can guess there is the word, ‘city’, somewhere and likely the word, ‘location’, and of course the word, ‘weather’, probably. You’d be right. You can see below, looking at the red arrows, the View is able to talk to the Model, model.Weather weather = _weatherCon.weather , and to the Controller, _weatherCon , and so knows the public functions and getters of both the Controller and the Model to supply the data and the functionality dictated by the project’s specifications.
Personally, although I’m not yet fully committed to the approach, I’m trying to use Flutter’s own API (it’s own public function names, getters, and properties) as a guide with regards to the very names I would use for my own function names, getters, and properties. And so, in the screenshot below, looking back at the build () function, you’ll see there’s two instances where I simply mirrored the name of a widget’s named parameters to the callback functions when implementing the specified functionality. In other words, there’s an onPressed () function for the named parameter, onPressed, and there’s an onRefresh () function of the named parameter, onRefresh. Note, however, the onPressed () function still has the wherewithal to take in the parameter, city.
In the StockMVC example app, you can further see a sample of the variations you can impose. Below, is two examples. In the first screenshot, the View (the build () function) has to know the name of the particular ‘fields’ to be displayed. It doesn’t need to know the type of widgets they happen to be, but it has to know the names of the particular data items to be displayed.
The Controller references in the screenshot are all in purple. Note, in two instances where there’s two red arrows, the Flutter API is merely mirrored in name by the Contoller’s getters. The second screenshot, takes it further however, and all the names of the functions and getters used to talk to the Controller reflect Flutter’s own API.
Looking at the second screenshot, you see the ‘Flutter API as your guide’ approach is taken on fully with the View only needing to know there’s a Controller getter matching the name, in this case, of its particular named parameters for its Scaffold widget. Makes for talking between the areas of work and responsibilities a little easier to development and maintain.
Next, is a little exercise on what paths are taken in the Contact Services app to get the data required by the View. It this particular app, the View gets the data from the Model but only by going through the Controller. Let’s see the path taken.
In the first screenshot, the getter, list, references the library-private variable containing the instantiated class, ContactList. Looking at the ContactList class in the second screenshot, we’re now in the Model portion of the app. The ContactList class extends the ContactFields class listed in the third screenshot. The last screenshot highlights all the ‘data’ references provided by the Controller in the View.
The example app, weather_cast, records the locations entered by the user in the app’s preferences. And so, the next time the app starts up, the last location is brought up — displaying its current weather conditions. When you use this Flutter framework, the preferences capability comes readily available.
Back in the home screen’s controller, WeatherCon, we see the mechanism provided to store and retrieve the ‘city’ location last entered. The red arrows highlight where the location is initially retrieved, and where the last city entered is recorded in the system’s preferences.
This framework uses another library package called, Prefs, to work with the plugin involved when reading and writing to the system’s preferences. This library in initialized and later disposed by the framework, and so you don’t have to worry about it. You merely call any of a number of static functions made available to you.
Below are the screenshots that depict the path of execution where the Prefs library is eventually initialized when any app using this software framework first starts up. The App class will instantiate the FutureBuilder widget supplying an initial data value of false, and then execute the function, init (), to return a Future object of type boolean. In the init () function, you see this View’s controller (if any) has its own init () function called.
In the example app, weather_cast, there is indeed a Controller for the View, and it’s the WeatherApp class. You can see its parent class, AppController, has its init () function first called, super.init(), and it is in this function that the Prefs library is initialized by the software framework. It then returns to the ‘Weather Cast’ Controller to set up features unique to that particular app.
In the last screenshot above, there is the static function call, DeviceInfo.init(). This supplies the software framework and subsequently any app written with it a wealth of information regarding its underlining platform (i.e. the mobile phone). Take a look below, and you’ll see a lot of information about the phone is made available if and when you need it.
Included in the framework, of course, is a means to ‘talk to’ databases. Admittedly, it currently has only SQLite in the Dart package, dbutils, but there’s room for others in the future. We’ll now look to the example app, contact services, as it involves working with a database. Note, there is an article dedicated to this Dart package, SQLite in Flutter, for your review.
The next three screenshots below trail through path of execution when the the example app, contact services, first starts up. The ‘home screen’ for this app is the class, ContactListPage.
Note, in the last screenshot, this ‘View’ is also injected with its ‘Controller.’ As it happens in this app, the Controller class is named, Controller. The red arrows highlight where the Controller is referenced. Two of which are static calls. Note, the property, controller, is the instantiated object of the class, Controller. It’s just here to demonstrate how the framework supplies such a property.
Let’s look at the framework at work. The first screenshot below is the State object’s initState () function. In there, it loops through all the controllers that were added/associated to the StateMVC object that is the class,. The second screenshot is the Controller named of all things, Controller. It had overwritten its initState () function, introducing its own init () function, one that calls system’s preferences (either the sorted alphabetically or not), and to ‘refresh’ of the list of contacts. The init () function calls the Model’s own initState () function.
From the Controller, we’re now gone over to the Model-side. The initState () function in the Model class, ContactsServices, calls it’s parent class, DBInterface, own function, init (). In the last screenshot, we see the class, DBInterface. We can see its init () function contains its open () function — getting to the database and opening it.
Let’s walk through the code used to now list out the current contacts if any in the app. Back in that initState () function, we see after opening the database, it’s put to task to list its contents. You see below the list is ‘refreshed.’ The refresh () function is also found in the class, ContactList.
And so, in the class, ContactList, we see the function, refresh(), is ironically called by the Controller in this case only to call the Controller in turn. It calls the Controller’s getContacts () function and then its rebuild () function to ‘rebuild’ (by calling setState()) the Widget tree.
In the Controller’s getContacts () function, a function in the app’s Model is called. The class, ContactService, has its own function, getContacts (), called returning a List of objects of class type, Contacts. Finally, if indicated in the app, the returning List of ‘Contacts’ is then sorted alphabetically.
The Model for this app, ContactsService, extends a class which is part of the Dart package, dbutils, called DBInterface. And so, calling its function, getContacts (), to retrieve all the contacts stored in the database will invoke the SELECT statement, SELECT * FROM Contacts WHERE deleted = 0 .
When it makes that call, the function, rawQuery (), in the dbutils library is there to make the query. If there’s data found in the database, a List of Map objects, Map , is returned.
You may have noted the Banner Ad displayed along the bottom of the screen when running the app. That’s an Admob Ad. Well, it’s a test ad actually, but it is demonstrating the Ads library package that’s brought to use in this example ad. Now, it’s not part of the underlining framework. I did write the Ads library package as well as, but chose not to include in the MVC framework. It is a ‘heavy’ resource, and making it readily available as a Dart package would suffice for most circumstances.
You can see the Ads object is initialized in the ‘library-private’ constructor listed above. Note, the class has a factory constructor assuring the Singleton pattern produces only one instance of this ‘App Controller.’ The class will have its initState () function.
Anticipate a new article the First Friday of every month.