Bazaar in MVC

Greg Perry
Follow Flutter
Published in
23 min readOct 4, 2019

Google’s Provider Design Pattern converted to MVC Design Pattern

I turned to the website, flutterawesome.com, to find a sample app that I would then implement into it the MVC design pattern. e-Bazaar is a wonderful sample app provided by Bugudi Ramu highlighting an array of UI features available in Flutter. With his permission, I adapted the MVC pattern into this app just to see how it would turn out. This article will detail the experience. As it happens, some further functionality was introduced to highlight what features are available in the MVC framework that I used in my conversion — to a sample app originally designed to demonstrate Flutter’s user interface capabilities.

e-Bazaar sample app

Merely An Exercise

This was merely an exercise. I found while developing apps using the MVC framework, I would draw in an innate bias. You see, while building an app from the ground up, I would have the tendency to ‘accommodate’ the code to work with the framework — I don’t want that to happen. And so, I began to seek out existing sample apps to see how and if the MVC framework could be implemented and yet result in the exact same app with regards to ‘look and feel’ and functionality. After all, this was a framework — it’s to be the foundation of any app and should work with any app. I found the sample app, e-Bazaar, to do just that.

Again, this exercise was no reflection on Bugudi Ramu’s own programming. His is a very good app. He intended it to only be a sample app. Further, he had no intention of applying any sort of pattern or structure to it. It was to reflect an interesting user interface alone. It was that that attracted me to it.

Admittedly, this article is little to do with the implementation of MVC to the e-Bazaar sample app and more about the MVC pattern interpretation itself — as it’s implemented by the library package, mvc_application. In fact, this article is not only to highlight the merits of using a design pattern in developing software or to demonstrate how the use of a design pattern accentuates attributes that accelerate software development. Attributes, for example, that allow for multiple developers to easily work on the project separately yet in parallel; that allows new developers to come in at any point in time and over time and easily get to work in developing and or later maintaining the app. This article is to also highlight the particular capabilities of the library package, mvc_application, as well. That’s because it already supplies the functions and features commonly found in every mobile app thus expediting the overall development with built-in capabilities.

I Like Screenshots. 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. 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.

No Moving Pictures On Social Media

Further, there are a few gif files in this article demonstrating functions and features in ‘moving pictures.’ However, I’m told viewing such files is not possible when reading this article on platforms like Instagram, Facebook, etc. You’ll see empty square spaces where the gif files should be. Please, be aware of this. I would recommend reading this in a browser on medium.com.

Let’s begin.

Other Articles by Greg Perry

MVC in its Code and in its Directories

In all the projects I’ve worked on that involved this MVC framework, I’ve arranged the directory structure itself to reflect the three components of the MVC design pattern:

model - the data, view - the interface, controller - the logic

Doing so organizes the source code into these three discernible roles allowing for other developers as well as myself to return to the project at a later time and readily determine where certain pieces of code reside and what it does.

Again, there was no intended directory structure in the original sample app. However, in the ‘MVC’ variation displayed below on the right, you can see the directories are presented with an intended structure. The ‘MVC’ variation is, of course, available for download on Github, bazaar.

Even if you’re not familiar with my past articles on the subject, you could possibly discern ‘the role’ of each Dart file listed below. What each does in the app and possibly even where in the app each file is to execute.

bazaar/lib

As an example, there’s a little red arrow displayed in the directory listing above. It’s pointing at a list of Dart files found in a directory labelled, view. Looking at the parent directory names staggered above that one, if you guessed these were the screens listed on a drawer widget found in the app’s home widget. You’d be right.

Note, during the design pattern integration, I made the effort to preserve as much as possible the existing code and the names of the Dart files from the original sample app and yet convey now the intended organization and structure.

Further, you can deduce there is a login screen for this app. Again, looking at the directories, you’ll find a folder named, login. In that folder, you’ll see the three folders with names associated with the MVC design pattern. Thus, you can see where the code for the data, for the screen, and for the logic is all located. As it happens, the directories are listed in alphabetical order and not conveniently in order of the design pattern’s abbreviation: MVC.

So there you are. You can see there are two screens (or views) involved in the logging into this app. One is named, loginPage.dart, and the other is named, signupPage.dart. Each has a corresponding controller under the folder, controller (you can guess by their file names which one is for which), and the ‘data source’ involves Google’s Firebase. All that from just looking at the directory structure. You could, therefore, utilize the directory structure itself to delegate your teams to separate ‘aspects’ of the app.

Who Talks To Who

Such design patterns encourages if not dictates particular lines of communication, as it happens with regards to MVC, between the View, the Controller and the Model. Like anything in Life, breaking up things into its amenable parts makes the developing and the maintenance that much easier. Easier for what on the whole may be a very complex system.

Embrace The Flutter

I found all the currently popular design patterns offered to Flutter developers simply places the design pattern ‘on top’ of Flutter. In some instances, they would impose ‘their way of doing things’ on top of the native framework. All by varying degrees, of course, with the Provider pattern (Google’s proposed pattern) possibly being the least obstructive and arguably the Redux pattern being the most. The MVC design pattern offered by the library package, mvc_application, doesn’t fight against but works with the Flutter framework. It embraces it — using the framework’s very characteristics.

For starters, the State class is seen as an important player in this MVC design pattern implemented for Flutter. So much so, that State class is extended to make a new class specifically for the MVC framework called, StateMVC. Instead of using the regular State class, when working with this MVC framework, you’re invited to use the class, StateMVC.

Now, why would you do that, you may ask. There are a few reasons. Firstly, because in this particular interpretation of the MVC design pattern for Flutter, it’s the contents of the State object’s build() function that’s considered ‘the View’ for the MVC design pattern. With the ‘vanilla version’ of Flutter, the widget returned from the build() function represents the app’s ‘interface’. Well, so it does in this MVC framework as well. The code there returning the widget is seen as the code for the ‘View’ in MVC. Below is a screenshot showing the first few lines of code of the StateMVC class. You can see it’s an abstract class. That’s because you’re to implement its View — that being its build() function.

class StateMVC

What’s Your Life Cycle?

Another reason to use the class, StateMVC, would involve a mobile app’s life cycle. With my Android background, one of my first ventures into Flutter had me looking for how a mobile app’s life cycle is conveyed in the Flutter framework. Every app has a life cycle, and it should be a consideration when you’re developing it. As my fellow Android developers attest, there is a list of functions found in Android’s ever prominent Activity class that readily captures the varying ‘states’ that could possibly occur throughout a mobile app’s life cycle. Well, there is such a list of functions in Flutter as well, but you need to make a new class to access them.

The event handler’s in Android’s Activity Class

I found an equivalent of sorts in the function, didChangeAppLifecycleState(). This function is found in the abstract class, WidgetsBindingObserver. In this case, it’s passed an object of type, AppLifecycleState, that represents the current state of the application. The screenshot below highlights the possible states currently passed to the didChangeAppLifecycleState() function.

mixin StateListener

I felt this class contributed a very desirable trait for a framework, and so the class, StateMVC, extends this abstract class. Doing so then provides access to the application’s life cycle by means of the didChangeAppLifecycleState() function. Doing so also provides a list of other event handler functions. All these functions are highlighted in the right-most screenshot below.

While this abstract class is extended, an ‘empty class’ called, StateListener, is also used by the StateMVC class to implement those event handlers. The idea being you can then selectively override any one of those functions to address particular system or user events in your app. A very helpful capability.

You can see that in the screenshot of the StateMVC class below. It may look complicated, but don’t worry about that. It’s a framework after all and is suppose to look complicated — making it easy for you to use it to build apps.

Control The State

Now wait a minute, you might say! If you have any familiarity with the MVC design pattern, you’d recall that it’s the Controller and not the View (StateMVC) that’s to address any particular system or user events. You’d be right! That’s the last but not least reason to use the StateMVC class. That’s because it knows how to work with the class called, ControllerMVC. It’s that class that you would use to address such events. You’ll see how that done shortly later on in the article. You see, the StateMVC class calls upon the Controller (or Controllers in turn) to address each event that occurs. You are to implement that class to address such events. However, that’s not the only reason to use the ControllerMVC class.

It’s Immutable. Keep It That Way

So why does Flutter have immutable objects called widgets? For example, why are the classes StatelessWidget and StatefulWidgets immutable? While a StatefulWidget’s State object typically has mutable content.

It’s a performance consideration. Such immutable elements in a computer program are, by definition, unchanging (once built, they’re never updated). Consequently, they’re then relatively cheap to run in terms of CPU and GPU cycles. That’s good. They don’t change state.

That’s why you may see the keyword, const, everywhere in a Flutter app. The keyword, const, means that the value of that instantiated class, for example, is known at compile-time and it is going to be constant for the whole duration of the application. In such a reactive UI like in Flutter, large parts of the widget tree are rebuilt regularly. Every single object (and all of its own variables) used in every single widget is recreated again and again on every single rebuild — except those marked withconst. In that case, the same instance is reused throughout the lifetime of the app. That’s good. You’ll see the keyword, const, used a lot in this sample app.

And so, parts of the Flutter framework is ‘stateless’ and hence immutable. That’s by design, and should be kept that way. However, if you’re like me when you were first learning Flutter, you’ve undoubtedly placed mutable variables, for example, in a StatefulWidget and had gotten the following warning:

Mutable fields in an immutable class

Well, if not there, where are you to place the logic for your app? By its very nature, such code changes over time in response to system and or user events! It’s the logic! Where does it go? Well, I know where it goes in the MVC design pattern. It goes in the Controller. And in this case, into the class, ControllerMVC. Of course, with the ‘vanilla version’ of Flutter, such code would typically go into a State object. One place for all that code. However, using this MVC library package, you’ve now got some options.

With this framework, you’re free to associate any number of Controllers to a particular View (StateMVC) to your heart’s content. Having more than one Controller allows you to ‘break up’ the logic into more manageable parts. Each controller, for example, could be concern with a different aspect of the app’s capabilities. There’s no need for it to be involved with the other Controllers. This allows for modular code. It allows for parallel development. You can have separate teams of developers responsible for their separate piece of the app — all encompassed in its own separate Controller with as many mutable variables as necessary. All with ready access the one View (StateMVC).

Talk To The State

The first screenshot below displays the start of the ControllerMVC class. You can see highlighted in the two adjacent screenshots that this Controller, in fact, shares the same functions you would find in Flutter’s own State object. Remember, embrace the Flutter. You’re going to love this.

It does that so the code you would normally place in a State object’s initState() function, for example, you could instead place in a Controller object’s own initState() function. Of course, being a Dart library file, this Controller can contain any number of classes, any number of high-level functions and any number of high-level variables. Nice. So what else does this mean?

Build The Tree. Control The Tree.

In Flutter, as you know, to indirectly call the build() function of a State object (to rebuild that part of the widget tree), you call that class’ setState() function. Well, guess what. The Controller has that function too. Calling that function in a Controller will call the build() function in the StateMVC object it’s associated with. When an event occurs and the logic addresses it, it can then tell ‘the View’ to rebuild the interface to reflect its response to that event. Essentially, the two can talk to each other.

All you can do in a State object, you can do in the Controller…any number of Controllers. So it’s there where you’d put ‘the logic’ that makes up your app.

We’re Home

Getting back to the sample app, and it’s use of the MVC design pattern in this framework, we can readily find the ‘home page’ for this app in the directory structure. There’s a folder called, home, under the directory, src. In there, there’s another folder called, view. That’s the only folder there and so the code in that folder must involve the interface — it’s a screen. In fact, this tells you in more cases than not, that the class that’s passed to the MaterialApp’s named parameter, home, lives in that folder. Looking below you can see there’s one lone Dart file named, homepage, in that folder.

Looking at the subsequent screenshots above, you can see the naming convention of the folders reflects those keywords used in Flutter and consequently gives you an idea of what does what and wherein the app.

In the second screenshot, for example, you can readily determine the ‘home’ widget involves an appbar, a drawer, and a database (model). You’d guess correctly that a Scaffold widget is returned from the Home widget’s build() function. Finally, in the last screenshot, you see there are a search feature and a shopping cart displayed on the appbar. There’s a Controller called, HomeAppBar, which undoubtedly responds to when the search icon and the shopping cart icon are tapped on by the user. All at a glance.

What’s Your View

The screenshot below is ‘the View’ MVC component for the Homepage. Again, the build() function is considered the View in MVC for Flutter, and what you see below is the contents of the Home widget’s build() function. Now like most MVC design patterns, the View usually has access to it’s associated Controller or Controllers. The red arrows in the screenshot below highlight how this particular View ‘talks to’ a particular Controller called, HomeAppBar.

As you see, this View draws on two specific objects from the Controller, HomeAppBar, to then be displayed in the app’s appbar. Do you see the separation there? The Controller is free to later make changes in the code behind ‘the search object’ and ‘the cart object’ without effecting one character of code in the screenshot above. Modular programming. Decoupled code.

Below, we have a screenshot of that Controller. We discover there that ‘the search object’ and ‘the cart object’ are in fact two getters. They’re two IconButton objects all self-contained with their chosen icon and the defined event handlers. Further, the directory structure hints there’s a showSearch object involved and the folder contains the ‘delegate class’ to be used.

Two Views One Controller

Now also in the Home widget’s build() function, the class, RecentProducts, is instantiated. Well, it’s another StatefulWidget and has a State object of type, StateMVC, as well — and so, it’s another View. And as you see below, it too uses the Controller, HomeAppBar. There’s no law you can’t use a Controller in more than one View. In fact, it’s encouraged. This View displays the main list of products in the lower half of the home page. Now, why does this View need the HomeAppBar Controller as well, I wonder?

Last In, First Refreshed

By design, the StatefulWidget, RecentProducts, is created after the StatefulWidget, HomePage. After all, the ‘HomePage’ widget is created and then the ‘RecentProducts’ widget is created in the ‘HomePage’ widget’s build() function. Because of that, by design, when they share a Controller, and that Controller runs its ‘refresh’ command (calls the setState() function) it only rebuilds the last StateMVC object it was added to. Get it? See the refresh() function below? The gif file beside the screenshot below demonstrates the search routine. When the user, for example, picks the word, jeans, the refresh() function calls the build() function in the ‘RecentProducts’ widget to display just jeans in that portion of the screen.

And so it operates with LIFO (Last In, First Out). Any commands directed to a State object will affect only the State object last associated with that Controller. When State object (hence that View) terminates, the previous State object is now back to being the ‘current’ state object. Understand? Not only can you have multiple Controllers for a View, but, over the lifetime of that Controller, it can be associated with multiple Views. As you go in and out of screens, one Controller could be responsible for ‘the logic’ behind them. Nice.

Three Controllers And A View

Below is a screenshot of the StatefulWidget, HomePage. Here you can readily see how three Controllers are added to the View. The first one, ThemeChanger, is added first, and so is readily referenced using the StateMVC’s property, controller. The other two are ‘added’ to the State object within its constructor, and you have other ways of getting their references. For example, in the screenshot below, the framework offers the function, controllerByType(), as a means to get a reference to the Controller, HomeAppBar.

What’s Your Preference?

There’s a library app for your system’s preferences. It’s provided by the framework so you don’t have to install anything yourself. It’s such a common functionality needed by most apps that it’s already set up and ready to go in this MVC framework.

Bugudi Ramu supplied a means to place the sample app into ‘Dark Mode.’ He had no intention in saving this setting, and so the next time you started up the sample app, that mode would be gone. That’s fine. The framework could help with that.

The Controller named, ThemeChanger, is in charge of setting the app’s theme and subsequently its mode. When the mode is changed in this version of the sample app, it’s setting is readily saved in the system’s preferences. And so, when the app is started up again, that setting is used again. Let’s take a look at the start of the Controller, ThemeChanger, below. Notice the library, Prefs, is called in the Controller’s initState() function setting the theme with the function, setDarkMode(). As you see in the gif file below, the function, setDarkMode(), is also called every time the user taps on the switch — it’s in that function the last ‘setting’ is saved to the system’s preferences.

See The Product

Let’s look at another instance where the system’s preference capability is utilized. The screenshot below presents the first part of a StatefulWidget that displays an individual Bazaar item when it’s tapped on. That item is re-displayed alone on a separate screen. You can see the class, StateMVC, is implemented. Hence, this tells you, of course, this is a View. This screen displays an individual Bazaar item. The gif file below is demonstrating this.

You can see the corresponding Controller is ‘injected’ into the View by the import statement highlighted by the first red arrow below. The second arrow below shows you where the Controller object is instantiated and passed to the StateMVC object. It then reemerges as an object in the StateMVC’s constructor with the variable, con, of type ProductDetails, assigned the property, controller, of type ControllerMVC. You’ve now an instance of that specific Controller to be used by that State object’s build() function. See how this is all working now?

The next screenshot below is that of the corresponding Controller class with the same name as the StatefulWidget class, ProductDetails. Note, how the Dart programming languages accommodate for duplicate class names by using the ‘as’ clause on the import statement. Further, we see the Prefs class is imported as well and is called in both the Controller’s initState() and deactivate() functions.

You can also see a reference to the Controller’s View object (StateMVC) is accessed in its initState() function. This is done by assigning the instance variable, state, of type ProductDetailsState, to the property, stateMVC, of type StateMVC. Stepping back then, you see how both the View and the Controller can ‘talk to’ each other. The Controller has a reference to its ‘current’ View.

In this case, the Controller ‘talks to’ the View (the State object) to get the name of the specific product item so to pass it to the Prefs static functions, getBool() and setBool(). And so, with each item tapped on by the user, the instance variable, selected, is set true, depending on whether that product name is found in the system’s preferences and stores a boolean value of true. If true, the ‘favorite’ heart is displayed in the upper-right corner. See below.

Set The State; Rebuild The State

No doubt you noticed the function, refresh(), again. It’s called after the user taps on the little heart in the upper right-hand corner. As the name implies, it’s to ‘refresh’ the app’s screen. In terms of the Flutter framework, it’s to rebuild that portion of the widget tree. You’d normally do that using the setState() function — the refresh() function calls it as well, but with an empty function: setState((){});

However, like any good framework, you’ve got options. The function, rebuild(), does the exact same thing. Judging by its name, it’s just more to the point. If you want to use the setState() function in its traditional fashion, you’re free to do that too. In fact, it’s recommended that you do use the setState() function to ensure the function you call within doesn’t return a Future object. Below is the same stretch of code, but using the two other functions instead: rebuild() and setState()

The same example using the rebuild() function and then the setState() function.

Control The View?

I’ve discovered with every Flutter project I’ve done using the MVC design, there’s a decision to be made as to how much of the ‘look and feel’ of the app’s UI is the responsibility of the View or of the Controller. Granted, on paper, the View is to be most concerned with the app’s interface, but in practice, I’ve found it’s more feasible for the Controller to supply the components that make up the interface in some instances. I’ll explain.

For example, the little magnify glass and shopping cart on the app’s appbar in the upper right-hand corner — remember where they’re coming from? You’ve already seen where in the large screenshot above. They’re coming from the Controller. The View and the Controller both named, ProductDetails, are again displayed below. This time you can see how the getters, shopping and iconButton, from the Controller are accessed in the View’s build() function.

The Controller is to address user events. In this case, if and when the magnifying glass or the shopping cart is tapped by the user. As you can see, the logic for such an event is in the Controller. It just turns out that placing the whole IconButton object into the Controller makes development and maintenance that much easier. The Controller imports all the code and uses all the necessary variables and logic to address the event as well as save info to the system preferences — and the View is none the wiser. The View part doesn’t know about the system preferences. It doesn’t need to.

Notice how I gave the ‘heart’ icon the more generic name, iconButton, in the View? That’s so later on in development or during the maintenance phase if the ‘powers that be’ want to change that heart to a star for some reason, the getter need not be changed at all on the ‘View side.’ Any changes necessary will be done in the Controller alone. Modular programming. Decoupled code.

Maybe we should change the getter, shopping, to a more generic term. No? It’s a judgement call on my part to make this distinction. One you may make on your own Flutter project as well.

Get In and Get Ads

There are two additional library packages explicitly used with this sample app. The screenshot of the pubspec.yaml file below shows not only the package, mvc_application, used to supply the MVC framework, but also shows two additional packages, auth and ads. One, of course, for login authorization into the app while the other allows you to place ads into your app. As it happens, I wrote these two library packages as well.

Control The App

Now, where would such libraries concerning the logging into an app and displaying ads be initialized and set up? I mean, they're not really part of the online shopping aspect of the app. They’re features common to any sort of mobile app. Remember, a framework should keep things organized with everything in its place. Now, where would you put code used by any sort of app? Under the folder, app, of course. See below.

In this project, under the folder, app, you’ll find two other folders with names you’ll recognize: controller and view. In the screenshots below, we’ll first take a quick peek at what’s in the folder, view. It’s an ‘App View’ class with the name, BazaarApp. It’s this class that calls the ‘Login’ page and goes on to the online shopping aspect of the app. However, as you see below, there are a few other things it does as well. Most importantly at this point, it instantiates the ‘App Controller’ and takes it in using the named parameter, con.

It’s in the ‘App Controller’ also named, BazaarApp, where you’ll first see references to the two library packages, auth and ads. Note, the Auth object is first initialized in the App Controller’s constructor while the Ads library is instantiated and set up in the App Controller’s initState() function. It’s all self-contained. When the app terminates, for example, these two objects are also disposed of in the App Controller’s function, dispose(). Nice.

Giving Them Time To Init

Now, what’s this init() function here in the App Controller with the Future return value of boolean? You see it above in the screenshot, and if you’ve guessed it’s checking to see if the user is already logged in, you’d be right. Now, you may not be familiar with this init() function, but it’s an important aspect of the MVC framework and can be demonstrated as such with the screenshot below. You see, it involves the Flutter’s FutureBuilder.

You can see in the Login’s State object, if the user is already logged in, we go straight to the ‘Home page’ instead of displaying the login screen.

_loginState class

Well, it takes time to determine if the user is already logged in or not. However, it has to be determined before the app can proceed. With the use of the FutureBuilder, a spinning circle is displayed in the center of the screen until that’s determined. In fact, there’s a number of operations performed at startup (initializing the Prefs library for example), the init() function is a convenient place to perform them to make the app ready and set up before continuing on.

Stop For Now

Let’s stop it here for now. I may continue with other aspects of the exercise in the future, but I feel you’ve gotten an appreciation of the process. The resulting app does as it was originally designed to do and more.

Simply put, I didn’t like the design pattern library packages offered when I first decided to move on to Flutter. I found they ‘sat on top’ of the Flutter framework and did not emulate its approach and instead ‘work with it.’ Redux, for example, was designed by Facebook when Web apps became the most popular platform. State management was a necessity to Web apps since the HTML protocol didn’t preserve the state of an app. With Flutter, we’re back to standalone executables since Flutter innately maintains their state. We’re back to desktop computers running standalone executables — but now these computers can fit in the palm of your hand. For these apps, we’re back to MVC.

Cheers.

→ Other Stories by Greg Perry

DECODE Flutter on YouTube

--

--